blob: ff1d571a10edd31c877e296eb1976bea35a31822 [file] [log] [blame]
H. Peter Anvinea6e34d2002-04-30 20:51:32 +00001/* 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 "nasm.h"
13#include "nasmlib.h"
14
15/*
16 * A local label is one that begins with exactly one period. Things
17 * that begin with _two_ periods are NASM-specific things.
18 */
19#define islocal(l) ((l)[0] == '.' && (l)[1] != '.')
20
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 */
24
25#define END_LIST -3 /* don't clash with NO_SEG! */
26#define END_BLOCK -2
27#define BOGUS_VALUE -4
28
29#define PERMTS_SIZE 4096 /* size of text blocks */
30
31/* values for label.defn.is_global */
32#define NOT_DEFINED_YET 0
33#define LOCAL_SYMBOL 1
34#define GLOBAL_SYMBOL 2
35#define GLOBAL_PLACEHOLDER 3
36
37union label { /* actual label structures */
38 struct {
39 long segment, offset;
40 char *label;
41 int is_global;
42 } defn;
43 struct {
44 long movingon, dummy;
45 union label *next;
46 } admin;
47};
48
49struct permts { /* permanent text storage */
50 struct permts *next; /* for the linked list */
51 int size, usage; /* size and used space in ... */
52 char data[PERMTS_SIZE]; /* ... the data block itself */
53};
54
55static union label *ltab[LABEL_HASHES];/* using a hash table */
56static union label *lfree[LABEL_HASHES];/* pointer into the above */
57static struct permts *perm_head; /* start of perm. text storage */
58static struct permts *perm_tail; /* end of perm. text storage */
59
60static void init_block (union label *blk);
61static char *perm_copy (char *string1, char *string2);
62
63static char *prevlabel;
64
65/*
66 * Internal routine: finds the `union label' corresponding to the
67 * given label name. Creates a new one, if it isn't found, and if
68 * `create' is TRUE.
69 */
70static union label *find_label (char *label, int create) {
71 int hash = 0;
72 char *p, *prev;
73 int prevlen;
74 union label *lptr;
75
76 if (islocal(label))
77 prev = prevlabel;
78 else
79 prev = "";
80 prevlen = strlen(prev);
81 p = prev;
82 while (*p) hash += *p++;
83 p = label;
84 while (*p) hash += *p++;
85 hash %= LABEL_HASHES;
86 lptr = ltab[hash];
87 while (lptr->admin.movingon != END_LIST) {
88 if (lptr->admin.movingon == END_BLOCK) {
89 lptr = lptr->admin.next;
90 }
91 if (!strncmp(lptr->defn.label, prev, prevlen) &&
92 !strcmp(lptr->defn.label+prevlen, label))
93 return lptr;
94 lptr++;
95 }
96 if (create) {
97 if (lfree[hash]->admin.movingon == END_BLOCK) {
98 /*
99 * must allocate a new block
100 */
101 lfree[hash]->admin.next = (union label *) nasm_malloc (LBLK_SIZE);
102 lfree[hash] = lfree[hash]->admin.next;
103 init_block(lfree[hash]);
104 }
105
106 lfree[hash]->admin.movingon = BOGUS_VALUE;
107 lfree[hash]->defn.label = perm_copy (prev, label);
108 lfree[hash]->defn.is_global = NOT_DEFINED_YET;
109 return lfree[hash]++;
110 } else
111 return NULL;
112}
113
114int lookup_label (char *label, long *segment, long *offset) {
115 union label *lptr;
116
117 lptr = find_label (label, 0);
118 if (lptr && (lptr->defn.is_global == LOCAL_SYMBOL ||
119 lptr->defn.is_global == GLOBAL_SYMBOL)) {
120 *segment = lptr->defn.segment;
121 *offset = lptr->defn.offset;
122 return 1;
123 } else
124 return 0;
125}
126
127void define_label_stub (char *label, efunc error) {
128 union label *lptr;
129
130 if (!islocal(label)) {
131 lptr = find_label (label, 1);
132 if (!lptr)
133 error (ERR_PANIC, "can't find label `%s' on pass two", label);
134 prevlabel = lptr->defn.label;
135 }
136}
137
138void define_label (char *label, long segment, long offset,
139 struct ofmt *ofmt, efunc error) {
140 union label *lptr;
141
142 lptr = find_label (label, 1);
143 switch (lptr->defn.is_global) {
144 case NOT_DEFINED_YET:
145 lptr->defn.is_global = LOCAL_SYMBOL;
146 break;
147 case GLOBAL_PLACEHOLDER:
148 lptr->defn.is_global = GLOBAL_SYMBOL;
149 break;
150 default:
151 error(ERR_NONFATAL, "symbol `%s' redefined", label);
152 return;
153 }
154
155 if (label[0] != '.') /* not local, but not special either */
156 prevlabel = lptr->defn.label;
157 else if (!*prevlabel)
158 error(ERR_NONFATAL, "attempt to define a local label before any"
159 " non-local labels");
160
161 lptr->defn.segment = segment;
162 lptr->defn.offset = offset;
163
164 ofmt->symdef (lptr->defn.label, segment, offset,
165 lptr->defn.is_global == GLOBAL_SYMBOL);
166}
167
168void define_common (char *label, long segment, long size,
169 struct ofmt *ofmt, efunc error) {
170 union label *lptr;
171
172 lptr = find_label (label, 1);
173 switch (lptr->defn.is_global) {
174 case NOT_DEFINED_YET:
175 lptr->defn.is_global = LOCAL_SYMBOL;
176 break;
177 case GLOBAL_PLACEHOLDER:
178 lptr->defn.is_global = GLOBAL_SYMBOL;
179 break;
180 default:
181 error(ERR_NONFATAL, "symbol `%s' redefined", label);
182 return;
183 }
184
185 if (label[0] != '.') /* not local, but not special either */
186 prevlabel = lptr->defn.label;
187 else
188 error(ERR_NONFATAL, "attempt to define a local label as a "
189 "common variable");
190
191 lptr->defn.segment = segment;
192 lptr->defn.offset = 0;
193
194 ofmt->symdef (lptr->defn.label, segment, size, 2);
195}
196
197void declare_as_global (char *label, efunc error) {
198 union label *lptr;
199
200 if (islocal(label)) {
201 error(ERR_NONFATAL, "attempt to declare local symbol `%s' as"
202 " global", label);
203 return;
204 }
205 lptr = find_label (label, 1);
206 switch (lptr->defn.is_global) {
207 case NOT_DEFINED_YET:
208 lptr->defn.is_global = GLOBAL_PLACEHOLDER;
209 break;
210 case GLOBAL_PLACEHOLDER: /* already done: silently ignore */
211 case GLOBAL_SYMBOL:
212 break;
213 case LOCAL_SYMBOL:
214 error(ERR_NONFATAL, "symbol `%s': [GLOBAL] directive must"
215 " appear before symbol definition", label);
216 break;
217 }
218}
219
220int init_labels (void) {
221 int i;
222
223 for (i=0; i<LABEL_HASHES; i++) {
224 ltab[i] = (union label *) nasm_malloc (LBLK_SIZE);
225 if (!ltab[i])
226 return -1; /* can't initialise, panic */
227 init_block (ltab[i]);
228 lfree[i] = ltab[i];
229 }
230
231 perm_head = perm_tail = (struct permts *) nasm_malloc (sizeof(struct permts));
232 if (!perm_head)
233 return -1;
234
235 perm_head->next = NULL;
236 perm_head->size = PERMTS_SIZE;
237 perm_head->usage = 0;
238
239 prevlabel = "";
240
241 return 0;
242}
243
244void cleanup_labels (void) {
245 int i;
246
247 for (i=0; i<LABEL_HASHES; i++) {
248 union label *lptr, *lhold;
249
250 lptr = lhold = ltab[i];
251
252 while (lptr) {
253 while (lptr->admin.movingon != END_BLOCK) lptr++;
254 lptr = lptr->admin.next;
255 nasm_free (lhold);
256 lhold = lptr;
257 }
258 }
259
260 while (perm_head) {
261 perm_tail = perm_head;
262 perm_head = perm_head->next;
263 nasm_free (perm_tail);
264 }
265}
266
267static void init_block (union label *blk) {
268 int j;
269
270 for (j=0; j<LABEL_BLOCK-1; j++)
271 blk[j].admin.movingon = END_LIST;
272 blk[LABEL_BLOCK-1].admin.movingon = END_BLOCK;
273 blk[LABEL_BLOCK-1].admin.next = NULL;
274}
275
276static char *perm_copy (char *string1, char *string2) {
277 char *p, *q;
278 int len = strlen(string1)+strlen(string2)+1;
279
280 if (perm_tail->size - perm_tail->usage < len) {
281 perm_tail->next = (struct permts *)nasm_malloc(sizeof(struct permts));
282 perm_tail = perm_tail->next;
283 perm_tail->size = PERMTS_SIZE;
284 perm_tail->usage = 0;
285 }
286 p = q = perm_tail->data + perm_tail->usage;
287 while ( (*q = *string1++) ) q++;
288 while ( (*q++ = *string2++) );
289 perm_tail->usage = q - perm_tail->data;
290
291 return p;
292}