diff options
Diffstat (limited to 'libdw/dwarf_next_lines.c')
-rw-r--r-- | libdw/dwarf_next_lines.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/libdw/dwarf_next_lines.c b/libdw/dwarf_next_lines.c new file mode 100644 index 00000000..9b76b47e --- /dev/null +++ b/libdw/dwarf_next_lines.c @@ -0,0 +1,197 @@ +/* Iterate through the debug line table. + Copyright (C) 2018 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <libdwP.h> + + +int +dwarf_next_lines (Dwarf *dbg, Dwarf_Off off, + Dwarf_Off *next_off, Dwarf_CU **cu, + Dwarf_Files **srcfiles, size_t *nfiles, + Dwarf_Lines **srclines, size_t *nlines) +{ + /* Ignore existing errors. */ + if (dbg == NULL) + return -1; + + Elf_Data *lines = dbg->sectiondata[IDX_debug_line]; + if (lines == NULL) + { + __libdw_seterrno (DWARF_E_NO_DEBUG_LINE); + return -1; + } + + if (off == (Dwarf_Off) -1 + || lines->d_size < 4 + || off >= lines->d_size) + { + *next_off = (Dwarf_Off) -1; + return 1; + } + + /* Read enough of the header to know where the next table is and + whether we need to lookup the CU (version < 5). */ + const unsigned char *linep = lines->d_buf + off; + const unsigned char *lineendp = lines->d_buf + lines->d_size; + + if ((size_t) (lineendp - linep) < 4) + { + invalid_data: + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); + return -1; + } + + *next_off = off + 4; + Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep); + if (unit_length == DWARF3_LENGTH_64_BIT) + { + if ((size_t) (lineendp - linep) < 8) + goto invalid_data; + unit_length = read_8ubyte_unaligned_inc (dbg, linep); + *next_off += 8; + } + + if (unit_length > (size_t) (lineendp - linep)) + goto invalid_data; + + *next_off += unit_length; + lineendp = linep + unit_length; + + if ((size_t) (lineendp - linep) < 2) + goto invalid_data; + uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep); + + Dwarf_Die cudie; + if (version < 5) + { + /* We need to find the matching CU to get the comp_dir. Use the + given CU as hint where to start searching. Normally it will + be the next CU that has a statement list. */ + Dwarf_CU *given_cu = *cu; + Dwarf_CU *next_cu = given_cu; + bool found = false; + while (dwarf_get_units (dbg, next_cu, &next_cu, NULL, NULL, + &cudie, NULL) == 0) + { + if (dwarf_hasattr (&cudie, DW_AT_stmt_list)) + { + Dwarf_Attribute attr; + Dwarf_Word stmt_off; + if (dwarf_formudata (dwarf_attr (&cudie, DW_AT_stmt_list, &attr), + &stmt_off) == 0 + && stmt_off == off) + { + found = true; + break; + } + } + else if (off == 0 + && (next_cu->unit_type == DW_UT_split_compile + || next_cu->unit_type == DW_UT_split_type)) + { + /* For split units (in .dwo files) there is only one table + at offset zero (containing just the files, no lines). */ + found = true; + break; + } + } + + if (!found && given_cu != NULL) + { + /* The CUs might be in a different order from the line + tables. Need to do a linear search (but stop at the given + CU, since we already searched those. */ + next_cu = NULL; + while (dwarf_get_units (dbg, next_cu, &next_cu, NULL, NULL, + &cudie, NULL) == 0 + && next_cu != given_cu) + { + Dwarf_Attribute attr; + Dwarf_Word stmt_off; + if (dwarf_formudata (dwarf_attr (&cudie, DW_AT_stmt_list, &attr), + &stmt_off) == 0 + && stmt_off == off) + { + found = true; + break; + } + } + } + + if (found) + *cu = next_cu; + else + *cu = NULL; + } + else + *cu = NULL; + + const char *comp_dir; + unsigned address_size; + if (*cu != NULL) + { + comp_dir = __libdw_getcompdir (&cudie); + address_size = (*cu)->address_size; + } + else + { + comp_dir = NULL; + + size_t esize; + char *ident = elf_getident (dbg->elf, &esize); + if (ident == NULL || esize < EI_NIDENT) + goto invalid_data; + address_size = ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + } + + if (__libdw_getsrclines (dbg, off, comp_dir, address_size, + srclines, srcfiles) != 0) + return -1; + + if (nlines != NULL) + { + if (srclines != NULL && *srclines != NULL) + *nlines = (*srclines)->nlines; + else + *nlines = 0; + } + + if (nfiles != NULL) + { + if (srcfiles != NULL && *srcfiles != NULL) + *nfiles = (*srcfiles)->nfiles; + else + *nfiles = 0; + } + + return 0; +} |