1 /*****************************************************************************/
5 /* Segment handling for the ld65 linker */
9 /* (C) 1998-2010, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
61 /*****************************************************************************/
63 /*****************************************************************************/
68 #define HASHTAB_MASK 0x3FU
69 #define HASHTAB_SIZE (HASHTAB_MASK + 1)
70 static Segment* HashTab [HASHTAB_SIZE];
72 static unsigned SegCount = 0; /* Segment count */
73 static Segment* SegRoot = 0; /* List of all segments */
77 /*****************************************************************************/
79 /*****************************************************************************/
83 static Segment* NewSegment (unsigned Name, unsigned char AddrSize)
84 /* Create a new segment and initialize it */
89 Segment* S = xmalloc (sizeof (Segment));
91 /* Initialize the fields */
101 S->AddrSize = AddrSize;
106 /* Insert the segment into the segment list and assign the segment id */
110 S->Id = SegRoot->Id + 1;
116 /* Insert the segment into the segment hash list */
117 Hash = (S->Name & HASHTAB_MASK);
118 S->Next = HashTab[Hash];
121 /* Return the new entry */
127 Segment* GetSegment (unsigned Name, unsigned char AddrSize, const char* ObjName)
128 /* Search for a segment and return an existing one. If the segment does not
129 * exist, create a new one and return that. ObjName is only used for the error
130 * message and may be NULL if the segment is linker generated.
133 /* Try to locate the segment in the table */
134 Segment* S = SegFind (Name);
136 /* If we don't have that segment already, allocate it using the type of
140 /* Create a new segment */
141 S = NewSegment (Name, AddrSize);
143 /* Check if the existing segment has the requested address size */
144 if (S->AddrSize != AddrSize) {
145 /* Allow an empty object name */
147 ObjName = "[linker generated]";
149 Error ("Module `%s': Type mismatch for segment `%s'", ObjName,
154 /* Return the segment */
160 Section* NewSection (Segment* Seg, unsigned char Align, unsigned char AddrSize)
161 /* Create a new section for the given segment */
166 /* Allocate memory */
167 Section* S = xmalloc (sizeof (Section));
169 /* Initialize the data */
176 S->AddrSize = AddrSize;
178 /* Calculate the alignment bytes needed for the section */
179 V = (0x01UL << S->Align) - 1;
180 S->Fill = (unsigned char) (((Seg->Size + V) & ~V) - Seg->Size);
182 /* Adjust the segment size and set the section offset */
183 Seg->Size += S->Fill;
184 S->Offs = Seg->Size; /* Current size is offset */
186 /* Insert the section into the segment */
187 if (Seg->SecRoot == 0) {
188 /* First section in this segment */
191 Seg->SecLast->Next = S;
195 /* Return the struct */
201 Section* ReadSection (FILE* F, ObjData* O)
202 /* Read a section from a file */
213 /* Read the segment data */
214 (void) Read32 (F); /* File size of data */
215 Name = MakeGlobalStringId (O, ReadVar (F)); /* Segment name */
216 Size = Read32 (F); /* Size of data */
217 Align = Read8 (F); /* Alignment */
218 Type = Read8 (F); /* Segment type */
219 FragCount = ReadVar (F); /* Number of fragments */
222 /* Print some data */
223 Print (stdout, 2, "Module `%s': Found segment `%s', size = %u, align = %u, type = %u\n",
224 GetObjFileName (O), GetString (Name), Size, Align, Type);
226 /* Get the segment for this section */
227 S = GetSegment (Name, Type, GetObjFileName (O));
229 /* Allocate the section we will return later */
230 Sec = NewSection (S, Align, Type);
232 /* Set up the minimum segment alignment */
233 if (Sec->Align > S->Align) {
234 /* Section needs larger alignment, use this one */
235 S->Align = Sec->Align;
239 /* Start reading fragments from the file and insert them into the section . */
241 while (FragCount--) {
245 unsigned LineInfoIndex;
247 /* Read the fragment type */
248 unsigned char Type = Read8 (F);
250 /* Extract the check mask from the type */
251 unsigned char Bytes = Type & FRAG_BYTEMASK;
252 Type &= FRAG_TYPEMASK;
254 /* Handle the different fragment types */
258 Frag = NewFragment (Type, ReadVar (F), Sec);
259 ReadData (F, Frag->LitBuf, Frag->Size);
264 Frag = NewFragment (Type, Bytes, Sec);
265 Frag->Expr = ReadExpr (F, O);
269 /* Will allocate memory, but we don't care... */
270 Frag = NewFragment (Type, ReadVar (F), Sec);
274 Error ("Unknown fragment type in module `%s', segment `%s': %02X",
275 GetObjFileName (O), GetString (S->Name), Type);
280 /* Read the file position of the fragment */
281 ReadFilePos (F, &Pos);
283 /* Generate a LineInfo for this fragment. First check if this fragment
284 * was generated by the same line than that before. If not, generate
287 if (LI == 0 || LI->Pos.Line != Pos.Line || LI->Pos.Col != Pos.Col ||
288 LI->Pos.Name != Pos.Name) {
289 /* We don't have a previous line info or this one is different */
290 LI = NewLineInfo (O, &Pos);
291 CollAppend (&O->LineInfos, LI);
293 AddLineInfo (Frag, LI);
295 /* Read additional line info and resolve it */
296 LineInfoIndex = ReadVar (F);
299 /* The line info index was written by the assembler and must
300 * therefore be part of the line infos read from the object file.
301 * To make sure this is true, don't compare against the count
302 * of line infos in the collection (which grows) but against the
303 * count initialized when reading from the file.
305 if (LineInfoIndex >= O->LineInfoCount) {
306 Internal ("In module `%s', file `%s', line %lu: Invalid line "
307 "info with index %u (max count %u)",
309 GetFragmentSourceName (Frag),
310 GetFragmentSourceLine (Frag),
314 /* Add line info to the fragment */
315 AddLineInfo (Frag, CollAt (&O->LineInfos, LineInfoIndex));
318 /* Remember the module we had this fragment from */
322 /* Return the section */
328 Segment* SegFind (unsigned Name)
329 /* Return the given segment or NULL if not found. */
331 Segment* S = HashTab[Name & HASHTAB_MASK];
333 if (Name == S->Name) {
345 int IsBSSType (Segment* S)
346 /* Check if the given segment is a BSS style segment, that is, it does not
347 * contain non-zero data.
350 /* Loop over all sections */
351 Section* Sec = S->SecRoot;
353 /* Loop over all fragments */
354 Fragment* F = Sec->FragRoot;
356 if (F->Type == FRAG_LITERAL) {
357 unsigned char* Data = F->LitBuf;
358 unsigned long Count = F->Size;
364 } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
365 if (GetExprVal (F->Expr) != 0) {
379 /* Dump the segments and it's contents */
385 Segment* Seg = SegRoot;
387 Section* S = Seg->SecRoot;
388 printf ("Segment: %s (%lu)\n", GetString (Seg->Name), Seg->Size);
390 Fragment* F = S->FragRoot;
391 printf (" Section:\n");
396 printf (" Literal (%u bytes):", F->Size);
405 printf (" %02X", *Data++);
412 printf (" Expression (%u bytes):\n", F->Size);
414 DumpExpr (F->Expr, 0);
418 printf (" Signed expression (%u bytes):\n", F->Size);
420 DumpExpr (F->Expr, 0);
424 printf (" Empty space (%u bytes)\n", F->Size);
428 Internal ("Invalid fragment type: %02X", F->Type);
440 unsigned SegWriteConstExpr (FILE* F, ExprNode* E, int Signed, unsigned Size)
441 /* Write a supposedly constant expression to the target file. Do a range
442 * check and return one of the SEG_EXPR_xxx codes.
445 static const unsigned long U_HighRange [4] = {
446 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF
448 static const long S_HighRange [4] = {
449 0x0000007F, 0x00007FFF, 0x007FFFFF, 0x7FFFFFFF
451 static const long S_LowRange [4] = {
452 0xFFFFFF80, 0xFFFF8000, 0xFF800000, 0x80000000
456 /* Get the expression value */
457 long Val = GetExprVal (E);
460 CHECK (Size >= 1 && Size <= 4);
462 /* Check for a range error */
464 if (Val > S_HighRange [Size-1] || Val < S_LowRange [Size-1]) {
466 return SEG_EXPR_RANGE_ERROR;
469 if (((unsigned long)Val) > U_HighRange [Size-1]) {
471 return SEG_EXPR_RANGE_ERROR;
475 /* Write the value to the file */
476 WriteVal (F, Val, Size);
484 void SegWrite (FILE* Tgt, Segment* S, SegWriteFunc F, void* Data)
485 /* Write the data from the given segment to a file. For expressions, F is
486 * called (see description of SegWriteFunc above).
490 unsigned long Offs = 0;
492 /* Loop over all sections in this segment */
493 Section* Sec = S->SecRoot;
497 /* If we have fill bytes, write them now */
498 WriteMult (Tgt, S->FillVal, Sec->Fill);
501 /* Loop over all fragments in this section */
502 Frag = Sec->FragRoot;
505 /* Do fragment alignment checks */
509 /* Output fragment data */
510 switch (Frag->Type) {
513 WriteData (Tgt, Frag->LitBuf, Frag->Size);
518 Sign = (Frag->Type == FRAG_SEXPR);
519 /* Call the users function and evaluate the result */
520 switch (F (Frag->Expr, Sign, Frag->Size, Offs, Data)) {
525 case SEG_EXPR_RANGE_ERROR:
526 Error ("Range error in module `%s', line %lu",
527 GetFragmentSourceName (Frag),
528 GetFragmentSourceLine (Frag));
531 case SEG_EXPR_TOO_COMPLEX:
532 Error ("Expression too complex in module `%s', line %lu",
533 GetFragmentSourceName (Frag),
534 GetFragmentSourceLine (Frag));
537 case SEG_EXPR_INVALID:
538 Error ("Invalid expression in module `%s', line %lu",
539 GetFragmentSourceName (Frag),
540 GetFragmentSourceLine (Frag));
544 Internal ("Invalid return code from SegWriteFunc");
549 WriteMult (Tgt, S->FillVal, Frag->Size);
553 Internal ("Invalid fragment type: %02X", Frag->Type);
556 /* Update the offset */
570 static int CmpSegStart (const void* K1, const void* K2)
571 /* Compare function for qsort */
573 /* Get the real segment pointers */
574 const Segment* S1 = *(const Segment**)K1;
575 const Segment* S2 = *(const Segment**)K2;
577 /* Compare the start addresses */
578 if (S1->PC > S2->PC) {
580 } else if (S1->PC < S2->PC) {
583 /* Sort segments with equal starts by name */
584 return strcmp (GetString (S1->Name), GetString (S2->Name));
590 void PrintSegmentMap (FILE* F)
591 /* Print a segment map to the given file */
597 /* Allocate memory for the segment pool */
598 SegPool = xmalloc (SegCount * sizeof (Segment*));
600 /* Collect pointers to the segments */
605 /* Check the count for safety */
606 CHECK (I < SegCount);
608 /* Remember the pointer */
611 /* Follow the linked list */
614 /* Next array index */
617 CHECK (I == SegCount);
619 /* Sort the array by increasing start addresses */
620 qsort (SegPool, SegCount, sizeof (Segment*), CmpSegStart);
623 fprintf (F, "Name Start End Size\n"
624 "--------------------------------------------\n");
626 /* Print the segments */
627 for (I = 0; I < SegCount; ++I) {
629 /* Get a pointer to the segment */
632 /* Print empty segments only if explicitly requested */
633 if (VerboseMap || S->Size > 0) {
634 /* Print the segment data */
635 long End = S->PC + S->Size;
637 /* Point to last element addressed */
640 fprintf (F, "%-20s %06lX %06lX %06lX\n",
641 GetString (S->Name), S->PC, End, S->Size);
645 /* Free the segment pool */
651 void PrintDbgSegments (FILE* F)
652 /* Output the segments to the debug file */
656 /* Walk over all segments */
660 /* Ignore empty segments */
663 /* Print the segment data */
665 "segment\tid=%u,name=\"%s\",start=0x%06lX,size=0x%04lX,addrsize=%s,type=%s\n",
666 S->Id, GetString (S->Name), S->PC, S->Size,
667 AddrSizeToStr (S->AddrSize),
668 S->ReadOnly? "ro" : "rw");
671 /* Follow the linked list */
678 void CheckSegments (void)
679 /* Walk through the segment list and check if there are segments that were
680 * not written to the output file. Output an error if this is the case.
683 Segment* S = SegRoot;
685 if (S->Size > 0 && S->Dumped == 0) {
686 Error ("Missing memory area assignment for segment `%s'",
687 GetString (S->Name));