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