1/*
2 * Stabs debugging format
3 *
4 *  Copyright (C) 2003-2007  Michael Urman
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27#include <util.h>
28
29#include <libyasm.h>
30
31typedef enum {
32    N_UNDF = 0x00,      /* Undefined */
33    N_GSYM = 0x20,      /* Global symbol */
34    N_FNAME = 0x22,     /* Function name (BSD Fortran) */
35    N_FUN = 0x24,       /* Function name or Text segment variable */
36    N_STSYM = 0x26,     /* Data segment file-scope variable */
37    N_LCSYM = 0x28,     /* BSS segment file-scope variable */
38    N_MAIN = 0x2a,      /* Name of main routine */
39    N_ROSYM = 0x2c,     /* Variable in .rodata section */
40    N_PC = 0x30,        /* Global symbol (Pascal) */
41    N_SYMS = 0x32,      /* Number of symbols (Ultrix V4.0) */
42    N_NOMAP = 0x34,     /* No DST map */
43    N_OBJ = 0x38,       /* Object file (Solaris2) */
44    N_OPT = 0x3c,       /* Debugger options (Solaris2) */
45    N_RSYM = 0x40,      /* Register variable */
46    N_M2C = 0x42,       /* Modula-2 compilation unit */
47    N_SLINE = 0x44,     /* Line numbers in .text segment */
48    N_DSLINE = 0x46,    /* Line numbers in .data segment */
49    N_BSLINE = 0x48,    /* Line numbers in .bss segment */
50    N_BROWS = 0x48,     /* Source code .cb file's path */
51    N_DEFD = 0x4a,      /* GNU Modula-2 definition module dependency */
52    N_FLINE = 0x4c,     /* Function start/body/end line numbers (Solaris2) */
53    N_EHDECL = 0x50,    /* GNU C++ exception variable */
54    N_MOD2 = 0x50,      /* Modula2 info for imc (Ultrix V4.0) */
55    N_CATCH = 0x54,     /* GNU C++ catch clause */
56    N_SSYM = 0x60,      /* Structure or union element */
57    N_ENDM = 0x62,      /* Last stab for module (Solaris2) */
58    N_SO = 0x64,        /* Path and name of source files */
59    N_LSYM = 0x80,      /* Stack variable */
60    N_BINCL = 0x84,     /* Beginning of include file */
61    N_SOL = 0x84,       /* Name of include file */
62    N_PSYM = 0xa0,      /* Parameter variable */
63    N_EINCL = 0xa2,     /* End of include file */
64    N_ENTRY = 0xa4,     /* Alternate entry point */
65    N_LBRAC = 0xc0,     /* Beginning of lexical block */
66    N_EXCL = 0xc2,      /* Placeholder for a deleted include file */
67    N_SCOPE = 0xc4,     /* Modula 2 scope info (Sun) */
68    N_RBRAC = 0xe0,     /* End of lexical block */
69    N_BCOMM = 0xe2,     /* Begin named common block */
70    N_ECOMM = 0xe4,     /* End named common block */
71    N_ECOML = 0xe8,     /* Member of common block */
72    N_WITH = 0xea,      /* Pascal with statement: type,,0,0,offset (Solaris2) */
73    N_NBTEXT = 0xf0,    /* Gould non-base registers */
74    N_NBDATA = 0xf2,    /* Gould non-base registers */
75    N_NBBSS = 0xf4,     /* Gould non-base registers */
76    N_NBSTS = 0xf6,     /* Gould non-base registers */
77    N_NBLCS = 0xf8      /* Gould non-base registers */
78} stabs_stab_type;
79
80typedef struct yasm_dbgfmt_stabs {
81    yasm_dbgfmt_base dbgfmt;        /* base structure */
82} yasm_dbgfmt_stabs;
83
84typedef struct {
85    unsigned long lastline;     /* track line and file of bytecodes */
86    unsigned long curline;
87    const char *lastfile;
88    const char *curfile;
89
90    unsigned int stablen;       /* size of a stab for current machine */
91    unsigned long stabcount;    /* count stored stabs; doesn't include first */
92
93    yasm_section *stab;         /* sections to which stabs, stabstrs appended */
94    yasm_section *stabstr;
95
96    yasm_bytecode *basebc;      /* base bytecode from which to track SLINEs */
97
98    yasm_object *object;
99    yasm_linemap *linemap;
100    yasm_errwarns *errwarns;
101} stabs_info;
102
103typedef struct {
104    /*@null@*/ yasm_bytecode *bcstr;    /* bytecode in stabstr for string */
105    stabs_stab_type type;               /* stab type: N_* */
106    unsigned char other;                /* unused, but stored here anyway */
107    unsigned short desc;                /* description element of a stab */
108    /*@null@*/ yasm_symrec *symvalue;   /* value element needing relocation */
109    /*@null@*/yasm_bytecode *bcvalue;   /* relocated stab's bytecode */
110    unsigned long value;                /* fallthrough value if above NULL */
111} stabs_stab;
112
113/* Bytecode callback function prototypes */
114
115static void stabs_bc_str_destroy(void *contents);
116static void stabs_bc_str_print(const void *contents, FILE *f, int
117                               indent_level);
118static int stabs_bc_str_calc_len
119    (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
120static int stabs_bc_str_tobytes
121    (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d,
122     yasm_output_value_func output_value,
123     /*@null@*/ yasm_output_reloc_func output_reloc);
124
125static void stabs_bc_stab_destroy(void *contents);
126static void stabs_bc_stab_print(const void *contents, FILE *f, int
127                                indent_level);
128static int stabs_bc_stab_calc_len
129    (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
130static int stabs_bc_stab_tobytes
131    (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d,
132     yasm_output_value_func output_value,
133     /*@null@*/ yasm_output_reloc_func output_reloc);
134
135/* Bytecode callback structures */
136
137static const yasm_bytecode_callback stabs_bc_str_callback = {
138    stabs_bc_str_destroy,
139    stabs_bc_str_print,
140    yasm_bc_finalize_common,
141    NULL,
142    stabs_bc_str_calc_len,
143    yasm_bc_expand_common,
144    stabs_bc_str_tobytes,
145    0
146};
147
148static const yasm_bytecode_callback stabs_bc_stab_callback = {
149    stabs_bc_stab_destroy,
150    stabs_bc_stab_print,
151    yasm_bc_finalize_common,
152    NULL,
153    stabs_bc_stab_calc_len,
154    yasm_bc_expand_common,
155    stabs_bc_stab_tobytes,
156    0
157};
158
159yasm_dbgfmt_module yasm_stabs_LTX_dbgfmt;
160
161
162static /*@null@*/ /*@only@*/ yasm_dbgfmt *
163stabs_dbgfmt_create(yasm_object *object)
164{
165    yasm_dbgfmt_stabs *dbgfmt_stabs = yasm_xmalloc(sizeof(yasm_dbgfmt_stabs));
166    dbgfmt_stabs->dbgfmt.module = &yasm_stabs_LTX_dbgfmt;
167    return (yasm_dbgfmt *)dbgfmt_stabs;
168}
169
170static void
171stabs_dbgfmt_destroy(/*@only@*/ yasm_dbgfmt *dbgfmt)
172{
173    yasm_xfree(dbgfmt);
174}
175
176/* Create and add a new strtab-style string bytecode to a section, updating
177 * offset on insertion; no optimization necessary */
178/* Copies the string, so you must still free yours as normal */
179static yasm_bytecode *
180stabs_dbgfmt_append_bcstr(yasm_section *sect, const char *str)
181{
182    yasm_bytecode *bc;
183
184    bc = yasm_bc_create_common(&stabs_bc_str_callback, yasm__xstrdup(str), 0);
185    bc->len = (unsigned long)(strlen(str)+1);
186    bc->offset = yasm_bc_next_offset(yasm_section_bcs_last(sect));
187
188    yasm_section_bcs_append(sect, bc);
189
190    return bc;
191}
192
193/* Create and add a new stab bytecode to a section, updating offset on
194 * insertion; no optimization necessary. */
195/* Requires a string bytecode, or NULL, for its string entry */
196static stabs_stab *
197stabs_dbgfmt_append_stab(stabs_info *info, yasm_section *sect,
198                         /*@null@*/ yasm_bytecode *bcstr, stabs_stab_type type,
199                         unsigned long desc, /*@null@*/ yasm_symrec *symvalue,
200                         /*@null@*/ yasm_bytecode *bcvalue, unsigned long value)
201{
202    yasm_bytecode *bc;
203    stabs_stab *stab = yasm_xmalloc(sizeof(stabs_stab));
204
205    stab->other = 0;
206    stab->bcstr = bcstr;
207    stab->type = type;
208    stab->desc = (unsigned short)desc;
209    stab->symvalue = symvalue;
210    stab->bcvalue = bcvalue;
211    stab->value = value;
212
213    bc = yasm_bc_create_common(&stabs_bc_stab_callback, stab,
214                               bcvalue ? bcvalue->line : 0);
215    bc->len = info->stablen;
216    bc->offset = yasm_bc_next_offset(yasm_section_bcs_last(sect));
217
218    yasm_section_bcs_append(sect, bc);
219
220    info->stabcount++;
221    return stab;
222}
223
224static void
225stabs_dbgfmt_generate_n_fun(stabs_info *info, yasm_bytecode *bc)
226{
227    /* check all syms at this bc for potential function syms */
228    int bcsym;
229    for (bcsym=0; bc->symrecs && bc->symrecs[bcsym]; bcsym++)
230    {
231        char *str;
232        yasm_symrec *sym = bc->symrecs[bcsym];
233        const char *name = yasm_symrec_get_name(sym);
234
235        /* best guess algorithm - ignore labels containing a . or $ */
236        if (strchr(name, '.') || strchr(name, '$'))
237            continue;
238
239        /* if a function, update basebc, and output a funcname:F1 stab */
240        info->basebc = bc;
241
242        str = yasm_xmalloc(strlen(name)+4);
243        strcpy(str, name);
244        strcat(str, ":F1");
245        stabs_dbgfmt_append_stab(info, info->stab,
246                                 stabs_dbgfmt_append_bcstr(info->stabstr, str),
247                                 N_FUN, 0, sym, info->basebc, 0);
248        yasm_xfree(str);
249        break;
250    }
251}
252
253static int
254stabs_dbgfmt_generate_bcs(yasm_bytecode *bc, void *d)
255{
256    stabs_info *info = (stabs_info *)d;
257    yasm_linemap_lookup(info->linemap, bc->line, &info->curfile,
258                        &info->curline);
259
260    /* check for new function */
261    stabs_dbgfmt_generate_n_fun(info, bc);
262
263    if (info->lastfile != info->curfile) {
264        info->lastline = 0; /* new file, so line changes */
265        /*stabs_dbgfmt_append_stab(info, info->stab,
266            stabs_dbgfmt_append_bcstr(info->stabstr, info->curfile),
267            N_SOL, 0, NULL, bc, 0);*/
268    }
269
270    /* output new line stabs if there's a basebc (known function) */
271    if (info->basebc != NULL && info->curline != info->lastline) {
272        info->lastline = bc->line;
273        stabs_dbgfmt_append_stab(info, info->stab, NULL, N_SLINE,
274                                 info->curline, NULL, NULL,
275                                 bc->offset - info->basebc->offset);
276    }
277
278    info->lastline = info->curline;
279    info->lastfile = info->curfile;
280
281    return 0;
282}
283
284static int
285stabs_dbgfmt_generate_sections(yasm_section *sect, /*@null@*/ void *d)
286{
287    stabs_info *info = (stabs_info *)d;
288    const char *sectname=yasm_section_get_name(sect);
289
290    /* each section has a different base symbol */
291    info->basebc = NULL;
292
293    /* handle first (pseudo) bc separately */
294    stabs_dbgfmt_generate_n_fun(d, yasm_section_bcs_first(sect));
295
296    yasm_section_bcs_traverse(sect, info->errwarns, d,
297                              stabs_dbgfmt_generate_bcs);
298
299    if (yasm__strcasecmp(sectname, ".text")==0) {
300        /* Close out last function by appending a null SO stab after last bc */
301        yasm_bytecode *bc = yasm_section_bcs_last(sect);
302        yasm_symrec *sym =
303            yasm_symtab_define_label(info->object->symtab, ".n_so", bc, 1,
304                                     bc->line);
305        stabs_dbgfmt_append_stab(info, info->stab, 0, N_SO, 0, sym, bc, 0);
306    }
307
308    return 1;
309}
310
311static void
312stabs_dbgfmt_generate(yasm_object *object, yasm_linemap *linemap,
313                      yasm_errwarns *errwarns)
314{
315    stabs_info info;
316    int new;
317    yasm_bytecode *dbgbc;
318    stabs_stab *stab;
319    yasm_bytecode *filebc, *nullbc, *laststr, *firstbc;
320    yasm_symrec *firstsym;
321    yasm_section *stext;
322
323    /* Stablen is determined by arch/machine */
324    if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") == 0) {
325        info.stablen = 12;
326    }
327    else /* unknown machine; generate nothing */
328        return;
329
330    info.object = object;
331    info.linemap = linemap;
332    info.errwarns = errwarns;
333    info.lastline = 0;
334    info.stabcount = 0;
335    info.stab = yasm_object_get_general(object, ".stab", 4, 0, 0, &new, 0);
336    if (!new) {
337        yasm_bytecode *last = yasm_section_bcs_last(info.stab);
338        if (last == NULL) {
339            yasm_error_set(YASM_ERROR_GENERAL,
340                N_("stabs debugging conflicts with user-defined section .stab"));
341            yasm_errwarn_propagate(errwarns,
342                                   yasm_section_bcs_first(info.stab)->line);
343        } else {
344            yasm_warn_set(YASM_WARN_GENERAL,
345                N_("stabs debugging overrides empty section .stab"));
346            yasm_errwarn_propagate(errwarns, 0);
347        }
348    }
349
350    info.stabstr =
351        yasm_object_get_general(object, ".stabstr", 1, 0, 0, &new, 0);
352    if (!new) {
353        yasm_bytecode *last = yasm_section_bcs_last(info.stabstr);
354        if (last == NULL) {
355            yasm_error_set(YASM_ERROR_GENERAL,
356                N_("stabs debugging conflicts with user-defined section .stabstr"));
357            yasm_errwarn_propagate(errwarns,
358                                   yasm_section_bcs_first(info.stab)->line);
359        } else {
360            yasm_warn_set(YASM_WARN_GENERAL,
361                N_("stabs debugging overrides empty section .stabstr"));
362            yasm_errwarn_propagate(errwarns, 0);
363        }
364    }
365
366    /* initial pseudo-stab */
367    stab = yasm_xmalloc(sizeof(stabs_stab));
368    dbgbc = yasm_bc_create_common(&stabs_bc_stab_callback, stab, 0);
369    dbgbc->len = info.stablen;
370    dbgbc->offset = 0;
371    yasm_section_bcs_append(info.stab, dbgbc);
372
373    /* initial strtab bytecodes */
374    nullbc = stabs_dbgfmt_append_bcstr(info.stabstr, "");
375    filebc = stabs_dbgfmt_append_bcstr(info.stabstr, object->src_filename);
376
377    stext = yasm_object_find_general(object, ".text");
378    firstsym = yasm_symtab_use(object->symtab, ".text", 0);
379    firstbc = yasm_section_bcs_first(stext);
380    /* N_SO file stab */
381    stabs_dbgfmt_append_stab(&info, info.stab, filebc, N_SO, 0,
382                             firstsym, firstbc, 0);
383
384    yasm_object_sections_traverse(object, (void *)&info,
385                                  stabs_dbgfmt_generate_sections);
386
387    /* fill initial pseudo-stab's fields */
388    laststr = yasm_section_bcs_last(info.stabstr);
389    if (laststr == NULL)
390        yasm_internal_error(".stabstr has no entries");
391
392    stab->bcvalue = NULL;
393    stab->symvalue = NULL;
394    stab->value = yasm_bc_next_offset(laststr);
395    stab->bcstr = filebc;
396    stab->type = N_UNDF;
397    stab->other = 0;
398    if (info.stabcount > 0xffff) {
399        yasm_warn_set(YASM_WARN_GENERAL, N_("over 65535 stabs"));
400        yasm_errwarn_propagate(errwarns, 0);
401        stab->desc = 0xffff;
402    } else
403        stab->desc = (unsigned short)info.stabcount;
404}
405
406static int
407stabs_bc_stab_tobytes(yasm_bytecode *bc, unsigned char **bufp,
408                      unsigned char *bufstart, void *d,
409                      yasm_output_value_func output_value,
410                      yasm_output_reloc_func output_reloc)
411{
412    /* This entire function, essentially the core of rendering stabs to a file,
413     * needs to become endian aware.  Size appears not to be an issue, as known
414     * 64-bit systems use truncated values in 32-bit fields. */
415
416    const stabs_stab *stab = (const stabs_stab *)bc->contents;
417    unsigned char *buf = *bufp;
418
419    YASM_WRITE_32_L(buf, stab->bcstr ? stab->bcstr->offset : 0);
420    YASM_WRITE_8(buf, stab->type);
421    YASM_WRITE_8(buf, stab->other);
422    YASM_WRITE_16_L(buf, stab->desc);
423
424    if (stab->symvalue != NULL) {
425        bc->offset += 8;
426        output_reloc(stab->symvalue, bc, buf, 4, 32, 0, d);
427        bc->offset -= 8;
428        buf += 4;
429    }
430    else if (stab->bcvalue != NULL) {
431        YASM_WRITE_32_L(buf, stab->bcvalue->offset);
432    }
433    else {
434        YASM_WRITE_32_L(buf, stab->value);
435    }
436
437    *bufp = buf;
438    return 0;
439}
440
441static int
442stabs_bc_str_tobytes(yasm_bytecode *bc, unsigned char **bufp,
443                     unsigned char *bufstart, void *d,
444                     yasm_output_value_func output_value,
445                     yasm_output_reloc_func output_reloc)
446{
447    const char *str = (const char *)bc->contents;
448    unsigned char *buf = *bufp;
449
450    strcpy((char *)buf, str);
451    buf += strlen(str)+1;
452
453    *bufp = buf;
454    return 0;
455}
456
457static void
458stabs_bc_stab_destroy(void *contents)
459{
460    yasm_xfree(contents);
461}
462
463static void
464stabs_bc_str_destroy(void *contents)
465{
466    yasm_xfree(contents);
467}
468
469static void
470stabs_bc_stab_print(const void *contents, FILE *f, int indent_level)
471{
472    const stabs_stab *stab = (const stabs_stab *)contents;
473    const char *str = "";
474    fprintf(f, "%*s.stabs \"%s\", 0x%x, 0x%x, 0x%x, 0x%lx\n",
475            indent_level, "", str, stab->type, stab->other, stab->desc,
476            stab->bcvalue ? stab->bcvalue->offset : stab->value);
477}
478
479static void
480stabs_bc_str_print(const void *contents, FILE *f, int indent_level)
481{
482    fprintf(f, "%*s\"%s\"\n", indent_level, "", (const char *)contents);
483}
484
485static int
486stabs_bc_stab_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
487                       void *add_span_data)
488{
489    yasm_internal_error(N_("tried to resolve a stabs stab bytecode"));
490    /*@notreached@*/
491    return 0;
492}
493
494static int
495stabs_bc_str_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
496                      void *add_span_data)
497{
498    yasm_internal_error(N_("tried to resolve a stabs str bytecode"));
499    /*@notreached@*/
500    return 0;
501}
502
503/* Define dbgfmt structure -- see dbgfmt.h for details */
504yasm_dbgfmt_module yasm_stabs_LTX_dbgfmt = {
505    "Stabs debugging format",
506    "stabs",
507    NULL,       /* no directives */
508    stabs_dbgfmt_create,
509    stabs_dbgfmt_destroy,
510    stabs_dbgfmt_generate
511};
512