]> git.sur5r.net Git - cc65/blob - src/ca65/symentry.c
Fixed a problem with .global: In some situations, exported symbols went
[cc65] / src / ca65 / symentry.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                symentry.c                                 */
4 /*                                                                           */
5 /*          Symbol table entry forward for the ca65 macroassembler           */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2004 Ullrich von Bassewitz                                       */
10 /*               Römerstraße 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 <string.h>
37
38 /* common */
39 #include "addrsize.h"
40 #include "xmalloc.h"
41
42 /* ca65 */
43 #include "error.h"
44 #include "expr.h"
45 #include "global.h"
46 #include "scanner.h"
47 #include "segment.h"
48 #include "spool.h"
49 #include "studyexpr.h"          /* ### */
50 #include "symentry.h"
51 #include "symtab.h"
52
53
54
55 /*****************************************************************************/
56 /*                                   Data                                    */
57 /*****************************************************************************/
58
59
60
61 /* List of all symbol table entries */
62 SymEntry* SymList = 0;
63
64 /* Pointer to last defined symbol */
65 SymEntry* SymLast = 0;
66
67
68
69 /*****************************************************************************/
70 /*                                   Code                                    */
71 /*****************************************************************************/
72
73
74
75 SymEntry* NewSymEntry (const char* Name, unsigned Flags)
76 /* Allocate a symbol table entry, initialize and return it */
77 {
78     /* Allocate memory */
79     SymEntry* S = xmalloc (sizeof (SymEntry));
80
81     /* Initialize the entry */
82     S->Left       = 0;
83     S->Right      = 0;
84     S->Locals     = 0;
85     S->SymTab     = 0;
86     S->Pos        = CurPos;
87     S->Flags      = Flags;
88     S->Expr       = 0;
89     S->ExprRefs   = AUTO_COLLECTION_INITIALIZER;
90     S->ExportSize = ADDR_SIZE_DEFAULT;
91     S->AddrSize   = ADDR_SIZE_DEFAULT;
92     memset (S->ConDesPrio, 0, sizeof (S->ConDesPrio));
93     S->Name       = GetStringId (Name);
94
95     /* Insert it into the list of all entries */
96     S->List = SymList;
97     SymList = S;
98
99     /* Return the initialized entry */
100     return S;
101 }
102
103
104
105 int SymSearchTree (SymEntry* T, const char* Name, SymEntry** E)
106 /* Search in the given tree for a name. If we find the symbol, the function
107  * will return 0 and put the entry pointer into E. If we did not find the
108  * symbol, and the tree is empty, E is set to NULL. If the tree is not empty,
109  * E will be set to the last entry, and the result of the function is <0 if
110  * the entry should be inserted on the left side, and >0 if it should get
111  * inserted on the right side.
112  */
113 {
114     /* Is there a tree? */
115     if (T == 0) {
116         *E = 0;
117         return 1;
118     }
119
120     /* We have a table, search it */
121     while (1) {
122
123         /* Get the symbol name */
124         const char* SymName = GetString (T->Name);
125
126         /* Choose next entry */
127         int Cmp = strcmp (Name, SymName);
128         if (Cmp < 0 && T->Left) {
129             T = T->Left;
130         } else if (Cmp > 0&& T->Right) {
131             T = T->Right;
132         } else {
133             /* Found or end of search, return the result */
134             *E = T;
135             return Cmp;
136         }
137     }
138 }
139
140
141
142 void SymRef (SymEntry* S)
143 /* Mark the given symbol as referenced */
144 {
145     /* Mark the symbol as referenced */
146     S->Flags |= SF_REFERENCED;
147 }
148
149
150
151 void SymTransferExprRefs (SymEntry* From, SymEntry* To)
152 /* Transfer all expression references from one symbol to another. */
153 {
154     unsigned I;
155
156     for (I = 0; I < CollCount (&From->ExprRefs); ++I) {
157
158         /* Get the expression node */
159         ExprNode* E = CollAtUnchecked (&From->ExprRefs, I);
160
161         /* Safety */
162         CHECK (E->Op == EXPR_SYMBOL && E->V.Sym == From);
163
164         /* Replace the symbol reference */
165         E->V.Sym = To;
166
167         /* Add the expression reference */
168         SymAddExprRef (To, E);
169     }
170
171     /* Remove all symbol references from the old symbol */
172     CollDeleteAll (&From->ExprRefs);
173 }
174
175
176
177 void SymDef (SymEntry* S, ExprNode* Expr, unsigned char AddrSize, unsigned Flags)
178 /* Define a new symbol */
179 {
180     if (S->Flags & SF_IMPORT) {
181         /* Defined symbol is marked as imported external symbol */
182         Error ("Symbol `%s' is already an import", GetSymName (S));
183         return;
184     }
185     if (S->Flags & SF_DEFINED) {
186         /* Multiple definition */
187         Error ("Symbol `%s' is already defined", GetSymName (S));
188         S->Flags |= SF_MULTDEF;
189         return;
190     }
191
192     /* Map a default address size to a real value */
193     if (AddrSize == ADDR_SIZE_DEFAULT) {
194         /* ### Must go! Delay address size calculation until end of assembly! */
195         ExprDesc ED;
196         ED_Init (&ED);
197         StudyExpr (Expr, &ED);
198         AddrSize = ED.AddrSize;
199         ED_Done (&ED);
200     }
201
202     /* Set the symbol value */
203     S->Expr = Expr;
204
205     /* If the symbol is marked as global, export it. Address size is checked
206      * below.
207      */
208     if (S->Flags & SF_GLOBAL) {
209         S->Flags = (S->Flags & ~SF_GLOBAL) | SF_EXPORT;
210     }
211
212     /* Mark the symbol as defined and use the given address size */
213     S->Flags |= (SF_DEFINED | Flags);
214     S->AddrSize = AddrSize;
215
216     /* If the symbol is exported, check the address sizes */
217     if (S->Flags & SF_EXPORT) {
218         if (S->ExportSize == ADDR_SIZE_DEFAULT) {
219             /* Use the real size of the symbol */
220             S->ExportSize = S->AddrSize;
221         } else if (S->AddrSize > S->ExportSize) {
222             /* We're exporting a symbol smaller than it actually is */
223             PWarning (GetSymPos (S), 1, "Symbol `%s' is %s but exported %s",
224                       GetSymName (S), AddrSizeToStr (S->AddrSize),
225                       AddrSizeToStr (S->ExportSize));
226         }
227     }
228
229     /* If this is not a local symbol, remember it as the last global one */
230     if ((S->Flags & SF_LOCAL) == 0) {
231         SymLast = S;
232     }
233 }
234
235
236
237 void SymImport (SymEntry* S, unsigned char AddrSize, unsigned Flags)
238 /* Mark the given symbol as an imported symbol */
239 {
240     if (S->Flags & SF_DEFINED) {
241         Error ("Symbol `%s' is already defined", GetSymName (S));
242         S->Flags |= SF_MULTDEF;
243         return;
244     }
245     if (S->Flags & SF_EXPORT) {
246         /* The symbol is already marked as exported symbol */
247         Error ("Cannot import exported symbol `%s'", GetSymName (S));
248         return;
249     }
250
251     /* If no address size is given, use the address size of the enclosing
252      * segment.
253      */
254     if (AddrSize == ADDR_SIZE_DEFAULT) {
255         AddrSize = GetCurrentSegAddrSize ();
256     }
257
258     /* If the symbol is marked as import or global, check the address size,
259      * then do silently remove the global flag.
260      */
261     if (S->Flags & SF_IMPORT) {
262         if ((Flags & SF_FORCED) != (S->Flags & SF_FORCED)) {
263             Error ("Redeclaration mismatch for symbol `%s'", GetSymName (S));
264         }
265         if (AddrSize != S->AddrSize) {
266             Error ("Address size mismatch for symbol `%s'", GetSymName (S));
267         }
268     }
269     if (S->Flags & SF_GLOBAL) {
270         S->Flags &= ~SF_GLOBAL;
271         if (AddrSize != S->AddrSize) {
272             Error ("Address size mismatch for symbol `%s'", GetSymName (S));
273         }
274     }
275
276     /* Set the symbol data */
277     S->Flags |= (SF_IMPORT | Flags);
278     S->AddrSize = AddrSize;
279 }
280
281
282
283 void SymExport (SymEntry* S, unsigned char AddrSize, unsigned Flags)
284 /* Mark the given symbol as an exported symbol */
285 {
286     /* Check if it's ok to export the symbol */
287     if (S->Flags & SF_IMPORT) {
288         /* The symbol is already marked as imported external symbol */
289         Error ("Symbol `%s' is already an import", GetSymName (S));
290         return;
291     }
292
293     /* If the symbol was marked as global before, remove the global flag and
294      * proceed, but check the address size.
295      */
296     if (S->Flags & SF_GLOBAL) {
297         if (AddrSize != S->ExportSize) {
298             Error ("Address size mismatch for symbol `%s'", GetSymName (S));
299         }
300         S->Flags &= ~SF_GLOBAL;
301     }
302
303     /* If the symbol was already marked as an export, but wasn't defined
304      * before, the address sizes in both definitions must match.
305      */
306     if ((S->Flags & (SF_EXPORT|SF_DEFINED)) == SF_EXPORT) {
307         if (S->ExportSize != AddrSize) {
308             Error ("Address size mismatch for symbol `%s'", GetSymName (S));
309         }
310     }
311     S->ExportSize = AddrSize;
312
313     /* If the symbol is already defined, check symbol size against the
314      * exported size.
315      */
316     if (S->Flags & SF_DEFINED) {
317         if (S->ExportSize == ADDR_SIZE_DEFAULT) {
318             /* No export size given, use the real size of the symbol */
319             S->ExportSize = S->AddrSize;
320         } else if (S->AddrSize > S->ExportSize) {
321             /* We're exporting a symbol smaller than it actually is */
322             Warning (1, "Symbol `%s' is %s but exported %s",
323                      GetSymName (S), AddrSizeToStr (S->AddrSize),
324                      AddrSizeToStr (S->ExportSize));
325         }
326     }
327
328     /* Set the symbol data */
329     S->Flags |= (SF_EXPORT | SF_REFERENCED | Flags);
330 }
331
332
333
334 void SymGlobal (SymEntry* S, unsigned char AddrSize, unsigned Flags)
335 /* Mark the given symbol as a global symbol, that is, as a symbol that is
336  * either imported or exported.
337  */
338 {
339     /* If the symbol is already marked as import, the address size must match.
340      * Apart from that, ignore the global declaration.
341      */
342     if (S->Flags & SF_IMPORT) {
343         if (AddrSize == ADDR_SIZE_DEFAULT) {
344             /* Use the size of the current segment */
345             AddrSize = GetCurrentSegAddrSize ();
346         }
347         if (AddrSize != S->AddrSize) {
348             Error ("Address size mismatch for symbol `%s'", GetSymName (S));
349         }
350         return;
351     }
352
353     /* If the symbol is already an export: If it is not defined, the address
354      * sizes must match.
355      */
356     if (S->Flags & SF_EXPORT) {
357         if ((S->Flags & SF_DEFINED) == 0) {
358             /* Symbol is undefined */
359             if (AddrSize != S->ExportSize) {
360                 Error ("Address size mismatch for symbol `%s'", GetSymName (S));
361             }
362         } else if (AddrSize != ADDR_SIZE_DEFAULT) {
363             /* Symbol is defined and address size given */
364             if (AddrSize != S->ExportSize) {
365                 Error ("Address size mismatch for symbol `%s'", GetSymName (S));
366             }
367         }
368         return;
369     }
370
371     /* If the symbol is already marked as global, the address size must match.
372      * Use the ExportSize here, since it contains the actual address size
373      * passed to this function.
374      */
375     if (S->Flags & SF_GLOBAL) {
376         if (AddrSize != S->ExportSize) {
377             Error ("Address size mismatch for symbol `%s'", GetSymName (S));
378         }
379         return;
380     }
381
382     /* If we come here, the symbol was neither declared as export, import or
383      * global before. Check if it is already defined, in which case it will
384      * become an export. If it is not defined, mark it as global and remember
385      * the given address sizes.
386      */
387     if (S->Flags & SF_DEFINED) {
388         /* The symbol is defined, export it */
389         S->ExportSize = AddrSize;
390         if (S->ExportSize == ADDR_SIZE_DEFAULT) {
391             /* No export size given, use the real size of the symbol */
392             S->ExportSize = S->AddrSize;
393         } else if (S->AddrSize > S->ExportSize) {
394             /* We're exporting a symbol smaller than it actually is */
395             Warning (1, "Symbol `%s' is %s but exported %s",
396                      GetSymName (S), AddrSizeToStr (S->AddrSize),
397                      AddrSizeToStr (S->ExportSize));
398         }
399         S->Flags |= (SF_EXPORT | Flags);
400     } else {
401         /* Since we don't know if the symbol will get exported or imported,
402          * remember two different address sizes: One for an import in AddrSize,
403          * and the other one for an export in ExportSize.
404          */
405         S->AddrSize = AddrSize;
406         if (S->AddrSize == ADDR_SIZE_DEFAULT) {
407             /* Use the size of the current segment */
408             S->AddrSize = GetCurrentSegAddrSize ();
409         }
410         S->ExportSize = AddrSize;
411         S->Flags |= (SF_GLOBAL | Flags);
412     }
413 }
414
415
416
417 void SymConDes (SymEntry* S, unsigned char AddrSize, unsigned Type, unsigned Prio)
418 /* Mark the given symbol as a module constructor/destructor. This will also
419  * mark the symbol as an export. Initializers may never be zero page symbols.
420  */
421 {
422     /* Check the parameters */
423 #if (CD_TYPE_MIN != 0)
424     CHECK (Type >= CD_TYPE_MIN && Type <= CD_TYPE_MAX);
425 #else
426     CHECK (Type <= CD_TYPE_MAX);
427 #endif
428     CHECK (Prio >= CD_PRIO_MIN && Prio <= CD_PRIO_MAX);
429
430     /* Check for errors */
431     if (S->Flags & SF_IMPORT) {
432         /* The symbol is already marked as imported external symbol */
433         Error ("Symbol `%s' is already an import", GetSymName (S));
434         return;
435     }
436
437     /* If the symbol was already marked as an export or global, check if
438      * this was done specifiying the same address size. In case of a global
439      * declaration, silently remove the global flag.
440      */
441     if (S->Flags & (SF_EXPORT | SF_GLOBAL)) {
442         if (S->ExportSize != AddrSize) {
443             Error ("Address size mismatch for symbol `%s'", GetSymName (S));
444         }
445         S->Flags &= ~SF_GLOBAL;
446     }
447     S->ExportSize = AddrSize;
448
449     /* If the symbol is already defined, check symbol size against the
450      * exported size.
451      */
452     if (S->Flags & SF_DEFINED) {
453         if (S->ExportSize == ADDR_SIZE_DEFAULT) {
454             /* Use the real size of the symbol */
455             S->ExportSize = S->AddrSize;
456         } else if (S->AddrSize != S->ExportSize) {
457             Error ("Address size mismatch for symbol `%s'", GetSymName (S));
458         }
459     }
460
461     /* If the symbol was already declared as a condes, check if the new
462      * priority value is the same as the old one.
463      */
464     if (S->ConDesPrio[Type] != CD_PRIO_NONE) {
465         if (S->ConDesPrio[Type] != Prio) {
466             Error ("Redeclaration mismatch for symbol `%s'", GetSymName (S));
467         }
468     }
469     S->ConDesPrio[Type] = Prio;
470
471     /* Set the symbol data */
472     S->Flags |= (SF_EXPORT | SF_REFERENCED);
473 }
474
475
476
477 void SymExportFromGlobal (SymEntry* S)
478 /* Called at the end of assembly. Converts a global symbol that is defined
479  * into an export.
480  */
481 {
482     /* Remove the global flag and make the symbol an export */
483     S->Flags &= ~SF_GLOBAL;
484     S->Flags |= SF_EXPORT;
485 }
486
487
488
489 void SymImportFromGlobal (SymEntry* S)
490 /* Called at the end of assembly. Converts a global symbol that is undefined
491  * into an import.
492  */
493 {
494     /* Remove the global flag and make it an import */
495     S->Flags &= ~SF_GLOBAL;
496     S->Flags |= SF_IMPORT;
497 }
498
499
500
501 int SymIsConst (SymEntry* S, long* Val)
502 /* Return true if the given symbol has a constant value. If Val is not NULL
503  * and the symbol has a constant value, store it's value there.
504  */
505 {
506     /* Check for constness */
507     return (SymHasExpr (S) && IsConstExpr (S->Expr, Val));
508 }
509
510
511
512 SymTable* GetSymParentScope (SymEntry* S)
513 /* Get the parent scope of the symbol (not the one it is defined in). Return
514  * NULL if the symbol is a cheap local, or defined on global level.
515  */
516 {
517     return (S->SymTab && S->SymTab->Parent)? S->SymTab->Parent : 0;
518 }
519
520
521
522 struct ExprNode* GetSymExpr (SymEntry* S)
523 /* Get the expression for a non-const symbol */
524 {
525     PRECONDITION (S != 0 && SymHasExpr (S));
526     return S->Expr;
527 }
528
529
530
531 const struct ExprNode* SymResolve (const SymEntry* S)
532 /* Helper function for DumpExpr. Resolves a symbol into an expression or return
533  * NULL. Do not call in other contexts!
534  */
535 {
536     return SymHasExpr (S)? S->Expr : 0;
537 }
538
539
540
541 long GetSymVal (SymEntry* S)
542 /* Return the value of a symbol assuming it's constant. FAIL will be called
543  * in case the symbol is undefined or not constant.
544  */
545 {
546     long Val;
547     CHECK (S != 0 && SymHasExpr (S) && IsConstExpr (GetSymExpr (S), &Val));
548     return Val;
549 }
550
551
552
553 unsigned GetSymIndex (const SymEntry* S)
554 /* Return the symbol index for the given symbol */
555 {
556     PRECONDITION (S != 0 && (S->Flags & SF_INDEXED) != 0);
557     return S->Index;
558 }
559
560
561