rpm/elfutils/libdwarf/dwarf_srcfiles.c

345 lines
8.8 KiB
C

/* Return source files of compilation unit.
Copyright (C) 2000, 2002 Red Hat, Inc.
Written by Ulrich Drepper <drepper@redhat.com>, 2000.
This program is Open Source software; you can redistribute it and/or
modify it under the terms of the Open Software License version 1.0 as
published by the Open Source Initiative.
You should have received a copy of the Open Software License along
with this program; if not, you may obtain a copy of the Open Software
License version 1.0 from http://www.opensource.org/licenses/osl.php or
by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
3001 King Ranch Road, Ukiah, CA 95482. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <dwarf.h>
#include <stdlib.h>
#include <string.h>
#include <libdwarfP.h>
struct dirlist
{
char *dir;
size_t len;
struct dirlist *next;
};
struct filelist
{
char *name;
Dwarf_Unsigned mtime;
Dwarf_Unsigned length;
struct filelist *next;
};
static int
read_file_names (Dwarf_Debug dbg, char *comp_dir, Dwarf_Small **linepp,
char ***result, Dwarf_Signed *nresult, Dwarf_Error *error)
{
Dwarf_Small *linep = *linepp;
struct dirlist comp_dir_elem;
struct dirlist *dirlist;
unsigned int ndirlist;
struct dirlist **dirarray;
struct filelist *filelist = NULL;
unsigned int nfilelist = 0;
/* First comes the list of directories. Add the compilation directory
first since the index zero is used for it. */
comp_dir_elem.dir = comp_dir;
comp_dir_elem.len = comp_dir ? strlen (comp_dir) : 0;
comp_dir_elem.next = NULL;
dirlist = &comp_dir_elem;
ndirlist = 1;
while (*linep != 0)
{
struct dirlist *new_dir = (struct dirlist *) alloca (sizeof (*new_dir));
new_dir->dir = (char *) linep;
new_dir->len = strlen ((char *) linep);
new_dir->next = dirlist;
dirlist = new_dir;
++ndirlist;
linep += new_dir->len + 1;
}
/* Skip the final NUL byte. */
++linep;
/* Rearrange the list in array form. */
dirarray = (struct dirlist **) alloca (sizeof (*dirarray));
while (ndirlist-- > 0)
{
dirarray[ndirlist] = dirlist;
dirlist = dirlist->next;
}
/* Now read the files. */
while (*linep != 0)
{
struct filelist *new_file =
(struct filelist *) alloca (sizeof (*new_file));
char *fname;
size_t fnamelen;
Dwarf_Unsigned diridx;
/* First comes the file name. */
fname = (char *) linep;
fnamelen = strlen (fname);
linep += fnamelen + 1;
/* Then the index. */
get_uleb128 (diridx, linep);
if (unlikely (diridx >= ndirlist))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DIR_IDX);
return DW_DLV_ERROR;
}
if (*fname == '/')
/* It's an absolute path. */
new_file->name = strdup (fname);
else
{
new_file->name = (char *) malloc (dirarray[diridx]->len + 1
+ fnamelen + 1);
if (new_file->name != NULL)
{
char *cp = new_file->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);
}
}
if (new_file->name == NULL)
{
/* XXX Should we bother to free all the memory? */
__libdwarf_error (dbg, error, DW_E_NOMEM);
return DW_DLV_ERROR;
}
/* Next comes the modification time. */
get_uleb128 (new_file->mtime, linep);
/* Finally the length of the file. */
get_uleb128 (new_file->length, linep);
new_file->next = filelist;
filelist = new_file;
++nfilelist;
}
/* Put all the files in an array. */
*result = (char **) malloc (nfilelist * sizeof (char *));
if (*result == NULL)
{
/* XXX Should we bother to free all the memory? */
__libdwarf_error (dbg, error, DW_E_NOMEM);
return DW_DLV_ERROR;
}
*nresult = nfilelist;
while (nfilelist-- > 0)
{
(*result)[nfilelist] = filelist->name;
filelist = filelist->next;
}
/* Provide caller address of next byte. */
*linepp = linep + 1;
return DW_DLV_OK;
}
int
dwarf_srcfiles (die, srcfiles, srcfilecount, error)
Dwarf_Die die;
char ***srcfiles;
Dwarf_Signed *srcfilecount;
Dwarf_Error *error;
{
Dwarf_CU_Info cu = die->cu;
Dwarf_Debug dbg = cu->dbg;
Dwarf_Attribute stmt_list;
Dwarf_Attribute comp_dir_attr;
char *comp_dir;
Dwarf_Unsigned offset;
Dwarf_Small *linep;
Dwarf_Small *lineendp;
Dwarf_Small *header_start;
Dwarf_Unsigned header_length;
unsigned int unit_length;
unsigned int version;
unsigned int opcode_base;
int length;
int res;
/* For now we haven't found anything. */
*srcfilecount = 0;
/* The die must be for a compilation unit. */
if (die->abbrev->tag != DW_TAG_compile_unit)
{
__libdwarf_error (die->cu->dbg, error, DW_E_NO_CU);
return DW_DLV_ERROR;
}
/* The die must have a statement list associated. */
res = dwarf_attr (die, DW_AT_stmt_list, &stmt_list, error);
if (res != DW_DLV_OK)
return res;
/* Get the offset into the .debug_line section. */
res = dwarf_formudata (stmt_list, &offset, error);
if (res != DW_DLV_OK)
{
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
return res;
}
/* We need a .debug_line section. */
if (dbg->sections[IDX_debug_line].addr == NULL)
{
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
__libdwarf_error (dbg, error, DW_E_NO_DEBUG_LINE);
return DW_DLV_ERROR;
}
linep = (Dwarf_Small *) dbg->sections[IDX_debug_line].addr + offset;
lineendp = ((Dwarf_Small *) dbg->sections[IDX_debug_line].addr
+ dbg->sections[IDX_debug_line].size);
/* Test whether at least the first 4 bytes are available. */
if (unlikely (linep + 4 > lineendp))
{
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
/* Get the compilation directory. */
res = dwarf_attr (die, DW_AT_comp_dir, &comp_dir_attr, error);
if (unlikely (res == DW_DLV_ERROR)
|| (res == DW_DLV_OK
&& unlikely (dwarf_formstring (comp_dir_attr, &comp_dir, error)
== DW_DLV_ERROR)))
{
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
else if (res == DW_DLV_OK)
dwarf_dealloc (dbg, comp_dir_attr, DW_DLA_ATTR);
else
comp_dir = NULL;
/* Read the unit_length. */
unit_length = read_4ubyte_unaligned (dbg, linep);
linep += 4;
length = 4;
if (unit_length == 0xffffffff)
{
if (unlikely (linep + 8 > lineendp))
{
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
unit_length = read_8ubyte_unaligned (dbg, linep);
linep += 8;
length = 8;
}
/* Check whether we have enough room in the section. */
if (unlikely (linep + unit_length > lineendp))
{
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
lineendp = linep + unit_length;
/* The next element of the header is the version identifier. */
version = read_2ubyte_unaligned (dbg, linep);
if (unlikely (version != DWARF_VERSION))
{
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
__libdwarf_error (dbg, error, DW_E_VERSION_ERROR);
return DW_DLV_ERROR;
}
linep += 2;
/* Next comes the header length. */
if (length == 4)
{
header_length = read_4ubyte_unaligned (dbg, linep);
linep += 4;
}
else
{
header_length = read_8ubyte_unaligned (dbg, linep);
linep += 8;
}
header_start = linep;
/* Next the minimum instruction length. Skip it. */
++linep;
/* Then the flag determining the default value of the is_stmt
register. Skip it. */
++linep;
/* Now the line base. Skip it. */
++linep;
/* And the line range. Skip it. */
++linep;
/* The opcode base. */
opcode_base = *linep++;
/* Skip the array with the standard opcode length. */
linep += opcode_base - 1;
/* Next the include directories and the file names. */
if (unlikely (read_file_names (dbg, comp_dir, &linep, srcfiles, srcfilecount,
error) != DW_DLV_OK))
{
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
return DW_DLV_ERROR;
}
/* Consistency check. */
if (unlikely (linep != header_start + header_length))
{
int i;
for (i = 0; i < *srcfilecount; ++i)
dwarf_dealloc (dbg, (*srcfiles)[i], DW_DLA_STRING);
dwarf_dealloc (dbg, *srcfiles, DW_DLA_LIST);
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR);
return DW_DLV_OK;
}