static int initial_offset_base (Dwarf_Attribute *attr, ptrdiff_t *offset, Dwarf_Addr *basep) { if (attr_base_address (attr, basep) != 0) return -1; Dwarf_Word start_offset; if (__libdw_formptr (attr, IDX_debug_loc, DWARF_E_NO_LOCLIST, NULL, &start_offset) == NULL) return -1; *offset = start_offset; return 0; }
int dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) { if (attr == NULL) return -1; const unsigned char *datap = attr->valp; const unsigned char *endp = attr->cu->endp; switch (attr->form) { case DW_FORM_data1: if (datap + 1 > endp) { invalid: __libdw_seterrno (DWARF_E_INVALID_DWARF); return -1; } *return_uval = *attr->valp; break; case DW_FORM_data2: if (datap + 2 > endp) goto invalid; *return_uval = read_2ubyte_unaligned (attr->cu->dbg, attr->valp); break; case DW_FORM_data4: case DW_FORM_data8: case DW_FORM_sec_offset: /* Before DWARF4 data4 and data8 are pure constants unless the attribute also allows offsets (*ptr classes), since DWARF4 they are always just constants (start_scope is special though, since it only could express a rangelist since DWARF4). */ if (attr->form == DW_FORM_sec_offset || (attr->cu->version < 4 && attr->code != DW_AT_start_scope)) { switch (attr->code) { case DW_AT_data_member_location: case DW_AT_frame_base: case DW_AT_location: case DW_AT_return_addr: case DW_AT_segment: case DW_AT_static_link: case DW_AT_string_length: case DW_AT_use_location: case DW_AT_vtable_elem_location: /* loclistptr */ if (__libdw_formptr (attr, IDX_debug_loc, DWARF_E_NO_LOCLIST, NULL, return_uval) == NULL) return -1; break; case DW_AT_macro_info: /* macptr into .debug_macinfo */ if (__libdw_formptr (attr, IDX_debug_macinfo, DWARF_E_NO_ENTRY, NULL, return_uval) == NULL) return -1; break; case DW_AT_GNU_macros: /* macptr into .debug_macro */ if (__libdw_formptr (attr, IDX_debug_macro, DWARF_E_NO_ENTRY, NULL, return_uval) == NULL) return -1; break; case DW_AT_ranges: case DW_AT_start_scope: /* rangelistptr */ if (__libdw_formptr (attr, IDX_debug_ranges, DWARF_E_NO_DEBUG_RANGES, NULL, return_uval) == NULL) return -1; break; case DW_AT_stmt_list: /* lineptr */ if (__libdw_formptr (attr, IDX_debug_line, DWARF_E_NO_DEBUG_LINE, NULL, return_uval) == NULL) return -1; break; default: /* sec_offset can only be used by one of the above attrs. */ if (attr->form == DW_FORM_sec_offset) { __libdw_seterrno (DWARF_E_INVALID_DWARF); return -1; } /* Not one of the special attributes, just a constant. */ if (__libdw_read_address (attr->cu->dbg, cu_sec_idx (attr->cu), attr->valp, attr->form == DW_FORM_data4 ? 4 : 8, return_uval)) return -1; break; } } else { /* We are dealing with a constant data4 or data8. */ if (__libdw_read_address (attr->cu->dbg, cu_sec_idx (attr->cu), attr->valp, attr->form == DW_FORM_data4 ? 4 : 8, return_uval)) return -1; } break; case DW_FORM_sdata: if (datap + 1 > endp) goto invalid; get_sleb128 (*return_uval, datap, endp); break; case DW_FORM_udata: if (datap + 1 > endp) goto invalid; get_uleb128 (*return_uval, datap, endp); break; default: __libdw_seterrno (DWARF_E_NO_CONSTANT); return -1; } return 0; }
int dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines) { if (unlikely (cudie == NULL || (INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit && INTUSE(dwarf_tag) (cudie) != DW_TAG_partial_unit))) return -1; int res = -1; struct linelist *linelist = NULL; unsigned int nlinelist = 0; /* If there are a large number of lines don't blow up the stack. Keep track of the last malloced linelist record and free them through the next pointer at the end. */ #define MAX_STACK_ALLOC 4096 struct linelist *malloc_linelist = NULL; /* Get the information if it is not already known. */ struct Dwarf_CU *const cu = cudie->cu; if (cu->lines == NULL) { /* Failsafe mode: no data found. */ cu->lines = (void *) -1l; cu->files = (void *) -1l; /* The die must have a statement list associated. */ Dwarf_Attribute stmt_list_mem; Dwarf_Attribute *stmt_list = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list, &stmt_list_mem); /* Get the offset into the .debug_line section. NB: this call also checks whether the previous dwarf_attr call failed. */ const unsigned char *lineendp; const unsigned char *linep = __libdw_formptr (stmt_list, IDX_debug_line, DWARF_E_NO_DEBUG_LINE, (unsigned char **) &lineendp, NULL); if (linep == NULL) goto out; /* Get the compilation directory. */ Dwarf_Attribute compdir_attr_mem; Dwarf_Attribute *compdir_attr = INTUSE(dwarf_attr) (cudie, DW_AT_comp_dir, &compdir_attr_mem); const char *comp_dir = INTUSE(dwarf_formstring) (compdir_attr); if (unlikely (linep + 4 > lineendp)) { invalid_data: __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); goto out; } Dwarf *dbg = cu->dbg; Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep); unsigned int length = 4; if (unlikely (unit_length == DWARF3_LENGTH_64_BIT)) { if (unlikely (linep + 8 > lineendp)) goto invalid_data; unit_length = read_8ubyte_unaligned_inc (dbg, linep); length = 8; } /* Check whether we have enough room in the section. */ if (unit_length < 2 + length + 5 * 1 || unlikely (linep + unit_length > lineendp)) goto invalid_data; lineendp = linep + unit_length; /* The next element of the header is the version identifier. */ uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep); if (unlikely (version < 2) || unlikely (version > 4)) { __libdw_seterrno (DWARF_E_VERSION); goto out; } /* Next comes the header length. */ Dwarf_Word header_length; if (length == 4) header_length = read_4ubyte_unaligned_inc (dbg, linep); else header_length = read_8ubyte_unaligned_inc (dbg, linep); const unsigned char *header_start = linep; /* Next the minimum instruction length. */ uint_fast8_t minimum_instr_len = *linep++; /* Next the maximum operations per instruction, in version 4 format. */ uint_fast8_t max_ops_per_instr = 1; if (version >= 4) { if (unlikely (lineendp - linep < 5)) goto invalid_data; max_ops_per_instr = *linep++; if (unlikely (max_ops_per_instr == 0)) goto invalid_data; } /* Then the flag determining the default value of the is_stmt register. */ uint_fast8_t default_is_stmt = *linep++; /* Now the line base. */ int_fast8_t line_base = (int8_t) *linep++; /* And the line range. */ uint_fast8_t line_range = *linep++; /* The opcode base. */ uint_fast8_t opcode_base = *linep++; /* Remember array with the standard opcode length (-1 to account for the opcode with value zero not being mentioned). */ const uint8_t *standard_opcode_lengths = linep - 1; if (unlikely (lineendp - linep < opcode_base - 1)) goto invalid_data; linep += opcode_base - 1; /* First comes the list of directories. Add the compilation directory first since the index zero is used for it. */ struct dirlist { const char *dir; size_t len; struct dirlist *next; } comp_dir_elem = { .dir = comp_dir, .len = comp_dir ? strlen (comp_dir) : 0, .next = NULL }; struct dirlist *dirlist = &comp_dir_elem; unsigned int ndirlist = 1; // XXX Directly construct array to conserve memory? while (*linep != 0) { struct dirlist *new_dir = (struct dirlist *) alloca (sizeof (*new_dir)); new_dir->dir = (char *) linep; uint8_t *endp = memchr (linep, '\0', lineendp - linep); if (endp == NULL) goto invalid_data; new_dir->len = endp - linep; new_dir->next = dirlist; dirlist = new_dir; ++ndirlist; linep = endp + 1; } /* Skip the final NUL byte. */ ++linep; /* Rearrange the list in array form. */ struct dirlist **dirarray = (struct dirlist **) alloca (ndirlist * sizeof (*dirarray)); for (unsigned int n = ndirlist; n-- > 0; dirlist = dirlist->next) dirarray[n] = dirlist; /* Now read the files. */ struct filelist null_file = { .info = { .name = "???", .mtime = 0, .length = 0 }, .next = NULL }; struct filelist *filelist = &null_file; unsigned int nfilelist = 1; if (unlikely (linep >= lineendp)) goto invalid_data; while (*linep != 0) { struct filelist *new_file = (struct filelist *) alloca (sizeof (*new_file)); /* First comes the file name. */ char *fname = (char *) linep; uint8_t *endp = memchr (fname, '\0', lineendp - linep); if (endp == NULL) goto invalid_data; size_t fnamelen = endp - (uint8_t *) fname; linep = endp + 1; /* Then the index. */ Dwarf_Word diridx; get_uleb128 (diridx, linep); if (unlikely (diridx >= ndirlist)) { __libdw_seterrno (DWARF_E_INVALID_DIR_IDX); goto out; } if (*fname == '/') /* It's an absolute path. */ new_file->info.name = fname; else { new_file->info.name = libdw_alloc (dbg, char, 1, dirarray[diridx]->len + 1 + fnamelen + 1); char *cp = new_file->info.name; if (dirarray[diridx]->dir != NULL) { /* This value could be NULL in case the DW_AT_comp_dir was not present. We cannot do much in this case. The easiest thing is to convert the path in an absolute path. */ cp = stpcpy (cp, dirarray[diridx]->dir); } *cp++ = '/'; strcpy (cp, fname); assert (strlen (new_file->info.name) < dirarray[diridx]->len + 1 + fnamelen + 1); } /* Next comes the modification time. */ get_uleb128 (new_file->info.mtime, linep); /* Finally the length of the file. */ get_uleb128 (new_file->info.length, linep); new_file->next = filelist; filelist = new_file; ++nfilelist; } /* Skip the final NUL byte. */ ++linep; /* Consistency check. */ if (unlikely (linep != header_start + header_length)) { __libdw_seterrno (DWARF_E_INVALID_DWARF); goto out; } /* We are about to process the statement program. Initialize the state machine registers (see 6.2.2 in the v2.1 specification). */ Dwarf_Word addr = 0; unsigned int op_index = 0; unsigned int file = 1; int line = 1; unsigned int column = 0; uint_fast8_t is_stmt = default_is_stmt; bool basic_block = false; bool prologue_end = false; bool epilogue_begin = false; unsigned int isa = 0; unsigned int discriminator = 0; /* Apply the "operation advance" from a special opcode or DW_LNS_advance_pc (as per DWARF4 6.2.5.1). */ inline void advance_pc (unsigned int op_advance) { addr += minimum_instr_len * ((op_index + op_advance) / max_ops_per_instr); op_index = (op_index + op_advance) % max_ops_per_instr; }
ptrdiff_t dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep, Dwarf_Addr *startp, Dwarf_Addr *endp) { if (die == NULL) return -1; if (offset == 0 /* Usually there is a single contiguous range. */ && INTUSE(dwarf_highpc) (die, endp) == 0 && INTUSE(dwarf_lowpc) (die, startp) == 0) /* A offset into .debug_ranges will never be 1, it must be at least a multiple of 4. So we can return 1 as a special case value to mark there are no ranges to look for on the next call. */ return 1; if (offset == 1) return 0; /* We have to look for a noncontiguous range. */ const Elf_Data *d = die->cu->dbg->sectiondata[IDX_debug_ranges]; if (d == NULL && offset != 0) { __libdw_seterrno (DWARF_E_NO_DEBUG_RANGES); return -1; } unsigned char *readp; unsigned char *readendp; if (offset == 0) { Dwarf_Attribute attr_mem; Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges, &attr_mem); if (attr == NULL) /* No PC attributes in this DIE at all, so an empty range list. */ return 0; Dwarf_Word start_offset; if ((readp = __libdw_formptr (attr, IDX_debug_ranges, DWARF_E_NO_DEBUG_RANGES, &readendp, &start_offset)) == NULL) return -1; offset = start_offset; assert ((Dwarf_Word) offset == start_offset); /* Fetch the CU's base address. */ Dwarf_Die cudie = CUDIE (attr->cu); /* Find the base address of the compilation unit. It will normally be specified by DW_AT_low_pc. In DWARF-3 draft 4, the base address could be overridden by DW_AT_entry_pc. It's been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc for compilation units with discontinuous ranges. */ if (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0) && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie, DW_AT_entry_pc, &attr_mem), basep) != 0) *basep = (Dwarf_Addr) -1; } else { if (__libdw_offset_in_section (die->cu->dbg, IDX_debug_ranges, offset, 1)) return -1l; readp = d->d_buf + offset; readendp = d->d_buf + d->d_size; } next: if (readendp - readp < die->cu->address_size * 2) goto invalid; Dwarf_Addr begin; Dwarf_Addr end; switch (__libdw_read_begin_end_pair_inc (die->cu->dbg, IDX_debug_ranges, &readp, die->cu->address_size, &begin, &end, basep)) { case 0: break; case 1: goto next; case 2: return 0; default: return -1l; } /* We have an address range entry. Check that we have a base. */ if (*basep == (Dwarf_Addr) -1) { if (INTUSE(dwarf_errno) () == 0) { invalid: __libdw_seterrno (DWARF_E_INVALID_DWARF); } return -1; } *startp = *basep + begin; *endp = *basep + end; return readp - (unsigned char *) d->d_buf; }