]> git.sur5r.net Git - cc65/blob - src/ld65/segments.c
f9154e194237ef2626cdc0f83ce9a9e9dbdd61e0
[cc65] / src / ld65 / segments.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                segments.c                                 */
4 /*                                                                           */
5 /*                   Segment handling for the ld65 linker                    */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2010, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
12 /* EMail:         uz@cc65.org                                                */
13 /*                                                                           */
14 /*                                                                           */
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.                                    */
18 /*                                                                           */
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:                            */
22 /*                                                                           */
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              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 /* common */
40 #include "check.h"
41 #include "exprdefs.h"
42 #include "fragdefs.h"
43 #include "hashstr.h"
44 #include "print.h"
45 #include "segdefs.h"
46 #include "symdefs.h"
47 #include "xmalloc.h"
48
49 /* ld65 */
50 #include "error.h"
51 #include "expr.h"
52 #include "fileio.h"
53 #include "fragment.h"
54 #include "global.h"
55 #include "lineinfo.h"
56 #include "segments.h"
57 #include "spool.h"
58
59
60
61 /*****************************************************************************/
62 /*                                   Data                                    */
63 /*****************************************************************************/
64
65
66
67 /* Hash table */
68 #define HASHTAB_MASK    0x3FU
69 #define HASHTAB_SIZE    (HASHTAB_MASK + 1)
70 static Segment*         HashTab [HASHTAB_SIZE];
71
72 static unsigned         SegCount = 0;   /* Segment count */
73 static Segment*         SegRoot = 0;    /* List of all segments */
74
75
76
77 /*****************************************************************************/
78 /*                                   Code                                    */
79 /*****************************************************************************/
80
81
82
83 static Segment* NewSegment (unsigned Name, unsigned char AddrSize)
84 /* Create a new segment and initialize it */
85 {
86     unsigned Hash;
87
88     /* Allocate memory */
89     Segment* S = xmalloc (sizeof (Segment));
90
91     /* Initialize the fields */
92     S->Name        = Name;
93     S->Next        = 0;
94     S->SecRoot     = 0;
95     S->SecLast     = 0;
96     S->PC          = 0;
97     S->Size        = 0;
98     S->AlignObj    = 0;
99     S->OutputName  = 0;
100     S->OutputOffs  = 0;
101     S->Align       = 0;
102     S->FillVal     = 0;
103     S->AddrSize    = AddrSize;
104     S->ReadOnly    = 0;
105     S->Relocatable = 0;
106     S->Dumped      = 0;
107
108     /* Insert the segment into the segment list and assign the segment id */
109     if (SegRoot == 0) {
110         S->Id = 0;
111     } else {
112         S->Id = SegRoot->Id + 1;
113     }
114     S->List = SegRoot;
115     SegRoot = S;
116     ++SegCount;
117
118     /* Insert the segment into the segment hash list */
119     Hash = (S->Name & HASHTAB_MASK);
120     S->Next = HashTab[Hash];
121     HashTab[Hash] = S;
122
123     /* Return the new entry */
124     return S;
125 }
126
127
128
129 Segment* GetSegment (unsigned Name, unsigned char AddrSize, const char* ObjName)
130 /* Search for a segment and return an existing one. If the segment does not
131  * exist, create a new one and return that. ObjName is only used for the error
132  * message and may be NULL if the segment is linker generated.
133  */
134 {
135     /* Try to locate the segment in the table */
136     Segment* S = SegFind (Name);
137
138     /* If we don't have that segment already, allocate it using the type of
139      * the first section.
140      */
141     if (S == 0) {
142         /* Create a new segment */
143         S = NewSegment (Name, AddrSize);
144     } else {
145         /* Check if the existing segment has the requested address size */
146         if (S->AddrSize != AddrSize) {
147             /* Allow an empty object name */
148             if (ObjName == 0) {
149                 ObjName = "[linker generated]";
150             }
151             Error ("Module `%s': Type mismatch for segment `%s'", ObjName,
152                    GetString (Name));
153         }
154     }
155
156     /* Return the segment */
157     return S;
158 }
159
160
161
162 Section* NewSection (Segment* Seg, unsigned char Align, unsigned char AddrSize)
163 /* Create a new section for the given segment */
164 {
165     unsigned long V;
166
167
168     /* Allocate memory */
169     Section* S = xmalloc (sizeof (Section));
170
171     /* Initialize the data */
172     S->Next     = 0;
173     S->Seg      = Seg;
174     S->FragRoot = 0;
175     S->FragLast = 0;
176     S->Size     = 0;
177     S->Align    = Align;
178     S->AddrSize = AddrSize;
179
180     /* Calculate the alignment bytes needed for the section */
181     V = (0x01UL << S->Align) - 1;
182     S->Fill = (unsigned char) (((Seg->Size + V) & ~V) - Seg->Size);
183
184     /* Adjust the segment size and set the section offset */
185     Seg->Size  += S->Fill;
186     S->Offs     = Seg->Size;    /* Current size is offset */
187
188     /* Insert the section into the segment */
189     if (Seg->SecRoot == 0) {
190         /* First section in this segment */
191         Seg->SecRoot = S;
192     } else {
193         Seg->SecLast->Next = S;
194     }
195     Seg->SecLast = S;
196
197     /* Return the struct */
198     return S;
199 }
200
201
202
203 Section* ReadSection (FILE* F, ObjData* O)
204 /* Read a section from a file */
205 {
206     unsigned      Name;
207     unsigned      Size;
208     unsigned char Align;
209     unsigned char Type;
210     unsigned      FragCount;
211     Segment*      S;
212     Section*      Sec;
213     LineInfo*     LI;
214
215     /* Read the segment data */
216     (void) Read32 (F);            /* File size of data */
217     Name      = MakeGlobalStringId (O, ReadVar (F));    /* Segment name */
218     Size      = Read32 (F);       /* Size of data */
219     Align     = Read8 (F);        /* Alignment */
220     Type      = Read8 (F);        /* Segment type */
221     FragCount = ReadVar (F);      /* Number of fragments */
222
223
224     /* Print some data */
225     Print (stdout, 2, "Module `%s': Found segment `%s', size = %u, align = %u, type = %u\n",
226            GetObjFileName (O), GetString (Name), Size, Align, Type);
227
228     /* Get the segment for this section */
229     S = GetSegment (Name, Type, GetObjFileName (O));
230
231     /* Allocate the section we will return later */
232     Sec = NewSection (S, Align, Type);
233
234     /* Set up the minimum segment alignment */
235     if (Sec->Align > S->Align) {
236         /* Section needs larger alignment, use this one */
237         S->Align    = Sec->Align;
238         S->AlignObj = O;
239     }
240
241     /* Start reading fragments from the file and insert them into the section . */
242     LI = 0;
243     while (FragCount--) {
244
245         Fragment* Frag;
246         FilePos   Pos;
247         unsigned  LineInfoIndex;
248
249         /* Read the fragment type */
250         unsigned char Type = Read8 (F);
251
252         /* Extract the check mask from the type */
253         unsigned char Bytes = Type & FRAG_BYTEMASK;
254         Type &= FRAG_TYPEMASK;
255
256         /* Handle the different fragment types */
257         switch (Type) {
258
259             case FRAG_LITERAL:
260                 Frag = NewFragment (Type, ReadVar (F), Sec);
261                 ReadData (F, Frag->LitBuf, Frag->Size);
262                 break;
263
264             case FRAG_EXPR:
265             case FRAG_SEXPR:
266                 Frag = NewFragment (Type, Bytes, Sec);
267                 Frag->Expr = ReadExpr (F, O);
268                 break;
269
270             case FRAG_FILL:
271                 /* Will allocate memory, but we don't care... */
272                 Frag = NewFragment (Type, ReadVar (F), Sec);
273                 break;
274
275             default:
276                 Error ("Unknown fragment type in module `%s', segment `%s': %02X",
277                        GetObjFileName (O), GetString (S->Name), Type);
278                 /* NOTREACHED */
279                 return 0;
280         }
281
282         /* Read the file position of the fragment */
283         ReadFilePos (F, &Pos);
284
285         /* Generate a LineInfo for this fragment. First check if this fragment
286          * was generated by the same line than that before. If not, generate
287          * a new LineInfo.
288          */
289         if (LI == 0 || LI->Pos.Line != Pos.Line || LI->Pos.Col != Pos.Col ||
290             LI->Pos.Name != Pos.Name) {
291             /* We don't have a previous line info or this one is different */
292             LI = NewLineInfo (O, &Pos);
293             CollAppend (&O->LineInfos, LI);
294         }
295         AddLineInfo (Frag, LI);
296
297         /* Read additional line info and resolve it */
298         LineInfoIndex = ReadVar (F);
299         if (LineInfoIndex) {
300             --LineInfoIndex;
301             /* The line info index was written by the assembler and must
302              * therefore be part of the line infos read from the object file.
303              * To make sure this is true, don't compare against the count
304              * of line infos in the collection (which grows) but against the
305              * count initialized when reading from the file.
306              */
307             if (LineInfoIndex >= O->LineInfoCount) {
308                 Internal ("In module `%s', file `%s', line %lu: Invalid line "
309                           "info with index %u (max count %u)",
310                           GetObjFileName (O),
311                           GetFragmentSourceName (Frag),
312                           GetFragmentSourceLine (Frag),
313                           LineInfoIndex,
314                           O->LineInfoCount);
315             }
316             /* Add line info to the fragment */
317             AddLineInfo (Frag, CollAt (&O->LineInfos, LineInfoIndex));
318         }
319
320         /* Remember the module we had this fragment from */
321         Frag->Obj = O;
322     }
323
324     /* Return the section */
325     return Sec;
326 }
327
328
329
330 Segment* SegFind (unsigned Name)
331 /* Return the given segment or NULL if not found. */
332 {
333     Segment* S = HashTab[Name & HASHTAB_MASK];
334     while (S) {
335         if (Name == S->Name) {
336             /* Found */
337             break;
338         }
339         S = S->Next;
340     }
341     /* Not found */
342     return S;
343 }
344
345
346
347 int IsBSSType (Segment* S)
348 /* Check if the given segment is a BSS style segment, that is, it does not
349  * contain non-zero data.
350  */
351 {
352     /* Loop over all sections */
353     Section* Sec = S->SecRoot;
354     while (Sec) {
355         /* Loop over all fragments */
356         Fragment* F = Sec->FragRoot;
357         while (F) {
358             if (F->Type == FRAG_LITERAL) {
359                 unsigned char* Data = F->LitBuf;
360                 unsigned long Count = F->Size;
361                 while (Count--) {
362                     if (*Data++ != 0) {
363                         return 0;
364                     }
365                 }
366             } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
367                 if (GetExprVal (F->Expr) != 0) {
368                     return 0;
369                 }
370             }
371             F = F->Next;
372         }
373         Sec = Sec->Next;
374     }
375     return 1;
376 }
377
378
379
380 void SegDump (void)
381 /* Dump the segments and it's contents */
382 {
383     unsigned I;
384     unsigned long Count;
385     unsigned char* Data;
386
387     Segment* Seg = SegRoot;
388     while (Seg) {
389         Section* S = Seg->SecRoot;
390         printf ("Segment: %s (%lu)\n", GetString (Seg->Name), Seg->Size);
391         while (S) {
392             Fragment* F = S->FragRoot;
393             printf ("  Section:\n");
394             while (F) {
395                 switch (F->Type) {
396
397                     case FRAG_LITERAL:
398                         printf ("    Literal (%u bytes):", F->Size);
399                         Count = F->Size;
400                         Data  = F->LitBuf;
401                         I = 100;
402                         while (Count--) {
403                             if (I > 75) {
404                                 printf ("\n   ");
405                                 I = 3;
406                             }
407                             printf (" %02X", *Data++);
408                             I += 3;
409                         }
410                         printf ("\n");
411                         break;
412
413                     case FRAG_EXPR:
414                         printf ("    Expression (%u bytes):\n", F->Size);
415                         printf ("    ");
416                         DumpExpr (F->Expr, 0);
417                         break;
418
419                     case FRAG_SEXPR:
420                         printf ("    Signed expression (%u bytes):\n", F->Size);
421                         printf ("      ");
422                         DumpExpr (F->Expr, 0);
423                         break;
424
425                     case FRAG_FILL:
426                         printf ("    Empty space (%u bytes)\n", F->Size);
427                         break;
428
429                     default:
430                         Internal ("Invalid fragment type: %02X", F->Type);
431                 }
432                 F = F->Next;
433             }
434             S = S->Next;
435         }
436         Seg = Seg->List;
437     }
438 }
439
440
441
442 unsigned SegWriteConstExpr (FILE* F, ExprNode* E, int Signed, unsigned Size)
443 /* Write a supposedly constant expression to the target file. Do a range
444  * check and return one of the SEG_EXPR_xxx codes.
445  */
446 {
447     static const unsigned long U_HighRange [4] = {
448         0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF
449     };
450     static const long S_HighRange [4] = {
451         0x0000007F, 0x00007FFF, 0x007FFFFF, 0x7FFFFFFF
452     };
453     static const long S_LowRange [4] = {
454         0xFFFFFF80, 0xFFFF8000, 0xFF800000, 0x80000000
455     };
456
457
458     /* Get the expression value */
459     long Val = GetExprVal (E);
460
461     /* Check the size */
462     CHECK (Size >= 1 && Size <= 4);
463
464     /* Check for a range error */
465     if (Signed) {
466         if (Val > S_HighRange [Size-1] || Val < S_LowRange [Size-1]) {
467             /* Range error */
468             return SEG_EXPR_RANGE_ERROR;
469         }
470     } else {
471         if (((unsigned long)Val) > U_HighRange [Size-1]) {
472             /* Range error */
473             return SEG_EXPR_RANGE_ERROR;
474         }
475     }
476
477     /* Write the value to the file */
478     WriteVal (F, Val, Size);
479
480     /* Success */
481     return SEG_EXPR_OK;
482 }
483
484
485
486 void SegWrite (const char* TgtName, FILE* Tgt, Segment* S, SegWriteFunc F, void* Data)
487 /* Write the data from the given segment to a file. For expressions, F is
488  * called (see description of SegWriteFunc above).
489  */
490 {   
491     Section*      Sec;
492     int           Sign;
493     unsigned long Offs = 0;
494
495
496     /* Remember the output file and offset for the segment */
497     S->OutputName = TgtName;
498     S->OutputOffs = (unsigned long) ftell (Tgt);
499
500     /* Loop over all sections in this segment */
501     Sec = S->SecRoot;
502     while (Sec) {
503         Fragment* Frag;
504
505         /* If we have fill bytes, write them now */
506         WriteMult (Tgt, S->FillVal, Sec->Fill);
507         Offs += Sec->Fill;
508
509         /* Loop over all fragments in this section */
510         Frag = Sec->FragRoot;
511         while (Frag) {
512
513             /* Do fragment alignment checks */
514
515
516
517             /* Output fragment data */
518             switch (Frag->Type) {
519
520                 case FRAG_LITERAL:
521                     WriteData (Tgt, Frag->LitBuf, Frag->Size);
522                     break;
523
524                 case FRAG_EXPR:
525                 case FRAG_SEXPR:
526                     Sign = (Frag->Type == FRAG_SEXPR);
527                     /* Call the users function and evaluate the result */
528                     switch (F (Frag->Expr, Sign, Frag->Size, Offs, Data)) {
529
530                         case SEG_EXPR_OK:
531                             break;
532
533                         case SEG_EXPR_RANGE_ERROR:
534                             Error ("Range error in module `%s', line %lu",
535                                    GetFragmentSourceName (Frag),
536                                    GetFragmentSourceLine (Frag));
537                             break;
538
539                         case SEG_EXPR_TOO_COMPLEX:
540                             Error ("Expression too complex in module `%s', line %lu",
541                                    GetFragmentSourceName (Frag),
542                                    GetFragmentSourceLine (Frag));
543                             break;
544
545                         case SEG_EXPR_INVALID:
546                             Error ("Invalid expression in module `%s', line %lu",
547                                    GetFragmentSourceName (Frag),
548                                    GetFragmentSourceLine (Frag));
549                             break;
550
551                         default:
552                             Internal ("Invalid return code from SegWriteFunc");
553                     }
554                     break;
555
556                 case FRAG_FILL:
557                     WriteMult (Tgt, S->FillVal, Frag->Size);
558                     break;
559
560                 default:
561                     Internal ("Invalid fragment type: %02X", Frag->Type);
562             }
563
564             /* Update the offset */
565             Offs += Frag->Size;
566
567             /* Next fragment */
568             Frag = Frag->Next;
569         }
570
571         /* Next section */
572         Sec = Sec->Next;
573     }
574 }
575
576
577
578 static int CmpSegStart (const void* K1, const void* K2)
579 /* Compare function for qsort */
580 {
581     /* Get the real segment pointers */
582     const Segment* S1 = *(const Segment**)K1;
583     const Segment* S2 = *(const Segment**)K2;
584
585     /* Compare the start addresses */
586     if (S1->PC > S2->PC) {
587         return 1;
588     } else if (S1->PC < S2->PC) {
589         return -1;
590     } else {
591         /* Sort segments with equal starts by name */
592         return strcmp (GetString (S1->Name), GetString (S2->Name));
593     }
594 }
595
596
597
598 void PrintSegmentMap (FILE* F)
599 /* Print a segment map to the given file */
600 {
601     unsigned I;
602     Segment* S;
603     Segment** SegPool;
604
605     /* Allocate memory for the segment pool */
606     SegPool = xmalloc (SegCount * sizeof (Segment*));
607
608     /* Collect pointers to the segments */
609     I = 0;
610     S = SegRoot;
611     while (S) {
612
613         /* Check the count for safety */
614         CHECK (I < SegCount);
615
616         /* Remember the pointer */
617         SegPool [I] = S;
618
619         /* Follow the linked list */
620         S = S->List;
621
622         /* Next array index */
623         ++I;
624     }
625     CHECK (I == SegCount);
626
627     /* Sort the array by increasing start addresses */
628     qsort (SegPool, SegCount, sizeof (Segment*), CmpSegStart);
629
630     /* Print a header */
631     fprintf (F, "Name                  Start   End     Size\n"
632                 "--------------------------------------------\n");
633
634     /* Print the segments */
635     for (I = 0; I < SegCount; ++I) {
636
637         /* Get a pointer to the segment */
638         S = SegPool [I];
639
640         /* Print empty segments only if explicitly requested */
641         if (VerboseMap || S->Size > 0) {
642             /* Print the segment data */
643             long End = S->PC + S->Size;
644             if (S->Size > 0) {
645                 /* Point to last element addressed */
646                 --End;
647             }
648             fprintf (F, "%-20s  %06lX  %06lX  %06lX\n",
649                      GetString (S->Name), S->PC, End, S->Size);
650         }
651     }
652
653     /* Free the segment pool */
654     xfree (SegPool);
655 }
656
657
658
659 void PrintDbgSegments (FILE* F)
660 /* Output the segments to the debug file */
661 {
662     /* Walk over all segments */
663     Segment* S = SegRoot;
664     while (S) {
665
666         /* Print the segment data */
667         fprintf (F,
668                  "segment\tid=%u,name=\"%s\",start=0x%06lX,size=0x%04lX,addrsize=%s,type=%s",
669                  S->Id, GetString (S->Name), S->PC, S->Size,
670                  AddrSizeToStr (S->AddrSize),
671                  S->ReadOnly? "ro" : "rw");
672         if (S->OutputName) {
673             fprintf (F, ",outputname=\"%s\",outputoffs=%lu",
674                      S->OutputName, S->OutputOffs);
675         }
676         fputc ('\n', F);
677
678         /* Follow the linked list */
679         S = S->List;
680     }
681 }
682
683
684
685 void CheckSegments (void)
686 /* Walk through the segment list and check if there are segments that were
687  * not written to the output file. Output an error if this is the case.
688  */
689 {
690     Segment* S = SegRoot;
691     while (S) {
692         if (S->Size > 0 && S->Dumped == 0) {
693             Error ("Missing memory area assignment for segment `%s'",
694                    GetString (S->Name));
695         }
696         S = S->List;
697     }
698 }
699
700
701