Squashed 'third_party/elfutils/' content from commit 555e15e
Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c
new file mode 100644
index 0000000..d02c38d
--- /dev/null
+++ b/libdw/dwarf_getsrclines.c
@@ -0,0 +1,952 @@
+/* Return line number information of CU.
+ Copyright (C) 2004-2010, 2013, 2014, 2015, 2016 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <search.h>
+
+#include "dwarf.h"
+#include "libdwP.h"
+
+
+struct filelist
+{
+ Dwarf_Fileinfo info;
+ struct filelist *next;
+};
+
+struct linelist
+{
+ Dwarf_Line line;
+ struct linelist *next;
+ size_t sequence;
+};
+
+
+/* Compare by Dwarf_Line.addr, given pointers into an array of pointers. */
+static int
+compare_lines (const void *a, const void *b)
+{
+ struct linelist *const *p1 = a;
+ struct linelist *const *p2 = b;
+ struct linelist *list1 = *p1;
+ struct linelist *list2 = *p2;
+ Dwarf_Line *line1 = &list1->line;
+ Dwarf_Line *line2 = &list2->line;
+
+ if (line1->addr != line2->addr)
+ return (line1->addr < line2->addr) ? -1 : 1;
+
+ /* An end_sequence marker precedes a normal record at the same address. */
+ if (line1->end_sequence != line2->end_sequence)
+ return line2->end_sequence - line1->end_sequence;
+
+ /* Otherwise, the linelist sequence maintains a stable sort. */
+ return (list1->sequence < list2->sequence) ? -1
+ : (list1->sequence > list2->sequence) ? 1
+ : 0;
+}
+
+struct line_state
+{
+ Dwarf_Word addr;
+ unsigned int op_index;
+ unsigned int file;
+ int64_t line;
+ unsigned int column;
+ uint_fast8_t is_stmt;
+ bool basic_block;
+ bool prologue_end;
+ bool epilogue_begin;
+ unsigned int isa;
+ unsigned int discriminator;
+ struct linelist *linelist;
+ size_t nlinelist;
+ unsigned int end_sequence;
+};
+
+static inline void
+run_advance_pc (struct line_state *state, unsigned int op_advance,
+ uint_fast8_t minimum_instr_len, uint_fast8_t max_ops_per_instr)
+{
+ state->addr += minimum_instr_len * ((state->op_index + op_advance)
+ / max_ops_per_instr);
+ state->op_index = (state->op_index + op_advance) % max_ops_per_instr;
+}
+
+static inline bool
+add_new_line (struct line_state *state, struct linelist *new_line)
+{
+ /* Set the line information. For some fields we use bitfields,
+ so we would lose information if the encoded values are too large.
+ Check just for paranoia, and call the data "invalid" if it
+ violates our assumptions on reasonable limits for the values. */
+ new_line->next = state->linelist;
+ new_line->sequence = state->nlinelist;
+ state->linelist = new_line;
+ ++(state->nlinelist);
+
+ /* Set the line information. For some fields we use bitfields,
+ so we would lose information if the encoded values are too large.
+ Check just for paranoia, and call the data "invalid" if it
+ violates our assumptions on reasonable limits for the values. */
+#define SET(field) \
+ do { \
+ new_line->line.field = state->field; \
+ if (unlikely (new_line->line.field != state->field)) \
+ return true; \
+ } while (0)
+
+ SET (addr);
+ SET (op_index);
+ SET (file);
+ SET (line);
+ SET (column);
+ SET (is_stmt);
+ SET (basic_block);
+ SET (end_sequence);
+ SET (prologue_end);
+ SET (epilogue_begin);
+ SET (isa);
+ SET (discriminator);
+
+#undef SET
+
+ return false;
+}
+
+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)
+{
+ int res = -1;
+
+ size_t nfilelist = 0;
+ unsigned int ndirlist = 0;
+
+ struct filelist null_file =
+ {
+ .info =
+ {
+ .name = "???",
+ .mtime = 0,
+ .length = 0
+ },
+ .next = NULL
+ };
+ struct filelist *filelist = &null_file;
+
+ /* 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
+#define MAX_STACK_FILES (MAX_STACK_ALLOC / 4)
+#define MAX_STACK_DIRS (MAX_STACK_ALLOC / 16)
+
+ struct dirlist
+ {
+ const char *dir;
+ size_t len;
+ };
+ struct dirlist dirstack[MAX_STACK_DIRS];
+ struct dirlist *dirarray = dirstack;
+
+ /* We are about to process the statement program. Initialize the
+ state machine registers (see 6.2.2 in the v2.1 specification). */
+ 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
+ };
+
+ if (unlikely (linep + 4 > lineendp))
+ {
+ invalid_data:
+ __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE);
+ goto out;
+ }
+
+ Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep);
+ unsigned int length = 4;
+ if (unlikely (unit_length == DWARF3_LENGTH_64_BIT))
+ {
+ if (unlikely (linep + 8 > lineendp))
+ goto invalid_data;
+ unit_length = read_8ubyte_unaligned_inc (dbg, linep);
+ length = 8;
+ }
+
+ /* Check whether we have enough room in the section. */
+ if (unlikely (unit_length > (size_t) (lineendp - linep)
+ || unit_length < 2 + length + 5 * 1))
+ goto invalid_data;
+ lineendp = linep + unit_length;
+
+ /* The next element of the header is the version identifier. */
+ uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
+ if (unlikely (version < 2) || unlikely (version > 4))
+ {
+ __libdw_seterrno (DWARF_E_VERSION);
+ goto out;
+ }
+
+ /* Next comes the header length. */
+ Dwarf_Word header_length;
+ if (length == 4)
+ header_length = read_4ubyte_unaligned_inc (dbg, linep);
+ else
+ header_length = read_8ubyte_unaligned_inc (dbg, linep);
+ const unsigned char *header_start = linep;
+
+ /* Next the minimum instruction length. */
+ uint_fast8_t minimum_instr_len = *linep++;
+
+ /* Next the maximum operations per instruction, in version 4 format. */
+ uint_fast8_t max_ops_per_instr = 1;
+ if (version >= 4)
+ {
+ if (unlikely (lineendp - linep < 5))
+ goto invalid_data;
+ max_ops_per_instr = *linep++;
+ if (unlikely (max_ops_per_instr == 0))
+ goto invalid_data;
+ }
+
+ /* Then the flag determining the default value of the is_stmt
+ register. */
+ uint_fast8_t default_is_stmt = *linep++;
+
+ /* Now the line base. */
+ int_fast8_t line_base = (int8_t) *linep++;
+
+ /* And the line range. */
+ uint_fast8_t line_range = *linep++;
+
+ /* The opcode base. */
+ uint_fast8_t opcode_base = *linep++;
+
+ /* Remember array with the standard opcode length (-1 to account for
+ the opcode with value zero not being mentioned). */
+ const uint8_t *standard_opcode_lengths = linep - 1;
+ if (unlikely (lineendp - linep < opcode_base - 1))
+ goto invalid_data;
+ linep += opcode_base - 1;
+
+ /* First comes the list of directories. Add the compilation
+ directory first since the index zero is used for it. */
+ struct dirlist comp_dir_elem =
+ {
+ .dir = comp_dir,
+ .len = comp_dir ? strlen (comp_dir) : 0,
+ };
+ ndirlist = 1;
+
+ /* First count the entries. */
+ const unsigned char *dirp = linep;
+ unsigned int ndirs = 0;
+ while (*dirp != 0)
+ {
+ uint8_t *endp = memchr (dirp, '\0', lineendp - dirp);
+ if (endp == NULL)
+ goto invalid_data;
+ ++ndirs;
+ dirp = endp + 1;
+ }
+ ndirlist += ndirs;
+
+ /* Arrange the list in array form. */
+ if (ndirlist >= MAX_STACK_DIRS)
+ {
+ dirarray = (struct dirlist *) malloc (ndirlist * sizeof (*dirarray));
+ if (unlikely (dirarray == NULL))
+ {
+ no_mem:
+ __libdw_seterrno (DWARF_E_NOMEM);
+ goto out;
+ }
+ }
+ dirarray[0] = comp_dir_elem;
+ for (unsigned int n = 1; n < ndirlist; n++)
+ {
+ dirarray[n].dir = (char *) linep;
+ uint8_t *endp = memchr (linep, '\0', lineendp - linep);
+ assert (endp != NULL);
+ dirarray[n].len = endp - linep;
+ linep = endp + 1;
+ }
+ /* Skip the final NUL byte. */
+ ++linep;
+
+ /* Allocate memory for a new file. For the first MAX_STACK_FILES
+ entries just return a slot in the preallocated stack array. */
+ struct filelist flstack[MAX_STACK_FILES];
+#define NEW_FILE() ({ \
+ struct filelist *fl = (nfilelist < MAX_STACK_FILES \
+ ? &flstack[nfilelist] \
+ : malloc (sizeof (struct filelist))); \
+ if (unlikely (fl == NULL)) \
+ goto no_mem; \
+ ++nfilelist; \
+ fl->next = filelist; \
+ filelist = fl; \
+ fl; })
+
+ /* Now read the files. */
+ nfilelist = 1;
+
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ while (*linep != 0)
+ {
+ struct filelist *new_file = NEW_FILE ();
+
+ /* First comes the file name. */
+ char *fname = (char *) linep;
+ uint8_t *endp = memchr (fname, '\0', lineendp - linep);
+ if (endp == NULL)
+ goto invalid_data;
+ size_t fnamelen = endp - (uint8_t *) fname;
+ linep = endp + 1;
+
+ /* Then the index. */
+ Dwarf_Word diridx;
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (diridx, linep, lineendp);
+ if (unlikely (diridx >= ndirlist))
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
+ goto out;
+ }
+
+ if (*fname == '/')
+ /* It's an absolute path. */
+ new_file->info.name = fname;
+ else
+ {
+ new_file->info.name = libdw_alloc (dbg, char, 1,
+ dirarray[diridx].len + 1
+ + fnamelen + 1);
+ char *cp = new_file->info.name;
+
+ if (dirarray[diridx].dir != NULL)
+ {
+ /* This value could be NULL in case the DW_AT_comp_dir
+ was not present. We cannot do much in this case.
+ The easiest thing is to convert the path in an
+ absolute path. */
+ cp = stpcpy (cp, dirarray[diridx].dir);
+ }
+ *cp++ = '/';
+ strcpy (cp, fname);
+ assert (strlen (new_file->info.name)
+ < dirarray[diridx].len + 1 + fnamelen + 1);
+ }
+
+ /* Next comes the modification time. */
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (new_file->info.mtime, linep, lineendp);
+
+ /* Finally the length of the file. */
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (new_file->info.length, linep, lineendp);
+ }
+ /* Skip the final NUL byte. */
+ ++linep;
+
+ /* Consistency check. */
+ if (unlikely (linep != header_start + header_length))
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ goto out;
+ }
+
+ state.is_stmt = 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)
+
+ /* Process the instructions. */
+
+ /* Adds a new line to the matrix. For the first MAX_STACK_LINES
+ entries just return a slot in the preallocated stack array. */
+ struct linelist llstack[MAX_STACK_LINES];
+#define NEW_LINE(end_seq) \
+ do { \
+ struct linelist *ll = (state.nlinelist < MAX_STACK_LINES \
+ ? &llstack[state.nlinelist] \
+ : malloc (sizeof (struct linelist))); \
+ if (unlikely (ll == NULL)) \
+ goto no_mem; \
+ state.end_sequence = end_seq; \
+ if (unlikely (add_new_line (&state, ll))) \
+ goto invalid_data; \
+ } while (0)
+
+ while (linep < lineendp)
+ {
+ unsigned int opcode;
+ unsigned int u128;
+ int s128;
+
+ /* Read the opcode. */
+ opcode = *linep++;
+
+ /* Is this a special opcode? */
+ if (likely (opcode >= opcode_base))
+ {
+ if (unlikely (line_range == 0))
+ goto invalid_data;
+
+ /* Yes. Handling this is quite easy since the opcode value
+ is computed with
+
+ opcode = (desired line increment - line_base)
+ + (line_range * address advance) + opcode_base
+ */
+ int line_increment = (line_base
+ + (opcode - opcode_base) % line_range);
+
+ /* Perform the increments. */
+ state.line += line_increment;
+ advance_pc ((opcode - opcode_base) / line_range);
+
+ /* Add a new line with the current state machine values. */
+ NEW_LINE (0);
+
+ /* Reset the flags. */
+ state.basic_block = false;
+ state.prologue_end = false;
+ state.epilogue_begin = false;
+ state.discriminator = 0;
+ }
+ else if (opcode == 0)
+ {
+ /* This an extended opcode. */
+ if (unlikely (lineendp - linep < 2))
+ goto invalid_data;
+
+ /* The length. */
+ uint_fast8_t len = *linep++;
+
+ if (unlikely ((size_t) (lineendp - linep) < len))
+ goto invalid_data;
+
+ /* The sub-opcode. */
+ opcode = *linep++;
+
+ switch (opcode)
+ {
+ case DW_LNE_end_sequence:
+ /* Add a new line with the current state machine values.
+ The is the end of the sequence. */
+ NEW_LINE (1);
+
+ /* Reset the registers. */
+ state.addr = 0;
+ state.op_index = 0;
+ state.file = 1;
+ state.line = 1;
+ state.column = 0;
+ state.is_stmt = default_is_stmt;
+ state.basic_block = false;
+ state.prologue_end = false;
+ state.epilogue_begin = false;
+ state.isa = 0;
+ state.discriminator = 0;
+ break;
+
+ case DW_LNE_set_address:
+ /* The value is an address. The size is defined as
+ apporiate for the target machine. We use the
+ address size field from the CU header. */
+ state.op_index = 0;
+ if (unlikely (lineendp - linep < (uint8_t) address_size))
+ goto invalid_data;
+ if (__libdw_read_address_inc (dbg, IDX_debug_line, &linep,
+ address_size, &state.addr))
+ 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))
+ {
+ __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);
+
+ 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. The easiest
+ thing is to convert the path in an
+ absolute path. */
+ cp = stpcpy (cp, dirarray[diridx].dir);
+ *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))
+ goto invalid_data;
+
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (state.discriminator, linep, lineendp);
+ break;
+
+ default:
+ /* Unknown, ignore it. */
+ if (unlikely ((size_t) (lineendp - (linep - 1)) < len))
+ goto invalid_data;
+ linep += len - 1;
+ break;
+ }
+ }
+ else if (opcode <= DW_LNS_set_isa)
+ {
+ /* This is a known standard opcode. */
+ switch (opcode)
+ {
+ case DW_LNS_copy:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ /* Add a new line with the current state machine values. */
+ NEW_LINE (0);
+
+ /* Reset the flags. */
+ state.basic_block = false;
+ state.prologue_end = false;
+ state.epilogue_begin = false;
+ state.discriminator = 0;
+ break;
+
+ case DW_LNS_advance_pc:
+ /* Takes one uleb128 parameter which is added to the
+ address. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_data;
+
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (u128, linep, lineendp);
+ advance_pc (u128);
+ break;
+
+ case DW_LNS_advance_line:
+ /* Takes one sleb128 parameter which is added to the
+ line. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_data;
+
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_sleb128 (s128, linep, lineendp);
+ state.line += s128;
+ break;
+
+ case DW_LNS_set_file:
+ /* Takes one uleb128 parameter which is stored in file. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_data;
+
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (u128, linep, lineendp);
+ state.file = u128;
+ break;
+
+ case DW_LNS_set_column:
+ /* Takes one uleb128 parameter which is stored in column. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_data;
+
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (u128, linep, lineendp);
+ state.column = u128;
+ break;
+
+ case DW_LNS_negate_stmt:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ state.is_stmt = 1 - state.is_stmt;
+ break;
+
+ case DW_LNS_set_basic_block:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ state.basic_block = true;
+ break;
+
+ case DW_LNS_const_add_pc:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ if (unlikely (line_range == 0))
+ goto invalid_data;
+
+ advance_pc ((255 - opcode_base) / 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)
+ || unlikely (lineendp - linep < 2))
+ goto invalid_data;
+
+ state.addr += read_2ubyte_unaligned_inc (dbg, linep);
+ state.op_index = 0;
+ break;
+
+ case DW_LNS_set_prologue_end:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ state.prologue_end = true;
+ break;
+
+ case DW_LNS_set_epilogue_begin:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ state.epilogue_begin = true;
+ break;
+
+ case DW_LNS_set_isa:
+ /* Takes one uleb128 parameter which is stored in isa. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_data;
+
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (state.isa, linep, lineendp);
+ break;
+ }
+ }
+ else
+ {
+ /* 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)
+ {
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ get_uleb128 (u128, linep, lineendp);
+ }
+
+ /* Next round, ignore this opcode. */
+ continue;
+ }
+ }
+
+ /* 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--)
+ {
+ 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. */
+ if (filesp != NULL)
+ *filesp = files;
+
+ size_t buf_size = (sizeof (Dwarf_Lines)
+ + (sizeof (Dwarf_Line) * state.nlinelist));
+ void *buf = libdw_alloc (dbg, Dwarf_Lines, buf_size, 1);
+
+ /* First use the buffer for the pointers, and sort the entries.
+ We'll write the pointers in the end of the buffer, and then
+ copy into the buffer from the beginning so the overlap works. */
+ assert (sizeof (Dwarf_Line) >= sizeof (struct linelist *));
+ struct linelist **sortlines = (buf + buf_size
+ - sizeof (struct linelist **) * state.nlinelist);
+
+ /* The list is in LIFO order and usually they come in clumps with
+ ascending addresses. So fill from the back to probably start with
+ runs already in order before we sort. */
+ struct linelist *lineslist = state.linelist;
+ for (size_t i = state.nlinelist; i-- > 0; )
+ {
+ sortlines[i] = lineslist;
+ lineslist = lineslist->next;
+ }
+ assert (lineslist == NULL);
+
+ /* Sort by ascending address. */
+ qsort (sortlines, state.nlinelist, sizeof sortlines[0], &compare_lines);
+
+ /* Now that they are sorted, put them in the final array.
+ The buffers overlap, so we've clobbered the early elements
+ of SORTLINES by the time we're reading the later ones. */
+ Dwarf_Lines *lines = buf;
+ lines->nlines = state.nlinelist;
+ for (size_t i = 0; i < state.nlinelist; ++i)
+ {
+ lines->info[i] = sortlines[i]->line;
+ lines->info[i].files = files;
+ }
+
+ /* Make sure the highest address for the CU is marked as end_sequence.
+ This is required by the DWARF spec, but some compilers forget and
+ dwfl_module_getsrc depends on it. */
+ if (state.nlinelist > 0)
+ lines->info[state.nlinelist - 1].end_sequence = 1;
+
+ /* Pass the line structure back to the caller. */
+ if (linesp != NULL)
+ *linesp = lines;
+
+ /* Success. */
+ res = 0;
+
+ out:
+ /* Free malloced line records, if any. */
+ for (size_t i = MAX_STACK_LINES; i < state.nlinelist; i++)
+ {
+ struct linelist *ll = state.linelist->next;
+ free (state.linelist);
+ state.linelist = ll;
+ }
+ if (ndirlist >= MAX_STACK_DIRS)
+ 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
+files_lines_compare (const void *p1, const void *p2)
+{
+ const struct files_lines_s *t1 = p1;
+ const struct files_lines_s *t2 = p2;
+
+ if (t1->debug_line_offset < t2->debug_line_offset)
+ return -1;
+ if (t1->debug_line_offset > t2->debug_line_offset)
+ return 1;
+
+ 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)
+{
+ 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)
+ {
+ 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 = 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)
+ return -1;
+
+ node->debug_line_offset = debug_line_offset;
+
+ found = tsearch (node, &dbg->files_lines, files_lines_compare);
+ if (found == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return -1;
+ }
+ }
+
+ if (linesp != NULL)
+ *linesp = (*found)->lines;
+
+ if (filesp != NULL)
+ *filesp = (*found)->files;
+
+ return 0;
+}
+
+/* Get the compilation directory, if any is set. */
+const char *
+__libdw_getcompdir (Dwarf_Die *cudie)
+{
+ Dwarf_Attribute compdir_attr_mem;
+ Dwarf_Attribute *compdir_attr = INTUSE(dwarf_attr) (cudie,
+ DW_AT_comp_dir,
+ &compdir_attr_mem);
+ return INTUSE(dwarf_formstring) (compdir_attr);
+}
+
+int
+dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
+{
+ if (cudie == NULL)
+ return -1;
+ if (! is_cudie (cudie))
+ {
+ __libdw_seterrno (DWARF_E_NOT_CUDIE);
+ return -1;
+ }
+
+ /* Get the information if it is not already known. */
+ struct Dwarf_CU *const cu = cudie->cu;
+ if (cu->lines == NULL)
+ {
+ /* Failsafe mode: no data found. */
+ cu->lines = (void *) -1l;
+ cu->files = (void *) -1l;
+
+ /* The die must have a statement list associated. */
+ Dwarf_Attribute stmt_list_mem;
+ Dwarf_Attribute *stmt_list = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list,
+ &stmt_list_mem);
+
+ /* Get the offset into the .debug_line section. NB: this call
+ also checks whether the previous dwarf_attr call failed. */
+ 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;
+
+ if (__libdw_getsrclines (cu->dbg, debug_line_offset,
+ __libdw_getcompdir (cudie),
+ cu->address_size, &cu->lines, &cu->files) < 0)
+ return -1;
+ }
+ else if (cu->lines == (void *) -1l)
+ return -1;
+
+ *lines = cu->lines;
+ *nlines = cu->lines->nlines;
+
+ // XXX Eventually: unlocking here.
+
+ return 0;
+}
+INTDEF(dwarf_getsrclines)