Copyright 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2007
Free Software Foundation, Inc.
Written by Gavin Romig-Koch of Cygnus Solutions (gavin@cygnus.com).
This file is part of BFD.
This program 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.
This program 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, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
#include "sysdep.h"
#include "bfd.h"
#include "libiberty.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/dwarf.h"
struct dwarf1_debug
{
bfd* abfd;
asymbol** syms;
struct dwarf1_unit* lastUnit;
Zero indicates that the .debug section failed to load. */
bfd_byte *debug_section;
bfd_byte *debug_section_end;
bfd_byte *line_section;
bfd_byte *line_section_end;
bfd_byte *currentDie;
};
struct dwarf1_unit
{
struct dwarf1_unit* prev;
char *name;
unsigned long low_pc;
unsigned long high_pc;
int has_stmt_list;
unsigned long stmt_list_offset;
bfd_byte *first_child;
unsigned long line_count;
struct linenumber* linenumber_table;
struct dwarf1_func* func_list;
};
struct dwarf1_func
{
struct dwarf1_func* prev;
char* name;
unsigned long low_pc;
unsigned long high_pc;
};
struct die_info
{
unsigned long length;
unsigned long sibling;
unsigned long low_pc;
unsigned long high_pc;
unsigned long stmt_list_offset;
char* name;
int has_stmt_list;
unsigned short tag;
};
struct linenumber
{
unsigned long addr;
unsigned long linenumber;
};
#define FORM_FROM_ATTR(attr) ((attr) & 0xF) /* Implicitly specified. */
then attached into the 'stash' at 'stash->lastUnit'. */
static struct dwarf1_unit*
alloc_dwarf1_unit (struct dwarf1_debug* stash)
{
bfd_size_type amt = sizeof (struct dwarf1_unit);
struct dwarf1_unit* x = bfd_zalloc (stash->abfd, amt);
if (x)
{
x->prev = stash->lastUnit;
stash->lastUnit = x;
}
return x;
}
attached into 'aUnit' at 'aUnit->func_list'. */
static struct dwarf1_func *
alloc_dwarf1_func (struct dwarf1_debug* stash, struct dwarf1_unit* aUnit)
{
bfd_size_type amt = sizeof (struct dwarf1_func);
struct dwarf1_func* x = bfd_zalloc (stash->abfd, amt);
if (x)
{
x->prev = aUnit->func_list;
aUnit->func_list = x;
}
return x;
}
Parse the die starting at 'aDiePtr' into 'aDieInfo'.
'abfd' must be the bfd from which the section that 'aDiePtr'
points to was pulled from.
Return FALSE if the die is invalidly formatted; TRUE otherwise. */
static bfd_boolean
parse_die (bfd * abfd,
struct die_info * aDieInfo,
bfd_byte * aDiePtr,
bfd_byte * aDiePtrEnd)
{
bfd_byte *this_die = aDiePtr;
bfd_byte *xptr = this_die;
memset (aDieInfo, 0, sizeof (* aDieInfo));
aDieInfo->length = bfd_get_32 (abfd, (bfd_byte *) xptr);
xptr += 4;
if (aDieInfo->length == 0
|| (this_die + aDieInfo->length) >= aDiePtrEnd)
return FALSE;
if (aDieInfo->length < 6)
{
aDieInfo->tag = TAG_padding;
return TRUE;
}
aDieInfo->tag = bfd_get_16 (abfd, (bfd_byte *) xptr);
xptr += 2;
while (xptr < (this_die + aDieInfo->length))
{
unsigned short attr;
must handle all dwarf1 forms, but need only handle the
actual attributes that we care about. */
attr = bfd_get_16 (abfd, (bfd_byte *) xptr);
xptr += 2;
switch (FORM_FROM_ATTR (attr))
{
case FORM_DATA2:
xptr += 2;
break;
case FORM_DATA4:
case FORM_REF:
if (attr == AT_sibling)
aDieInfo->sibling = bfd_get_32 (abfd, (bfd_byte *) xptr);
else if (attr == AT_stmt_list)
{
aDieInfo->stmt_list_offset = bfd_get_32 (abfd, (bfd_byte *) xptr);
aDieInfo->has_stmt_list = 1;
}
xptr += 4;
break;
case FORM_DATA8:
xptr += 8;
break;
case FORM_ADDR:
if (attr == AT_low_pc)
aDieInfo->low_pc = bfd_get_32 (abfd, (bfd_byte *) xptr);
else if (attr == AT_high_pc)
aDieInfo->high_pc = bfd_get_32 (abfd, (bfd_byte *) xptr);
xptr += 4;
break;
case FORM_BLOCK2:
xptr += 2 + bfd_get_16 (abfd, (bfd_byte *) xptr);
break;
case FORM_BLOCK4:
xptr += 4 + bfd_get_32 (abfd, (bfd_byte *) xptr);
break;
case FORM_STRING:
if (attr == AT_name)
aDieInfo->name = (char *)xptr;
xptr += strlen (aDieInfo->name) + 1;
break;
}
}
return TRUE;
}
into 'aUnit->linenumber_table'. Return FALSE if an error
occurs; TRUE otherwise. */
static bfd_boolean
parse_line_table (struct dwarf1_debug* stash, struct dwarf1_unit* aUnit)
{
bfd_byte *xptr;
if (stash->line_section == 0)
{
asection *msec;
bfd_size_type size;
msec = bfd_get_section_by_name (stash->abfd, ".line");
if (! msec)
return FALSE;
size = msec->rawsize ? msec->rawsize : msec->size;
stash->line_section
= bfd_simple_get_relocated_section_contents
(stash->abfd, msec, NULL, stash->syms);
if (! stash->line_section)
return FALSE;
stash->line_section_end = stash->line_section + size;
}
xptr = stash->line_section + aUnit->stmt_list_offset;
if (xptr < stash->line_section_end)
{
unsigned long eachLine;
bfd_byte *tblend;
unsigned long base;
bfd_size_type amt;
tblend = bfd_get_32 (stash->abfd, (bfd_byte *) xptr) + xptr;
xptr += 4;
base = bfd_get_32 (stash->abfd, (bfd_byte *) xptr);
xptr += 4;
10 = 4 (line number) + 2 (pos in line) + 4 (address in line). */
aUnit->line_count = (tblend - xptr) / 10;
amt = sizeof (struct linenumber) * aUnit->line_count;
aUnit->linenumber_table = bfd_alloc (stash->abfd, amt);
if (!aUnit->linenumber_table)
return FALSE;
for (eachLine = 0; eachLine < aUnit->line_count; eachLine++)
{
aUnit->linenumber_table[eachLine].linenumber
= bfd_get_32 (stash->abfd, (bfd_byte *) xptr);
xptr += 4;
xptr += 2;
aUnit->linenumber_table[eachLine].addr
= base + bfd_get_32 (stash->abfd, (bfd_byte *) xptr);
xptr += 4;
}
}
return TRUE;
}
The first child die of 'aUnit' should be in 'aUnit->first_child',
the result is placed in 'aUnit->func_list'.
Return FALSE if error; TRUE otherwise. */
static bfd_boolean
parse_functions_in_unit (struct dwarf1_debug* stash, struct dwarf1_unit* aUnit)
{
bfd_byte *eachDie;
if (aUnit->first_child)
for (eachDie = aUnit->first_child;
eachDie < stash->debug_section_end;
)
{
struct die_info eachDieInfo;
if (! parse_die (stash->abfd, &eachDieInfo, eachDie,
stash->debug_section_end))
return FALSE;
if (eachDieInfo.tag == TAG_global_subroutine
|| eachDieInfo.tag == TAG_subroutine
|| eachDieInfo.tag == TAG_inlined_subroutine
|| eachDieInfo.tag == TAG_entry_point)
{
struct dwarf1_func* aFunc = alloc_dwarf1_func (stash,aUnit);
if (!aFunc)
return FALSE;
aFunc->name = eachDieInfo.name;
aFunc->low_pc = eachDieInfo.low_pc;
aFunc->high_pc = eachDieInfo.high_pc;
}
if (eachDieInfo.sibling)
eachDie = stash->debug_section + eachDieInfo.sibling;
else
break;
}
return TRUE;
}
Return whether we found the line (or a function) without error. */
static bfd_boolean
dwarf1_unit_find_nearest_line (struct dwarf1_debug* stash,
struct dwarf1_unit* aUnit,
unsigned long addr,
const char **filename_ptr,
const char **functionname_ptr,
unsigned int *linenumber_ptr)
{
int line_p = FALSE;
int func_p = FALSE;
if (aUnit->low_pc <= addr && addr < aUnit->high_pc)
{
if (aUnit->has_stmt_list)
{
unsigned long i;
struct dwarf1_func* eachFunc;
if (! aUnit->linenumber_table)
{
if (! parse_line_table (stash, aUnit))
return FALSE;
}
if (! aUnit->func_list)
{
if (! parse_functions_in_unit (stash, aUnit))
return FALSE;
}
for (i = 0; i < aUnit->line_count; i++)
{
if (aUnit->linenumber_table[i].addr <= addr
&& addr < aUnit->linenumber_table[i+1].addr)
{
*filename_ptr = aUnit->name;
*linenumber_ptr = aUnit->linenumber_table[i].linenumber;
line_p = TRUE;
break;
}
}
for (eachFunc = aUnit->func_list;
eachFunc;
eachFunc = eachFunc->prev)
{
if (eachFunc->low_pc <= addr
&& addr < eachFunc->high_pc)
{
*functionname_ptr = eachFunc->name;
func_p = TRUE;
break;
}
}
}
}
return line_p || func_p;
}
Return TRUE if the line is found without error. */
bfd_boolean
_bfd_dwarf1_find_nearest_line (bfd *abfd,
asection *section,
asymbol **symbols,
bfd_vma offset,
const char **filename_ptr,
const char **functionname_ptr,
unsigned int *linenumber_ptr)
{
struct dwarf1_debug *stash = elf_tdata (abfd)->dwarf1_find_line_info;
struct dwarf1_unit* eachUnit;
unsigned long addr = (unsigned long)(offset + section->vma);
*filename_ptr = NULL;
*functionname_ptr = NULL;
*linenumber_ptr = 0;
if (! stash)
{
asection *msec;
bfd_size_type size = sizeof (struct dwarf1_debug);
stash = elf_tdata (abfd)->dwarf1_find_line_info
= bfd_zalloc (abfd, size);
if (! stash)
return FALSE;
msec = bfd_get_section_by_name (abfd, ".debug");
if (! msec)
has been allocated, but contains zeros, this lets
future calls to this function fail quicker. */
return FALSE;
size = msec->rawsize ? msec->rawsize : msec->size;
stash->debug_section
= bfd_simple_get_relocated_section_contents (abfd, msec, NULL,
symbols);
if (! stash->debug_section)
return FALSE;
stash->debug_section_end = stash->debug_section + size;
stash->currentDie = stash->debug_section;
stash->abfd = abfd;
stash->syms = symbols;
}
or that an error occured while setting up the stash. */
if (! stash->debug_section)
return FALSE;
the addr. */
for (eachUnit = stash->lastUnit; eachUnit; eachUnit = eachUnit->prev)
if (eachUnit->low_pc <= addr && addr < eachUnit->high_pc)
return dwarf1_unit_find_nearest_line (stash, eachUnit, addr,
filename_ptr,
functionname_ptr,
linenumber_ptr);
while (stash->currentDie < stash->debug_section_end)
{
struct die_info aDieInfo;
if (! parse_die (stash->abfd, &aDieInfo, stash->currentDie,
stash->debug_section_end))
return FALSE;
if (aDieInfo.tag == TAG_compile_unit)
{
struct dwarf1_unit* aUnit
= alloc_dwarf1_unit (stash);
if (!aUnit)
return FALSE;
aUnit->name = aDieInfo.name;
aUnit->low_pc = aDieInfo.low_pc;
aUnit->high_pc = aDieInfo.high_pc;
aUnit->has_stmt_list = aDieInfo.has_stmt_list;
aUnit->stmt_list_offset = aDieInfo.stmt_list_offset;
not it's sibling. */
if (aDieInfo.sibling
&& stash->currentDie + aDieInfo.length
< stash->debug_section_end
&& stash->currentDie + aDieInfo.length
!= stash->debug_section + aDieInfo.sibling)
aUnit->first_child = stash->currentDie + aDieInfo.length;
else
aUnit->first_child = 0;
if (aUnit->low_pc <= addr && addr < aUnit->high_pc)
return dwarf1_unit_find_nearest_line (stash, aUnit, addr,
filename_ptr,
functionname_ptr,
linenumber_ptr);
}
if (aDieInfo.sibling != 0)
stash->currentDie = stash->debug_section + aDieInfo.sibling;
else
stash->currentDie += aDieInfo.length;
}
return FALSE;
}