1 /* labels.c label handling for the Netwide Assembler
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.
16 * A local label is one that begins with exactly one period. Things
17 * that begin with _two_ periods are NASM-specific things.
19 #define islocal(l) ((l)[0] == '.' && (l)[1] != '.')
21 #define LABEL_BLOCK 320 /* no. of labels/block */
22 #define LBLK_SIZE (LABEL_BLOCK*sizeof(union label))
23 #define LABEL_HASHES 32 /* no. of hash table entries */
25 #define END_LIST -3 /* don't clash with NO_SEG! */
27 #define BOGUS_VALUE -4
29 #define PERMTS_SIZE 4096 /* size of text blocks */
31 /* values for label.defn.is_global */
36 #define NOT_DEFINED_YET 0
38 #define LOCAL_SYMBOL (DEFINED_BIT)
39 #define GLOBAL_PLACEHOLDER (GLOBAL_BIT)
40 #define GLOBAL_SYMBOL (DEFINED_BIT|GLOBAL_BIT)
42 union label
{ /* actual label structures */
45 char *label
, *special
;
46 int is_global
, is_norm
;
54 struct permts
{ /* permanent text storage */
55 struct permts
*next
; /* for the linked list */
56 int size
, usage
; /* size and used space in ... */
57 char data
[PERMTS_SIZE
]; /* ... the data block itself */
60 static union label
*ltab
[LABEL_HASHES
];/* using a hash table */
61 static union label
*lfree
[LABEL_HASHES
];/* pointer into the above */
62 static struct permts
*perm_head
; /* start of perm. text storage */
63 static struct permts
*perm_tail
; /* end of perm. text storage */
65 static void init_block (union label
*blk
);
66 static char *perm_copy (char *string1
, char *string2
);
68 static char *prevlabel
;
70 static int initialised
= FALSE
;
73 * Internal routine: finds the `union label' corresponding to the
74 * given label name. Creates a new one, if it isn't found, and if
77 static union label
*find_label (char *label
, int create
)
88 prevlen
= strlen(prev
);
90 while (*p
) hash
+= *p
++;
92 while (*p
) hash
+= *p
++;
95 while (lptr
->admin
.movingon
!= END_LIST
) {
96 if (lptr
->admin
.movingon
== END_BLOCK
) {
97 lptr
= lptr
->admin
.next
;
101 if (!strncmp(lptr
->defn
.label
, prev
, prevlen
) &&
102 !strcmp(lptr
->defn
.label
+prevlen
, label
))
107 if (lfree
[hash
]->admin
.movingon
== END_BLOCK
) {
109 * must allocate a new block
111 lfree
[hash
]->admin
.next
= (union label
*) nasm_malloc (LBLK_SIZE
);
112 lfree
[hash
] = lfree
[hash
]->admin
.next
;
113 init_block(lfree
[hash
]);
116 lfree
[hash
]->admin
.movingon
= BOGUS_VALUE
;
117 lfree
[hash
]->defn
.label
= perm_copy (prev
, label
);
118 lfree
[hash
]->defn
.special
= NULL
;
119 lfree
[hash
]->defn
.is_global
= NOT_DEFINED_YET
;
120 return lfree
[hash
]++;
126 int lookup_label (char *label
, long *segment
, long *offset
)
133 lptr
= find_label (label
, 0);
134 if (lptr
&& (lptr
->defn
.is_global
& DEFINED_BIT
)) {
135 *segment
= lptr
->defn
.segment
;
136 *offset
= lptr
->defn
.offset
;
143 int is_extern (char *label
)
150 lptr
= find_label (label
, 0);
151 if (lptr
&& (lptr
->defn
.is_global
& EXTERN_BIT
))
157 void redefine_label (char *label
, long segment
, long offset
, char *special
,
158 int is_norm
, int isextrn
, struct ofmt
*ofmt
, efunc error
)
162 /* This routine possibly ought to check for phase errors. Most assemblers
163 * check for phase errors at this point. I don't know whether phase errors
164 * are even possible, nor whether they are checked somewhere else
167 (void) segment
; /* Don't warn that this parameter is unused */
168 (void) offset
; /* Don't warn that this parameter is unused */
169 (void) special
; /* Don't warn that this parameter is unused */
170 (void) is_norm
; /* Don't warn that this parameter is unused */
171 (void) isextrn
; /* Don't warn that this parameter is unused */
172 (void) ofmt
; /* Don't warn that this parameter is unused */
175 if (!strncmp(label
, "debugdump", 9))
176 fprintf(stderr
, "debug: redefine_label (%s, %ld, %08lx, %s, %d, %d)\n",
177 label
, segment
, offset
, special
, is_norm
, isextrn
);
180 if (!islocal(label
)) {
181 lptr
= find_label (label
, 1);
183 error (ERR_PANIC
, "can't find label `%s' on pass two", label
);
184 if (*label
!= '.' && lptr
->defn
.is_norm
)
185 prevlabel
= lptr
->defn
.label
;
189 void define_label (char *label
, long segment
, long offset
, char *special
,
190 int is_norm
, int isextrn
, struct ofmt
*ofmt
, efunc error
)
195 if (!strncmp(label
, "debugdump", 9))
196 fprintf(stderr
, "debug: define_label (%s, %ld, %08lx, %s, %d, %d)\n",
197 label
, segment
, offset
, special
, is_norm
, isextrn
);
199 lptr
= find_label (label
, 1);
200 if (lptr
->defn
.is_global
& DEFINED_BIT
) {
201 error(ERR_NONFATAL
, "symbol `%s' redefined", label
);
204 lptr
->defn
.is_global
|= DEFINED_BIT
;
206 lptr
->defn
.is_global
|= EXTERN_BIT
;
208 if (label
[0] != '.' && is_norm
) /* not local, but not special either */
209 prevlabel
= lptr
->defn
.label
;
210 else if (label
[0] == '.' && label
[1] != '.' && !*prevlabel
)
211 error(ERR_NONFATAL
, "attempt to define a local label before any"
212 " non-local labels");
214 lptr
->defn
.segment
= segment
;
215 lptr
->defn
.offset
= offset
;
216 lptr
->defn
.is_norm
= (label
[0] != '.' && is_norm
);
218 ofmt
->symdef (lptr
->defn
.label
, segment
, offset
,
219 !!(lptr
->defn
.is_global
& GLOBAL_BIT
),
220 special
? special
: lptr
->defn
.special
);
221 ofmt
->current_dfmt
->debug_deflabel (label
, segment
, offset
,
222 !!(lptr
->defn
.is_global
& GLOBAL_BIT
),
223 special
? special
: lptr
->defn
.special
);
226 void define_common (char *label
, long segment
, long size
, char *special
,
227 struct ofmt
*ofmt
, efunc error
)
231 lptr
= find_label (label
, 1);
232 if (lptr
->defn
.is_global
& DEFINED_BIT
) {
233 error(ERR_NONFATAL
, "symbol `%s' redefined", label
);
236 lptr
->defn
.is_global
|= DEFINED_BIT
;
238 if (label
[0] != '.') /* not local, but not special either */
239 prevlabel
= lptr
->defn
.label
;
241 error(ERR_NONFATAL
, "attempt to define a local label as a "
244 lptr
->defn
.segment
= segment
;
245 lptr
->defn
.offset
= 0;
247 ofmt
->symdef (lptr
->defn
.label
, segment
, size
, 2,
248 special
? special
: lptr
->defn
.special
);
249 ofmt
->current_dfmt
->debug_deflabel(lptr
->defn
.label
, segment
, size
, 2,
250 special
? special
: lptr
->defn
.special
);
253 void declare_as_global (char *label
, char *special
, efunc error
)
257 if (islocal(label
)) {
258 error(ERR_NONFATAL
, "attempt to declare local symbol `%s' as"
262 lptr
= find_label (label
, 1);
263 switch (lptr
->defn
.is_global
& TYPE_MASK
) {
264 case NOT_DEFINED_YET
:
265 lptr
->defn
.is_global
= GLOBAL_PLACEHOLDER
;
266 lptr
->defn
.special
= special
? perm_copy(special
, "") : NULL
;
268 case GLOBAL_PLACEHOLDER
: /* already done: silently ignore */
272 if (!lptr
->defn
.is_global
& EXTERN_BIT
)
273 error(ERR_NONFATAL
, "symbol `%s': GLOBAL directive must"
274 " appear before symbol definition", label
);
279 int init_labels (void)
283 for (i
=0; i
<LABEL_HASHES
; i
++) {
284 ltab
[i
] = (union label
*) nasm_malloc (LBLK_SIZE
);
286 return -1; /* can't initialise, panic */
287 init_block (ltab
[i
]);
292 perm_tail
= (struct permts
*) nasm_malloc (sizeof(struct permts
));
297 perm_head
->next
= NULL
;
298 perm_head
->size
= PERMTS_SIZE
;
299 perm_head
->usage
= 0;
308 void cleanup_labels (void)
314 for (i
=0; i
<LABEL_HASHES
; i
++) {
315 union label
*lptr
, *lhold
;
317 lptr
= lhold
= ltab
[i
];
320 while (lptr
->admin
.movingon
!= END_BLOCK
) lptr
++;
321 lptr
= lptr
->admin
.next
;
328 perm_tail
= perm_head
;
329 perm_head
= perm_head
->next
;
330 nasm_free (perm_tail
);
334 static void init_block (union label
*blk
)
338 for (j
=0; j
<LABEL_BLOCK
-1; j
++)
339 blk
[j
].admin
.movingon
= END_LIST
;
340 blk
[LABEL_BLOCK
-1].admin
.movingon
= END_BLOCK
;
341 blk
[LABEL_BLOCK
-1].admin
.next
= NULL
;
344 static char *perm_copy (char *string1
, char *string2
)
347 int len
= strlen(string1
)+strlen(string2
)+1;
349 if (perm_tail
->size
- perm_tail
->usage
< len
) {
350 perm_tail
->next
= (struct permts
*)nasm_malloc(sizeof(struct permts
));
351 perm_tail
= perm_tail
->next
;
352 perm_tail
->next
= NULL
;
353 perm_tail
->size
= PERMTS_SIZE
;
354 perm_tail
->usage
= 0;
356 p
= q
= perm_tail
->data
+ perm_tail
->usage
;
357 while ( (*q
= *string1
++) ) q
++;
358 while ( (*q
++ = *string2
++) ) ;
359 perm_tail
->usage
= q
- perm_tail
->data
;
365 * Notes regarding bug involving redefinition of external segments.
367 * Up to and including v0.97, the following code didn't work. From 0.97
368 * developers release 2 onwards, it will generate an error.
371 * newlabel EQU extlabel + 1
373 * The results of allowing this code through are that two import records
374 * are generated, one for 'extlabel' and one for 'newlabel'.
376 * The reason for this is an inadequacy in the defined interface between
377 * the label manager and the output formats. The problem lies in how the
378 * output format driver tells that a label is an external label for which
379 * a label import record must be produced. Most (all except bin?) produce
380 * the record if the segment number of the label is not one of the internal
381 * segments that the output driver is producing.
383 * A simple fix to this would be to make the output formats keep track of
384 * which symbols they've produced import records for, and make them not
385 * produce import records for segments that are already defined.
387 * The best way, which is slightly harder but reduces duplication of code
388 * and should therefore make the entire system smaller and more stable is
389 * to change the interface between assembler, define_label(), and
390 * the output module. The changes that are needed are:
392 * The semantics of the 'isextern' flag passed to define_label() need
393 * examining. This information may or may not tell us what we need to
394 * know (ie should we be generating an import record at this point for this
395 * label). If these aren't the semantics, the semantics should be changed
398 * The output module interface needs changing, so that the `isextern' flag
399 * is passed to the module, so that it can be easily tested for.