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 | * outdbg.c output routines for the Netwide Assembler to produce |
36 | * a debugging trace |
37 | */ |
38 | |
39 | #include "compiler.h" |
40 | |
41 | #include <stdio.h> |
42 | #include <stdlib.h> |
43 | #include <string.h> |
44 | #include <ctype.h> |
45 | #include <errno.h> |
46 | |
47 | #include "nasm.h" |
48 | #include "nasmlib.h" |
49 | #include "outform.h" |
50 | #include "outlib.h" |
51 | #include "insns.h" |
52 | |
53 | #ifdef OF_DBG |
54 | |
55 | struct Section { |
56 | struct Section *next; |
57 | int32_t number; |
58 | char *name; |
59 | } *dbgsect; |
60 | |
61 | static unsigned long dbg_max_data_dump = 128; |
62 | static bool section_labels = true; |
63 | static bool subsections_via_symbols = false; |
64 | static int32_t init_seg; |
65 | |
66 | const struct ofmt of_dbg; |
67 | static void dbg_init(void) |
68 | { |
69 | dbgsect = NULL; |
70 | fprintf(ofile, "NASM Output format debug dump\n" ); |
71 | fprintf(ofile, "input file = %s\n" , inname); |
72 | fprintf(ofile, "output file = %s\n" , outname); |
73 | init_seg = seg_alloc(); |
74 | } |
75 | |
76 | static void dbg_reset(void) |
77 | { |
78 | fprintf(ofile, "*** pass reset: pass0 = %d, passn = %" PRId64"\n" , |
79 | pass0, passn); |
80 | } |
81 | |
82 | static void dbg_cleanup(void) |
83 | { |
84 | dfmt->cleanup(); |
85 | while (dbgsect) { |
86 | struct Section *tmp = dbgsect; |
87 | dbgsect = dbgsect->next; |
88 | nasm_free(tmp->name); |
89 | nasm_free(tmp); |
90 | } |
91 | } |
92 | |
93 | static int32_t dbg_add_section(char *name, int pass, int *bits, |
94 | const char *whatwecallit) |
95 | { |
96 | int seg; |
97 | |
98 | /* |
99 | * We must have an initial default: let's make it 16. |
100 | */ |
101 | if (!name) |
102 | *bits = 16; |
103 | |
104 | if (!name) { |
105 | fprintf(ofile, "section_name on init: returning %d\n" , init_seg); |
106 | seg = init_seg; |
107 | } else { |
108 | int n = strcspn(name, " \t" ); |
109 | char *sname = nasm_strndup(name, n); |
110 | char *tail = nasm_skip_spaces(name+n); |
111 | struct Section *s; |
112 | |
113 | seg = NO_SEG; |
114 | for (s = dbgsect; s; s = s->next) |
115 | if (!strcmp(s->name, sname)) |
116 | seg = s->number; |
117 | |
118 | if (seg == NO_SEG) { |
119 | s = nasm_malloc(sizeof(*s)); |
120 | s->name = sname; |
121 | s->number = seg = seg_alloc(); |
122 | s->next = dbgsect; |
123 | dbgsect = s; |
124 | fprintf(ofile, "%s %s (%s) pass %d: returning %d\n" , |
125 | whatwecallit, name, tail, pass, seg); |
126 | |
127 | if (section_labels) |
128 | backend_label(s->name, s->number + 1, 0); |
129 | } |
130 | } |
131 | return seg; |
132 | } |
133 | |
134 | static int32_t dbg_section_names(char *name, int pass, int *bits) |
135 | { |
136 | return dbg_add_section(name, pass, bits, "section_names" ); |
137 | } |
138 | |
139 | static int32_t dbg_herelabel(const char *name, enum label_type type, |
140 | int32_t oldseg, int32_t *subsection, |
141 | bool *copyoffset) |
142 | { |
143 | int32_t newseg = oldseg; |
144 | |
145 | if (subsections_via_symbols && type != LBL_LOCAL) { |
146 | newseg = *subsection; |
147 | if (newseg == NO_SEG) { |
148 | newseg = *subsection = seg_alloc(); |
149 | *copyoffset = true; /* Minic MachO for now */ |
150 | } |
151 | } |
152 | fprintf(ofile, "herelabel %s type %d (seg %08x) -> %08x\n" , |
153 | name, type, oldseg, newseg); |
154 | |
155 | return newseg; |
156 | } |
157 | |
158 | static void dbg_deflabel(char *name, int32_t segment, int64_t offset, |
159 | int is_global, char *special) |
160 | { |
161 | fprintf(ofile, "deflabel %s := %08" PRIx32":%016" PRIx64" %s (%d)%s%s\n" , |
162 | name, segment, offset, |
163 | is_global == 2 ? "common" : is_global ? "global" : "local" , |
164 | is_global, special ? ": " : "" , special); |
165 | } |
166 | |
167 | static const char *out_type(enum out_type type) |
168 | { |
169 | static const char *out_types[] = { |
170 | "rawdata" , |
171 | "reserve" , |
172 | "zerodata" , |
173 | "address" , |
174 | "reladdr" , |
175 | "segment" |
176 | }; |
177 | static char invalid_buf[64]; |
178 | |
179 | if (type >= sizeof(out_types)/sizeof(out_types[0])) { |
180 | sprintf(invalid_buf, "[invalid type %d]" , type); |
181 | return invalid_buf; |
182 | } |
183 | |
184 | return out_types[type]; |
185 | } |
186 | |
187 | static const char *out_sign(enum out_sign sign) |
188 | { |
189 | static const char *out_signs[] = { |
190 | "wrap" , |
191 | "signed" , |
192 | "unsigned" |
193 | }; |
194 | static char invalid_buf[64]; |
195 | |
196 | if (sign >= sizeof(out_signs)/sizeof(out_signs[0])) { |
197 | sprintf(invalid_buf, "[invalid sign %d]" , sign); |
198 | return invalid_buf; |
199 | } |
200 | |
201 | return out_signs[sign]; |
202 | } |
203 | |
204 | static void dbg_out(const struct out_data *data) |
205 | { |
206 | fprintf(ofile, |
207 | "out to %" PRIx32":%" PRIx64" %s %s bits %d insoffs %d/%d " |
208 | "size %" PRIu64, |
209 | data->segment, data->offset, |
210 | out_type(data->type), out_sign(data->sign), |
211 | data->bits, data->insoffs, data->inslen, data->size); |
212 | if (data->itemp) { |
213 | fprintf(ofile, " ins %s(%d)" , |
214 | nasm_insn_names[data->itemp->opcode], data->itemp->operands); |
215 | } else { |
216 | fprintf(ofile, " no ins (plain data)" ); |
217 | } |
218 | |
219 | if (data->type == OUT_ADDRESS || data->type == OUT_RELADDR || |
220 | data->type == OUT_SEGMENT) { |
221 | fprintf(ofile, " target %" PRIx32":%" PRIx64, |
222 | data->tsegment, data->toffset); |
223 | if (data->twrt != NO_SEG) |
224 | fprintf(ofile, " wrt %" PRIx32, data->twrt); |
225 | } |
226 | if (data->type == OUT_RELADDR) |
227 | fprintf(ofile, " relbase %" PRIx64, data->relbase); |
228 | |
229 | putc('\n', ofile); |
230 | |
231 | if (data->type == OUT_RAWDATA) { |
232 | if ((size_t)data->size != data->size) { |
233 | fprintf(ofile, " data: <error: impossible size>\n" ); |
234 | } else if (!data->data) { |
235 | fprintf(ofile, " data: <error: null pointer>\n" ); |
236 | } else if (dbg_max_data_dump != -1UL && |
237 | data->size > dbg_max_data_dump) { |
238 | fprintf(ofile, " data: <%" PRIu64" bytes>\n" , data->size); |
239 | } else { |
240 | size_t i, j; |
241 | const uint8_t *bytes = data->data; |
242 | for (i = 0; i < data->size; i += 16) { |
243 | fprintf(ofile, " data:" ); |
244 | for (j = 0; j < 16; j++) { |
245 | if (i+j >= data->size) |
246 | fprintf(ofile, " " ); |
247 | else |
248 | fprintf(ofile, "%c%02x" , |
249 | (j == 8) ? '-' : ' ', bytes[i+j]); |
250 | } |
251 | fprintf(ofile," " ); |
252 | for (j = 0; j < 16; j++) { |
253 | if (i+j >= data->size) { |
254 | putc(' ', ofile); |
255 | } else { |
256 | if (bytes[i+j] >= 32 && bytes[i+j] <= 126) |
257 | putc(bytes[i+j], ofile); |
258 | else |
259 | putc('.', ofile); |
260 | } |
261 | } |
262 | putc('\n', ofile); |
263 | } |
264 | } |
265 | } |
266 | |
267 | /* This is probably the only place were we'll call this this way... */ |
268 | nasm_do_legacy_output(data); |
269 | } |
270 | |
271 | static void dbg_legacy_out(int32_t segto, const void *data, |
272 | enum out_type type, uint64_t size, |
273 | int32_t segment, int32_t wrt) |
274 | { |
275 | int32_t ldata; |
276 | |
277 | if (type == OUT_ADDRESS) |
278 | fprintf(ofile, " legacy: out to %" PRIx32", len = %d: " , |
279 | segto, (int)abs((int)size)); |
280 | else |
281 | fprintf(ofile, " legacy: out to %" PRIx32", len = %" PRId64" (0x%" PRIx64"): " , |
282 | segto, (int64_t)size, size); |
283 | |
284 | switch (type) { |
285 | case OUT_RESERVE: |
286 | fprintf(ofile, "reserved.\n" ); |
287 | break; |
288 | case OUT_RAWDATA: |
289 | fprintf(ofile, "rawdata\n" ); /* Already have a data dump */ |
290 | break; |
291 | case OUT_ADDRESS: |
292 | ldata = *(int64_t *)data; |
293 | fprintf(ofile, "addr %08" PRIx32" (seg %08" PRIx32", wrt %08" PRIx32")\n" , |
294 | ldata, segment, wrt); |
295 | break; |
296 | case OUT_REL1ADR: |
297 | fprintf(ofile, "rel1adr %02" PRIx8" (seg %08" PRIx32")\n" , |
298 | (uint8_t)*(int64_t *)data, segment); |
299 | break; |
300 | case OUT_REL2ADR: |
301 | fprintf(ofile, "rel2adr %04" PRIx16" (seg %08" PRIx32")\n" , |
302 | (uint16_t)*(int64_t *)data, segment); |
303 | break; |
304 | case OUT_REL4ADR: |
305 | fprintf(ofile, "rel4adr %08" PRIx32" (seg %08" PRIx32")\n" , |
306 | (uint32_t)*(int64_t *)data, |
307 | segment); |
308 | break; |
309 | case OUT_REL8ADR: |
310 | fprintf(ofile, "rel8adr %016" PRIx64" (seg %08" PRIx32")\n" , |
311 | (uint64_t)*(int64_t *)data, segment); |
312 | break; |
313 | default: |
314 | fprintf(ofile, "unknown\n" ); |
315 | break; |
316 | } |
317 | } |
318 | |
319 | static void dbg_sectalign(int32_t seg, unsigned int value) |
320 | { |
321 | fprintf(ofile, "set alignment (%d) for segment (%u)\n" , |
322 | seg, value); |
323 | } |
324 | |
325 | static int32_t dbg_segbase(int32_t segment) |
326 | { |
327 | return segment; |
328 | } |
329 | |
330 | static enum directive_result |
331 | dbg_directive(enum directive directive, char *value, int pass) |
332 | { |
333 | switch (directive) { |
334 | /* |
335 | * The .obj GROUP directive is nontrivial to emulate in a macro. |
336 | * It effectively creates a "pseudo-section" containing the first |
337 | * space-separated argument; the rest we ignore. |
338 | */ |
339 | case D_GROUP: |
340 | { |
341 | int dummy; |
342 | dbg_add_section(value, pass, &dummy, "directive:group" ); |
343 | break; |
344 | } |
345 | |
346 | default: |
347 | break; |
348 | } |
349 | |
350 | fprintf(ofile, "directive [%s] value [%s] (pass %d)\n" , |
351 | directive_dname(directive), value, pass); |
352 | return DIRR_OK; |
353 | } |
354 | |
355 | static enum directive_result |
356 | dbg_pragma(const struct pragma *pragma); |
357 | |
358 | static const struct pragma_facility dbg_pragma_list[] = { |
359 | { NULL, dbg_pragma } |
360 | }; |
361 | |
362 | static enum directive_result |
363 | dbg_pragma(const struct pragma *pragma) |
364 | { |
365 | fprintf(ofile, "pragma %s(%s) %s[%s] %s\n" , |
366 | pragma->facility_name, |
367 | pragma->facility->name ? pragma->facility->name : "<default>" , |
368 | pragma->opname, directive_dname(pragma->opcode), |
369 | pragma->tail); |
370 | |
371 | if (pragma->facility == &dbg_pragma_list[0]) { |
372 | switch (pragma->opcode) { |
373 | case D_MAXDUMP: |
374 | if (!nasm_stricmp(pragma->tail, "unlimited" )) { |
375 | dbg_max_data_dump = -1UL; |
376 | } else { |
377 | char *ep; |
378 | unsigned long arg; |
379 | |
380 | errno = 0; |
381 | arg = strtoul(pragma->tail, &ep, 0); |
382 | if (errno || *nasm_skip_spaces(ep)) { |
383 | nasm_error(ERR_WARNING | WARN_BAD_PRAGMA | ERR_PASS2, |
384 | "invalid %%pragma dbg maxdump argument" ); |
385 | return DIRR_ERROR; |
386 | } else { |
387 | dbg_max_data_dump = arg; |
388 | } |
389 | } |
390 | break; |
391 | case D_NOSECLABELS: |
392 | section_labels = false; |
393 | break; |
394 | case D_SUBSECTIONS_VIA_SYMBOLS: |
395 | subsections_via_symbols = true; |
396 | break; |
397 | default: |
398 | break; |
399 | } |
400 | } |
401 | return DIRR_OK; |
402 | } |
403 | |
404 | static const char * const types[] = { |
405 | "unknown" , "label" , "byte" , "word" , "dword" , "float" , "qword" , "tbyte" |
406 | }; |
407 | static void dbgdbg_init(void) |
408 | { |
409 | fprintf(ofile, " With debug info\n" ); |
410 | } |
411 | static void dbgdbg_cleanup(void) |
412 | { |
413 | } |
414 | |
415 | static void dbgdbg_linnum(const char *lnfname, int32_t lineno, int32_t segto) |
416 | { |
417 | fprintf(ofile, "dbglinenum %s(%" PRId32") segment %" PRIx32"\n" , |
418 | lnfname, lineno, segto); |
419 | } |
420 | static void dbgdbg_deflabel(char *name, int32_t segment, |
421 | int64_t offset, int is_global, char *special) |
422 | { |
423 | fprintf(ofile, "dbglabel %s := %08" PRIx32":%016" PRIx64" %s (%d)%s%s\n" , |
424 | name, |
425 | segment, offset, |
426 | is_global == 2 ? "common" : is_global ? "global" : "local" , |
427 | is_global, special ? ": " : "" , special); |
428 | } |
429 | static void dbgdbg_define(const char *type, const char *params) |
430 | { |
431 | fprintf(ofile, "dbgdirective [%s] value [%s]\n" , type, params); |
432 | } |
433 | static void dbgdbg_output(int output_type, void *param) |
434 | { |
435 | (void)output_type; |
436 | (void)param; |
437 | } |
438 | static void dbgdbg_typevalue(int32_t type) |
439 | { |
440 | fprintf(ofile, "new type: %s(%" PRIX32")\n" , |
441 | types[TYM_TYPE(type) >> 3], TYM_ELEMENTS(type)); |
442 | } |
443 | |
444 | static const struct pragma_facility dbgdbg_pragma_list[] = { |
445 | { "dbgdbg" , dbg_pragma }, |
446 | { NULL, dbg_pragma } /* Won't trigger, "debug" is a reserved ns */ |
447 | }; |
448 | |
449 | static const struct dfmt debug_debug_form = { |
450 | "Trace of all info passed to debug stage" , |
451 | "debug" , |
452 | dbgdbg_init, |
453 | dbgdbg_linnum, |
454 | dbgdbg_deflabel, |
455 | dbgdbg_define, |
456 | dbgdbg_typevalue, |
457 | dbgdbg_output, |
458 | dbgdbg_cleanup, |
459 | dbgdbg_pragma_list |
460 | }; |
461 | |
462 | static const struct dfmt * const debug_debug_arr[3] = { |
463 | &debug_debug_form, |
464 | &null_debug_form, |
465 | NULL |
466 | }; |
467 | |
468 | extern macros_t dbg_stdmac[]; |
469 | |
470 | const struct ofmt of_dbg = { |
471 | "Trace of all info passed to output stage" , |
472 | "dbg" , |
473 | ".dbg" , |
474 | OFMT_TEXT, |
475 | 64, |
476 | debug_debug_arr, |
477 | &debug_debug_form, |
478 | dbg_stdmac, |
479 | dbg_init, |
480 | dbg_reset, |
481 | dbg_out, |
482 | dbg_legacy_out, |
483 | dbg_deflabel, |
484 | dbg_section_names, |
485 | dbg_herelabel, |
486 | dbg_sectalign, |
487 | dbg_segbase, |
488 | dbg_directive, |
489 | dbg_cleanup, |
490 | dbg_pragma_list |
491 | }; |
492 | |
493 | #endif /* OF_DBG */ |
494 | |