1 /* ----------------------------------------------------------------------- *
3 * Copyright 1996-2018 The NASM Authors - All Rights Reserved
4 * See the file AUTHORS included with the NASM distribution for
5 * the specific copyright holders.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * ----------------------------------------------------------------------- */
35 * labels.c label handling for the Netwide Assembler
51 * A dot-local label is one that begins with exactly one period. Things
52 * that begin with _two_ periods are NASM-specific things.
54 * If TASM compatibility is enabled, a local label can also begin with
57 static bool islocal(const char *l
)
59 if (tasm_compatible_mode
) {
60 if (l
[0] == '@' && l
[1] == '@')
64 return (l
[0] == '.' && l
[1] != '.');
68 * Return true if this falls into NASM's '..' namespace
70 static bool ismagic(const char *l
)
72 return l
[0] == '.' && l
[1] == '.' && l
[2] != '@';
76 * Return true if we should update the local label base
77 * as a result of this symbol. We must exclude local labels
78 * as well as any kind of special labels, including ..@ ones.
80 static bool set_prevlabel(const char *l
)
82 if (tasm_compatible_mode
) {
83 if (l
[0] == '@' && l
[1] == '@')
90 #define LABEL_BLOCK 128 /* no. of labels/block */
91 #define LBLK_SIZE (LABEL_BLOCK * sizeof(union label))
93 #define END_LIST -3 /* don't clash with NO_SEG! */
96 #define PERMTS_SIZE 16384 /* size of text blocks */
97 #if (PERMTS_SIZE < IDLEN_MAX)
98 #error "IPERMTS_SIZE must be greater than or equal to IDLEN_MAX"
101 /* string values for enum label_type */
102 static const char * const types
[] =
103 {"local", "global", "static", "extern", "common", "special",
104 "output format special"};
106 union label
{ /* actual label structures */
109 int32_t subsection
; /* Available for ofmt->herelabel() */
112 char *label
, *mangled
, *special
;
113 enum label_type type
, mangled_type
;
123 struct permts
{ /* permanent text storage */
124 struct permts
*next
; /* for the linked list */
125 unsigned int size
, usage
; /* size and used space in ... */
126 char data
[PERMTS_SIZE
]; /* ... the data block itself */
128 #define PERMTS_HEADER offsetof(struct permts, data)
130 uint64_t global_offset_changed
; /* counter for global offset changes */
132 static struct hash_table ltab
; /* labels hash table */
133 static union label
*ldata
; /* all label data blocks */
134 static union label
*lfree
; /* labels free block */
135 static struct permts
*perm_head
; /* start of perm. text storage */
136 static struct permts
*perm_tail
; /* end of perm. text storage */
138 static void init_block(union label
*blk
);
139 static char *perm_alloc(size_t len
);
140 static char *perm_copy(const char *string
);
141 static char *perm_copy3(const char *s1
, const char *s2
, const char *s3
);
142 static const char *mangle_label_name(union label
*lptr
);
144 static const char *prevlabel
;
146 static bool initialized
= false;
149 * Emit a symdef to the output and the debug format backends.
151 static void out_symdef(union label
*lptr
)
154 int64_t backend_offset
;
156 /* Backend-defined special segments are passed to symdef immediately */
158 /* Emit special fixups for globals and commons */
159 switch (lptr
->defn
.type
) {
163 if (lptr
->defn
.special
)
164 ofmt
->symdef(lptr
->defn
.mangled
, 0, 0, 3, lptr
->defn
.special
);
172 if (pass0
!= 1 && lptr
->defn
.type
!= LBL_BACKEND
)
175 /* Clean up this hack... */
176 switch(lptr
->defn
.type
) {
180 backend_offset
= lptr
->defn
.offset
;
184 backend_offset
= lptr
->defn
.size
;
188 backend_offset
= lptr
->defn
.offset
;
192 /* Might be necessary for a backend symbol */
193 mangle_label_name(lptr
);
195 ofmt
->symdef(lptr
->defn
.mangled
, lptr
->defn
.segment
,
196 backend_offset
, backend_type
,
200 * NASM special symbols are not passed to the debug format; none
201 * of the current backends want to see them.
203 if (lptr
->defn
.type
== LBL_SPECIAL
|| lptr
->defn
.type
== LBL_BACKEND
)
206 dfmt
->debug_deflabel(lptr
->defn
.mangled
, lptr
->defn
.segment
,
207 lptr
->defn
.offset
, backend_type
,
212 * Internal routine: finds the `union label' corresponding to the
213 * given label name. Creates a new one, if it isn't found, and if
216 static union label
*find_label(const char *label
, bool create
, bool *created
)
218 union label
*lptr
, **lpp
;
219 char *label_str
= NULL
;
220 struct hash_insert ip
;
222 nasm_assert(label
!= NULL
);
225 label
= label_str
= nasm_strcat(prevlabel
, label
);
227 lpp
= (union label
**) hash_find(<ab
, label
, &ip
);
228 lptr
= lpp
? *lpp
: NULL
;
230 if (lptr
|| !create
) {
236 /* Create a new label... */
237 if (lfree
->admin
.movingon
== END_BLOCK
) {
239 * must allocate a new block
241 lfree
->admin
.next
= nasm_malloc(LBLK_SIZE
);
242 lfree
= lfree
->admin
.next
;
250 lfree
->defn
.label
= perm_copy(label
);
251 lfree
->defn
.subsection
= NO_SEG
;
253 nasm_free(label_str
);
255 hash_add(&ip
, lfree
->defn
.label
, lfree
);
259 bool lookup_label(const char *label
, int32_t *segment
, int64_t *offset
)
266 lptr
= find_label(label
, false, NULL
);
267 if (lptr
&& lptr
->defn
.defined
) {
268 *segment
= lptr
->defn
.segment
;
269 *offset
= lptr
->defn
.offset
;
276 bool is_extern(const char *label
)
283 lptr
= find_label(label
, false, NULL
);
284 return lptr
&& lptr
->defn
.type
== LBL_EXTERN
;
287 static const char *mangle_strings
[] = {"", "", "", ""};
288 static bool mangle_string_set
[ARRAY_SIZE(mangle_strings
)];
291 * Set a prefix or suffix
293 void set_label_mangle(enum mangle_index which
, const char *what
)
295 if (mangle_string_set
[which
])
296 return; /* Once set, do not change */
298 mangle_strings
[which
] = perm_copy(what
);
299 mangle_string_set
[which
] = true;
303 * Format a label name with appropriate prefixes and suffixes
305 static const char *mangle_label_name(union label
*lptr
)
310 if (likely(lptr
->defn
.mangled
&&
311 lptr
->defn
.mangled_type
== lptr
->defn
.type
))
312 return lptr
->defn
.mangled
; /* Already mangled */
314 switch (lptr
->defn
.type
) {
318 prefix
= mangle_strings
[LM_GPREFIX
];
319 suffix
= mangle_strings
[LM_GSUFFIX
];
323 prefix
= suffix
= "";
326 prefix
= mangle_strings
[LM_LPREFIX
];
327 suffix
= mangle_strings
[LM_LSUFFIX
];
331 lptr
->defn
.mangled_type
= lptr
->defn
.type
;
333 if (!(*prefix
) && !(*suffix
))
334 lptr
->defn
.mangled
= lptr
->defn
.label
;
336 lptr
->defn
.mangled
= perm_copy3(prefix
, lptr
->defn
.label
, suffix
);
338 return lptr
->defn
.mangled
;
342 handle_herelabel(union label
*lptr
, int32_t *segment
, int64_t *offset
)
346 if (likely(!ofmt
->herelabel
))
349 if (unlikely(location
.segment
== NO_SEG
))
354 if (oldseg
== location
.segment
&& *offset
== location
.offset
) {
355 /* This label is defined at this location */
357 bool copyoffset
= false;
359 nasm_assert(lptr
->defn
.mangled
);
360 newseg
= ofmt
->herelabel(lptr
->defn
.mangled
, lptr
->defn
.type
,
361 oldseg
, &lptr
->defn
.subsection
, ©offset
);
362 if (likely(newseg
== oldseg
))
367 /* Maintain the offset from the old to the new segment */
368 switch_segment(newseg
);
369 location
.offset
= *offset
;
371 /* Keep a separate offset for the new segment */
372 *offset
= switch_segment(newseg
);
377 static bool declare_label_lptr(union label
*lptr
,
378 enum label_type type
, const char *special
)
380 if (special
&& !special
[0])
383 if (lptr
->defn
.type
== type
||
384 (pass0
== 0 && lptr
->defn
.type
== LBL_LOCAL
)) {
385 lptr
->defn
.type
= type
;
387 if (!lptr
->defn
.special
)
388 lptr
->defn
.special
= perm_copy(special
);
389 else if (nasm_stricmp(lptr
->defn
.special
, special
))
390 nasm_error(ERR_NONFATAL
,
391 "symbol `%s' has inconsistent attributes `%s' and `%s'",
392 lptr
->defn
.label
, lptr
->defn
.special
, special
);
397 /* EXTERN can be replaced with GLOBAL or COMMON */
398 if (lptr
->defn
.type
== LBL_EXTERN
&&
399 (type
== LBL_GLOBAL
|| type
== LBL_COMMON
)) {
400 lptr
->defn
.type
= type
;
401 /* Override special unconditionally */
403 lptr
->defn
.special
= perm_copy(special
);
407 /* GLOBAL or COMMON ignore subsequent EXTERN */
408 if ((lptr
->defn
.type
== LBL_GLOBAL
|| lptr
->defn
.type
== LBL_COMMON
) &&
409 type
== LBL_EXTERN
) {
410 if (!lptr
->defn
.special
)
411 lptr
->defn
.special
= perm_copy(special
);
412 return false; /* Don't call define_label() after this! */
415 nasm_error(ERR_NONFATAL
, "symbol `%s' declared both as %s and %s",
416 lptr
->defn
.label
, types
[lptr
->defn
.type
], types
[type
]);
421 bool declare_label(const char *label
, enum label_type type
, const char *special
)
423 union label
*lptr
= find_label(label
, true, NULL
);
424 return declare_label_lptr(lptr
, type
, special
);
428 * The "normal" argument decides if we should update the local segment
431 void define_label(const char *label
, int32_t segment
,
432 int64_t offset
, bool normal
)
435 bool created
, changed
;
439 * Phase errors here can be one of two types: a new label appears,
440 * or the offset changes. Increment global_offset_changed when that
441 * happens, to tell the assembler core to make another pass.
443 lptr
= find_label(label
, true, &created
);
446 /* We are actually defining this label */
447 if (lptr
->defn
.type
== LBL_EXTERN
) /* auto-promote EXTERN to GLOBAL */
448 lptr
->defn
.type
= LBL_GLOBAL
;
450 /* It's a pseudo-segment (extern, common) */
451 segment
= lptr
->defn
.segment
? lptr
->defn
.segment
: seg_alloc();
454 if (lptr
->defn
.defined
|| lptr
->defn
.type
== LBL_BACKEND
) {
455 /* We have seen this on at least one previous pass */
456 mangle_label_name(lptr
);
457 handle_herelabel(lptr
, &segment
, &offset
);
460 if (ismagic(label
) && lptr
->defn
.type
== LBL_LOCAL
)
461 lptr
->defn
.type
= LBL_SPECIAL
;
463 if (set_prevlabel(label
) && normal
)
464 prevlabel
= lptr
->defn
.label
;
466 if (lptr
->defn
.type
== LBL_COMMON
) {
470 size
= 0; /* This is a hack... */
473 changed
= created
|| !lptr
->defn
.defined
||
474 lptr
->defn
.segment
!= segment
||
475 lptr
->defn
.offset
!= offset
|| lptr
->defn
.size
!= size
;
476 global_offset_changed
+= changed
;
479 * This probably should be ERR_NONFATAL, but not quite yet. As a
480 * special case, LBL_SPECIAL symbols are allowed to be changed
481 * even during the last pass.
483 if (changed
&& pass0
> 1 && lptr
->defn
.type
!= LBL_SPECIAL
) {
484 nasm_error(ERR_WARNING
, "label `%s' %s during code generation",
486 created
? "defined" : "changed");
489 lptr
->defn
.segment
= segment
;
490 lptr
->defn
.offset
= offset
;
491 lptr
->defn
.size
= size
;
492 lptr
->defn
.defined
= true;
498 * Define a special backend label
500 void backend_label(const char *label
, int32_t segment
, int64_t offset
)
502 if (!declare_label(label
, LBL_BACKEND
, NULL
))
505 define_label(label
, segment
, offset
, false);
508 int init_labels(void)
510 hash_init(<ab
, HASH_LARGE
);
512 ldata
= lfree
= nasm_malloc(LBLK_SIZE
);
515 perm_head
= perm_tail
=
516 nasm_malloc(sizeof(struct permts
));
518 perm_head
->next
= NULL
;
519 perm_head
->size
= PERMTS_SIZE
;
520 perm_head
->usage
= 0;
529 void cleanup_labels(void)
531 union label
*lptr
, *lhold
;
537 lptr
= lhold
= ldata
;
539 lptr
= &lptr
[LABEL_BLOCK
-1];
540 lptr
= lptr
->admin
.next
;
546 perm_tail
= perm_head
;
547 perm_head
= perm_head
->next
;
548 nasm_free(perm_tail
);
552 static void init_block(union label
*blk
)
556 for (j
= 0; j
< LABEL_BLOCK
- 1; j
++)
557 blk
[j
].admin
.movingon
= END_LIST
;
558 blk
[LABEL_BLOCK
- 1].admin
.movingon
= END_BLOCK
;
559 blk
[LABEL_BLOCK
- 1].admin
.next
= NULL
;
562 static char * safe_alloc
perm_alloc(size_t len
)
566 if (perm_tail
->size
- perm_tail
->usage
< len
) {
567 size_t alloc_len
= (len
> PERMTS_SIZE
) ? len
: PERMTS_SIZE
;
568 perm_tail
->next
= nasm_malloc(PERMTS_HEADER
+ alloc_len
);
569 perm_tail
= perm_tail
->next
;
570 perm_tail
->next
= NULL
;
571 perm_tail
->size
= alloc_len
;
572 perm_tail
->usage
= 0;
574 p
= perm_tail
->data
+ perm_tail
->usage
;
575 perm_tail
->usage
+= len
;
579 static char *perm_copy(const char *string
)
587 len
= strlen(string
)+1; /* Include final NUL */
590 memcpy(p
, string
, len
);
596 perm_copy3(const char *s1
, const char *s2
, const char *s3
)
599 size_t l1
= strlen(s1
);
600 size_t l2
= strlen(s2
);
601 size_t l3
= strlen(s3
)+1; /* Include final NUL */
603 p
= perm_alloc(l1
+l2
+l3
);
605 memcpy(p
+l1
, s2
, l2
);
606 memcpy(p
+l1
+l2
, s3
, l3
);
611 const char *local_scope(const char *label
)
613 return islocal(label
) ? prevlabel
: "";
617 * Notes regarding bug involving redefinition of external segments.
619 * Up to and including v0.97, the following code didn't work. From 0.97
620 * developers release 2 onwards, it will generate an error.
623 * newlabel EQU extlabel + 1
625 * The results of allowing this code through are that two import records
626 * are generated, one for 'extlabel' and one for 'newlabel'.
628 * The reason for this is an inadequacy in the defined interface between
629 * the label manager and the output formats. The problem lies in how the
630 * output format driver tells that a label is an external label for which
631 * a label import record must be produced. Most (all except bin?) produce
632 * the record if the segment number of the label is not one of the internal
633 * segments that the output driver is producing.
635 * A simple fix to this would be to make the output formats keep track of
636 * which symbols they've produced import records for, and make them not
637 * produce import records for segments that are already defined.
639 * The best way, which is slightly harder but reduces duplication of code
640 * and should therefore make the entire system smaller and more stable is
641 * to change the interface between assembler, define_label(), and
642 * the output module. The changes that are needed are:
644 * The semantics of the 'isextern' flag passed to define_label() need
645 * examining. This information may or may not tell us what we need to
646 * know (ie should we be generating an import record at this point for this
647 * label). If these aren't the semantics, the semantics should be changed
650 * The output module interface needs changing, so that the `isextern' flag
651 * is passed to the module, so that it can be easily tested for.