1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* exports.c */ |
4 | /* */ |
5 | /* Exports handling for the ld65 linker */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2013, 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 <stdio.h> |
37 | #include <stdlib.h> |
38 | #include <string.h> |
39 | |
40 | /* common */ |
41 | #include "addrsize.h" |
42 | #include "check.h" |
43 | #include "hashfunc.h" |
44 | #include "lidefs.h" |
45 | #include "symdefs.h" |
46 | #include "xmalloc.h" |
47 | |
48 | /* ld65 */ |
49 | #include "condes.h" |
50 | #include "error.h" |
51 | #include "exports.h" |
52 | #include "expr.h" |
53 | #include "fileio.h" |
54 | #include "global.h" |
55 | #include "lineinfo.h" |
56 | #include "memarea.h" |
57 | #include "objdata.h" |
58 | #include "spool.h" |
59 | |
60 | |
61 | |
62 | /*****************************************************************************/ |
63 | /* Data */ |
64 | /*****************************************************************************/ |
65 | |
66 | |
67 | |
68 | /* Hash table */ |
69 | #define HASHTAB_MASK 0x0FFFU |
70 | #define HASHTAB_SIZE (HASHTAB_MASK + 1) |
71 | static Export* HashTab[HASHTAB_SIZE]; |
72 | |
73 | /* Import management variables */ |
74 | static unsigned ImpCount = 0; /* Import count */ |
75 | static unsigned ImpOpen = 0; /* Count of open imports */ |
76 | |
77 | /* Export management variables */ |
78 | static unsigned ExpCount = 0; /* Export count */ |
79 | static Export** ExpPool = 0; /* Exports array */ |
80 | |
81 | /* Defines for the flags in Import */ |
82 | #define IMP_INLIST 0x0001U /* Import is in exports list */ |
83 | |
84 | /* Defines for the flags in Export */ |
85 | #define EXP_INLIST 0x0001U /* Export is in exports list */ |
86 | #define EXP_USERMARK 0x0002U /* User setable flag */ |
87 | |
88 | |
89 | |
90 | /*****************************************************************************/ |
91 | /* Import handling */ |
92 | /*****************************************************************************/ |
93 | |
94 | |
95 | |
96 | static Export* NewExport (unsigned Type, unsigned char AddrSize, |
97 | unsigned Name, ObjData* Obj); |
98 | /* Create a new export and initialize it */ |
99 | |
100 | |
101 | |
102 | static Import* NewImport (unsigned char AddrSize, ObjData* Obj) |
103 | /* Create a new import and initialize it */ |
104 | { |
105 | /* Allocate memory */ |
106 | Import* I = xmalloc (sizeof (Import)); |
107 | |
108 | /* Initialize the fields */ |
109 | I->Next = 0; |
110 | I->Obj = Obj; |
111 | I->DefLines = EmptyCollection; |
112 | I->RefLines = EmptyCollection; |
113 | I->Exp = 0; |
114 | I->Name = INVALID_STRING_ID; |
115 | I->Flags = 0; |
116 | I->AddrSize = AddrSize; |
117 | |
118 | /* Return the new structure */ |
119 | return I; |
120 | } |
121 | |
122 | |
123 | |
124 | void FreeImport (Import* I) |
125 | /* Free an import. NOTE: This won't remove the import from the exports table, |
126 | ** so it may only be called for unused imports (imports from modules that |
127 | ** aren't referenced). |
128 | */ |
129 | { |
130 | /* Safety */ |
131 | PRECONDITION ((I->Flags & IMP_INLIST) == 0); |
132 | |
133 | /* Free the line info collections */ |
134 | DoneCollection (&I->DefLines); |
135 | DoneCollection (&I->RefLines); |
136 | |
137 | /* Free the struct */ |
138 | xfree (I); |
139 | } |
140 | |
141 | |
142 | |
143 | Import* ReadImport (FILE* F, ObjData* Obj) |
144 | /* Read an import from a file and return it */ |
145 | { |
146 | Import* I; |
147 | |
148 | /* Read the import address size */ |
149 | unsigned char AddrSize = Read8 (F); |
150 | |
151 | /* Create a new import */ |
152 | I = NewImport (AddrSize, Obj); |
153 | |
154 | /* Read the name */ |
155 | I->Name = MakeGlobalStringId (Obj, ReadVar (F)); |
156 | |
157 | /* Read the line infos */ |
158 | ReadLineInfoList (F, Obj, &I->DefLines); |
159 | ReadLineInfoList (F, Obj, &I->RefLines); |
160 | |
161 | /* Check the address size */ |
162 | if (I->AddrSize == ADDR_SIZE_DEFAULT || I->AddrSize > ADDR_SIZE_LONG) { |
163 | /* Beware: This function may be called in cases where the object file |
164 | ** is not read completely into memory. In this case, the file list is |
165 | ** invalid. Be sure not to access it in this case. |
166 | */ |
167 | if (ObjHasFiles (I->Obj)) { |
168 | const LineInfo* LI = GetImportPos (I); |
169 | Error ("Invalid import size in for '%s', imported from %s(%u): 0x%02X" , |
170 | GetString (I->Name), |
171 | GetSourceName (LI), |
172 | GetSourceLine (LI), |
173 | I->AddrSize); |
174 | } else { |
175 | Error ("Invalid import size in for '%s', imported from %s: 0x%02X" , |
176 | GetString (I->Name), |
177 | GetObjFileName (I->Obj), |
178 | I->AddrSize); |
179 | } |
180 | } |
181 | |
182 | /* Return the new import */ |
183 | return I; |
184 | } |
185 | |
186 | |
187 | |
188 | Import* GenImport (unsigned Name, unsigned char AddrSize) |
189 | /* Generate a new import with the given name and address size and return it */ |
190 | { |
191 | /* Create a new import */ |
192 | Import* I = NewImport (AddrSize, 0); |
193 | |
194 | /* Read the name */ |
195 | I->Name = Name; |
196 | |
197 | /* Check the address size */ |
198 | if (I->AddrSize == ADDR_SIZE_DEFAULT || I->AddrSize > ADDR_SIZE_LONG) { |
199 | /* We have no object file information and no line info for a new |
200 | ** import |
201 | */ |
202 | Error ("Invalid import size 0x%02X for symbol '%s'" , |
203 | I->AddrSize, |
204 | GetString (I->Name)); |
205 | } |
206 | |
207 | /* Return the new import */ |
208 | return I; |
209 | } |
210 | |
211 | |
212 | |
213 | Import* InsertImport (Import* I) |
214 | /* Insert an import into the table, return I */ |
215 | { |
216 | Export* E; |
217 | |
218 | /* As long as the import is not inserted, V.Name is valid */ |
219 | unsigned Name = I->Name; |
220 | |
221 | /* Create a hash value for the given name */ |
222 | unsigned Hash = (Name & HASHTAB_MASK); |
223 | |
224 | /* Search through the list in that slot for a symbol with that name */ |
225 | if (HashTab[Hash] == 0) { |
226 | /* The slot is empty, we need to insert a dummy export */ |
227 | E = HashTab[Hash] = NewExport (0, ADDR_SIZE_DEFAULT, Name, 0); |
228 | ++ExpCount; |
229 | } else { |
230 | E = HashTab [Hash]; |
231 | while (1) { |
232 | if (E->Name == Name) { |
233 | /* We have an entry, L points to it */ |
234 | break; |
235 | } |
236 | if (E->Next == 0) { |
237 | /* End of list an entry not found, insert a dummy */ |
238 | E->Next = NewExport (0, ADDR_SIZE_DEFAULT, Name, 0); |
239 | E = E->Next; /* Point to dummy */ |
240 | ++ExpCount; /* One export more */ |
241 | break; |
242 | } else { |
243 | E = E->Next; |
244 | } |
245 | } |
246 | } |
247 | |
248 | /* Ok, E now points to a valid exports entry for the given import. Insert |
249 | ** the import into the imports list and update the counters. |
250 | */ |
251 | I->Exp = E; |
252 | I->Next = E->ImpList; |
253 | E->ImpList = I; |
254 | E->ImpCount++; |
255 | ++ImpCount; /* Total import count */ |
256 | if (E->Expr == 0) { |
257 | /* This is a dummy export */ |
258 | ++ImpOpen; |
259 | } |
260 | |
261 | /* Mark the import so we know it's in the list */ |
262 | I->Flags |= IMP_INLIST; |
263 | |
264 | /* Return the import to allow shorter code */ |
265 | return I; |
266 | } |
267 | |
268 | |
269 | |
270 | const LineInfo* GetImportPos (const Import* Imp) |
271 | /* Return the basic line info of an import */ |
272 | { |
273 | /* Search in DefLines, then in RefLines */ |
274 | const LineInfo* LI = GetAsmLineInfo (&Imp->DefLines); |
275 | if (LI == 0) { |
276 | LI = GetAsmLineInfo (&Imp->RefLines); |
277 | } |
278 | return LI; |
279 | } |
280 | |
281 | |
282 | |
283 | /*****************************************************************************/ |
284 | /* Code */ |
285 | /*****************************************************************************/ |
286 | |
287 | |
288 | |
289 | static Export* NewExport (unsigned Type, unsigned char AddrSize, |
290 | unsigned Name, ObjData* Obj) |
291 | /* Create a new export and initialize it */ |
292 | { |
293 | unsigned I; |
294 | |
295 | /* Allocate memory */ |
296 | Export* E = xmalloc (sizeof (Export)); |
297 | |
298 | /* Initialize the fields */ |
299 | E->Name = Name; |
300 | E->Next = 0; |
301 | E->Flags = 0; |
302 | E->Obj = Obj; |
303 | E->ImpCount = 0; |
304 | E->ImpList = 0; |
305 | E->Expr = 0; |
306 | E->Size = 0; |
307 | E->DefLines = EmptyCollection; |
308 | E->RefLines = EmptyCollection; |
309 | E->DbgSymId = ~0U; |
310 | E->Type = Type | SYM_EXPORT; |
311 | E->AddrSize = AddrSize; |
312 | for (I = 0; I < sizeof (E->ConDes) / sizeof (E->ConDes[0]); ++I) { |
313 | E->ConDes[I] = CD_PRIO_NONE; |
314 | } |
315 | |
316 | /* Return the new entry */ |
317 | return E; |
318 | } |
319 | |
320 | |
321 | |
322 | void FreeExport (Export* E) |
323 | /* Free an export. NOTE: This won't remove the export from the exports table, |
324 | ** so it may only be called for unused exports (exports from modules that |
325 | ** aren't referenced). |
326 | */ |
327 | { |
328 | /* Safety */ |
329 | PRECONDITION ((E->Flags & EXP_INLIST) == 0); |
330 | |
331 | /* Free the line infos */ |
332 | DoneCollection (&E->DefLines); |
333 | DoneCollection (&E->RefLines); |
334 | |
335 | /* Free the export expression */ |
336 | FreeExpr (E->Expr); |
337 | |
338 | /* Free the struct */ |
339 | xfree (E); |
340 | } |
341 | |
342 | |
343 | |
344 | Export* ReadExport (FILE* F, ObjData* O) |
345 | /* Read an export from a file */ |
346 | { |
347 | unsigned ConDesCount; |
348 | unsigned I; |
349 | Export* E; |
350 | |
351 | /* Read the type */ |
352 | unsigned Type = ReadVar (F); |
353 | |
354 | /* Read the address size */ |
355 | unsigned char AddrSize = Read8 (F); |
356 | |
357 | /* Create a new export without a name */ |
358 | E = NewExport (Type, AddrSize, INVALID_STRING_ID, O); |
359 | |
360 | /* Read the constructor/destructor decls if we have any */ |
361 | ConDesCount = SYM_GET_CONDES_COUNT (Type); |
362 | if (ConDesCount > 0) { |
363 | |
364 | unsigned char ConDes[CD_TYPE_COUNT]; |
365 | |
366 | /* Read the data into temp storage */ |
367 | ReadData (F, ConDes, ConDesCount); |
368 | |
369 | /* Re-order the data. In the file, each decl is encoded into a byte |
370 | ** which contains the type and the priority. In memory, we will use |
371 | ** an array of types which contain the priority. |
372 | */ |
373 | for (I = 0; I < ConDesCount; ++I) { |
374 | E->ConDes[CD_GET_TYPE (ConDes[I])] = CD_GET_PRIO (ConDes[I]); |
375 | } |
376 | } |
377 | |
378 | /* Read the name */ |
379 | E->Name = MakeGlobalStringId (O, ReadVar (F)); |
380 | |
381 | /* Read the value */ |
382 | if (SYM_IS_EXPR (Type)) { |
383 | E->Expr = ReadExpr (F, O); |
384 | } else { |
385 | E->Expr = LiteralExpr (Read32 (F), O); |
386 | } |
387 | |
388 | /* Read the size */ |
389 | if (SYM_HAS_SIZE (Type)) { |
390 | E->Size = ReadVar (F); |
391 | } |
392 | |
393 | /* Last are the locations */ |
394 | ReadLineInfoList (F, O, &E->DefLines); |
395 | ReadLineInfoList (F, O, &E->RefLines); |
396 | |
397 | /* If this symbol is exported as a condes, and the condes type declares a |
398 | ** forced import, add this import to the object module. |
399 | */ |
400 | for (I = 0; I < CD_TYPE_COUNT; ++I) { |
401 | const ConDesImport* CDI; |
402 | |
403 | if (E->ConDes[I] != CD_PRIO_NONE && (CDI = ConDesGetImport (I)) != 0) { |
404 | unsigned J; |
405 | |
406 | /* Generate a new import, and add it to the module's import list. */ |
407 | Import* Imp = GenImport (CDI->Name, CDI->AddrSize); |
408 | |
409 | Imp->Obj = O; |
410 | CollAppend (&O->Imports, Imp); |
411 | |
412 | /* Add line info for the export that is actually the condes that |
413 | ** forces the import. Then, add line info for the config. file. |
414 | ** The export's info is added first because the import pretends |
415 | ** that it came from the object module instead of the config. file. |
416 | */ |
417 | for (J = 0; J < CollCount (&E->DefLines); ++J) { |
418 | CollAppend (&Imp->RefLines, DupLineInfo (CollAt (&E->DefLines, J))); |
419 | } |
420 | CollAppend (&Imp->RefLines, GenLineInfo (&CDI->Pos)); |
421 | } |
422 | } |
423 | |
424 | /* Return the new export */ |
425 | return E; |
426 | } |
427 | |
428 | |
429 | |
430 | void InsertExport (Export* E) |
431 | /* Insert an exported identifier and check if it's already in the list */ |
432 | { |
433 | Export* L; |
434 | Export* Last; |
435 | Import* Imp; |
436 | unsigned Hash; |
437 | |
438 | /* Mark the export as inserted */ |
439 | E->Flags |= EXP_INLIST; |
440 | |
441 | /* Insert the export into any condes tables if needed */ |
442 | if (SYM_IS_CONDES (E->Type)) { |
443 | ConDesAddExport (E); |
444 | } |
445 | |
446 | /* Create a hash value for the given name */ |
447 | Hash = (E->Name & HASHTAB_MASK); |
448 | |
449 | /* Search through the list in that slot */ |
450 | if (HashTab[Hash] == 0) { |
451 | /* The slot is empty */ |
452 | HashTab[Hash] = E; |
453 | ++ExpCount; |
454 | } else { |
455 | |
456 | Last = 0; |
457 | L = HashTab[Hash]; |
458 | do { |
459 | if (L->Name == E->Name) { |
460 | /* This may be an unresolved external */ |
461 | if (L->Expr == 0) { |
462 | |
463 | /* This *is* an unresolved external. Use the actual export |
464 | ** in E instead of the dummy one in L. |
465 | */ |
466 | E->Next = L->Next; |
467 | E->ImpCount = L->ImpCount; |
468 | E->ImpList = L->ImpList; |
469 | if (Last) { |
470 | Last->Next = E; |
471 | } else { |
472 | HashTab[Hash] = E; |
473 | } |
474 | ImpOpen -= E->ImpCount; /* Decrease open imports now */ |
475 | xfree (L); |
476 | /* We must run through the import list and change the |
477 | ** export pointer now. |
478 | */ |
479 | Imp = E->ImpList; |
480 | while (Imp) { |
481 | Imp->Exp = E; |
482 | Imp = Imp->Next; |
483 | } |
484 | } else if (AllowMultDef == 0) { |
485 | /* Duplicate entry, this is fatal unless allowed by the user */ |
486 | Error ("Duplicate external identifier: '%s'" , |
487 | GetString (L->Name)); |
488 | } |
489 | return; |
490 | } |
491 | Last = L; |
492 | L = L->Next; |
493 | |
494 | } while (L); |
495 | |
496 | /* Insert export at end of queue */ |
497 | Last->Next = E; |
498 | ++ExpCount; |
499 | } |
500 | } |
501 | |
502 | |
503 | |
504 | const LineInfo* GetExportPos (const Export* E) |
505 | /* Return the basic line info of an export */ |
506 | { |
507 | /* Search in DefLines, then in RefLines */ |
508 | const LineInfo* LI = GetAsmLineInfo (&E->DefLines); |
509 | if (LI == 0) { |
510 | LI = GetAsmLineInfo (&E->RefLines); |
511 | } |
512 | return LI; |
513 | } |
514 | |
515 | |
516 | |
517 | Export* CreateConstExport (unsigned Name, long Value) |
518 | /* Create an export for a literal date */ |
519 | { |
520 | /* Create a new export */ |
521 | Export* E = NewExport (SYM_CONST|SYM_EQUATE, ADDR_SIZE_ABS, Name, 0); |
522 | |
523 | /* Assign the value */ |
524 | E->Expr = LiteralExpr (Value, 0); |
525 | |
526 | /* Insert the export */ |
527 | InsertExport (E); |
528 | |
529 | /* Return the new export */ |
530 | return E; |
531 | } |
532 | |
533 | |
534 | |
535 | Export* CreateExprExport (unsigned Name, ExprNode* Expr, unsigned char AddrSize) |
536 | /* Create an export for an expression */ |
537 | { |
538 | /* Create a new export */ |
539 | Export* E = NewExport (SYM_EXPR|SYM_EQUATE, AddrSize, Name, 0); |
540 | |
541 | /* Assign the value expression */ |
542 | E->Expr = Expr; |
543 | |
544 | /* Insert the export */ |
545 | InsertExport (E); |
546 | |
547 | /* Return the new export */ |
548 | return E; |
549 | } |
550 | |
551 | |
552 | |
553 | Export* CreateMemoryExport (unsigned Name, MemoryArea* Mem, unsigned long Offs) |
554 | /* Create an relative export for a memory area offset */ |
555 | { |
556 | /* Create a new export */ |
557 | Export* E = NewExport (SYM_EXPR | SYM_LABEL, ADDR_SIZE_ABS, Name, 0); |
558 | |
559 | /* Assign the value */ |
560 | E->Expr = MemoryExpr (Mem, Offs, 0); |
561 | |
562 | /* Insert the export */ |
563 | InsertExport (E); |
564 | |
565 | /* Return the new export */ |
566 | return E; |
567 | } |
568 | |
569 | |
570 | |
571 | Export* CreateSegmentExport (unsigned Name, Segment* Seg, unsigned long Offs) |
572 | /* Create a relative export to a segment */ |
573 | { |
574 | /* Create a new export */ |
575 | Export* E = NewExport (SYM_EXPR | SYM_LABEL, Seg->AddrSize, Name, 0); |
576 | |
577 | /* Assign the value */ |
578 | E->Expr = SegmentExpr (Seg, Offs, 0); |
579 | |
580 | /* Insert the export */ |
581 | InsertExport (E); |
582 | |
583 | /* Return the new export */ |
584 | return E; |
585 | } |
586 | |
587 | |
588 | |
589 | Export* CreateSectionExport (unsigned Name, Section* Sec, unsigned long Offs) |
590 | /* Create a relative export to a section */ |
591 | { |
592 | /* Create a new export */ |
593 | Export* E = NewExport (SYM_EXPR | SYM_LABEL, Sec->AddrSize, Name, 0); |
594 | |
595 | /* Assign the value */ |
596 | E->Expr = SectionExpr (Sec, Offs, 0); |
597 | |
598 | /* Insert the export */ |
599 | InsertExport (E); |
600 | |
601 | /* Return the new export */ |
602 | return E; |
603 | } |
604 | |
605 | |
606 | |
607 | Export* FindExport (unsigned Name) |
608 | /* Check for an identifier in the list. Return 0 if not found, otherwise |
609 | ** return a pointer to the export. |
610 | */ |
611 | { |
612 | /* Get a pointer to the list with the symbols hash value */ |
613 | Export* L = HashTab[Name & HASHTAB_MASK]; |
614 | while (L) { |
615 | /* Search through the list in that slot */ |
616 | if (L->Name == Name) { |
617 | /* Entry found */ |
618 | return L; |
619 | } |
620 | L = L->Next; |
621 | } |
622 | |
623 | /* Not found */ |
624 | return 0; |
625 | } |
626 | |
627 | |
628 | |
629 | int IsUnresolved (unsigned Name) |
630 | /* Check if this symbol is an unresolved export */ |
631 | { |
632 | /* Find the export */ |
633 | return IsUnresolvedExport (FindExport (Name)); |
634 | } |
635 | |
636 | |
637 | |
638 | int IsUnresolvedExport (const Export* E) |
639 | /* Return true if the given export is unresolved */ |
640 | { |
641 | /* Check if it's unresolved */ |
642 | return E != 0 && E->Expr == 0; |
643 | } |
644 | |
645 | |
646 | |
647 | int IsConstExport (const Export* E) |
648 | /* Return true if the expression associated with this export is const */ |
649 | { |
650 | if (E->Expr == 0) { |
651 | /* External symbols cannot be const */ |
652 | return 0; |
653 | } else { |
654 | return IsConstExpr (E->Expr); |
655 | } |
656 | } |
657 | |
658 | |
659 | |
660 | long GetExportVal (const Export* E) |
661 | /* Get the value of this export */ |
662 | { |
663 | if (E->Expr == 0) { |
664 | /* OOPS */ |
665 | Internal ("'%s' is an undefined external" , GetString (E->Name)); |
666 | } |
667 | return GetExprVal (E->Expr); |
668 | } |
669 | |
670 | |
671 | |
672 | static void CheckSymType (const Export* E) |
673 | /* Check the types for one export */ |
674 | { |
675 | /* External with matching imports */ |
676 | Import* I = E->ImpList; |
677 | while (I) { |
678 | if (E->AddrSize != I->AddrSize) { |
679 | /* Export and import address sizes do not match */ |
680 | StrBuf ExportLoc = STATIC_STRBUF_INITIALIZER; |
681 | StrBuf ImportLoc = STATIC_STRBUF_INITIALIZER; |
682 | const char* ExpAddrSize = AddrSizeToStr ((unsigned char) E->AddrSize); |
683 | const char* ImpAddrSize = AddrSizeToStr ((unsigned char) I->AddrSize); |
684 | const LineInfo* ExportLI = GetExportPos (E); |
685 | const LineInfo* ImportLI = GetImportPos (I); |
686 | |
687 | /* Generate strings that describe the location of the im- and |
688 | ** exports. This depends on the place from where they come: |
689 | ** Object file or linker config. |
690 | */ |
691 | if (E->Obj) { |
692 | /* The export comes from an object file */ |
693 | SB_Printf (&ExportLoc, "%s, %s(%u)" , |
694 | GetString (E->Obj->Name), |
695 | GetSourceName (ExportLI), |
696 | GetSourceLine (ExportLI)); |
697 | } else { |
698 | SB_Printf (&ExportLoc, "%s(%u)" , |
699 | GetSourceName (ExportLI), |
700 | GetSourceLine (ExportLI)); |
701 | } |
702 | if (I->Obj) { |
703 | /* The import comes from an object file */ |
704 | SB_Printf (&ImportLoc, "%s, %s(%u)" , |
705 | GetString (I->Obj->Name), |
706 | GetSourceName (ImportLI), |
707 | GetSourceLine (ImportLI)); |
708 | } else if (ImportLI) { |
709 | /* The import is linker generated and we have line |
710 | ** information |
711 | */ |
712 | SB_Printf (&ImportLoc, "%s(%u)" , |
713 | GetSourceName (ImportLI), |
714 | GetSourceLine (ImportLI)); |
715 | } else { |
716 | /* The import is linker generated and we don't have line |
717 | ** information |
718 | */ |
719 | SB_Printf (&ImportLoc, "%s" , GetObjFileName (I->Obj)); |
720 | } |
721 | |
722 | /* Output the diagnostic */ |
723 | Warning ("Address size mismatch for '%s': " |
724 | "Exported from %s as '%s', " |
725 | "import in %s as '%s'" , |
726 | GetString (E->Name), |
727 | SB_GetConstBuf (&ExportLoc), |
728 | ExpAddrSize, |
729 | SB_GetConstBuf (&ImportLoc), |
730 | ImpAddrSize); |
731 | |
732 | /* Free the temporary strings */ |
733 | SB_Done (&ExportLoc); |
734 | SB_Done (&ImportLoc); |
735 | } |
736 | I = I->Next; |
737 | } |
738 | } |
739 | |
740 | |
741 | |
742 | static void CheckSymTypes (void) |
743 | /* Check for symbol tape mismatches */ |
744 | { |
745 | unsigned I; |
746 | |
747 | /* Print all open imports */ |
748 | for (I = 0; I < ExpCount; ++I) { |
749 | const Export* E = ExpPool [I]; |
750 | if (E->Expr != 0 && E->ImpCount > 0) { |
751 | /* External with matching imports */ |
752 | CheckSymType (E); |
753 | } |
754 | } |
755 | } |
756 | |
757 | |
758 | |
759 | static void PrintUnresolved (ExpCheckFunc F, void* Data) |
760 | /* Print a list of unresolved symbols. On unresolved symbols, F is |
761 | ** called (see the comments on ExpCheckFunc in the data section). |
762 | */ |
763 | { |
764 | unsigned I; |
765 | |
766 | /* Print all open imports */ |
767 | for (I = 0; I < ExpCount; ++I) { |
768 | Export* E = ExpPool [I]; |
769 | if (E->Expr == 0 && E->ImpCount > 0 && F (E->Name, Data) == 0) { |
770 | /* Unresolved external */ |
771 | Import* Imp = E->ImpList; |
772 | fprintf (stderr, |
773 | "Unresolved external '%s' referenced in:\n" , |
774 | GetString (E->Name)); |
775 | while (Imp) { |
776 | unsigned J; |
777 | for (J = 0; J < CollCount (&Imp->RefLines); ++J) { |
778 | const LineInfo* LI = CollConstAt (&Imp->RefLines, J); |
779 | fprintf (stderr, |
780 | " %s(%u)\n" , |
781 | GetSourceName (LI), |
782 | GetSourceLine (LI)); |
783 | } |
784 | Imp = Imp->Next; |
785 | } |
786 | } |
787 | } |
788 | } |
789 | |
790 | |
791 | |
792 | static int CmpExpName (const void* K1, const void* K2) |
793 | /* Compare function for qsort */ |
794 | { |
795 | return SB_Compare (GetStrBuf ((*(Export**)K1)->Name), |
796 | GetStrBuf ((*(Export**)K2)->Name)); |
797 | } |
798 | |
799 | |
800 | |
801 | static void CreateExportPool (void) |
802 | /* Create an array with pointer to all exports */ |
803 | { |
804 | unsigned I, J; |
805 | |
806 | /* Allocate memory */ |
807 | if (ExpPool) { |
808 | xfree (ExpPool); |
809 | } |
810 | ExpPool = xmalloc (ExpCount * sizeof (Export*)); |
811 | |
812 | /* Walk through the list and insert the exports */ |
813 | for (I = 0, J = 0; I < sizeof (HashTab) / sizeof (HashTab [0]); ++I) { |
814 | Export* E = HashTab[I]; |
815 | while (E) { |
816 | CHECK (J < ExpCount); |
817 | ExpPool[J++] = E; |
818 | E = E->Next; |
819 | } |
820 | } |
821 | |
822 | /* Sort them by name */ |
823 | qsort (ExpPool, ExpCount, sizeof (Export*), CmpExpName); |
824 | } |
825 | |
826 | |
827 | |
828 | void CheckExports (void) |
829 | /* Setup the list of all exports and check for export/import symbol type |
830 | ** mismatches. |
831 | */ |
832 | { |
833 | /* Create an export pool */ |
834 | CreateExportPool (); |
835 | |
836 | /* Check for symbol type mismatches */ |
837 | CheckSymTypes (); |
838 | } |
839 | |
840 | |
841 | |
842 | void CheckUnresolvedImports (ExpCheckFunc F, void* Data) |
843 | /* Check if there are any unresolved imports. On unresolved imports, F is |
844 | ** called (see the comments on ExpCheckFunc in the data section). |
845 | */ |
846 | { |
847 | /* Check for unresolved externals */ |
848 | if (ImpOpen != 0) { |
849 | /* Print all open imports */ |
850 | PrintUnresolved (F, Data); |
851 | } |
852 | } |
853 | |
854 | |
855 | |
856 | static char GetAddrSizeCode (unsigned char AddrSize) |
857 | /* Get a one char code for the address size */ |
858 | { |
859 | switch (AddrSize) { |
860 | case ADDR_SIZE_ZP: return 'Z'; |
861 | case ADDR_SIZE_ABS: return 'A'; |
862 | case ADDR_SIZE_FAR: return 'F'; |
863 | case ADDR_SIZE_LONG: return 'L'; |
864 | default: |
865 | Internal ("Invalid address size: %u" , AddrSize); |
866 | /* NOTREACHED */ |
867 | return '-'; |
868 | } |
869 | } |
870 | |
871 | |
872 | |
873 | void PrintExportMapByName (FILE* F) |
874 | /* Print an export map, sorted by symbol name, to the given file */ |
875 | { |
876 | unsigned I; |
877 | unsigned Count; |
878 | |
879 | /* Print all exports */ |
880 | Count = 0; |
881 | for (I = 0; I < ExpCount; ++I) { |
882 | const Export* E = ExpPool [I]; |
883 | |
884 | /* Print unreferenced symbols only if explictly requested */ |
885 | if (VerboseMap || E->ImpCount > 0 || SYM_IS_CONDES (E->Type)) { |
886 | fprintf (F, |
887 | "%-25s %06lX %c%c%c%c " , |
888 | GetString (E->Name), |
889 | GetExportVal (E), |
890 | E->ImpCount? 'R' : ' ', |
891 | SYM_IS_LABEL (E->Type)? 'L' : 'E', |
892 | GetAddrSizeCode ((unsigned char) E->AddrSize), |
893 | SYM_IS_CONDES (E->Type)? 'I' : ' '); |
894 | if (++Count == 2) { |
895 | Count = 0; |
896 | fprintf (F, "\n" ); |
897 | } |
898 | } |
899 | } |
900 | fprintf (F, "\n" ); |
901 | } |
902 | |
903 | |
904 | |
905 | static int CmpExpValue (const void* I1, const void* I2) |
906 | /* Compare function for qsort */ |
907 | { |
908 | long V1 = GetExportVal (ExpPool [*(unsigned *)I1]); |
909 | long V2 = GetExportVal (ExpPool [*(unsigned *)I2]); |
910 | |
911 | return V1 < V2 ? -1 : V1 == V2 ? 0 : 1; |
912 | } |
913 | |
914 | |
915 | |
916 | void PrintExportMapByValue (FILE* F) |
917 | /* Print an export map, sorted by symbol value, to the given file */ |
918 | { |
919 | unsigned I; |
920 | unsigned Count; |
921 | unsigned *ExpValXlat; |
922 | |
923 | /* Create a translation table where the symbols are sorted by value. */ |
924 | ExpValXlat = xmalloc (ExpCount * sizeof (unsigned)); |
925 | for (I = 0; I < ExpCount; ++I) { |
926 | /* Initialize table with current sort order. */ |
927 | ExpValXlat [I] = I; |
928 | } |
929 | |
930 | /* Sort them by value */ |
931 | qsort (ExpValXlat, ExpCount, sizeof (unsigned), CmpExpValue); |
932 | |
933 | /* Print all exports */ |
934 | Count = 0; |
935 | for (I = 0; I < ExpCount; ++I) { |
936 | const Export* E = ExpPool [ExpValXlat [I]]; |
937 | |
938 | /* Print unreferenced symbols only if explictly requested */ |
939 | if (VerboseMap || E->ImpCount > 0 || SYM_IS_CONDES (E->Type)) { |
940 | fprintf (F, |
941 | "%-25s %06lX %c%c%c%c " , |
942 | GetString (E->Name), |
943 | GetExportVal (E), |
944 | E->ImpCount? 'R' : ' ', |
945 | SYM_IS_LABEL (E->Type)? 'L' : 'E', |
946 | GetAddrSizeCode ((unsigned char) E->AddrSize), |
947 | SYM_IS_CONDES (E->Type)? 'I' : ' '); |
948 | if (++Count == 2) { |
949 | Count = 0; |
950 | fprintf (F, "\n" ); |
951 | } |
952 | } |
953 | } |
954 | fprintf (F, "\n" ); |
955 | xfree (ExpValXlat); |
956 | } |
957 | |
958 | |
959 | |
960 | void PrintImportMap (FILE* F) |
961 | /* Print an import map to the given file */ |
962 | { |
963 | unsigned I; |
964 | const Import* Imp; |
965 | |
966 | /* Loop over all exports */ |
967 | for (I = 0; I < ExpCount; ++I) { |
968 | |
969 | /* Get the export */ |
970 | const Export* Exp = ExpPool [I]; |
971 | |
972 | /* Print the symbol only if there are imports, or if a verbose map |
973 | ** file is requested. |
974 | */ |
975 | if (VerboseMap || Exp->ImpCount > 0) { |
976 | |
977 | /* Print the export */ |
978 | fprintf (F, |
979 | "%s (%s):\n" , |
980 | GetString (Exp->Name), |
981 | GetObjFileName (Exp->Obj)); |
982 | |
983 | /* Print all imports for this symbol */ |
984 | Imp = Exp->ImpList; |
985 | while (Imp) { |
986 | |
987 | /* Print the import. Beware: The import might be linker |
988 | ** generated, in which case there is no object file and |
989 | ** sometimes no line information. |
990 | */ |
991 | const LineInfo* LI = GetImportPos (Imp); |
992 | if (LI) { |
993 | fprintf (F, |
994 | " %-25s %s(%u)\n" , |
995 | GetObjFileName (Imp->Obj), |
996 | GetSourceName (LI), |
997 | GetSourceLine (LI)); |
998 | } else { |
999 | fprintf (F, |
1000 | " %-25s\n" , |
1001 | GetObjFileName (Imp->Obj)); |
1002 | } |
1003 | |
1004 | /* Next import */ |
1005 | Imp = Imp->Next; |
1006 | } |
1007 | } |
1008 | } |
1009 | fprintf (F, "\n" ); |
1010 | } |
1011 | |
1012 | |
1013 | |
1014 | void PrintExportLabels (FILE* F) |
1015 | /* Print the exports in a VICE label file */ |
1016 | { |
1017 | unsigned I; |
1018 | |
1019 | /* Print all exports */ |
1020 | for (I = 0; I < ExpCount; ++I) { |
1021 | const Export* E = ExpPool [I]; |
1022 | fprintf (F, "al %06lX .%s\n" , GetExportVal (E), GetString (E->Name)); |
1023 | } |
1024 | } |
1025 | |
1026 | |
1027 | |
1028 | void MarkExport (Export* E) |
1029 | /* Mark the export */ |
1030 | { |
1031 | E->Flags |= EXP_USERMARK; |
1032 | } |
1033 | |
1034 | |
1035 | |
1036 | void UnmarkExport (Export* E) |
1037 | /* Remove the mark from the export */ |
1038 | { |
1039 | E->Flags &= ~EXP_USERMARK; |
1040 | } |
1041 | |
1042 | |
1043 | |
1044 | int ExportHasMark (Export* E) |
1045 | /* Return true if the export has a mark */ |
1046 | { |
1047 | return (E->Flags & EXP_USERMARK) != 0; |
1048 | } |
1049 | |
1050 | |
1051 | |
1052 | void CircularRefError (const Export* E) |
1053 | /* Print an error about a circular reference using to define the given export */ |
1054 | { |
1055 | const LineInfo* LI = GetExportPos (E); |
1056 | Error ("Circular reference for symbol '%s', %s(%u)" , |
1057 | GetString (E->Name), |
1058 | GetSourceName (LI), |
1059 | GetSourceLine (LI)); |
1060 | } |
1061 | |