diff options
| -rw-r--r-- | libdw/dwarf_getsrcfiles.c | 24 | ||||
| -rw-r--r-- | libdw/dwarf_getsrclines.c | 571 | ||||
| -rw-r--r-- | libdw/dwarf_macro_getsrcfiles.c | 4 | ||||
| -rw-r--r-- | libdw/libdwP.h | 10 | ||||
| -rw-r--r-- | tests/.gitignore | 1 | ||||
| -rw-r--r-- | tests/Makefile.am | 6 | ||||
| -rw-r--r-- | tests/get-files-define-file.c | 162 | ||||
| -rw-r--r-- | tests/get-files.c | 8 | ||||
| -rw-r--r-- | tests/get-lines.c | 20 | ||||
| -rwxr-xr-x | tests/run-get-files.sh | 73 | ||||
| -rw-r--r-- | tests/testfile-define-file.bz2 | bin | 0 -> 24910 bytes |
11 files changed, 697 insertions, 182 deletions
diff --git a/libdw/dwarf_getsrcfiles.c b/libdw/dwarf_getsrcfiles.c index cd2e5b5a..24e4b7d2 100644 --- a/libdw/dwarf_getsrcfiles.c +++ b/libdw/dwarf_getsrcfiles.c @@ -70,10 +70,9 @@ dwarf_getsrcfiles (Dwarf_Die *cudie, Dwarf_Files **files, size_t *nfiles) { /* We are only interested in the files, the lines will always come from the skeleton. */ - res = __libdw_getsrclines (cu->dbg, dwp_off, + res = __libdw_getsrcfiles (cu->dbg, dwp_off, __libdw_getcompdir (cudie), - cu->address_size, NULL, - &cu->files); + cu->address_size, &cu->files); } } else @@ -89,12 +88,19 @@ dwarf_getsrcfiles (Dwarf_Die *cudie, Dwarf_Files **files, size_t *nfiles) } else { - Dwarf_Lines *lines; - size_t nlines; - - /* Let the more generic function do the work. It'll create more - data but that will be needed in an real program anyway. */ - res = INTUSE(dwarf_getsrclines) (cudie, &lines, &nlines); + /* 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); + + Dwarf_Off debug_line_offset; + if (__libdw_formptr (stmt_list, IDX_debug_line, DWARF_E_NO_DEBUG_LINE, + NULL, &debug_line_offset) == NULL) + return -1; + + res = __libdw_getsrcfiles (cu->dbg, debug_line_offset, + __libdw_getcompdir (cudie), + cu->address_size, &cu->files); } } else if (cu->files != (void *) -1l) diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c index 69e10c7b..987a86fd 100644 --- a/libdw/dwarf_getsrclines.c +++ b/libdw/dwarf_getsrclines.c @@ -52,6 +52,11 @@ struct linelist size_t sequence; }; +struct dirlist +{ + const char *dir; + size_t len; +}; /* Compare by Dwarf_Line.addr, given pointers into an array of pointers. */ static int @@ -77,6 +82,28 @@ compare_lines (const void *a, const void *b) : 0; } +/* Decoded .debug_line program header. */ +struct line_header +{ + /* Header entries */ + Dwarf_Word unit_length; + unsigned int length; + uint_fast16_t version; + size_t line_address_size; + size_t segment_selector_size; + Dwarf_Word header_length; + const unsigned char *header_start; + uint_fast8_t minimum_instr_len; + uint_fast8_t max_ops_per_instr; + uint_fast8_t default_is_stmt; + int_fast8_t line_base; + uint_fast8_t line_range; + uint_fast8_t opcode_base; + const uint8_t *standard_opcode_lengths; + unsigned int debug_str_offset; /* CUBIN only */ + size_t files_start; +}; + struct line_state { Dwarf_Word addr; @@ -155,127 +182,81 @@ add_new_line (struct line_state *state, struct linelist *new_line) return false; } +/* Read the .debug_line program header. Return 0 if sucessful, otherwise set + libdw errno and return -1. */ + static int -read_srclines (Dwarf *dbg, - const unsigned char *linep, const unsigned char *lineendp, - const char *comp_dir, unsigned address_size, - Dwarf_Lines **linesp, Dwarf_Files **filesp) +read_line_header (Dwarf *dbg, unsigned address_size, + const unsigned char *linep, const unsigned char *lineendp, + struct line_header *lh) { - int res = -1; - - struct filelist *filelist = NULL; - size_t nfilelist = 0; - size_t ndirlist = 0; - - /* If there are a large number of lines, files or dirs don't blow up - the stack. Stack allocate some entries, only dynamically malloc - when more than MAX. */ -#define MAX_STACK_ALLOC 4096 -#define MAX_STACK_LINES (MAX_STACK_ALLOC / 2) -#define MAX_STACK_FILES (MAX_STACK_ALLOC / 4) -#define MAX_STACK_DIRS (MAX_STACK_ALLOC / 16) - - /* Initial statement program state (except for stmt_list, see below). */ - struct line_state state = - { - .linelist = NULL, - .nlinelist = 0, - .addr = 0, - .op_index = 0, - .file = 1, - /* We only store int but want to check for overflow (see SET above). */ - .line = 1, - .column = 0, - .basic_block = false, - .prologue_end = false, - .epilogue_begin = false, - .isa = 0, - .discriminator = 0, - .context = 0, - .function_name = 0 - }; - - /* The dirs normally go on the stack, but if there are too many - we alloc them all. Set up stack storage early, so we can check on - error if we need to free them or not. */ - struct dirlist - { - const char *dir; - size_t len; - }; - struct dirlist dirstack[MAX_STACK_DIRS]; - struct dirlist *dirarray = dirstack; + const unsigned char *line_start = linep; if (unlikely (linep + 4 > lineendp)) - { - invalid_data: - __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); - goto out; - } + goto invalid_data; - Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep); - unsigned int length = 4; - if (unlikely (unit_length == DWARF3_LENGTH_64_BIT)) + lh->unit_length = read_4ubyte_unaligned_inc (dbg, linep); + lh->length = 4; + if (unlikely (lh->unit_length == DWARF3_LENGTH_64_BIT)) { if (unlikely (linep + 8 > lineendp)) goto invalid_data; - unit_length = read_8ubyte_unaligned_inc (dbg, linep); - length = 8; + lh->unit_length = read_8ubyte_unaligned_inc (dbg, linep); + lh->length = 8; } /* Check whether we have enough room in the section. */ - if (unlikely (unit_length > (size_t) (lineendp - linep))) + if (unlikely (lh->unit_length > (size_t) (lineendp - linep))) goto invalid_data; - lineendp = linep + unit_length; + lineendp = linep + lh->unit_length; /* The next element of the header is the version identifier. */ if ((size_t) (lineendp - linep) < 2) goto invalid_data; - uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep); - if (unlikely (version < 2) || unlikely (version > 5)) + lh->version = read_2ubyte_unaligned_inc (dbg, linep); + if (unlikely (lh->version < 2) || unlikely (lh->version > 5)) { __libdw_seterrno (DWARF_E_VERSION); - goto out; + return -1; } /* DWARF5 explicitly lists address and segment_selector sizes. */ - if (version >= 5) + if (lh->version >= 5) { if ((size_t) (lineendp - linep) < 2) goto invalid_data; - size_t line_address_size = *linep++; - size_t segment_selector_size = *linep++; - if (line_address_size != address_size || segment_selector_size != 0) + lh->line_address_size = *linep++; + lh->segment_selector_size = *linep++; + if (lh->line_address_size != address_size || lh->segment_selector_size != 0) goto invalid_data; } /* Next comes the header length. */ - Dwarf_Word header_length; - if (length == 4) + if (lh->length == 4) { if ((size_t) (lineendp - linep) < 4) goto invalid_data; - header_length = read_4ubyte_unaligned_inc (dbg, linep); + lh->header_length = read_4ubyte_unaligned_inc (dbg, linep); } else { if ((size_t) (lineendp - linep) < 8) goto invalid_data; - header_length = read_8ubyte_unaligned_inc (dbg, linep); + lh->header_length = read_8ubyte_unaligned_inc (dbg, linep); } - const unsigned char *header_start = linep; + lh->header_start = linep; /* Next the minimum instruction length. */ - uint_fast8_t minimum_instr_len = *linep++; + lh->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) + lh->max_ops_per_instr = 1; + if (lh->version >= 4) { if (unlikely ((size_t) (lineendp - linep) < 1)) goto invalid_data; - max_ops_per_instr = *linep++; - if (unlikely (max_ops_per_instr == 0)) + lh->max_ops_per_instr = *linep++; + if (unlikely (lh->max_ops_per_instr == 0)) goto invalid_data; } @@ -285,23 +266,71 @@ read_srclines (Dwarf *dbg, /* Then the flag determining the default value of the is_stmt register. */ - uint_fast8_t default_is_stmt = *linep++; + lh->default_is_stmt = *linep++; /* Now the line base. */ - int_fast8_t line_base = (int8_t) *linep++; + lh->line_base = (int8_t) *linep++; /* And the line range. */ - uint_fast8_t line_range = *linep++; + lh->line_range = *linep++; /* The opcode base. */ - uint_fast8_t opcode_base = *linep++; + lh->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)) + lh->standard_opcode_lengths = linep - 1; + if (unlikely (lineendp - linep < lh->opcode_base - 1)) goto invalid_data; - linep += opcode_base - 1; + linep += lh->opcode_base - 1; + + /* Record beginning of the file information. */ + lh->files_start = (size_t) (linep - line_start); + + return 0; + +invalid_data: + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); + return -1; +} + +/* If there are a large number of lines, files or dirs don't blow up + the stack. Stack allocate some entries, only dynamically malloc + when more than MAX. */ +#define MAX_STACK_ALLOC 4096 +#define MAX_STACK_LINES (MAX_STACK_ALLOC / 2) +#define MAX_STACK_FILES (MAX_STACK_ALLOC / 4) +#define MAX_STACK_DIRS (MAX_STACK_ALLOC / 16) + +static int +read_srcfiles (Dwarf *dbg, + const unsigned char *linep, const unsigned char *lineendp, + const char *comp_dir, unsigned address_size, + struct line_header *lh, Dwarf_Files **filesp) +{ + if (filesp == NULL) + return -1; + + struct line_header lh_local; + + if (lh == NULL) + { + if (read_line_header (dbg, address_size, linep, lineendp, &lh_local) != 0) + return -1; + lh = &lh_local; + } + + int res = -1; + + struct filelist *filelist = NULL; + size_t nfilelist = 0; + size_t ndirlist = 0; + + /* The dirs normally go on the stack, but if there are too many + we alloc them all. Set up stack storage early, so we can check on + error if we need to free them or not. */ + struct dirlist dirstack[MAX_STACK_DIRS]; + struct dirlist *dirarray = dirstack; /* To read DWARF5 dir and file lists we need to know the forms. For now we skip everything, except the DW_LNCT_path and @@ -311,12 +340,18 @@ read_srclines (Dwarf *dbg, unsigned char form_path = -1; /* Which forms is DW_LNCT_path. */ unsigned char form_idx = -1; /* And which is DW_LNCT_directory_index. */ + /* Set lineendp to the end of the file information. */ + lineendp = lh->header_start + lh->header_length; + + /* Advance linep to the beginning of the header's srcfile information. */ + linep += lh->files_start; + /* To read/skip form data. */ Dwarf_CU fake_cu = { .dbg = dbg, .sec_idx = IDX_debug_line, .version = 5, - .offset_size = length, + .offset_size = lh->length, .address_size = address_size, .startp = (void *) linep, .endp = (void *) lineendp, @@ -324,7 +359,7 @@ read_srclines (Dwarf *dbg, /* First count the entries. */ size_t ndirs = 0; - if (version < 5) + if (lh->version < 5) { const unsigned char *dirp = linep; while (dirp < lineendp && *dirp != 0) @@ -395,7 +430,7 @@ read_srclines (Dwarf *dbg, /* Entry zero is implicit for older versions, but explicit for 5+. */ struct dirlist comp_dir_elem; - if (version < 5) + if (lh->version < 5) { /* First comes the list of directories. Add the compilation directory first since the index zero is used for it. */ @@ -482,7 +517,7 @@ read_srclines (Dwarf *dbg, fl; }) /* Now read the files. */ - if (version < 5) + if (lh->version < 5) { if (unlikely (linep >= lineendp)) goto invalid_data; @@ -662,29 +697,114 @@ read_srclines (Dwarf *dbg, } } - unsigned int debug_str_offset = 0; - if (unlikely (linep == header_start + header_length - 4)) + if (unlikely (linep == lh->header_start + lh->header_length - 4)) { /* CUBINs contain an unsigned 4-byte offset */ - debug_str_offset = read_4ubyte_unaligned_inc (dbg, linep); + lh->debug_str_offset = read_4ubyte_unaligned_inc (dbg, linep); } /* Consistency check. */ - if (unlikely (linep != header_start + header_length)) + if (unlikely (linep != lh->header_start + lh->header_length)) + goto invalid_data; + + /* Put all the files in an array. */ + Dwarf_Files *files = libdw_alloc (dbg, Dwarf_Files, + sizeof (Dwarf_Files) + + nfilelist * sizeof (Dwarf_Fileinfo) + + (ndirlist + 1) * sizeof (char *), + 1); + + if (unlikely (files == NULL)) + goto no_mem; + + const char **dirs = (void *) &files->info[nfilelist]; + + struct filelist *fileslist = filelist; + files->nfiles = nfilelist; + for (size_t n = nfilelist; n > 0; n--) { - __libdw_seterrno (DWARF_E_INVALID_DWARF); - goto out; + files->info[n - 1] = fileslist->info; + fileslist = fileslist->next; + } + assert (fileslist == NULL); + + /* Put all the directory strings in an array. */ + files->ndirs = ndirlist; + for (unsigned int i = 0; i < ndirlist; ++i) + dirs[i] = dirarray[i].dir; + dirs[ndirlist] = NULL; + + /* Pass the file data structure to the caller. */ + *filesp = files; + + res = 0; + goto out; + +invalid_data: + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); + +out: + if (dirarray != dirstack) + free (dirarray); + for (size_t i = MAX_STACK_FILES; i < nfilelist; i++) + { + struct filelist *fl = filelist->next; + free (filelist); + filelist = fl; } + return res; +} + +static int +read_srclines (Dwarf *dbg, + const unsigned char *linep, const unsigned char *lineendp, + const char *comp_dir, unsigned address_size, + Dwarf_Lines **linesp, Dwarf_Files **filesp, + bool use_cached_files) +{ + int res = -1; + struct line_header lh; + + if (read_line_header (dbg, address_size, linep, lineendp, &lh) != 0) + return res; + + /* Use the filesp srcfiles if they've already been read. */ + if (!use_cached_files + && read_srcfiles (dbg, linep, lineendp, comp_dir, + address_size, &lh, filesp) != 0) + return res; + + /* Initial statement program state (except for stmt_list, see below). */ + struct line_state state = + { + .linelist = NULL, + .nlinelist = 0, + .addr = 0, + .op_index = 0, + .file = 1, + /* We only store int but want to check for overflow (see SET above). */ + .line = 1, + .column = 0, + .basic_block = false, + .prologue_end = false, + .epilogue_begin = false, + .isa = 0, + .discriminator = 0, + .context = 0, + .function_name = 0 + }; + /* We are about to process the statement program. Most state machine registers have already been initialize above. Just add the is_stmt default. See 6.2.2 in the v2.1 specification. */ - state.is_stmt = default_is_stmt; + state.is_stmt = lh.default_is_stmt; /* Apply the "operation advance" from a special opcode or DW_LNS_advance_pc (as per DWARF4 6.2.5.1). */ #define advance_pc(op_advance) \ - run_advance_pc (&state, op_advance, minimum_instr_len, max_ops_per_instr) + run_advance_pc (&state, op_advance, lh.minimum_instr_len, \ + lh.max_ops_per_instr) /* Process the instructions. */ @@ -697,12 +817,26 @@ read_srclines (Dwarf *dbg, ? &llstack[state.nlinelist] \ : malloc (sizeof (struct linelist))); \ if (unlikely (ll == NULL)) \ - goto no_mem; \ + { \ + __libdw_seterrno (DWARF_E_NOMEM); \ + goto out; \ + } \ state.end_sequence = end_seq; \ if (unlikely (add_new_line (&state, ll))) \ goto invalid_data; \ } while (0) + /* If DW_LNE_define_file is present, then additional files will be + added to filesp. */ + size_t nfilelist = 0; + struct filelist *filelist = NULL; + + /* Set lineendp to the end of the line program. */ + lineendp = linep + lh.length + lh.unit_length; + + /* Set linep to the beginning of the line program. */ + linep = lh.header_start + lh.header_length; + while (linep < lineendp) { unsigned int opcode; @@ -713,9 +847,9 @@ read_srclines (Dwarf *dbg, opcode = *linep++; /* Is this a special opcode? */ - if (likely (opcode >= opcode_base)) + if (likely (opcode >= lh.opcode_base)) { - if (unlikely (line_range == 0)) + if (unlikely (lh.line_range == 0)) goto invalid_data; /* Yes. Handling this is quite easy since the opcode value @@ -724,12 +858,12 @@ read_srclines (Dwarf *dbg, opcode = (desired line increment - line_base) + (line_range * address advance) + opcode_base */ - int line_increment = (line_base - + (opcode - opcode_base) % line_range); + int line_increment = (lh.line_base + + (opcode - lh.opcode_base) % lh.line_range); /* Perform the increments. */ state.line += line_increment; - advance_pc ((opcode - opcode_base) / line_range); + advance_pc ((opcode - lh.opcode_base) / lh.line_range); /* Add a new line with the current state machine values. */ NEW_LINE (0); @@ -768,7 +902,7 @@ read_srclines (Dwarf *dbg, state.file = 1; state.line = 1; state.column = 0; - state.is_stmt = default_is_stmt; + state.is_stmt = lh.default_is_stmt; state.basic_block = false; state.prologue_end = false; state.epilogue_begin = false; @@ -803,7 +937,9 @@ read_srclines (Dwarf *dbg, if (unlikely (linep >= lineendp)) goto invalid_data; get_uleb128 (diridx, linep, lineendp); - if (unlikely (diridx >= ndirlist)) + + size_t ndirs = (*filesp)->ndirs; + if (unlikely (diridx >= ndirs)) { __libdw_seterrno (DWARF_E_INVALID_DIR_IDX); goto invalid_data; @@ -817,23 +953,43 @@ read_srclines (Dwarf *dbg, goto invalid_data; get_uleb128 (filelength, linep, lineendp); - struct filelist *new_file = NEW_FILE (); + /* Add new_file to filelist that will be merged with filesp. */ + struct filelist *new_file = malloc (sizeof (struct filelist)); + if (unlikely (new_file == NULL)) + { + __libdw_seterrno (DWARF_E_NOMEM); + goto out; + } + nfilelist++; + new_file->next = filelist; + filelist = new_file; + if (fname[0] == '/') new_file->info.name = fname; else { + /* Directory names are stored in a char *[ndirs] located + after the last Dwarf_Fileinfo_s. */ + size_t nfiles = (*filesp)->nfiles; + const char **dirarray + = (const char **) &((*filesp)->info[nfiles]); + + const char *dname = dirarray[diridx]; + size_t dnamelen = strlen (dname); + new_file->info.name = - libdw_alloc (dbg, char, 1, (dirarray[diridx].len + 1 - + fnamelen + 1)); + libdw_alloc (dbg, char, 1, (dnamelen + fnamelen + 2)); char *cp = new_file->info.name; - if (dirarray[diridx].dir != NULL) + if (dname != NULL) + /* This value could be NULL in case the DW_AT_comp_dir was not present. We cannot do much in this case. Just keep the file relative. */ + { - cp = stpcpy (cp, dirarray[diridx].dir); + cp = stpcpy (cp, dname); *cp++ = '/'; } strcpy (cp, fname); @@ -846,7 +1002,7 @@ read_srclines (Dwarf *dbg, case DW_LNE_set_discriminator: /* Takes one ULEB128 parameter, the discriminator. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -861,14 +1017,14 @@ read_srclines (Dwarf *dbg, if (unlikely (linep >= lineendp)) goto invalid_data; get_uleb128 (state.function_name, linep, lineendp); - state.function_name += debug_str_offset; + state.function_name += lh.debug_str_offset; break; case DW_LNE_NVIDIA_set_function_name: if (unlikely (linep >= lineendp)) goto invalid_data; get_uleb128 (state.function_name, linep, lineendp); - state.function_name += debug_str_offset; + state.function_name += lh.debug_str_offset; break; default: @@ -886,7 +1042,7 @@ read_srclines (Dwarf *dbg, { case DW_LNS_copy: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; /* Add a new line with the current state machine values. */ @@ -902,7 +1058,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_advance_pc: /* Takes one uleb128 parameter which is added to the address. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -914,7 +1070,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_advance_line: /* Takes one sleb128 parameter which is added to the line. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -925,7 +1081,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_file: /* Takes one uleb128 parameter which is stored in file. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -936,7 +1092,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_column: /* Takes one uleb128 parameter which is stored in column. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -947,7 +1103,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_negate_stmt: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; state.is_stmt = 1 - state.is_stmt; @@ -955,7 +1111,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_basic_block: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; state.basic_block = true; @@ -963,19 +1119,19 @@ read_srclines (Dwarf *dbg, case DW_LNS_const_add_pc: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; - if (unlikely (line_range == 0)) + if (unlikely (lh.line_range == 0)) goto invalid_data; - advance_pc ((255 - opcode_base) / line_range); + advance_pc ((255 - lh.opcode_base) / lh.line_range); break; case DW_LNS_fixed_advance_pc: /* Takes one 16 bit parameter which is added to the address. */ - if (unlikely (standard_opcode_lengths[opcode] != 1) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1) || unlikely (lineendp - linep < 2)) goto invalid_data; @@ -985,7 +1141,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_prologue_end: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; state.prologue_end = true; @@ -993,7 +1149,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_epilogue_begin: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; state.epilogue_begin = true; @@ -1001,7 +1157,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_isa: /* Takes one uleb128 parameter which is stored in isa. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -1015,7 +1171,7 @@ read_srclines (Dwarf *dbg, /* This is a new opcode the generator but not we know about. Read the parameters associated with it but then discard everything. Read all the parameters for this opcode. */ - for (int n = standard_opcode_lengths[opcode]; n > 0; --n) + for (int n = lh.standard_opcode_lengths[opcode]; n > 0; --n) { if (unlikely (linep >= lineendp)) goto invalid_data; @@ -1027,32 +1183,49 @@ read_srclines (Dwarf *dbg, } } - /* Put all the files in an array. */ - Dwarf_Files *files = libdw_alloc (dbg, Dwarf_Files, - sizeof (Dwarf_Files) - + nfilelist * sizeof (Dwarf_Fileinfo) - + (ndirlist + 1) * sizeof (char *), - 1); - const char **dirs = (void *) &files->info[nfilelist]; - - struct filelist *fileslist = filelist; - files->nfiles = nfilelist; - for (size_t n = nfilelist; n > 0; n--) + /* Merge filesp with the files from DW_LNE_define_file, if any. */ + if (unlikely (filelist != NULL)) { - files->info[n - 1] = fileslist->info; - fileslist = fileslist->next; - } - assert (fileslist == NULL); + Dwarf_Files *prevfiles = *filesp; + size_t ndirs = prevfiles->ndirs; + size_t nprevfiles = prevfiles->nfiles; + size_t nnewfiles = nprevfiles + nfilelist; + + Dwarf_Files *newfiles + = libdw_alloc (dbg, Dwarf_Files, + sizeof (Dwarf_Files) + + nnewfiles * sizeof (Dwarf_Fileinfo) + + (ndirs + 1) * sizeof (char *), + 1); + + + /* Copy prevfiles to newfiles. */ + for (size_t n = 0; n < nprevfiles; n++) + newfiles->info[n] = prevfiles->info[n]; + + /* Add files from DW_LNE_define_file to newfiles. */ + struct filelist *fileslist = filelist; + for (size_t n = nfilelist; n > 0; n--) + { + newfiles->info[nprevfiles + n - 1] = fileslist->info; + fileslist = fileslist->next; + } - /* Put all the directory strings in an array. */ - files->ndirs = ndirlist; - for (unsigned int i = 0; i < ndirlist; ++i) - dirs[i] = dirarray[i].dir; - dirs[ndirlist] = NULL; + if (fileslist != NULL) + goto invalid_data; - /* Pass the file data structure to the caller. */ - if (filesp != NULL) - *filesp = files; + const char **newdirs = (void *) &newfiles->info[nnewfiles]; + const char **prevdirs = (void *) &prevfiles->info[nprevfiles]; + + /* Copy prevdirs to newdirs. */ + for (size_t n = 0; n < ndirs; n++) + newdirs[n] = prevdirs[n]; + + /* Update filesp. */ + newfiles->nfiles = nnewfiles; + newfiles->ndirs = prevfiles->ndirs; + *filesp = newfiles; + } size_t buf_size = (sizeof (Dwarf_Lines) + (sizeof (Dwarf_Line) * state.nlinelist)); @@ -1087,7 +1260,7 @@ read_srclines (Dwarf *dbg, for (size_t i = 0; i < state.nlinelist; ++i) { lines->info[i] = sortlines[i]->line; - lines->info[i].files = files; + lines->info[i].files = *filesp; } /* Make sure the highest address for the CU is marked as end_sequence. @@ -1102,8 +1275,12 @@ read_srclines (Dwarf *dbg, /* Success. */ res = 0; + goto out; - out: +invalid_data: + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); + +out: /* Free malloced line records, if any. */ for (size_t i = MAX_STACK_LINES; i < state.nlinelist; i++) { @@ -1111,14 +1288,14 @@ read_srclines (Dwarf *dbg, free (state.linelist); state.linelist = ll; } - if (dirarray != dirstack) - free (dirarray); - for (size_t i = MAX_STACK_FILES; i < nfilelist; i++) - { - struct filelist *fl = filelist->next; - free (filelist); - filelist = fl; - } + + /* Free file records from DW_LNE_define_file, if any. */ + for (size_t i = 0; i < nfilelist; i++) + { + struct filelist *fl = filelist->next; + free (filelist); + filelist = fl; + } return res; } @@ -1137,17 +1314,17 @@ files_lines_compare (const void *p1, const void *p2) return 0; } -int -internal_function -__libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, - const char *comp_dir, unsigned address_size, - Dwarf_Lines **linesp, Dwarf_Files **filesp) +static int +get_lines_or_files (Dwarf *dbg, Dwarf_Off debug_line_offset, + const char *comp_dir, unsigned address_size, + Dwarf_Lines **linesp, Dwarf_Files **filesp) { struct files_lines_s fake = { .debug_line_offset = debug_line_offset }; struct files_lines_s **found = tfind (&fake, &dbg->files_lines, files_lines_compare); if (found == NULL) { + /* This .debug_line is being read for the first time. */ Elf_Data *data = __libdw_checked_get_data (dbg, IDX_debug_line); if (data == NULL || __libdw_offset_in_section (dbg, IDX_debug_line, @@ -1160,8 +1337,19 @@ __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, struct files_lines_s *node = libdw_alloc (dbg, struct files_lines_s, sizeof *node, 1); - if (read_srclines (dbg, linep, lineendp, comp_dir, address_size, - &node->lines, &node->files) != 0) + /* Srcfiles will be read but srclines might not. Set lines here + to avoid possible uninitialized value errors. */ + node->lines = NULL; + + /* If linesp is NULL then read srcfiles without reading srclines. */ + if (linesp == NULL) + { + if (read_srcfiles (dbg, linep, lineendp, comp_dir, address_size, + NULL, &node->files) != 0) + return -1; + } + else if (read_srclines (dbg, linep, lineendp, comp_dir, address_size, + &node->lines, &node->files, false) != 0) return -1; node->debug_line_offset = debug_line_offset; @@ -1173,6 +1361,35 @@ __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, return -1; } } + else if (*found != NULL + && (*found)->files != NULL + && (*found)->lines == NULL) + { + /* Srcfiles were already read from this .debug_line. Now read + srclines. */ + Elf_Data *data = __libdw_checked_get_data (dbg, IDX_debug_line); + if (data == NULL + || __libdw_offset_in_section (dbg, IDX_debug_line, + debug_line_offset, 1) != 0) + return -1; + + const unsigned char *linep = data->d_buf + debug_line_offset; + const unsigned char *lineendp = data->d_buf + data->d_size; + + struct files_lines_s *node = *found; + + if (read_srclines (dbg, linep, lineendp, comp_dir, address_size, + &node->lines, &node->files, true) != 0) + return -1; + } + else if (*found != NULL + && (*found)->files == NULL + && (*found)->lines != NULL) + { + /* If srclines were read then srcfiles should have also been read. */ + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); + return -1; + } if (linesp != NULL) *linesp = (*found)->lines; @@ -1183,6 +1400,26 @@ __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, return 0; } +int +internal_function +__libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, + const char *comp_dir, unsigned address_size, + Dwarf_Lines **linesp, Dwarf_Files **filesp) +{ + return get_lines_or_files (dbg, debug_line_offset, comp_dir, + address_size, linesp, filesp); +} + +int +internal_function +__libdw_getsrcfiles (Dwarf *dbg, Dwarf_Off debug_line_offset, + const char *comp_dir, unsigned address_size, + Dwarf_Files **filesp) +{ + return get_lines_or_files (dbg, debug_line_offset, comp_dir, + address_size, NULL, filesp); +} + /* Get the compilation directory, if any is set. */ const char * __libdw_getcompdir (Dwarf_Die *cudie) diff --git a/libdw/dwarf_macro_getsrcfiles.c b/libdw/dwarf_macro_getsrcfiles.c index 11c587af..5e02935d 100644 --- a/libdw/dwarf_macro_getsrcfiles.c +++ b/libdw/dwarf_macro_getsrcfiles.c @@ -74,8 +74,8 @@ dwarf_macro_getsrcfiles (Dwarf *dbg, Dwarf_Macro *macro, the same unit through dwarf_getsrcfiles, and the file names will be broken. */ - if (__libdw_getsrclines (table->dbg, line_offset, table->comp_dir, - table->address_size, NULL, &table->files) < 0) + if (__libdw_getsrcfiles (table->dbg, line_offset, table->comp_dir, + table->address_size, &table->files) < 0) table->files = (void *) -1; } diff --git a/libdw/libdwP.h b/libdw/libdwP.h index c1c84ed3..e55ff50a 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -1108,6 +1108,16 @@ int __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, internal_function __nonnull_attribute__ (1); +/* Load .debug_line unit at DEBUG_LINE_OFFSET. COMP_DIR is a value of + DW_AT_comp_dir or NULL if that attribute is not available. Caches + the loaded unit and set *FILESP with loaded information. Returns 0 + for success or a negative value for failure. */ +int __libdw_getsrcfiles (Dwarf *dbg, Dwarf_Off debug_line_offset, + const char *comp_dir, unsigned address_size, + Dwarf_Files **filesp) + internal_function + __nonnull_attribute__ (1); + /* Load and return value of DW_AT_comp_dir from CUDIE. */ const char *__libdw_getcompdir (Dwarf_Die *cudie); diff --git a/tests/.gitignore b/tests/.gitignore index 772c7881..4937f0d1 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -75,6 +75,7 @@ /funcscopes /get-aranges /get-files +/get-files-define_file /get-lines /get-pubnames /get-units-invalid diff --git a/tests/Makefile.am b/tests/Makefile.am index b7fb7238..7aae3d8a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,7 +35,7 @@ endif check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ showptable update1 update2 update3 update4 test-nlist \ show-die-info get-files next-files get-lines next-lines \ - get-pubnames \ + get-pubnames get-files-define-file \ get-aranges allfcts line2addr addrscopes funcscopes \ show-abbrev hash newscn ecp dwflmodtest \ find-prologues funcretval allregs rdwrmmap \ @@ -655,7 +655,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ testfile-dwp-5-cu-index-overflow.dwp.bz2 \ testfile-dwp-4-cu-index-overflow.bz2 \ testfile-dwp-4-cu-index-overflow.dwp.bz2 \ - testfile-dwp-cu-index-overflow.source + testfile-dwp-cu-index-overflow.source \ + testfile-define-file.bz2 if USE_VALGRIND @@ -734,6 +735,7 @@ show_abbrev_LDADD = $(libdw) $(libelf) get_lines_LDADD = $(libdw) $(libelf) next_lines_LDADD = $(libdw) $(libelf) get_files_LDADD = $(libdw) $(libelf) +get_files_define_file_LDADD = $(libdw) $(libelf) next_files_LDADD = $(libdw) $(libelf) get_aranges_LDADD = $(libdw) $(libelf) allfcts_LDADD = $(libdw) $(libelf) diff --git a/tests/get-files-define-file.c b/tests/get-files-define-file.c new file mode 100644 index 00000000..583f9852 --- /dev/null +++ b/tests/get-files-define-file.c @@ -0,0 +1,162 @@ +/* Copyright (C) 2002, 2004, 2005, 2007 Red Hat, Inc. + This file is part of elfutils. + Written by Ulrich Drepper <[email protected]>, 2002. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 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 a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <fcntl.h> +#include <libelf.h> +#include ELFUTILS_HEADER(dw) +#include <stdio.h> +#include <unistd.h> +#include "../libdw/libdwP.h" + +static void +print_dirs_and_files (Dwarf_Files *files, const char *const *dirs, + size_t nfiles, size_t ndirs) +{ + if (dirs[0] == NULL) + puts (" dirs[0] = (null)"); + else + printf (" dirs[0] = \"%s\"\n", dirs[0]); + for (size_t i = 1; i < ndirs; ++i) + printf (" dirs[%zu] = \"%s\"\n", i, dirs[i]); + + for (size_t i = 0; i < nfiles; ++i) + printf (" file[%zu] = \"%s\"\n", i, + dwarf_filesrc (files, i, NULL, NULL)); +} + + +int +main (int argc, char *argv[]) +{ + int result = 0; + int cnt = argc - 1; + + int fd = open (argv[cnt], O_RDONLY); + + Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ); + if (dbg == NULL) + { + printf ("%s not usable\n", argv[cnt]); + result = 1; + if (fd != -1) + close (fd); + goto out; + } + + Dwarf_Off o = 0; + Dwarf_Off ncu; + size_t cuhl; + + /* Just inspect the first CU. */ + if (dwarf_nextcu (dbg, o, &ncu, &cuhl, NULL, NULL, NULL) != 0) + { + printf ("%s: cannot get CU\n", argv[cnt]); + result = 1; + goto out; + } + + Dwarf_Die die_mem; + Dwarf_Die *die = dwarf_offdie (dbg, o + cuhl, &die_mem); + + if (die == NULL) + { + printf ("%s: cannot get CU die\n", argv[cnt]); + result = 1; + goto out; + } + + Dwarf_Files *files; + size_t nfiles; + + /* The files from DW_LNE_define_file should not be included + until dwarf_getsrclines is called. */ + if (dwarf_getsrcfiles (die, &files, &nfiles) != 0) + { + printf ("%s: cannot get files\n", argv[cnt]); + result = 1; + goto out; + } + + if (die->cu->lines != NULL) + { + printf ("%s: dwarf_getsrcfiles should not get lines\n", argv[cnt]); + result = 1; + goto out; + } + + const char *const *dirs; + size_t ndirs; + if (dwarf_getsrcdirs (files, &dirs, &ndirs) != 0) + { + printf ("%s: cannot get include directories\n", argv[cnt]); + result = 1; + goto out; + } + + /* Print file info without files from DW_LNE_define_file. */ + print_dirs_and_files (files, dirs, nfiles, ndirs); + + Dwarf_Lines *lines; + size_t nlines; + + /* Reading the line program should add the new files. */ + if (dwarf_getsrclines (die, &lines, &nlines) != 0) + { + printf ("%s: cannot get lines\n", argv[cnt]); + result = 1; + goto out; + } + + Dwarf_Files *updated_files; + size_t num_updated_files; + + /* Get the new files. */ + if (dwarf_getsrcfiles (die, &updated_files, &num_updated_files) != 0) + { + printf ("%s: cannot get files\n", argv[cnt]); + result = 1; + goto out; + } + + const char *const *updated_dirs; + size_t num_updated_dirs; + + /* The dirs shouldn't change but verify that getsrcdirs still works. */ + if (dwarf_getsrcdirs (updated_files, &updated_dirs, &num_updated_dirs) != 0) + { + printf ("%s: cannot get include directories\n", argv[cnt]); + result = 1; + goto out; + } + + /* Verify that we didn't invalidate the old file info. */ + print_dirs_and_files (files, dirs, nfiles, ndirs); + + /* Print all files including those from DW_LNE_define_file. */ + print_dirs_and_files (updated_files, updated_dirs, + num_updated_files, num_updated_dirs); + +out: + dwarf_end (dbg); + close (fd); + + return result; +} diff --git a/tests/get-files.c b/tests/get-files.c index 04091733..fa65aa93 100644 --- a/tests/get-files.c +++ b/tests/get-files.c @@ -24,6 +24,7 @@ #include ELFUTILS_HEADER(dw) #include <stdio.h> #include <unistd.h> +#include "../libdw/libdwP.h" int @@ -76,6 +77,13 @@ main (int argc, char *argv[]) break; } + if (die->cu->lines != NULL) + { + printf ("%s: dwarf_getsrcfiles should not get lines\n", argv[cnt]); + result = 1; + break; + } + const char *const *dirs; size_t ndirs; if (dwarf_getsrcdirs (files, &dirs, &ndirs) != 0) diff --git a/tests/get-lines.c b/tests/get-lines.c index 188d0162..77fb3c54 100644 --- a/tests/get-lines.c +++ b/tests/get-lines.c @@ -26,6 +26,7 @@ #include <stdio.h> #include <string.h> #include <unistd.h> +#include "../libdw/libdwP.h" int @@ -69,6 +70,24 @@ main (int argc, char *argv[]) } old_cuoff = cuoff; + Dwarf_Files *files; + size_t nfiles; + + /* Get files first to test that lines are read separately. */ + if (dwarf_getsrcfiles (&die, &files, &nfiles) != 0) + { + printf ("%s: cannot get files\n", argv[cnt]); + result = 1; + break; + } + + if (die.cu->lines != NULL) + { + printf ("%s: dwarf_getsrcfiles should not get lines\n", argv[cnt]); + result = 1; + break; + } + Dwarf_Lines *lb; size_t nlb; if (dwarf_getsrclines (&die, &lb, &nlb) != 0) @@ -103,7 +122,6 @@ main (int argc, char *argv[]) /* Getting the file path through the Dwarf_Files should result in the same path. */ - Dwarf_Files *files; size_t idx; if (dwarf_line_file (l, &files, &idx) != 0) { diff --git a/tests/run-get-files.sh b/tests/run-get-files.sh index 1306544d..baf107d6 100755 --- a/tests/run-get-files.sh +++ b/tests/run-get-files.sh @@ -18,7 +18,7 @@ . $srcdir/test-subr.sh -testfiles testfile testfile2 +testfiles testfile testfile2 testfile-define-file testrun_compare ${abs_builddir}/get-files testfile testfile2 <<\EOF cuhl = 11, o = 0, asz = 4, osz = 4, ncu = 191 @@ -245,4 +245,75 @@ cuhl = 11, o = 0, asz = 8, osz = 4, ncu = 857 file[3] = "/usr/include/stdc-predef.h" EOF +tempfiles files define-files.out get-files-define-file.out + +cat > files <<\EOF + dirs[0] = "session" + dirs[1] = "/home/wcohen/minimal_mod" + dirs[2] = "include/asm" + dirs[3] = "include/linux" + dirs[4] = "include/asm-generic" + file[0] = "???" + file[1] = "/home/wcohen/minimal_mod/minimal_mod.c" + file[2] = "include/asm/gcc_intrin.h" + file[3] = "include/linux/kernel.h" + file[4] = "include/asm/processor.h" + file[5] = "include/asm/types.h" + file[6] = "include/asm/ptrace.h" + file[7] = "include/linux/sched.h" + file[8] = "include/asm/thread_info.h" + file[9] = "include/linux/thread_info.h" + file[10] = "include/asm/atomic.h" + file[11] = "include/linux/list.h" + file[12] = "include/linux/cpumask.h" + file[13] = "include/linux/rbtree.h" + file[14] = "include/asm/page.h" + file[15] = "include/linux/rwsem.h" + file[16] = "include/asm/rwsem.h" + file[17] = "include/asm/spinlock.h" + file[18] = "include/linux/completion.h" + file[19] = "include/linux/wait.h" + file[20] = "include/linux/aio.h" + file[21] = "include/linux/workqueue.h" + file[22] = "include/linux/timer.h" + file[23] = "include/linux/types.h" + file[24] = "include/asm/posix_types.h" + file[25] = "include/linux/pid.h" + file[26] = "include/linux/time.h" + file[27] = "include/linux/capability.h" + file[28] = "include/linux/signal.h" + file[29] = "include/linux/resource.h" + file[30] = "include/linux/sem.h" + file[31] = "include/asm/fpu.h" + file[32] = "include/linux/fs_struct.h" + file[33] = "include/asm/signal.h" + file[34] = "include/asm/siginfo.h" + file[35] = "include/asm-generic/siginfo.h" + file[36] = "include/asm/nodedata.h" + file[37] = "include/linux/mmzone.h" + file[38] = "include/linux/jiffies.h" + file[39] = "include/asm/io.h" + file[40] = "include/asm/machvec.h" + file[41] = "include/asm/smp.h" + file[42] = "include/asm/numa.h" + file[43] = "include/linux/slab.h" +EOF + +# Files should be printed 3 times, followed by the two files from +# DW_LNE_define_file +cat files > define-files.out +cat files >> define-files.out +cat files >> define-files.out +echo ' file[44] = "include/asm/abc.c"' >> define-files.out +echo ' file[45] = "include/linux/01.c"' >> define-files.out + +# testfile-define-file is a copy of testfile36.debug but with a modified +# line program. The line program in testfile-define-file consists of +# two DW_LNE_define_file opcodes. +# +# xxd was used to create a hexdump of testfile36.debug and a text editor +# was used to modify the line program. The modified hexdump was converted +# back to a binary with xxd -r. +cat define-files.out | testrun_compare ${abs_builddir}/get-files-define-file testfile-define-file + exit 0 diff --git a/tests/testfile-define-file.bz2 b/tests/testfile-define-file.bz2 Binary files differnew file mode 100644 index 00000000..32ee7b76 --- /dev/null +++ b/tests/testfile-define-file.bz2 |
