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