1/* ----------------------------------------------------------------------- *
2 *
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.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following
9 * conditions are met:
10 *
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.
17 *
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.
31 *
32 * ----------------------------------------------------------------------- */
33
34/*
35 * labels.c label handling for the Netwide Assembler
36 */
37
38#include "compiler.h"
39
40#include <stdio.h>
41#include <string.h>
42#include <stdlib.h>
43
44#include "nasm.h"
45#include "nasmlib.h"
46#include "error.h"
47#include "hashtbl.h"
48#include "labels.h"
49
50/*
51 * A dot-local label is one that begins with exactly one period. Things
52 * that begin with _two_ periods are NASM-specific things.
53 *
54 * If TASM compatibility is enabled, a local label can also begin with
55 * @@.
56 */
57static bool islocal(const char *l)
58{
59 if (tasm_compatible_mode) {
60 if (l[0] == '@' && l[1] == '@')
61 return true;
62 }
63
64 return (l[0] == '.' && l[1] != '.');
65}
66
67/*
68 * Return true if this falls into NASM's '..' namespace
69 */
70static bool ismagic(const char *l)
71{
72 return l[0] == '.' && l[1] == '.' && l[2] != '@';
73}
74
75/*
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.
79 */
80static bool set_prevlabel(const char *l)
81{
82 if (tasm_compatible_mode) {
83 if (l[0] == '@' && l[1] == '@')
84 return false;
85 }
86
87 return l[0] != '.';
88}
89
90#define LABEL_BLOCK 128 /* no. of labels/block */
91#define LBLK_SIZE (LABEL_BLOCK * sizeof(union label))
92
93#define END_LIST -3 /* don't clash with NO_SEG! */
94#define END_BLOCK -2
95
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"
99#endif
100
101/* string values for enum label_type */
102static const char * const types[] =
103{"local", "global", "static", "extern", "common", "special",
104 "output format special"};
105
106union label { /* actual label structures */
107 struct {
108 int32_t segment;
109 int32_t subsection; /* Available for ofmt->herelabel() */
110 int64_t offset;
111 int64_t size;
112 int64_t defined; /* 0 if undefined, passn+1 for when defn seen */
113 char *label, *mangled, *special;
114 const char *def_file; /* Where defined */
115 int32_t def_line;
116 enum label_type type, mangled_type;
117 } defn;
118 struct {
119 int32_t movingon;
120 int64_t dummy;
121 union label *next;
122 } admin;
123};
124
125struct permts { /* permanent text storage */
126 struct permts *next; /* for the linked list */
127 unsigned int size, usage; /* size and used space in ... */
128 char data[PERMTS_SIZE]; /* ... the data block itself */
129};
130#define PERMTS_HEADER offsetof(struct permts, data)
131
132uint64_t global_offset_changed; /* counter for global offset changes */
133
134static struct hash_table ltab; /* labels hash table */
135static union label *ldata; /* all label data blocks */
136static union label *lfree; /* labels free block */
137static struct permts *perm_head; /* start of perm. text storage */
138static struct permts *perm_tail; /* end of perm. text storage */
139
140static void init_block(union label *blk);
141static char *perm_alloc(size_t len);
142static char *perm_copy(const char *string);
143static char *perm_copy3(const char *s1, const char *s2, const char *s3);
144static const char *mangle_label_name(union label *lptr);
145
146static const char *prevlabel;
147
148static bool initialized = false;
149
150/*
151 * Emit a symdef to the output and the debug format backends.
152 */
153static void out_symdef(union label *lptr)
154{
155 int backend_type;
156 int64_t backend_offset;
157
158 /* Backend-defined special segments are passed to symdef immediately */
159 if (pass0 == 2) {
160 /* Emit special fixups for globals and commons */
161 switch (lptr->defn.type) {
162 case LBL_GLOBAL:
163 case LBL_EXTERN:
164 case LBL_COMMON:
165 if (lptr->defn.special)
166 ofmt->symdef(lptr->defn.mangled, 0, 0, 3, lptr->defn.special);
167 break;
168 default:
169 break;
170 }
171 return;
172 }
173
174 if (pass0 != 1 && lptr->defn.type != LBL_BACKEND)
175 return;
176
177 /* Clean up this hack... */
178 switch(lptr->defn.type) {
179 case LBL_GLOBAL:
180 case LBL_EXTERN:
181 backend_type = 1;
182 backend_offset = lptr->defn.offset;
183 break;
184 case LBL_COMMON:
185 backend_type = 2;
186 backend_offset = lptr->defn.size;
187 break;
188 default:
189 backend_type = 0;
190 backend_offset = lptr->defn.offset;
191 break;
192 }
193
194 /* Might be necessary for a backend symbol */
195 mangle_label_name(lptr);
196
197 ofmt->symdef(lptr->defn.mangled, lptr->defn.segment,
198 backend_offset, backend_type,
199 lptr->defn.special);
200
201 /*
202 * NASM special symbols are not passed to the debug format; none
203 * of the current backends want to see them.
204 */
205 if (lptr->defn.type == LBL_SPECIAL || lptr->defn.type == LBL_BACKEND)
206 return;
207
208 dfmt->debug_deflabel(lptr->defn.mangled, lptr->defn.segment,
209 lptr->defn.offset, backend_type,
210 lptr->defn.special);
211}
212
213/*
214 * Internal routine: finds the `union label' corresponding to the
215 * given label name. Creates a new one, if it isn't found, and if
216 * `create' is true.
217 */
218static union label *find_label(const char *label, bool create, bool *created)
219{
220 union label *lptr, **lpp;
221 char *label_str = NULL;
222 struct hash_insert ip;
223
224 nasm_assert(label != NULL);
225
226 if (islocal(label))
227 label = label_str = nasm_strcat(prevlabel, label);
228
229 lpp = (union label **) hash_find(&ltab, label, &ip);
230 lptr = lpp ? *lpp : NULL;
231
232 if (lptr || !create) {
233 if (created)
234 *created = false;
235 return lptr;
236 }
237
238 /* Create a new label... */
239 if (lfree->admin.movingon == END_BLOCK) {
240 /*
241 * must allocate a new block
242 */
243 lfree->admin.next = nasm_malloc(LBLK_SIZE);
244 lfree = lfree->admin.next;
245 init_block(lfree);
246 }
247
248 if (created)
249 *created = true;
250
251 nasm_zero(*lfree);
252 lfree->defn.label = perm_copy(label);
253 lfree->defn.subsection = NO_SEG;
254 if (label_str)
255 nasm_free(label_str);
256
257 hash_add(&ip, lfree->defn.label, lfree);
258 return lfree++;
259}
260
261bool lookup_label(const char *label, int32_t *segment, int64_t *offset)
262{
263 union label *lptr;
264
265 if (!initialized)
266 return false;
267
268 lptr = find_label(label, false, NULL);
269 if (lptr && lptr->defn.defined) {
270 *segment = lptr->defn.segment;
271 *offset = lptr->defn.offset;
272 return true;
273 }
274
275 return false;
276}
277
278bool is_extern(const char *label)
279{
280 union label *lptr;
281
282 if (!initialized)
283 return false;
284
285 lptr = find_label(label, false, NULL);
286 return lptr && lptr->defn.type == LBL_EXTERN;
287}
288
289static const char *mangle_strings[] = {"", "", "", ""};
290static bool mangle_string_set[ARRAY_SIZE(mangle_strings)];
291
292/*
293 * Set a prefix or suffix
294 */
295void set_label_mangle(enum mangle_index which, const char *what)
296{
297 if (mangle_string_set[which])
298 return; /* Once set, do not change */
299
300 mangle_strings[which] = perm_copy(what);
301 mangle_string_set[which] = true;
302}
303
304/*
305 * Format a label name with appropriate prefixes and suffixes
306 */
307static const char *mangle_label_name(union label *lptr)
308{
309 const char *prefix;
310 const char *suffix;
311
312 if (likely(lptr->defn.mangled &&
313 lptr->defn.mangled_type == lptr->defn.type))
314 return lptr->defn.mangled; /* Already mangled */
315
316 switch (lptr->defn.type) {
317 case LBL_GLOBAL:
318 case LBL_STATIC:
319 case LBL_EXTERN:
320 prefix = mangle_strings[LM_GPREFIX];
321 suffix = mangle_strings[LM_GSUFFIX];
322 break;
323 case LBL_BACKEND:
324 case LBL_SPECIAL:
325 prefix = suffix = "";
326 break;
327 default:
328 prefix = mangle_strings[LM_LPREFIX];
329 suffix = mangle_strings[LM_LSUFFIX];
330 break;
331 }
332
333 lptr->defn.mangled_type = lptr->defn.type;
334
335 if (!(*prefix) && !(*suffix))
336 lptr->defn.mangled = lptr->defn.label;
337 else
338 lptr->defn.mangled = perm_copy3(prefix, lptr->defn.label, suffix);
339
340 return lptr->defn.mangled;
341}
342
343static void
344handle_herelabel(union label *lptr, int32_t *segment, int64_t *offset)
345{
346 int32_t oldseg;
347
348 if (likely(!ofmt->herelabel))
349 return;
350
351 if (unlikely(location.segment == NO_SEG))
352 return;
353
354 oldseg = *segment;
355
356 if (oldseg == location.segment && *offset == location.offset) {
357 /* This label is defined at this location */
358 int32_t newseg;
359 bool copyoffset = false;
360
361 nasm_assert(lptr->defn.mangled);
362 newseg = ofmt->herelabel(lptr->defn.mangled, lptr->defn.type,
363 oldseg, &lptr->defn.subsection, &copyoffset);
364 if (likely(newseg == oldseg))
365 return;
366
367 *segment = newseg;
368 if (copyoffset) {
369 /* Maintain the offset from the old to the new segment */
370 switch_segment(newseg);
371 location.offset = *offset;
372 } else {
373 /* Keep a separate offset for the new segment */
374 *offset = switch_segment(newseg);
375 }
376 }
377}
378
379static bool declare_label_lptr(union label *lptr,
380 enum label_type type, const char *special)
381{
382 if (special && !special[0])
383 special = NULL;
384
385 if (lptr->defn.type == type ||
386 (pass0 == 0 && lptr->defn.type == LBL_LOCAL)) {
387 lptr->defn.type = type;
388 if (special) {
389 if (!lptr->defn.special)
390 lptr->defn.special = perm_copy(special);
391 else if (nasm_stricmp(lptr->defn.special, special))
392 nasm_error(ERR_NONFATAL,
393 "symbol `%s' has inconsistent attributes `%s' and `%s'",
394 lptr->defn.label, lptr->defn.special, special);
395 }
396 return true;
397 }
398
399 /* EXTERN can be replaced with GLOBAL or COMMON */
400 if (lptr->defn.type == LBL_EXTERN &&
401 (type == LBL_GLOBAL || type == LBL_COMMON)) {
402 lptr->defn.type = type;
403 /* Override special unconditionally */
404 if (special)
405 lptr->defn.special = perm_copy(special);
406 return true;
407 }
408
409 /* GLOBAL or COMMON ignore subsequent EXTERN */
410 if ((lptr->defn.type == LBL_GLOBAL || lptr->defn.type == LBL_COMMON) &&
411 type == LBL_EXTERN) {
412 if (!lptr->defn.special)
413 lptr->defn.special = perm_copy(special);
414 return false; /* Don't call define_label() after this! */
415 }
416
417 nasm_error(ERR_NONFATAL, "symbol `%s' declared both as %s and %s",
418 lptr->defn.label, types[lptr->defn.type], types[type]);
419
420 return false;
421}
422
423bool declare_label(const char *label, enum label_type type, const char *special)
424{
425 union label *lptr = find_label(label, true, NULL);
426 return declare_label_lptr(lptr, type, special);
427}
428
429/*
430 * The "normal" argument decides if we should update the local segment
431 * base name or not.
432 */
433void define_label(const char *label, int32_t segment,
434 int64_t offset, bool normal)
435{
436 union label *lptr;
437 bool created, changed;
438 int64_t size;
439 int64_t lastdef;
440
441 /*
442 * The backend may invoke this before pass 1, so treat that as
443 * a special "pass".
444 */
445 const int64_t lpass = passn + 1;
446
447 /*
448 * Phase errors here can be one of two types: a new label appears,
449 * or the offset changes. Increment global_offset_changed when that
450 * happens, to tell the assembler core to make another pass.
451 */
452 lptr = find_label(label, true, &created);
453
454 lastdef = lptr->defn.defined;
455
456 if (segment) {
457 /* We are actually defining this label */
458 if (lptr->defn.type == LBL_EXTERN) {
459 /* auto-promote EXTERN to GLOBAL */
460 lptr->defn.type = LBL_GLOBAL;
461 lastdef = 0; /* We are "re-creating" this label */
462 }
463 } else {
464 /* It's a pseudo-segment (extern, common) */
465 segment = lptr->defn.segment ? lptr->defn.segment : seg_alloc();
466 }
467
468 if (lastdef || lptr->defn.type == LBL_BACKEND) {
469 /*
470 * We have seen this on at least one previous pass, or
471 * potentially earlier in this same pass (in which case we
472 * will probably error out further down.)
473 */
474 mangle_label_name(lptr);
475 handle_herelabel(lptr, &segment, &offset);
476 }
477
478 if (ismagic(label) && lptr->defn.type == LBL_LOCAL)
479 lptr->defn.type = LBL_SPECIAL;
480
481 if (set_prevlabel(label) && normal)
482 prevlabel = lptr->defn.label;
483
484 if (lptr->defn.type == LBL_COMMON) {
485 size = offset;
486 offset = 0;
487 } else {
488 size = 0; /* This is a hack... */
489 }
490
491 changed = created || !lastdef ||
492 lptr->defn.segment != segment ||
493 lptr->defn.offset != offset ||
494 lptr->defn.size != size;
495 global_offset_changed += changed;
496
497 if (lastdef == lpass) {
498 int32_t saved_line = 0;
499 const char *saved_fname = NULL;
500 int noteflags;
501
502 /*
503 * Defined elsewhere in the program, seen in this pass.
504 */
505 if (changed) {
506 nasm_error(ERR_NONFATAL,
507 "label `%s' inconsistently redefined",
508 lptr->defn.label);
509 noteflags = ERR_NOTE|ERR_HERE;
510 } else {
511 nasm_error(ERR_WARNING|WARN_LABEL_REDEF|ERR_PASS2,
512 "label `%s' redefined to an identical value",
513 lptr->defn.label);
514 noteflags = ERR_NOTE|ERR_HERE|WARN_LABEL_REDEF|ERR_PASS2;
515 }
516
517 src_get(&saved_line, &saved_fname);
518 src_set(lptr->defn.def_line, lptr->defn.def_file);
519 nasm_error(noteflags, "label `%s' originally defined",
520 lptr->defn.label);
521 src_set(saved_line, saved_fname);
522 } else if (changed && pass0 > 1 && lptr->defn.type != LBL_SPECIAL) {
523 /*
524 * WARN_LABEL_LATE defaults to an error, as this should never
525 * actually happen. Just in case this is a backwards
526 * compatibility problem, still make it a warning so that the
527 * user can suppress or demote it.
528 *
529 * As a special case, LBL_SPECIAL symbols are allowed to be changed
530 * even during the last pass.
531 */
532 nasm_error(ERR_WARNING|WARN_LABEL_LATE,
533 "label `%s' %s during code generation",
534 lptr->defn.label, created ? "defined" : "changed");
535 }
536 lptr->defn.segment = segment;
537 lptr->defn.offset = offset;
538 lptr->defn.size = size;
539 lptr->defn.defined = lpass;
540
541 if (changed || lastdef != lpass)
542 src_get(&lptr->defn.def_line, &lptr->defn.def_file);
543
544 if (lastdef != lpass)
545 out_symdef(lptr);
546}
547
548/*
549 * Define a special backend label
550 */
551void backend_label(const char *label, int32_t segment, int64_t offset)
552{
553 if (!declare_label(label, LBL_BACKEND, NULL))
554 return;
555
556 define_label(label, segment, offset, false);
557}
558
559int init_labels(void)
560{
561 hash_init(&ltab, HASH_LARGE);
562
563 ldata = lfree = nasm_malloc(LBLK_SIZE);
564 init_block(lfree);
565
566 perm_head = perm_tail =
567 nasm_malloc(sizeof(struct permts));
568
569 perm_head->next = NULL;
570 perm_head->size = PERMTS_SIZE;
571 perm_head->usage = 0;
572
573 prevlabel = "";
574
575 initialized = true;
576
577 return 0;
578}
579
580void cleanup_labels(void)
581{
582 union label *lptr, *lhold;
583
584 initialized = false;
585
586 hash_free(&ltab);
587
588 lptr = lhold = ldata;
589 while (lptr) {
590 lptr = &lptr[LABEL_BLOCK-1];
591 lptr = lptr->admin.next;
592 nasm_free(lhold);
593 lhold = lptr;
594 }
595
596 while (perm_head) {
597 perm_tail = perm_head;
598 perm_head = perm_head->next;
599 nasm_free(perm_tail);
600 }
601}
602
603static void init_block(union label *blk)
604{
605 int j;
606
607 for (j = 0; j < LABEL_BLOCK - 1; j++)
608 blk[j].admin.movingon = END_LIST;
609 blk[LABEL_BLOCK - 1].admin.movingon = END_BLOCK;
610 blk[LABEL_BLOCK - 1].admin.next = NULL;
611}
612
613static char * safe_alloc perm_alloc(size_t len)
614{
615 char *p;
616
617 if (perm_tail->size - perm_tail->usage < len) {
618 size_t alloc_len = (len > PERMTS_SIZE) ? len : PERMTS_SIZE;
619 perm_tail->next = nasm_malloc(PERMTS_HEADER + alloc_len);
620 perm_tail = perm_tail->next;
621 perm_tail->next = NULL;
622 perm_tail->size = alloc_len;
623 perm_tail->usage = 0;
624 }
625 p = perm_tail->data + perm_tail->usage;
626 perm_tail->usage += len;
627 return p;
628}
629
630static char *perm_copy(const char *string)
631{
632 char *p;
633 size_t len;
634
635 if (!string)
636 return NULL;
637
638 len = strlen(string)+1; /* Include final NUL */
639
640 p = perm_alloc(len);
641 memcpy(p, string, len);
642
643 return p;
644}
645
646static char *
647perm_copy3(const char *s1, const char *s2, const char *s3)
648{
649 char *p;
650 size_t l1 = strlen(s1);
651 size_t l2 = strlen(s2);
652 size_t l3 = strlen(s3)+1; /* Include final NUL */
653
654 p = perm_alloc(l1+l2+l3);
655 memcpy(p, s1, l1);
656 memcpy(p+l1, s2, l2);
657 memcpy(p+l1+l2, s3, l3);
658
659 return p;
660}
661
662const char *local_scope(const char *label)
663{
664 return islocal(label) ? prevlabel : "";
665}
666
667/*
668 * Notes regarding bug involving redefinition of external segments.
669 *
670 * Up to and including v0.97, the following code didn't work. From 0.97
671 * developers release 2 onwards, it will generate an error.
672 *
673 * EXTERN extlabel
674 * newlabel EQU extlabel + 1
675 *
676 * The results of allowing this code through are that two import records
677 * are generated, one for 'extlabel' and one for 'newlabel'.
678 *
679 * The reason for this is an inadequacy in the defined interface between
680 * the label manager and the output formats. The problem lies in how the
681 * output format driver tells that a label is an external label for which
682 * a label import record must be produced. Most (all except bin?) produce
683 * the record if the segment number of the label is not one of the internal
684 * segments that the output driver is producing.
685 *
686 * A simple fix to this would be to make the output formats keep track of
687 * which symbols they've produced import records for, and make them not
688 * produce import records for segments that are already defined.
689 *
690 * The best way, which is slightly harder but reduces duplication of code
691 * and should therefore make the entire system smaller and more stable is
692 * to change the interface between assembler, define_label(), and
693 * the output module. The changes that are needed are:
694 *
695 * The semantics of the 'isextern' flag passed to define_label() need
696 * examining. This information may or may not tell us what we need to
697 * know (ie should we be generating an import record at this point for this
698 * label). If these aren't the semantics, the semantics should be changed
699 * to this.
700 *
701 * The output module interface needs changing, so that the `isextern' flag
702 * is passed to the module, so that it can be easily tested for.
703 */
704