d907b352caef07da6889b61d106d85326fe0b116
[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 "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, offset;
63         char *label, *special;
64         int is_global, is_norm;
65     } defn;
66     struct {
67         int32_t movingon, dummy;
68         union label *next;
69     } admin;
70 };
71
72 struct permts {                 /* permanent text storage */
73     struct permts *next;        /* for the linked list */
74     int size, usage;            /* size and used space in ... */
75     char data[PERMTS_SIZE];     /* ... the data block itself */
76 };
77
78 extern bool global_offset_changed;       /* defined in nasm.c */
79
80 static struct hash_table *ltab;         /* labels hash table */
81 static union label *ldata;              /* all label data blocks */
82 static union label *lfree;              /* labels free block */
83 static struct permts *perm_head;        /* start of perm. text storage */
84 static struct permts *perm_tail;        /* end of perm. text storage */
85
86 static void init_block(union label *blk);
87 static char *perm_copy(const char *string);
88
89 static char *prevlabel;
90
91 static bool initialized = false;
92
93 char lprefix[PREFIX_MAX] = { 0 };
94 char lpostfix[PREFIX_MAX] = { 0 };
95
96 /*
97  * Internal routine: finds the `union label' corresponding to the
98  * given label name. Creates a new one, if it isn't found, and if
99  * `create' is true.
100  */
101 static union label *find_label(char *label, int create)
102 {
103     char *prev;
104     int prevlen, len;
105     union label *lptr, **lpp;
106     char label_str[IDLEN_MAX];
107     struct hash_insert ip;
108
109     if (islocal(label)) {
110         prev = prevlabel;
111         prevlen = strlen(prev);
112         len = strlen(label);
113         if (prevlen+len >= IDLEN_MAX)
114             return NULL;        /* Error... */
115         memcpy(label_str, prev, prevlen);
116         memcpy(label_str+prevlen, label, len+1);
117         label = label_str;
118     } else {
119         prev = "";
120         prevlen = 0;
121     }
122
123     lpp = (union label **) hash_find(ltab, label, &ip);
124     lptr = lpp ? *lpp : NULL;
125
126     if (lptr || !create)
127         return lptr;
128
129     /* Create a new label... */
130     if (lfree->admin.movingon == END_BLOCK) {
131         /*
132          * must allocate a new block
133          */
134         lfree->admin.next =
135             (union label *)nasm_malloc(LBLK_SIZE);
136         lfree = lfree->admin.next;
137         init_block(lfree);
138     }
139     
140     lfree->admin.movingon = BOGUS_VALUE;
141     lfree->defn.label = perm_copy(label);
142     lfree->defn.special = NULL;
143     lfree->defn.is_global = NOT_DEFINED_YET;
144     
145     hash_add(&ip, lfree->defn.label, lfree);
146     return lfree++;
147 }
148
149 bool lookup_label(char *label, int32_t *segment, int32_t *offset)
150 {
151     union label *lptr;
152
153     if (!initialized)
154         return false;
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 true;
161     } else
162         return false;
163 }
164
165 bool is_extern(char *label)
166 {
167     union label *lptr;
168
169     if (!initialized)
170         return false;
171
172     lptr = find_label(label, 0);
173     return (lptr && (lptr->defn.is_global & EXTERN_BIT));
174 }
175
176 void redefine_label(char *label, int32_t segment, int32_t offset, char *special,
177                     bool is_norm, bool isextrn, struct ofmt *ofmt,
178                     efunc error)
179 {
180     union label *lptr;
181     int exi;
182
183     /* This routine possibly ought to check for phase errors.  Most assemblers
184      * check for phase errors at this point.  I don't know whether phase errors
185      * are even possible, nor whether they are checked somewhere else
186      */
187
188     (void)segment;              /* Don't warn that this parameter is unused */
189     (void)special;              /* Don't warn that this parameter is unused */
190     (void)is_norm;              /* Don't warn that this parameter is unused */
191     (void)isextrn;              /* Don't warn that this parameter is unused */
192     (void)ofmt;                 /* Don't warn that this parameter is unused */
193
194 #ifdef DEBUG
195 #if DEBUG<3
196     if (!strncmp(label, "debugdump", 9))
197 #endif
198         error(ERR_DEBUG, "redefine_label (%s, %ld, %08lx, %s, %d, %d)",
199               label, segment, offset, special, is_norm, isextrn);
200 #endif
201
202     lptr = find_label(label, 1);
203     if (!lptr)
204         error(ERR_PANIC, "can't find label `%s' on pass two", label);
205
206     if (!islocal(label)) {
207         if (!islocalchar(*label) && lptr->defn.is_norm)
208             prevlabel = lptr->defn.label;
209     }
210
211     global_offset_changed |= (lptr->defn.offset != offset);
212     lptr->defn.offset = offset;
213
214     if (pass0 == 1) {
215         exi = !!(lptr->defn.is_global & GLOBAL_BIT);
216         if (exi) {
217             char *xsymbol;
218             int slen;
219             slen = strlen(lprefix);
220             slen += strlen(lptr->defn.label);
221             slen += strlen(lpostfix);
222             slen++;             /* room for that null char */
223             xsymbol = nasm_malloc(slen);
224             snprintf(xsymbol, slen, "%s%s%s", lprefix, lptr->defn.label,
225                      lpostfix);
226
227             ofmt->symdef(xsymbol, segment, offset, exi,
228                          special ? special : lptr->defn.special);
229             ofmt->current_dfmt->debug_deflabel(xsymbol, segment, offset,
230                                                exi,
231                                                special ? special : lptr->
232                                                defn.special);
233 /**     nasm_free(xsymbol);  ! outobj.c stores the pointer; ouch!!! **/
234         } else {
235             if ((lptr->defn.is_global & (GLOBAL_BIT | EXTERN_BIT)) !=
236                 EXTERN_BIT) {
237                 ofmt->symdef(lptr->defn.label, segment, offset, exi,
238                              special ? special : lptr->defn.special);
239                 ofmt->current_dfmt->debug_deflabel(label, segment, offset,
240                                                    exi,
241                                                    special ? special :
242                                                    lptr->defn.special);
243             }
244         }
245     }
246     /* if (pass0 == 1) */
247 }
248
249 void define_label(char *label, int32_t segment, int32_t offset, char *special,
250                   bool is_norm, bool isextrn, struct ofmt *ofmt, efunc error)
251 {
252     union label *lptr;
253     int exi;
254
255 #ifdef DEBUG
256 #if DEBUG<3
257     if (!strncmp(label, "debugdump", 9))
258 #endif
259         error(ERR_DEBUG, "define_label (%s, %ld, %08lx, %s, %d, %d)",
260               label, segment, offset, special, is_norm, isextrn);
261 #endif
262     lptr = find_label(label, 1);
263     if (lptr->defn.is_global & DEFINED_BIT) {
264         error(ERR_NONFATAL, "symbol `%s' redefined", label);
265         return;
266     }
267     lptr->defn.is_global |= DEFINED_BIT;
268     if (isextrn)
269         lptr->defn.is_global |= EXTERN_BIT;
270
271     if (!islocalchar(label[0]) && is_norm)      /* not local, but not special either */
272         prevlabel = lptr->defn.label;
273     else if (islocal(label) && !*prevlabel) {
274         error(ERR_NONFATAL, "attempt to define a local label before any"
275               " non-local labels");
276     }
277
278     lptr->defn.segment = segment;
279     lptr->defn.offset = offset;
280     lptr->defn.is_norm = (!islocalchar(label[0]) && is_norm);
281
282     if (pass0 == 1 || (!is_norm && !isextrn && (segment & 1))) {
283         exi = !!(lptr->defn.is_global & GLOBAL_BIT);
284         if (exi) {
285             char *xsymbol;
286             int slen;
287             slen = strlen(lprefix);
288             slen += strlen(lptr->defn.label);
289             slen += strlen(lpostfix);
290             slen++;             /* room for that null char */
291             xsymbol = nasm_malloc(slen);
292             snprintf(xsymbol, slen, "%s%s%s", lprefix, lptr->defn.label,
293                      lpostfix);
294
295             ofmt->symdef(xsymbol, segment, offset, exi,
296                          special ? special : lptr->defn.special);
297             ofmt->current_dfmt->debug_deflabel(xsymbol, segment, offset,
298                                                exi,
299                                                special ? special : lptr->
300                                                defn.special);
301 /**     nasm_free(xsymbol);  ! outobj.c stores the pointer; ouch!!! **/
302         } else {
303             if ((lptr->defn.is_global & (GLOBAL_BIT | EXTERN_BIT)) !=
304                 EXTERN_BIT) {
305                 ofmt->symdef(lptr->defn.label, segment, offset, exi,
306                              special ? special : lptr->defn.special);
307                 ofmt->current_dfmt->debug_deflabel(label, segment, offset,
308                                                    exi,
309                                                    special ? special :
310                                                    lptr->defn.special);
311             }
312         }
313     }                           /* if (pass0 == 1) */
314 }
315
316 void define_common(char *label, int32_t segment, int32_t size, char *special,
317                    struct ofmt *ofmt, efunc error)
318 {
319     union label *lptr;
320
321     lptr = find_label(label, 1);
322     if (lptr->defn.is_global & DEFINED_BIT) {
323         error(ERR_NONFATAL, "symbol `%s' redefined", label);
324         return;
325     }
326     lptr->defn.is_global |= DEFINED_BIT;
327
328     if (!islocalchar(label[0])) /* not local, but not special either */
329         prevlabel = lptr->defn.label;
330     else
331         error(ERR_NONFATAL, "attempt to define a local label as a "
332               "common variable");
333
334     lptr->defn.segment = segment;
335     lptr->defn.offset = 0;
336
337     ofmt->symdef(lptr->defn.label, segment, size, 2,
338                  special ? special : lptr->defn.special);
339     ofmt->current_dfmt->debug_deflabel(lptr->defn.label, segment, size, 2,
340                                        special ? special : lptr->defn.
341                                        special);
342 }
343
344 void declare_as_global(char *label, char *special, efunc error)
345 {
346     union label *lptr;
347
348     if (islocal(label)) {
349         error(ERR_NONFATAL, "attempt to declare local symbol `%s' as"
350               " global", label);
351         return;
352     }
353     lptr = find_label(label, 1);
354     switch (lptr->defn.is_global & TYPE_MASK) {
355     case NOT_DEFINED_YET:
356         lptr->defn.is_global = GLOBAL_PLACEHOLDER;
357         lptr->defn.special = special ? perm_copy(special) : NULL;
358         break;
359     case GLOBAL_PLACEHOLDER:   /* already done: silently ignore */
360     case GLOBAL_SYMBOL:
361         break;
362     case LOCAL_SYMBOL:
363         if (!lptr->defn.is_global & EXTERN_BIT)
364             error(ERR_NONFATAL, "symbol `%s': GLOBAL directive must"
365                   " appear before symbol definition", label);
366         break;
367     }
368 }
369
370 int init_labels(void)
371 {
372     ltab = hash_init();
373
374     ldata = lfree = (union label *)nasm_malloc(LBLK_SIZE);
375     init_block(lfree);
376
377     perm_head =
378         perm_tail = (struct permts *)nasm_malloc(sizeof(struct permts));
379
380     perm_head->next = NULL;
381     perm_head->size = PERMTS_SIZE;
382     perm_head->usage = 0;
383
384     prevlabel = "";
385
386     initialized = true;
387
388     return 0;
389 }
390
391 void cleanup_labels(void)
392 {
393     union label *lptr, *lhold;
394
395     initialized = false;
396
397     hash_free(ltab);
398
399     lptr = lhold = ldata;
400     while (lptr) {
401         lptr = &lptr[LABEL_BLOCK-1];
402         lptr = lptr->admin.next;
403         nasm_free(lhold);
404         lhold = lptr;
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(const char *string)
425 {
426     char *p;
427     int len = strlen(string)+1;
428
429     if (perm_tail->size - perm_tail->usage < len) {
430         perm_tail->next =
431             (struct permts *)nasm_malloc(sizeof(struct permts));
432         perm_tail = perm_tail->next;
433         perm_tail->next = NULL;
434         perm_tail->size = PERMTS_SIZE;
435         perm_tail->usage = 0;
436     }
437     p = perm_tail->data + perm_tail->usage;
438     memcpy(p, string, len);
439     perm_tail->usage += len;
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  */