More CHANGES that were never documented properly.
[platform/upstream/nasm.git] / labels.c
1 /* labels.c  label handling for the Netwide Assembler
2  *
3  * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
4  * Julian Hall. All rights reserved. The software is
5  * redistributable under the licence given in the file "Licence"
6  * distributed in the NASM archive.
7  */
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include "nasm.h"
13 #include "nasmlib.h"
14
15 /*
16  * A local label is one that begins with exactly one period. Things
17  * that begin with _two_ periods are NASM-specific things.
18  *
19  * If TASM compatibility is enabled, a local label can also begin with
20  * @@, so @@local is a TASM compatible local label. Note that we only
21  * check for the first @ symbol, although TASM requires both.
22  */
23 #define islocal(l)                                              \
24         (tasm_compatible_mode ?                                 \
25         (((l)[0] == '.' || (l)[0] == '@') && (l)[1] != '.') :   \
26         ((l)[0] == '.' && (l)[1] != '.'))
27 #define islocalchar(c)                                          \
28         (tasm_compatible_mode ?                                  \
29         ((c) == '.' || (c) == '@') :                            \
30         ((c) == '.'))
31
32 #define LABEL_BLOCK  32               /* no. of labels/block */
33 #define LBLK_SIZE    (LABEL_BLOCK*sizeof(union label))
34 #define LABEL_HASHES 37                       /* no. of hash table entries */
35
36 #define END_LIST -3                       /* don't clash with NO_SEG! */
37 #define END_BLOCK -2
38 #define BOGUS_VALUE -4
39
40 #define PERMTS_SIZE  4096               /* size of text blocks */
41
42 /* values for label.defn.is_global */
43 #define DEFINED_BIT 1
44 #define GLOBAL_BIT 2
45 #define EXTERN_BIT 4
46
47 #define NOT_DEFINED_YET 0
48 #define TYPE_MASK 3
49 #define LOCAL_SYMBOL (DEFINED_BIT)
50 #define GLOBAL_PLACEHOLDER (GLOBAL_BIT)
51 #define GLOBAL_SYMBOL (DEFINED_BIT|GLOBAL_BIT)
52
53 union label {                               /* actual label structures */
54     struct {
55         long segment, offset;
56         char *label, *special;
57         int is_global, is_norm;
58     } defn;
59     struct {
60         long movingon, dummy;
61         union label *next;
62     } admin;
63 };
64
65 struct permts {                               /* permanent text storage */
66     struct permts *next;               /* for the linked list */
67     int size, usage;                       /* size and used space in ... */
68     char data[PERMTS_SIZE];               /* ... the data block itself */
69 };
70
71 extern int global_offset_changed;   /* defined in nasm.c */
72
73 static union label *ltab[LABEL_HASHES];/* using a hash table */
74 static union label *lfree[LABEL_HASHES];/* pointer into the above */
75 static struct permts *perm_head;      /* start of perm. text storage */
76 static struct permts *perm_tail;      /* end of perm. text storage */
77
78 static void init_block (union label *blk);
79 static char *perm_copy (char *string1, char *string2);
80
81 static char *prevlabel;
82
83 static int initialised = FALSE;
84
85 char lprefix[PREFIX_MAX] = {0};
86 char lpostfix[PREFIX_MAX] = {0};
87
88 /*
89  * Internal routine: finds the `union label' corresponding to the
90  * given label name. Creates a new one, if it isn't found, and if
91  * `create' is TRUE.
92  */
93 static union label *find_label (char *label, int create) 
94 {
95     int hash = 0;
96     char *p, *prev;
97     int prevlen;
98     union label *lptr;
99
100     if (islocal(label))
101         prev = prevlabel;
102     else
103         prev = "";
104     prevlen = strlen(prev);
105     p = prev;
106     while (*p) hash += *p++;
107     p = label;
108     while (*p) hash += *p++;
109     hash %= LABEL_HASHES;
110     lptr = ltab[hash];
111     while (lptr->admin.movingon != END_LIST) {
112         if (lptr->admin.movingon == END_BLOCK) {
113             lptr = lptr->admin.next;
114             if (!lptr)
115                 break;
116         }
117         if (!strncmp(lptr->defn.label, prev, prevlen) &&
118             !strcmp(lptr->defn.label+prevlen, label))
119             return lptr;
120         lptr++;
121     }
122     if (create) {
123         if (lfree[hash]->admin.movingon == END_BLOCK) {
124             /*
125              * must allocate a new block
126              */
127             lfree[hash]->admin.next = (union label *) nasm_malloc (LBLK_SIZE);
128             lfree[hash] = lfree[hash]->admin.next;
129             init_block(lfree[hash]);
130         }
131
132         lfree[hash]->admin.movingon = BOGUS_VALUE;
133         lfree[hash]->defn.label = perm_copy (prev, label);
134         lfree[hash]->defn.special = NULL;
135         lfree[hash]->defn.is_global = NOT_DEFINED_YET;
136         return lfree[hash]++;
137     } 
138     else
139         return NULL;
140 }
141
142 int lookup_label (char *label, long *segment, long *offset) 
143 {
144     union label *lptr;
145
146     if (!initialised)
147         return 0;
148
149     lptr = find_label (label, 0);
150     if (lptr && (lptr->defn.is_global & DEFINED_BIT)) {
151         *segment = lptr->defn.segment;
152         *offset = lptr->defn.offset;
153         return 1;
154     } 
155     else
156         return 0;
157 }
158
159 int is_extern (char *label) 
160 {
161     union label *lptr;
162
163     if (!initialised)
164         return 0;
165
166     lptr = find_label (label, 0);
167     if (lptr && (lptr->defn.is_global & EXTERN_BIT))
168         return 1;
169     else
170         return 0;
171 }
172
173 void redefine_label (char *label, long segment, long offset, char *special,
174                    int is_norm, int isextrn, struct ofmt *ofmt, efunc error) 
175 {
176     union label *lptr;
177     int exi;
178     
179     /* This routine possibly ought to check for phase errors.  Most assemblers
180      * check for phase errors at this point.  I don't know whether phase errors
181      * are even possible, nor whether they are checked somewhere else
182      */
183
184     (void) segment;  /* Don't warn that this parameter is unused */
185     (void) special;  /* Don't warn that this parameter is unused */
186     (void) is_norm;  /* Don't warn that this parameter is unused */
187     (void) isextrn;  /* Don't warn that this parameter is unused */
188     (void) ofmt;     /* Don't warn that this parameter is unused */
189
190 #ifdef DEBUG
191 #if DEBUG<3
192     if (!strncmp(label, "debugdump", 9))
193 #endif
194         error(ERR_DEBUG, "redefine_label (%s, %ld, %08lx, %s, %d, %d)",
195                 label, segment, offset, special, is_norm, isextrn);
196 #endif
197
198     lptr = find_label (label, 1);
199     if (!lptr)
200         error (ERR_PANIC, "can't find label `%s' on pass two", label);
201     
202     if (!islocal(label)) {
203         if (!islocalchar(*label) && lptr->defn.is_norm)
204             prevlabel = lptr->defn.label;
205     }
206
207     global_offset_changed |= (lptr->defn.offset != offset);
208     lptr->defn.offset = offset;
209     
210 if (pass0 == 1) {
211     exi = !!(lptr->defn.is_global & GLOBAL_BIT);
212     if (exi)
213     {
214         char *xsymbol;
215         int slen;
216         slen = strlen(lprefix);
217         slen += strlen(lptr->defn.label);
218         slen += strlen(lpostfix);
219         slen++; /* room for that null char */
220         xsymbol = nasm_malloc(slen);
221         sprintf(xsymbol,"%s%s%s",lprefix,lptr->defn.label,lpostfix);
222
223         ofmt->symdef (xsymbol, segment, offset, exi, 
224                 special ? special : lptr->defn.special);
225         ofmt->current_dfmt->debug_deflabel (xsymbol, segment, offset, exi,
226                 special ? special : lptr->defn.special);
227 /**     nasm_free(xsymbol);  ! outobj.c stores the pointer; ouch!!! **/
228     }
229     else
230     {
231         if ( (lptr->defn.is_global & (GLOBAL_BIT|EXTERN_BIT)) != EXTERN_BIT ) {
232         ofmt->symdef (lptr->defn.label, segment, offset, exi,
233                 special ? special : lptr->defn.special);
234         ofmt->current_dfmt->debug_deflabel (label, segment, offset, exi,
235                 special ? special : lptr->defn.special);
236         }
237     }
238 } /* if (pass0 == 1) */
239
240 }
241
242 void define_label (char *label, long segment, long offset, char *special,
243                    int is_norm, int isextrn, struct ofmt *ofmt, efunc error) 
244 {
245     union label *lptr;
246     int exi;
247
248 #ifdef DEBUG
249 #if DEBUG<3
250     if (!strncmp(label, "debugdump", 9))
251 #endif
252         error(ERR_DEBUG, "define_label (%s, %ld, %08lx, %s, %d, %d)",
253                 label, segment, offset, special, is_norm, isextrn);
254 #endif
255     lptr = find_label (label, 1);
256     if (lptr->defn.is_global & DEFINED_BIT) {
257         error(ERR_NONFATAL, "symbol `%s' redefined", label);
258         return;
259     }
260     lptr->defn.is_global |= DEFINED_BIT;
261     if (isextrn)
262         lptr->defn.is_global |= EXTERN_BIT;
263
264     if (!islocalchar(label[0]) && is_norm)    /* not local, but not special either */
265         prevlabel = lptr->defn.label;
266     else if (islocal(label) && !*prevlabel) {
267         error(ERR_NONFATAL, "attempt to define a local label before any"
268               " non-local labels");
269         }
270
271     lptr->defn.segment = segment;
272     lptr->defn.offset = offset;
273     lptr->defn.is_norm = (!islocalchar(label[0]) && is_norm);
274
275 if (pass0 == 1 || (!is_norm && !isextrn && (segment&1))) {
276     exi = !!(lptr->defn.is_global & GLOBAL_BIT);
277     if (exi)
278     {
279         char *xsymbol;
280         int slen;
281         slen = strlen(lprefix);
282         slen += strlen(lptr->defn.label);
283         slen += strlen(lpostfix);
284         slen++; /* room for that null char */
285         xsymbol = nasm_malloc(slen);
286         sprintf(xsymbol,"%s%s%s",lprefix,lptr->defn.label,lpostfix);
287
288         ofmt->symdef (xsymbol, segment, offset, exi, 
289                 special ? special : lptr->defn.special);
290         ofmt->current_dfmt->debug_deflabel (xsymbol, segment, offset, exi,
291                 special ? special : lptr->defn.special);
292 /**     nasm_free(xsymbol);  ! outobj.c stores the pointer; ouch!!! **/
293     }
294     else
295     {
296         if ( (lptr->defn.is_global & (GLOBAL_BIT|EXTERN_BIT)) != EXTERN_BIT ) {
297         ofmt->symdef (lptr->defn.label, segment, offset, exi,
298                 special ? special : lptr->defn.special);
299         ofmt->current_dfmt->debug_deflabel (label, segment, offset, exi,
300                 special ? special : lptr->defn.special);
301         }
302     }
303 } /* if (pass0 == 1) */
304 }
305
306 void define_common (char *label, long segment, long size, char *special,
307                     struct ofmt *ofmt, efunc error) 
308 {
309     union label *lptr;
310
311     lptr = find_label (label, 1);
312     if (lptr->defn.is_global & DEFINED_BIT) {
313         error(ERR_NONFATAL, "symbol `%s' redefined", label);
314         return;
315     }
316     lptr->defn.is_global |= DEFINED_BIT;
317
318     if (!islocalchar(label[0]))        /* not local, but not special either */
319         prevlabel = lptr->defn.label;
320     else
321         error(ERR_NONFATAL, "attempt to define a local label as a "
322               "common variable");
323
324     lptr->defn.segment = segment;
325     lptr->defn.offset = 0;
326
327     ofmt->symdef (lptr->defn.label, segment, size, 2,
328                   special ? special : lptr->defn.special);
329     ofmt->current_dfmt->debug_deflabel(lptr->defn.label, segment, size, 2,
330                   special ? special : lptr->defn.special);
331 }
332
333 void declare_as_global (char *label, char *special, efunc error) 
334 {
335     union label *lptr;
336
337     if (islocal(label)) {
338         error(ERR_NONFATAL, "attempt to declare local symbol `%s' as"
339               " global", label);
340         return;
341     }
342     lptr = find_label (label, 1);
343     switch (lptr->defn.is_global & TYPE_MASK) {
344       case NOT_DEFINED_YET:
345         lptr->defn.is_global = GLOBAL_PLACEHOLDER;
346         lptr->defn.special = special ? perm_copy(special, "") : NULL;
347         break;
348       case GLOBAL_PLACEHOLDER:               /* already done: silently ignore */
349       case GLOBAL_SYMBOL:
350         break;
351       case LOCAL_SYMBOL:
352         if (!lptr->defn.is_global & EXTERN_BIT)
353             error(ERR_NONFATAL, "symbol `%s': GLOBAL directive must"
354                   " appear before symbol definition", label);
355         break;
356     }
357 }
358
359 int init_labels (void) 
360 {
361     int i;
362
363     for (i=0; i<LABEL_HASHES; i++) {
364         ltab[i] = (union label *) nasm_malloc (LBLK_SIZE);
365         if (!ltab[i])
366             return -1;                       /* can't initialise, panic */
367         init_block (ltab[i]);
368         lfree[i] = ltab[i];
369     }
370
371     perm_head = 
372         perm_tail = (struct permts *) nasm_malloc (sizeof(struct permts));
373
374     if (!perm_head)
375             return -1;
376
377     perm_head->next = NULL;
378     perm_head->size = PERMTS_SIZE;
379     perm_head->usage = 0;
380
381     prevlabel = "";
382
383     initialised = TRUE;
384
385     return 0;
386 }
387
388 void cleanup_labels (void) 
389 {
390     int i;
391
392     initialised = FALSE;
393
394     for (i=0; i<LABEL_HASHES; i++) {
395         union label *lptr, *lhold;
396
397         lptr = lhold = ltab[i];
398
399         while (lptr) {
400             while (lptr->admin.movingon != END_BLOCK) lptr++;
401             lptr = lptr->admin.next;
402             nasm_free (lhold);
403             lhold = lptr;
404         }
405     }
406
407     while (perm_head) {
408         perm_tail = perm_head;
409         perm_head = perm_head->next;
410         nasm_free (perm_tail);
411     }
412 }
413
414 static void init_block (union label *blk) 
415 {
416     int j;
417
418     for (j=0; j<LABEL_BLOCK-1; j++)
419             blk[j].admin.movingon = END_LIST;
420     blk[LABEL_BLOCK-1].admin.movingon = END_BLOCK;
421     blk[LABEL_BLOCK-1].admin.next = NULL;
422 }
423
424 static char *perm_copy (char *string1, char *string2) 
425 {
426     char *p, *q;
427     int len = strlen(string1)+strlen(string2)+1;
428
429     if (perm_tail->size - perm_tail->usage < len) {
430         perm_tail->next = (struct permts *)nasm_malloc(sizeof(struct permts));
431         perm_tail = perm_tail->next;
432         perm_tail->next = NULL;
433         perm_tail->size = PERMTS_SIZE;
434         perm_tail->usage = 0;
435     }
436     p = q = perm_tail->data + perm_tail->usage;
437     while ( (*q = *string1++) ) q++;
438     while ( (*q++ = *string2++) ) ;
439     perm_tail->usage = q - perm_tail->data;
440
441     return p;
442 }
443
444 /*
445  * Notes regarding bug involving redefinition of external segments.
446  *
447  * Up to and including v0.97, the following code didn't work. From 0.97
448  * developers release 2 onwards, it will generate an error.
449  *
450  * EXTERN extlabel
451  * newlabel EQU extlabel + 1
452  *
453  * The results of allowing this code through are that two import records
454  * are generated, one for 'extlabel' and one for 'newlabel'.
455  *
456  * The reason for this is an inadequacy in the defined interface between
457  * the label manager and the output formats. The problem lies in how the
458  * output format driver tells that a label is an external label for which
459  * a label import record must be produced. Most (all except bin?) produce
460  * the record if the segment number of the label is not one of the internal
461  * segments that the output driver is producing.
462  *
463  * A simple fix to this would be to make the output formats keep track of
464  * which symbols they've produced import records for, and make them not
465  * produce import records for segments that are already defined.
466  *
467  * The best way, which is slightly harder but reduces duplication of code
468  * and should therefore make the entire system smaller and more stable is
469  * to change the interface between assembler, define_label(), and
470  * the output module. The changes that are needed are:
471  *
472  * The semantics of the 'isextern' flag passed to define_label() need
473  * examining. This information may or may not tell us what we need to
474  * know (ie should we be generating an import record at this point for this
475  * label). If these aren't the semantics, the semantics should be changed
476  * to this.
477  *
478  * The output module interface needs changing, so that the `isextern' flag
479  * is passed to the module, so that it can be easily tested for.
480  */