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