]> git.sur5r.net Git - cc65/blob - src/ld65/segments.c
17976e52d804514084187442744e32bd99fc623b
[cc65] / src / ld65 / segments.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                segments.c                                 */
4 /*                                                                           */
5 /*                   Segment handling for the ld65 linker                    */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2000 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
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 "hashstr.h"
43 #include "segdefs.h"
44 #include "symdefs.h"
45 #include "xmalloc.h"
46
47 /* ld65 */
48 #include "error.h"
49 #include "expr.h"
50 #include "fileio.h"
51 #include "global.h"
52 #include "segments.h"
53
54
55
56 /*****************************************************************************/
57 /*                                   Data                                    */
58 /*****************************************************************************/
59
60
61
62 /* Fragment structure */
63 typedef struct Fragment_ Fragment;
64 struct Fragment_ {
65     Fragment*           Next;           /* Next fragment in list */
66     ObjData*            Obj;            /* Source of fragment */
67     unsigned long       Size;           /* Size of data/expression */
68     ExprNode*           Expr;           /* Expression if FRAG_EXPR */
69     FilePos             Pos;            /* File position in source */
70     unsigned char       Type;           /* Type of fragment */
71     unsigned char       LitBuf [1];     /* Dynamically alloc'ed literal buffer */
72 };
73
74
75
76 /* Hash table */
77 #define HASHTAB_SIZE    253
78 static Segment*         HashTab [HASHTAB_SIZE];
79
80 static unsigned         SegCount = 0;   /* Segment count */
81 static Segment*         SegRoot = 0;    /* List of all segments */
82
83
84
85 /*****************************************************************************/
86 /*                                   Code                                    */
87 /*****************************************************************************/
88
89
90
91 static Fragment* NewFragment (unsigned char Type, unsigned long Size, Section* S)
92 /* Create a new fragment and insert it into the segment S */
93 {
94     /* Allocate memory */
95     Fragment* F = xmalloc (sizeof (Fragment) - 1 + Size);       /* Portable? */
96
97     /* Initialize the data */
98     F->Next = 0;
99     F->Obj  = 0;
100     F->Size = Size;
101     F->Expr = 0;
102     F->Type = Type;
103
104     /* Insert the code fragment into the segment */
105     if (S->FragRoot == 0) {
106         /* First fragment */
107         S->FragRoot = F;
108     } else {
109         S->FragLast->Next = F;
110     }
111     S->FragLast = F;
112     S->Size += Size;
113
114     /* Return the new fragment */
115     return F;
116 }
117
118
119
120 static Segment* NewSegment (const char* Name, unsigned char Type)
121 /* Create a new segment and initialize it */
122 {
123     /* Get the length of the symbol name */
124     unsigned Len = strlen (Name);
125
126     /* Allocate memory */
127     Segment* S = xmalloc (sizeof (Segment) + Len);
128
129     /* Initialize the fields */
130     S->Next     = 0;
131     S->SecRoot  = 0;
132     S->SecLast  = 0;
133     S->PC       = 0;
134     S->Size     = 0;
135     S->AlignObj = 0;
136     S->Align    = 0;
137     S->FillVal  = 0;
138     S->Type     = Type;
139     S->Dumped   = 0;
140     memcpy (S->Name, Name, Len);
141     S->Name [Len] = '\0';
142
143     /* Insert the segment into the segment list */
144     S->List = SegRoot;
145     SegRoot = S;
146     ++SegCount;
147
148     /* Return the new entry */
149     return S;
150 }
151
152
153
154 static Section* NewSection (Segment* Seg, unsigned char Align, unsigned char Type)
155 /* Create a new section for the given segment */
156 {
157     unsigned long V;
158
159
160     /* Allocate memory */
161     Section* S = xmalloc (sizeof (Segment));
162
163     /* Initialize the data */
164     S->Next     = 0;
165     S->Seg      = Seg;
166     S->FragRoot = 0;
167     S->FragLast = 0;
168     S->Size     = 0;
169     S->Align    = Align;
170     S->Type     = Type;
171
172     /* Calculate the alignment bytes needed for the section */
173     V = (0x01UL << S->Align) - 1;
174     S->Fill = (unsigned char) (((Seg->Size + V) & ~V) - Seg->Size);
175
176     /* Adjust the segment size and set the section offset */
177     Seg->Size  += S->Fill;
178     S->Offs     = Seg->Size;    /* Current size is offset */
179
180     /* Insert the section into the segment */
181     if (Seg->SecRoot == 0) {
182         /* First section in this segment */
183         Seg->SecRoot = S;
184     } else {
185         Seg->SecLast->Next = S;
186     }
187     Seg->SecLast = S;
188
189     /* Return the struct */
190     return S;
191 }
192
193
194
195 static Segment* SegFindInternal (const char* Name, unsigned HashVal)
196 /* Try to find the segment with the given name, return a pointer to the
197  * segment structure, or 0 if not found.
198  */
199 {
200     Segment* S = HashTab [HashVal];
201     while (S) {
202         if (strcmp (Name, S->Name) == 0) {
203             /* Found */
204             break;
205         }
206         S = S->Next;
207     }
208     /* Not found */
209     return S;
210 }
211
212
213
214 Section* ReadSection (FILE* F, ObjData* O)
215 /* Read a section from a file */
216 {
217     unsigned HashVal;
218     char Name [256];
219     unsigned long Size;
220     unsigned char Align;
221     unsigned char Type;
222     Segment* S;
223     Section* Sec;
224
225     /* Read the name */
226     ReadStr (F, Name);
227
228     /* Read the size */
229     Size = Read32 (F);
230
231     /* Read the alignment */
232     Align = Read8 (F);
233
234     /* Read the segment type */
235     Type = Read8 (F);
236
237     /* Print some data */
238     if (Verbose > 1) {
239         printf ("Module `%s': Found segment `%s', size = %lu, align = %u, type = %u\n",
240                 O->Name, Name, Size, Align, Type);
241     }
242
243     /* Create a hash over the name and try to locate the segment in the table */
244     HashVal = HashStr (Name) % HASHTAB_SIZE;
245     S = SegFindInternal (Name, HashVal);
246
247     /* If we don't have that segment already, allocate it using the type of
248      * the first section.
249      */
250     if (S == 0) {
251         /* Create a new segment and insert it */
252         S = NewSegment (Name, Type);
253         S->Next = HashTab [HashVal];
254         HashTab [HashVal] = S;
255     }
256
257     /* Allocate the section we will return later */
258     Sec = NewSection (S, Align, Type);
259
260     /* Check if the section has the same type as the segment */
261     if (Sec->Type != S->Type) {
262         /* OOPS */
263         Error ("Module `%s': Type mismatch for segment `%s'", O->Name, S->Name);
264     }
265
266     /* Set up the minimum segment alignment */
267     if (Sec->Align > S->Align) {
268         /* Section needs larger alignment, use this one */
269         S->Align    = Sec->Align;
270         S->AlignObj = O;
271     }
272
273     /* Start reading fragments from the file and insert them into the section . */
274     while (Size) {
275
276         Fragment* Frag;
277
278         /* Read the fragment type */
279         unsigned char Type = Read8 (F);
280
281         /* Handle the different fragment types */
282         switch (Type) {
283
284             case FRAG_LITERAL8:
285                 Frag = NewFragment (FRAG_LITERAL, Read8 (F), Sec);
286                 break;
287
288             case FRAG_LITERAL16:
289                 Frag = NewFragment (FRAG_LITERAL, Read16 (F), Sec);
290                 break;
291
292             case FRAG_LITERAL24:
293                 Frag = NewFragment (FRAG_LITERAL, Read24 (F), Sec);
294                 break;
295
296             case FRAG_LITERAL32:
297                 Frag = NewFragment (FRAG_LITERAL, Read32 (F), Sec);
298                 break;
299
300             case FRAG_EXPR8:
301             case FRAG_EXPR16:
302             case FRAG_EXPR24:
303             case FRAG_EXPR32:
304             case FRAG_SEXPR8:
305             case FRAG_SEXPR16:
306             case FRAG_SEXPR24:
307             case FRAG_SEXPR32:
308                 Frag = NewFragment (Type & FRAG_TYPEMASK, Type & FRAG_BYTEMASK, Sec);
309                 break;
310
311             case FRAG_FILL:
312                 /* Will allocate memory, but we don't care... */
313                 Frag = NewFragment (FRAG_FILL, Read16 (F), Sec);
314                 break;
315
316             default:
317                 Error ("Unknown fragment type in module `%s', segment `%s': %02X",
318                        O->Name, S->Name, Type);
319                 /* NOTREACHED */
320                 return 0;
321         }
322
323         /* Now read the fragment data */
324         switch (Frag->Type) {
325
326             case FRAG_LITERAL:
327                 /* Literal data */
328                 ReadData (F, Frag->LitBuf, Frag->Size);
329                 break;
330
331             case FRAG_EXPR:
332             case FRAG_SEXPR:
333                 /* An expression */
334                 Frag->Expr = ReadExpr (F, O);
335                 break;
336
337         }
338
339         /* Read the file position of the fragment */
340         ReadFilePos (F, &Frag->Pos);
341
342         /* Remember the module we had this fragment from */
343         Frag->Obj = O;
344
345         /* Next one */
346         CHECK (Size >= Frag->Size);
347         Size -= Frag->Size;
348     }
349
350     /* Increment the segment size by the section size */
351     S->Size += Sec->Size;
352
353     /* Return the section */
354     return Sec;
355 }
356
357
358
359 Segment* SegFind (const char* Name)
360 /* Return the given segment or NULL if not found. */
361 {
362     return SegFindInternal (Name, HashStr (Name) % HASHTAB_SIZE);
363 }
364
365
366
367 int IsBSSType (Segment* S)
368 /* Check if the given segment is a BSS style segment, that is, it does not
369  * contain non-zero data.
370  */
371 {
372     /* Loop over all sections */
373     Section* Sec = S->SecRoot;
374     while (Sec) {
375         /* Loop over all fragments */
376         Fragment* F = Sec->FragRoot;
377         while (F) {
378             if (F->Type == FRAG_LITERAL) {
379                 unsigned char* Data = F->LitBuf;
380                 unsigned long Count = F->Size;
381                 while (Count--) {
382                     if (*Data++ != 0) {
383                         return 0;
384                     }
385                 }
386             } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
387                 if (GetExprVal (F->Expr) != 0) {
388                     return 0;
389                 }
390             }
391             F = F->Next;
392         }
393         Sec = Sec->Next;
394     }
395     return 1;
396 }
397
398
399
400 void SegDump (void)
401 /* Dump the segments and it's contents */
402 {
403     unsigned I;
404     unsigned long Count;
405     unsigned char* Data;
406
407     Segment* Seg = SegRoot;
408     while (Seg) {
409         Section* S = Seg->SecRoot;
410         printf ("Segment: %s (%lu)\n", Seg->Name, Seg->Size);
411         while (S) {
412             Fragment* F = S->FragRoot;
413             printf ("  Section:\n");
414             while (F) {
415                 switch (F->Type) {
416
417                     case FRAG_LITERAL:
418                         printf ("    Literal (%lu bytes):", F->Size);
419                         Count = F->Size;
420                         Data  = F->LitBuf;
421                         I = 100;
422                         while (Count--) {
423                             if (I > 75) {
424                                 printf ("\n   ");
425                                 I = 3;
426                             }
427                             printf (" %02X", *Data++);
428                             I += 3;
429                         }
430                         printf ("\n");
431                         break;
432
433                     case FRAG_EXPR:
434                         printf ("    Expression (%lu bytes):\n", F->Size);
435                         printf ("    ");
436                         DumpExpr (F->Expr);
437                         break;
438
439                     case FRAG_SEXPR:
440                         printf ("    Signed expression (%lu bytes):\n", F->Size);
441                         printf ("      ");
442                         DumpExpr (F->Expr);
443                         break;
444
445                     case FRAG_FILL:
446                         printf ("    Empty space (%lu bytes)\n", F->Size);
447                         break;
448
449                     default:
450                         Internal ("Invalid fragment type: %02X", F->Type);
451                 }
452                 F = F->Next;
453             }
454             S = S->Next;
455         }
456         Seg = Seg->List;
457     }
458 }
459
460
461
462 unsigned SegWriteConstExpr (FILE* F, ExprNode* E, int Signed, unsigned Size)
463 /* Write a supposedly constant expression to the target file. Do a range
464  * check and return one of the SEG_EXPR_xxx codes.
465  */
466 {
467     static const unsigned long U_HighRange [4] = {
468         0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF
469     };
470     static const long S_HighRange [4] = {
471         0x0000007F, 0x00007FFF, 0x007FFFFF, 0x7FFFFFFF
472     };
473     static const long S_LowRange [4] = {
474         0xFFFFFF80, 0xFFFF8000, 0xFF800000, 0x80000000
475     };
476
477
478     /* Get the expression value */
479     long Val = GetExprVal (E);
480
481     /* Check the size */
482     CHECK (Size >= 1 && Size <= 4);
483
484     /* Check for a range error */
485     if (Signed) {
486         if (Val > S_HighRange [Size-1] || Val < S_LowRange [Size-1]) {
487             /* Range error */
488             return SEG_EXPR_RANGE_ERROR;
489         }
490     } else {
491         if (((unsigned long)Val) > U_HighRange [Size-1]) {
492             /* Range error */
493             return SEG_EXPR_RANGE_ERROR;
494         }
495     }
496
497     /* Write the value to the file */
498     WriteVal (F, Val, Size);
499
500     /* Success */
501     return SEG_EXPR_OK;
502 }
503
504
505
506 void SegWrite (FILE* Tgt, Segment* S, SegWriteFunc F, void* Data)
507 /* Write the data from the given segment to a file. For expressions, F is
508  * called (see description of SegWriteFunc above).
509  */
510 {
511     int Sign;
512     unsigned long Offs = 0;
513
514     /* Loop over all sections in this segment */
515     Section* Sec = S->SecRoot;
516     while (Sec) {
517         Fragment* Frag;
518
519         /* If we have fill bytes, write them now */
520         WriteMult (Tgt, S->FillVal, Sec->Fill);
521
522         /* Loop over all fragments in this section */
523         Frag = Sec->FragRoot;
524         while (Frag) {
525
526             switch (Frag->Type) {
527
528                 case FRAG_LITERAL:
529                     WriteData (Tgt, Frag->LitBuf, Frag->Size);
530                     break;
531
532                 case FRAG_EXPR:
533                 case FRAG_SEXPR:
534                     Sign = (Frag->Type == FRAG_SEXPR);
535                     /* Call the users function and evaluate the result */
536                     switch (F (Frag->Expr, Sign, Frag->Size, Offs, Data)) {
537
538                         case SEG_EXPR_OK:
539                             break;
540
541                         case SEG_EXPR_RANGE_ERROR:
542                             Error ("Range error in module `%s', line %lu",
543                                    Frag->Obj->Files [Frag->Pos.Name], Frag->Pos.Line);
544                             break;
545
546                         case SEG_EXPR_TOO_COMPLEX:
547                             Error ("Expression too complex in module `%s', line %lu",
548                                    Frag->Obj->Files [Frag->Pos.Name], Frag->Pos.Line);
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 (S1->Name, 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             fprintf (F, "%-20s  %06lX  %06lX  %06lX\n",
644                      S->Name, S->PC, S->PC + S->Size, S->Size);
645         }
646     }
647
648     /* Free the segment pool */
649     xfree (SegPool);
650 }
651
652
653
654 void CheckSegments (void)
655 /* Walk through the segment list and check if there are segments that were
656  * not written to the output file. Output an error if this is the case.
657  */
658 {
659     Segment* S = SegRoot;
660     while (S) {
661         if (S->Size > 0 && S->Dumped == 0) {
662             Error ("Missing memory area assignment for segment `%s'", S->Name);
663         }
664         S = S->List;
665     }
666 }
667
668
669