Hi Aaron,
On Tue, 2024-04-09 at 23:45 -0400, Aaron Merey wrote:
> dwarf_getsrcfiles causes line data to be read in addition to file data.
> This is wasteful for programs which only need file or directory names.
> Debuginfod server is one such example.
>
> Fix this by moving the srcfile reading in read_srclines into a separate
> function read_srcfiles. This change improves debuginfod server's max
> resident set size by up to 75% during rpm indexing.
>
> * libdw/dwarf_getsrcfiles.c (dwarf_getsrcfiles): Replace
> dwarf_getsrclines and __libdw_getsrclines with
> __libdw_getsrcfiles.
> * libdw/dwarf_getsrclines.c (read_line_header): New function.
> (read_srcfiles): New function.
> (read_srclines): Move srcfile reading into read_srcfiles.
> Add parameter to use cached srcfiles if available.
> Also merge srcfiles with any files from DW_LNE_define_file.
> (__libdw_getsrclines): Changed to call get_lines_or_files.
> (__libdw_getsrcfiles): New function. Calls get_lines_or_files.
> (get_lines_or_files): New function based on the old
> __libdw_getsrclines. Call read_srcfiles if linesp is NULL,
> otherwise call read_srclines. Pass previously read srcfiles
> to read_srclines if available.
> * libdw/dwarf_macro_getsrcfiles.c (dwarf_macro_getsrcfiles):
> Replace __libdw_getsrclines with __libdw_getsrcfiles.
> * libdw/libdwP.h (__libdw_getsrcfiles): New declaration.
> * tests/.gitignore: Add new test binary.
> * tests/get-files.c: Verify that dwarf_getsrcfiles does
> not cause srclines to be read.
> * tests/get-lines.c: Verify that srclines can be read
> after srcfiles have been read.
> * tests/Makefile.am: Add new testfiles.
> * tests/get-files-define-file.c: Print file names before
> and after reading DW_LNE_define_file.
> * tests/run-get-files.sh: Add get-files-define-file test.
> * tests/testfile-define-file.bz2: New testfile. Copy of
> testfile36.debug but with a line program consisting of two
> DW_LNE_define_file opcodes.
>
> https://sourceware.org/bugzilla/show_bug.cgi?id=27405
>
> Signed-off-by: Aaron Merey <amerey@redhat.com>
> ---
>
> v2 changes:
> Restored support for DW_LNE_define_file.
Great. And sorry I first suggested to just drop it and then said I
would like it back. This was more work than I though.
> Added DW_LNE_define_file testcase and testfile.
Nice. How did you edit this file?
> Added new function get_lines_or_files which is based on the old
> __libdw_getsrclines. __libdw_getsrclines and __libdw_getsrcfiles now
> call get_lines_or_files.
It is just a simple rename, but so much nicer to my eyes. Thanks.
> Mentioned max resident set size improvement in the commit message. I
> did not mention a 5% speed up to rpm indexing that I have previously
> talked about since I could not reliably reproduce it with v2 of the patch.
> I suspect that speed up may have been due to the smaller max RSS reducing
> swapping during that round of testing.
Makes sense. And a reduction of 75% of the max resident size it pretty
huge in itself.
> + case DW_LNE_define_file:
> + {
> + char *fname = (char *) linep;
> + uint8_t *endp = memchr (linep, '\0', lineendp - linep);
> + if (endp == NULL)
> + goto invalid_data;
> + size_t fnamelen = endp - linep;
> + linep = endp + 1;
> +
> + unsigned int diridx;
> + if (unlikely (linep >= lineendp))
> + goto invalid_data;
> + get_uleb128 (diridx, linep, lineendp);
> +
> + size_t ndirs = (*filesp)->ndirs;
This one line uses tabs for indentation, all others spaces.
> + if (unlikely (diridx >= ndirs))
> + {
> + __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
> + goto invalid_data;
> + }
> + Dwarf_Word mtime;
> + if (unlikely (linep >= lineendp))
> + goto invalid_data;
> + get_uleb128 (mtime, linep, lineendp);
> + Dwarf_Word filelength;
> + if (unlikely (linep >= lineendp))
> + goto invalid_data;
> + get_uleb128 (filelength, linep, lineendp);
> +
> + /* 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_INVALID_DIR_IDX);
> - goto invalid_data;
> + __libdw_seterrno (DWARF_E_NOMEM);
> + goto out;
> }
And this last block uses tabs again.
> - Dwarf_Word mtime;
> - if (unlikely (linep >= lineendp))
> - goto invalid_data;
> - get_uleb128 (mtime, linep, lineendp);
> - Dwarf_Word filelength;
> - if (unlikely (linep >= lineendp))
> - goto invalid_data;
> - get_uleb128 (filelength, linep, lineendp);
> -
> - struct filelist *new_file = NEW_FILE ();
> - if (fname[0] == '/')
> - 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. Just
> - keep the file relative. */
> - {
> - cp = stpcpy (cp, dirarray[diridx].dir);
> - *cp++ = '/';
> - }
> - strcpy (cp, fname);
> - }
> -
> - new_file->info.mtime = mtime;
> - new_file->info.length = filelength;
> - }
> - break;
> + nfilelist++;
> + new_file->next = filelist;
> + filelist = new_file;
Here tabs again, then the rest spaces.
> + 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, (dnamelen + fnamelen + 2));
> + char *cp = new_file->info.name;
> +
> + 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, dname);
> + *cp++ = '/';
> + }
> + strcpy (cp, fname);
> + }
> +
> + new_file->info.mtime = mtime;
> + new_file->info.length = filelength;
> + }
> + break;
>
So inconsistent indentation, but the code looks good.
> @@ -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];
Again slightly off indentation.
But I also don't fully follow the prevdirs/newdirs copying.
Why is this? No newdirs are defined here, are there?
Maybe I don't understand the data-structure used here.
> diff --git a/tests/.gitignore b/tests/.gitignore
> index 0289959d..e40ad238 100644
> --- a/tests/.gitignore
> +++ b/tests/.gitignore
> @@ -74,6 +74,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 9141074f..7b016dc8 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 \
> @@ -646,7 +646,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
> @@ -725,6 +726,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 <drepper@redhat.com>, 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/run-get-files.sh b/tests/run-get-files.sh
> index 1306544d..c2bc01bf 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,67 @@ 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 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
> +
> +cat define-files.out | testrun_compare ${abs_builddir}/get-files-define-file testfile-define-file
> +
> exit 0
Nice.
So testfile-define-file is actually testfile36.debug but with a new
line program? How did you edit/insert that one?
Cheers,
Mark
@@ -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)
@@ -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;
@@ -790,63 +924,85 @@ read_srclines (Dwarf *dbg,
goto out;
break;
- case DW_LNE_define_file:
- {
- char *fname = (char *) linep;
- uint8_t *endp = memchr (linep, '\0', lineendp - linep);
- if (endp == NULL)
- goto invalid_data;
- size_t fnamelen = endp - linep;
- linep = endp + 1;
-
- unsigned int diridx;
- if (unlikely (linep >= lineendp))
- goto invalid_data;
- get_uleb128 (diridx, linep, lineendp);
- if (unlikely (diridx >= ndirlist))
+ case DW_LNE_define_file:
+ {
+ char *fname = (char *) linep;
+ uint8_t *endp = memchr (linep, '\0', lineendp - linep);
+ if (endp == NULL)
+ goto invalid_data;
+ size_t fnamelen = endp - linep;
+ linep = endp + 1;
+
+ unsigned int diridx;
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (diridx, linep, lineendp);
+
+ size_t ndirs = (*filesp)->ndirs;
+ if (unlikely (diridx >= ndirs))
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
+ goto invalid_data;
+ }
+ Dwarf_Word mtime;
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (mtime, linep, lineendp);
+ Dwarf_Word filelength;
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (filelength, linep, lineendp);
+
+ /* 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_INVALID_DIR_IDX);
- goto invalid_data;
+ __libdw_seterrno (DWARF_E_NOMEM);
+ goto out;
}
- Dwarf_Word mtime;
- if (unlikely (linep >= lineendp))
- goto invalid_data;
- get_uleb128 (mtime, linep, lineendp);
- Dwarf_Word filelength;
- if (unlikely (linep >= lineendp))
- goto invalid_data;
- get_uleb128 (filelength, linep, lineendp);
-
- struct filelist *new_file = NEW_FILE ();
- if (fname[0] == '/')
- 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. Just
- keep the file relative. */
- {
- cp = stpcpy (cp, dirarray[diridx].dir);
- *cp++ = '/';
- }
- strcpy (cp, fname);
- }
-
- new_file->info.mtime = mtime;
- new_file->info.length = filelength;
- }
- break;
+ 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, (dnamelen + fnamelen + 2));
+ char *cp = new_file->info.name;
+
+ 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, dname);
+ *cp++ = '/';
+ }
+ strcpy (cp, fname);
+ }
+
+ new_file->info.mtime = mtime;
+ new_file->info.length = filelength;
+ }
+ break;
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;
+
+invalid_data:
+ __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE);
- out:
+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)
@@ -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;
}
@@ -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);
@@ -74,6 +74,7 @@
/funcscopes
/get-aranges
/get-files
+/get-files-define_file
/get-lines
/get-pubnames
/get-units-invalid
@@ -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 \
@@ -646,7 +646,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
@@ -725,6 +726,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)
new file mode 100644
@@ -0,0 +1,162 @@
+/* Copyright (C) 2002, 2004, 2005, 2007 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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;
+}
@@ -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)
@@ -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)
{
@@ -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,67 @@ 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 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
+
+cat define-files.out | testrun_compare ${abs_builddir}/get-files-define-file testfile-define-file
+
exit 0
new file mode 100644
GIT binary patch
literal 24910
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
H+)L6<#`!(z
literal 0
HcmV?d00001
--
2.43.0