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