int dwarf_rip_debug_info(uintptr_t rip, struct Rip_debug_info *info) { // these symbols are defined in core/core.ld extern uint8_t _debug_aranges_begin[], _debug_aranges_end[]; info->file_name = "<unknown>"; info->fn_name = "<unknown>"; info->fn_offset = 0; if (_debug_aranges_end <= _debug_aranges_begin) // no debugging info? return -1; // Search the table of address ranges for the instruction pointer uint8_t *p = _debug_aranges_begin; while (p < _debug_aranges_end) { // read the header uint32_t length = extract_uint(p, 4, &p); uint8_t *e = p + length; extract_uint(p, 2, &p); // version uint32_t offset = extract_uint(p, 4, &p); extract_uint(p, 1, &p); // address size extract_uint(p, 1, &p); // segment size // the first touple begins at an offset that is a multiple of the size // of a single tuple (i.e. twice the size of an address) p += 4; while (p < e) { uint64_t range_addr = extract_uint(p, 8, &p); uint64_t range_length = extract_uint(p, 8, &p); //kprintf("%p - %p\n", range_addr, range_addr + range_length); // each set of tuples is terminated by a 0 for the address and 0 // for the length if (range_addr == 0 && range_length == 0) break; if (rip >= range_addr && rip < (range_addr + range_length)) // the instruction pointer belongs to this compile unit return parse_cu(pa2kva(offset), rip, info); } } return 0; }
// Find the abbreviation declaration for the given code. Returns pointer to the // declaration's tag. // If you pass an invalid code, the behaviour is unpredicted! TODO: fix this static uint8_t * find_abbrev_decl(uint8_t *p, uint64_t code) { for (;;) { uint64_t c = extract_uleb128(p, &p); if (c == code) return p; if (c == 0) continue; extract_uleb128(p, &p); extract_uint(p, 1, &p); for (;;) { uint64_t name = extract_uleb128(p, &p); uint64_t form = extract_uleb128(p, &p); if (name == 0 && form == 0) break; } } }
static void write_int (st_parameter_dt *dtp, const fnode *f, const char *source, int len, const char *(*conv) (GFC_UINTEGER_LARGEST, char *, size_t)) { GFC_UINTEGER_LARGEST n = 0; int w, m, digits, nzero, nblank; char *p; const char *q; char itoa_buf[GFC_BTOA_BUF_SIZE]; w = f->u.integer.w; m = f->u.integer.m; n = extract_uint (source, len); /* Special case: */ if (m == 0 && n == 0) { if (w == 0) w = 1; p = write_block (dtp, w); if (p == NULL) return; memset (p, ' ', w); goto done; } q = conv (n, itoa_buf, sizeof (itoa_buf)); digits = strlen (q); /* Select a width if none was specified. The idea here is to always print something. */ if (w == 0) w = ((digits < m) ? m : digits); p = write_block (dtp, w); if (p == NULL) return; nzero = 0; if (digits < m) nzero = m - digits; /* See if things will work. */ nblank = w - (nzero + digits); if (nblank < 0) { star_fill (p, w); goto done; } if (!dtp->u.p.no_leading_blank) { memset (p, ' ', nblank); p += nblank; memset (p, '0', nzero); p += nzero; memcpy (p, q, digits); } else { memset (p, '0', nzero); p += nzero; memcpy (p, q, digits); p += digits; memset (p, ' ', nblank); dtp->u.p.no_leading_blank = 0; } done: return; }
int main(int argc, char **argv){ char *optstr = "c:f:gmno:p:q:s:S:"; char *usage = "nbsp2ldm [-c ccbsize] [-f feedtype] [-g] [-m] [-n]" " [-o origin] [-p prodid] [-q pqfname] [-s seq]" " <filename> | -S <filesize> < stdin"; int status = 0; int c; while((status == 0) && ((c = getopt(argc, argv, optstr)) != -1)){ switch(c){ case 'c': status = extract_uint(optarg, &g.opt_ccbsize); if(status != 0) errx(1, "Invalid value for [-c]."); break; case 'f': status = extract_uint(optarg, &g.opt_feedtype); if(status != 0) errx(1, "Invalid value for [-f]."); break; case 'g': g.opt_gempak = 1; break; case 'm': g.opt_md5seq = 1; break; case 'n': g.opt_noccb = 1; break; case 'o': g.opt_origin = optarg; break; case 'p': g.opt_prodid = optarg; break; case 'q': g.opt_pqfname = optarg; break; case 's': g.opt_seq_str = optarg; status = extract_uint(optarg, &g.seq); if(status != 0) errx(1, "Invalid value for [-s]."); break; case 'S': status = extract_uint(optarg, &g.opt_filesize); if(status != 0) errx(1, "Invalid value for [-S]."); break; case 'h': default: status = 1; errx(1, usage); break; } } if(optind < argc - 1) errx(1, "Too many arguments."); else if(optind == argc - 1) g.input_fname = argv[optind++]; else if((g.opt_filesize == 0) || (g.opt_prodid == NULL)) errx(1, "Options [-S] and [-p] are mandatory when reading from stdin."); if(atexit(cleanup) != 0) err(1, "atexit"); status = process_file(); return(status != 0 ? 1 : 0); }
static void process_args(int argc, char **argv, char *optstr, int f_set_defaults) { char *usage = "nbsp2ldm [OPTIONS] <filename> \n\ nbsp2ldm [OPTIONS] < stdin \n\ nbsp2ldm -S <filesize> [OPTIONS] < stdin \n\ OPTIONS = [-b] [-c ccbsize] [-f feedtype] [-g] [-m] [-n] \n\ [-o origin] [-p prodid] [-q pqfname] [-s seq] [-v]"; int status = 0; int c; /* * If (optind != 1) it means we are re-entering this function. In * FreeBSD optreset must be set to 1 before the second and each additional * set of calls to getopt(), and the variable optind must be reinitialized. */ if(optind != 1){ optreset = 1; optind = 1; } while((c = getopt(argc, argv, optstr)) != -1) { switch(c){ case 'b': g.opt_background = 1; break; case 'c': status = extract_uint(optarg, &g.opt_ccbsize); if(status != 0) log_errx(1, "Invalid value for [-c]."); if(f_set_defaults) gdefault.opt_ccbsize = g.opt_ccbsize; break; case 'd': g.opt_strsplit_delim = optarg; break; case 'f': status = extract_uint(optarg, &g.opt_feedtype); if(status != 0) log_errx(1, "Invalid value for [-f]."); if(f_set_defaults) gdefault.opt_feedtype = g.opt_feedtype; break; case 'g': g.opt_gempak = 1; if(f_set_defaults) gdefault.opt_gempak = 1; break; case 'm': g.opt_md5seq = 1; if(f_set_defaults) gdefault.opt_md5seq = 1; break; case 'n': g.opt_noccb = 1; if(f_set_defaults) gdefault.opt_noccb = 1; break; case 'o': g.opt_origin = optarg; if(f_set_defaults) gdefault.opt_origin = optarg; break; case 'p': g.opt_prodid = optarg; break; case 'q': g.opt_pqfname = optarg; if(f_set_defaults) gdefault.opt_pqfname = optarg; break; case 's': g.opt_seq_str = optarg; status = extract_uint(optarg, &g.seq); if(status != 0) log_errx(1, "Invalid value for [-s]."); break; case 'S': status = extract_uint(optarg, &g.opt_filesize); if(status != 0) log_errx(1, "Invalid value for [-S]."); break; case 'v': g.opt_verbose = 1; if(f_set_defaults) gdefault.opt_verbose = 1; break; case 'h': default: status = 1; fprintf(stdout, "%s\n", usage); break; } } if(status != 0) exit(status); }
// Parse the compile unit. Find the function to which the instruction pointer // belongs and fill the info structure. static int parse_cu(uint8_t *p, uintptr_t rip, struct Rip_debug_info *info) { // read the compile unit header uint32_t length = extract_uint(p, 4, &p); uint8_t *e = p + length; extract_uint(p, 2, &p); // version uint32_t abbr_offset = extract_uint(p, 4, &p); extract_uint(p, 1, &p); // address size while (p < e) { uint64_t code; // skip null DIEs do { code = extract_uleb128(p, &p); } while (code == 0 && p < e); if (p >= e) break; // Search the abbreviation table for the declaration with this code uint8_t *abbr = find_abbrev_decl(pa2kva(abbr_offset), code); uint64_t tag = extract_uleb128(abbr, &abbr); extract_uint(abbr, 1, &abbr); // skip the has_children tag // read the attributes // we're interested only in several tags and attributes uintptr_t fn_lo = 0, fn_hi = 0; char *fn_name = NULL; for (;;) { uint64_t name = extract_uleb128(abbr, &abbr); uint64_t form = extract_uleb128(abbr, &abbr); union Attr_val val; if (name == 0 && form == 0) break; // extract the attribute value if (extract_value(p, form, &val, &p) == -1) return -1; if (tag == DW_TAG_compile_unit && name == DW_AT_name) { info->file_name = val.string; } else if (tag == DW_TAG_subprogram) { if (name == DW_AT_name) fn_name = val.string; else if (name == DW_AT_low_pc) fn_lo = val.number; else if (name == DW_AT_high_pc) fn_hi = val.number; } } if (tag == DW_TAG_subprogram) { // if the recently parsed tag was a function tag... if (rip >= fn_lo && rip <= fn_hi) { // ... and the instruction pointer belongs to this function if (fn_name) info->fn_name = fn_name; info->fn_offset = rip - fn_lo; } } } return 0; }
// Extract the attribute value of the given form static int extract_value(uint8_t *p, uint64_t form, union Attr_val *value, uint8_t **e) { switch (form) { case DW_FORM_data1: case DW_FORM_ref1: case DW_FORM_flag: value->number = extract_uint(p, 1, &p); break; case DW_FORM_data2: case DW_FORM_ref2: value->number = extract_uint(p, 2, &p); break; case DW_FORM_data4: case DW_FORM_ref4: value->number = extract_uint(p, 4, &p); break; case DW_FORM_data8: case DW_FORM_ref8: case DW_FORM_addr: case DW_FORM_ref_addr: value->number = extract_uint(p, 8, &p); break; case DW_FORM_sdata: value->number = extract_sleb128(p, &p); break; case DW_FORM_udata: case DW_FORM_ref_udata: value->number = extract_uleb128(p, &p); break; case DW_FORM_string: value->string = (char *) p; while (*p++) ; break; case DW_FORM_strp: value->string = (char *) pa2kva(extract_uint(p, 4, &p)); break; case DW_FORM_block1: value->block.length = extract_uint(p, 1, &p); value->block.data = (char *) p; p += value->block.length; break; case DW_FORM_block2: value->block.length = extract_uint(p, 2, &p); value->block.data = (char *) p; p += value->block.length; break; case DW_FORM_block4: value->block.length = extract_uint(p, 4, &p); value->block.data = (char *) p; p += value->block.length; break; case DW_FORM_block: value->block.length = extract_uleb128(p, &p); value->block.data = (char *) p; p += value->block.length; break; case DW_FORM_indirect: form = extract_uleb128(p, &p); return extract_value(p, form, value, &p); default: kprintf("uknown attribute form: %x\n", form); return -1; } if (e) *e = p; return 0; }
/* * Create an unwind header (.eh_frame_hdr) output section. * The section is created and space reserved, but the data * is not copied into place. That is done by a later call * to ld_unwind_populate(), after active relocations have been * processed. * * When GNU linkonce processing is in effect, we can end up in a situation * where the FDEs related to discarded sections remain in the eh_frame * section. Ideally, we would remove these dead entries from eh_frame. * However, that optimization has not yet been implemented. In the current * implementation, the number of dead FDEs cannot be determined until * active relocations are processed, and that processing follows the * call to this function. This means that we are unable to detect dead FDEs * here, and the section created by this routine is sized for maximum case * where all FDEs are valid. */ uintptr_t ld_unwind_make_hdr(Ofl_desc *ofl) { int bswap = (ofl->ofl_flags1 & FLG_OF1_ENCDIFF) != 0; Shdr *shdr; Elf_Data *elfdata; Is_desc *isp; size_t size; Xword fde_cnt; Aliste idx1; Os_desc *osp; /* * we only build a unwind header if we have * some unwind information in the file. */ if (ofl->ofl_unwind == NULL) return (1); /* * Allocate and initialize the Elf_Data structure. */ if ((elfdata = libld_calloc(sizeof (Elf_Data), 1)) == NULL) return (S_ERROR); elfdata->d_type = ELF_T_BYTE; elfdata->d_align = ld_targ.t_m.m_word_align; elfdata->d_version = ofl->ofl_dehdr->e_version; /* * Allocate and initialize the Shdr structure. */ if ((shdr = libld_calloc(sizeof (Shdr), 1)) == NULL) return (S_ERROR); shdr->sh_type = ld_targ.t_m.m_sht_unwind; shdr->sh_flags = SHF_ALLOC; shdr->sh_addralign = ld_targ.t_m.m_word_align; shdr->sh_entsize = 0; /* * Allocate and initialize the Is_desc structure. */ if ((isp = libld_calloc(1, sizeof (Is_desc))) == NULL) return (S_ERROR); isp->is_name = MSG_ORIG(MSG_SCN_UNWINDHDR); isp->is_shdr = shdr; isp->is_indata = elfdata; if ((ofl->ofl_unwindhdr = ld_place_section(ofl, isp, NULL, ld_targ.t_id.id_unwindhdr, NULL)) == (Os_desc *)S_ERROR) return (S_ERROR); /* * Scan through all of the input Frame information, counting each FDE * that requires an index. Each fde_entry gets a corresponding entry * in the binary search table. */ fde_cnt = 0; for (APLIST_TRAVERSE(ofl->ofl_unwind, idx1, osp)) { Aliste idx2; int os_isdescs_idx; OS_ISDESCS_TRAVERSE(os_isdescs_idx, osp, idx2, isp) { uchar_t *data; uint64_t off = 0; data = isp->is_indata->d_buf; size = isp->is_indata->d_size; while (off < size) { uint_t length, id; uint64_t ndx = 0; /* * Extract length in lsb format. A zero length * indicates that this CIE is a terminator and * that processing for unwind information is * complete. */ length = extract_uint(data + off, &ndx, bswap); if (length == 0) break; /* * Extract CIE id in lsb format. */ id = extract_uint(data + off, &ndx, bswap); /* * A CIE record has a id of '0', otherwise * this is a FDE entry and the 'id' is the * CIE pointer. */ if (id == 0) { uint_t cieversion; /* * The only CIE version supported * is '1' - quick sanity check * here. */ cieversion = data[off + ndx]; ndx += 1; /* BEGIN CSTYLED */ if (cieversion != 1) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_UNW_BADCIEVERS), isp->is_file->ifl_name, isp->is_name, off); return (S_ERROR); } /* END CSTYLED */ } else { fde_cnt++; } off += length + 4; } } }