#include "elf/dwarf2.h"
/* The data in the .debug_line statement prologue looks like this. */
+
struct line_head
- {
- unsigned int total_length;
- unsigned short version;
- unsigned int prologue_length;
- unsigned char minimum_instruction_length;
- unsigned char default_is_stmt;
- int line_base;
- unsigned char line_range;
- unsigned char opcode_base;
- unsigned char *standard_opcode_lengths;
- };
-
-/* Attributes have a name and a value */
+{
+ unsigned int total_length;
+ unsigned short version;
+ unsigned int prologue_length;
+ unsigned char minimum_instruction_length;
+ unsigned char default_is_stmt;
+ int line_base;
+ unsigned char line_range;
+ unsigned char opcode_base;
+ unsigned char *standard_opcode_lengths;
+};
+
+/* Attributes have a name and a value. */
+
struct attribute
+{
+ enum dwarf_attribute name;
+ enum dwarf_form form;
+ union
{
- enum dwarf_attribute name;
- enum dwarf_form form;
- union
- {
- char *str;
- struct dwarf_block *blk;
- unsigned int unsnd;
- int snd;
- bfd_vma addr;
- }
- u;
- };
-
-/* Get at parts of an attribute structure */
+ char *str;
+ struct dwarf_block *blk;
+ unsigned int unsnd;
+ int snd;
+ bfd_vma addr;
+ }
+ u;
+};
+
+/* Get at parts of an attribute structure. */
#define DW_STRING(attr) ((attr)->u.str)
#define DW_UNSND(attr) ((attr)->u.unsnd)
/* Blocks are a bunch of untyped bytes. */
struct dwarf_block
- {
- unsigned int size;
- char *data;
- };
-
-
-struct dwarf2_debug {
+{
+ unsigned int size;
+ char *data;
+};
- /* A list of all previously read comp_units. */
+struct dwarf2_debug
+{
+ /* A list of all previously read comp_units. */
struct comp_unit* all_comp_units;
/* The next unread compilation unit within the .debug_info section.
Zero indicates that the .debug_info section has not been loaded
- into a buffer yet.*/
+ into a buffer yet. */
char* info_ptr;
- /* Pointer to the end of the .debug_info section memory buffer. */
+ /* Pointer to the end of the .debug_info section memory buffer. */
char* info_ptr_end;
- /* Pointer to the .debug_abbrev section loaded into memory. */
+ /* Pointer to the .debug_abbrev section loaded into memory. */
char* dwarf_abbrev_buffer;
- /* Length of the loaded .debug_abbrev section. */
+ /* Length of the loaded .debug_abbrev section. */
unsigned long dwarf_abbrev_size;
/* Buffer for decode_line_info. */
unsigned long dwarf_line_size;
};
-struct arange {
+struct arange
+{
struct arange *next;
bfd_vma low;
bfd_vma high;
};
-
/* A minimal decoding of DWARF2 compilation units. We only decode
- what's needed to get to the line number information. */
+ what's needed to get to the line number information. */
-struct comp_unit {
-
- /* Chain the previously read compilation units. */
+struct comp_unit
+{
+ /* Chain the previously read compilation units. */
struct comp_unit* next_unit;
- /* Keep the bdf convenient (for memory allocation). */
+ /* Keep the bdf convenient (for memory allocation). */
bfd* abfd;
/* The lowest and higest addresses contained in this compilation
- unit as specified in the compilation unit header. */
+ unit as specified in the compilation unit header. */
struct arange arange;
- /* The DW_AT_name attribute (for error messages). */
+ /* The DW_AT_name attribute (for error messages). */
char* name;
- /* The abbrev hash table. */
+ /* The abbrev hash table. */
struct abbrev_info** abbrevs;
- /* Note that an error was found by comp_unit_find_nearest_line. */
+ /* Note that an error was found by comp_unit_find_nearest_line. */
int error;
- /* The DW_AT_comp_dir attribute */
+ /* The DW_AT_comp_dir attribute. */
char* comp_dir;
- /* True if there is a line number table associated with this comp. unit. */
+ /* True if there is a line number table associated with this comp. unit. */
int stmtlist;
- /* The offset into .debug_line of the line number table. */
+ /* The offset into .debug_line of the line number table. */
unsigned long line_offset;
- /* Pointer to the first child die for the comp unit. */
+ /* Pointer to the first child die for the comp unit. */
char *first_child_die_ptr;
- /* The end of the comp unit. */
+ /* The end of the comp unit. */
char *end_ptr;
- /* The decoded line number, NULL if not yet decoded. */
+ /* The decoded line number, NULL if not yet decoded. */
struct line_info_table* line_table;
- /* A list of the functions found in this comp. unit. */
+ /* A list of the functions found in this comp. unit. */
struct funcinfo* function_table;
- /* Address size for this unit - from unit header */
+ /* Address size for this unit - from unit header. */
unsigned char addr_size;
};
-
-
/* VERBATIM
The following function up to the END VERBATIM mark are
- copied directly from dwarf2read.c. */
+ copied directly from dwarf2read.c. */
-/* read dwarf information from a buffer */
+/* Read dwarf information from a buffer. */
static unsigned int
read_1_byte (abfd, buf)
return bfd_get_16 (abfd, (bfd_byte *) buf);
}
-#if 0
-
-/* This is not used. */
+#if 0 /* This is not used. */
static int
read_2_signed_bytes (abfd, buf)
return bfd_get_32 (abfd, (bfd_byte *) buf);
}
-#if 0
-
-/* This is not used. */
+#if 0 /* This is not used. */
static int
read_4_signed_bytes (abfd, buf)
*bytes_read_ptr = 1;
return NULL;
}
+
*bytes_read_ptr = strlen (buf) + 1;
return buf;
}
}
}
-
-
-
-
/* This data structure holds the information of an abbrev. */
struct abbrev_info
- {
- unsigned int number; /* number identifying abbrev */
- enum dwarf_tag tag; /* dwarf tag */
- int has_children; /* boolean */
- unsigned int num_attrs; /* number of attributes */
- struct attr_abbrev *attrs; /* an array of attribute descriptions */
- struct abbrev_info *next; /* next in chain */
- };
+{
+ unsigned int number; /* Number identifying abbrev. */
+ enum dwarf_tag tag; /* DWARF tag. */
+ int has_children; /* Boolean. */
+ unsigned int num_attrs; /* Number of attributes. */
+ struct attr_abbrev *attrs; /* An array of attribute descriptions. */
+ struct abbrev_info *next; /* Next in chain. */
+};
struct attr_abbrev
- {
- enum dwarf_attribute name;
- enum dwarf_form form;
- };
+{
+ enum dwarf_attribute name;
+ enum dwarf_form form;
+};
#ifndef ABBREV_HASH_SIZE
#define ABBREV_HASH_SIZE 121
else
abbrev = abbrev->next;
}
+
return NULL;
}
abbrev_number = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read);
abbrev_ptr += bytes_read;
- /* loop until we reach an abbrev number of 0 */
+ /* Loop until we reach an abbrev number of 0. */
while (abbrev_number)
{
cur_abbrev = (struct abbrev_info*)bfd_zalloc (abfd, sizeof (struct abbrev_info));
- /* read in abbrev header */
+ /* Read in abbrev header. */
cur_abbrev->number = abbrev_number;
cur_abbrev->tag = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read);
abbrev_ptr += bytes_read;
cur_abbrev->has_children = read_1_byte (abfd, abbrev_ptr);
abbrev_ptr += 1;
- /* now read in declarations */
+ /* Now read in declarations. */
abbrev_name = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read);
abbrev_ptr += bytes_read;
abbrev_form = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read);
abbrev_ptr += bytes_read;
+
while (abbrev_name)
{
if ((cur_abbrev->num_attrs % ATTR_ALLOC_CHUNK) == 0)
if (! cur_abbrev->attrs)
return 0;
}
+
cur_abbrev->attrs[cur_abbrev->num_attrs].name = abbrev_name;
cur_abbrev->attrs[cur_abbrev->num_attrs++].form = abbrev_form;
abbrev_name = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read);
attr->name = abbrev->name;
attr->form = abbrev->form;
+
switch (abbrev->form)
{
case DW_FORM_addr:
return info_ptr;
}
-
-/* Source line information table routines. */
+/* Source line information table routines. */
#define FILE_ALLOC_CHUNK 5
#define DIR_ALLOC_CHUNK 5
-struct line_info {
+struct line_info
+{
struct line_info* prev_line;
-
bfd_vma address;
char* filename;
unsigned int line;
unsigned int column;
- int end_sequence; /* end of (sequential) code sequence */
+ int end_sequence; /* End of (sequential) code sequence. */
};
-struct fileinfo {
+struct fileinfo
+{
char *name;
unsigned int dir;
unsigned int time;
unsigned int size;
};
-struct line_info_table {
+struct line_info_table
+{
bfd* abfd;
-
unsigned int num_files;
unsigned int num_dirs;
-
char* comp_dir;
char** dirs;
struct fileinfo* files;
info->end_sequence = end_sequence;
}
-static char*
+static char *
concat_filename (table, file)
struct line_info_table* table;
unsigned int file;
{
struct arange *arange;
- /* first see if we can cheaply extend an existing range: */
+ /* First see if we can cheaply extend an existing range. */
arange = &unit->arange;
+
do
{
if (low_pc == arange->high)
if (unit->arange.high == 0)
{
- /* this is the first address range: store it in unit->arange: */
+ /* This is the first address range: store it in unit->arange. */
unit->arange.next = 0;
unit->arange.low = low_pc;
unit->arange.high = high_pc;
return;
}
- /* need to allocate a new arange and insert it into the arange list: */
+ /* Need to allocate a new arange and insert it into the arange list. */
arange = bfd_zalloc (unit->abfd, sizeof (*arange));
arange->low = low_pc;
arange->high = high_pc;
unit->arange.next = arange;
}
-/* Decode the line number information for UNIT. */
+/* Decode the line number information for UNIT. */
static struct line_info_table*
decode_line_info (unit)
struct comp_unit *unit;
{
bfd *abfd = unit->abfd;
-
struct dwarf2_debug *stash;
-
struct line_info_table* table;
-
char *line_ptr;
char *line_end;
struct line_head lh;
return 0;
/* FIXME: We ought to apply the relocs against this section before
- we process it.... */
+ we process it... */
}
/* Since we are using un-relocated data, it is possible to get a bad value
line_ptr = stash->dwarf_line_buffer + unit->line_offset;
- /* read in the prologue */
+ /* Read in the prologue. */
lh.total_length = read_4_bytes (abfd, line_ptr);
line_ptr += 4;
line_end = line_ptr + lh.total_length;
bfd_alloc (abfd, lh.opcode_base * sizeof (unsigned char));
lh.standard_opcode_lengths[0] = 1;
+
for (i = 1; i < lh.opcode_base; ++i)
{
lh.standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
line_ptr += 1;
}
- /* Read directory table */
+ /* Read directory table. */
while ((cur_dir = read_string (abfd, line_ptr, &bytes_read)) != NULL)
{
line_ptr += bytes_read;
+
if ((table->num_dirs % DIR_ALLOC_CHUNK) == 0)
{
table->dirs = (char **)
if (! table->dirs)
return 0;
}
+
table->dirs[table->num_dirs++] = cur_dir;
}
+
line_ptr += bytes_read;
- /* Read file name table */
+ /* Read file name table. */
while ((cur_file = read_string (abfd, line_ptr, &bytes_read)) != NULL)
{
line_ptr += bytes_read;
+
if ((table->num_files % FILE_ALLOC_CHUNK) == 0)
{
table->files = (struct fileinfo *)
if (! table->files)
return 0;
}
+
table->files[table->num_files].name = cur_file;
table->files[table->num_files].dir =
read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
line_ptr += bytes_read;
table->num_files++;
}
+
line_ptr += bytes_read;
/* Read the statement sequences until there's nothing left. */
while (line_ptr < line_end)
{
- /* state machine registers */
+ /* State machine registers. */
bfd_vma address = 0;
char* filename = concat_filename (table, 1);
unsigned int line = 1;
int end_sequence = 0, need_low_pc = 1;
bfd_vma low_pc = 0;
- /* Decode the table. */
+ /* Decode the table. */
while (! end_sequence)
{
op_code = read_1_byte (abfd, line_ptr);
line_ptr += 1;
+
switch (op_code)
{
case DW_LNS_extended_op:
- line_ptr += 1; /* ignore length */
+ line_ptr += 1; /* Ignore length. */
extended_op = read_1_byte (abfd, line_ptr);
line_ptr += 1;
switch (extended_op)
address += read_2_bytes (abfd, line_ptr);
line_ptr += 2;
break;
- default: /* special operand */
+ default: /* Special operand. */
adj_opcode = op_code - lh.opcode_base;
address += (adj_opcode / lh.line_range)
* lh.minimum_instruction_length;
line += lh.line_base + (adj_opcode % lh.line_range);
- /* append row to matrix using current values */
+ /* Append row to matrix using current values. */
add_line_info (table, address, filename, line, column, 0);
basic_block = 1;
if (need_low_pc)
return table;
}
-
/* If ADDR is within TABLE set the output parameters and return true,
otherwise return false. The output parameters, FILENAME_PTR and
- LINENUMBER_PTR, are pointers to the objects to be filled in. */
+ LINENUMBER_PTR, are pointers to the objects to be filled in. */
static boolean
lookup_address_in_line_info_table (table,
return false;
}
+/* Function table functions. */
-
-
-/* Function table functions. */
-
-struct funcinfo {
+struct funcinfo
+{
struct funcinfo *prev_func;
-
char* name;
bfd_vma low;
bfd_vma high;
};
-
/* If ADDR is within TABLE, set FUNCTIONNAME_PTR, and return true. */
static boolean
return false;
}
-
-
-
-/* DWARF2 Compilation unit functions. */
-
+/* DWARF2 Compilation unit functions. */
/* Scan over each die in a comp. unit looking for functions to add
- to the function table. */
+ to the function table. */
static boolean
scan_unit_for_functions (unit)
return true;
}
-
-
-
-
-
/* Parse a DWARF2 compilation unit starting at INFO_PTR. This
includes the compilation unit header that proceeds the DIE's, but
does not include the length field that preceeds each compilation
return 0;
}
- /* Read the abbrevs for this compilation unit into a table */
+ /* Read the abbrevs for this compilation unit into a table. */
abbrevs = read_abbrevs (abfd, abbrev_offset);
if (! abbrevs)
return 0;
return unit;
}
-
-
-
-
-/* Return true if UNIT contains the address given by ADDR. */
+/* Return true if UNIT contains the address given by ADDR. */
static boolean
comp_unit_contains_address (unit, addr)
arange = arange->next;
}
while (arange);
+
return 0;
}
-
/* If UNIT contains ADDR, set the output parameters to the values for
the line containing ADDR. The output parameters, FILENAME_PTR,
FUNCTIONNAME_PTR, and LINENUMBER_PTR, are pointers to the objects
return line_p || func_p;
}
+/* Locate a section in a BFD containing debugging info. The search starts from the
+ section after AFTER_SEC, or from the first section in the BFD if AFTER_SEC is
+ NULL. The search works by examining the names of the sections. There are two
+ permissiable names. The first is .debug_info. This is the standard DWARF2 name.
+ The second is a prefix .gnu.linkonce.wi. This is a variation on the .debug_info
+ section which has a checksum describing the contents appended onto the name. This
+ allows the linker to identify and discard duplicate debugging sections for
+ different compilation units. */
+#define DWARF2_DEBUG_INFO ".debug_info"
+#define GNU_LINKONCE_INFO ".gnu.linkonce.wi."
+
+static asection *
+find_debug_info (abfd, after_sec)
+ bfd * abfd;
+ asection * after_sec;
+{
+ asection * msec;
+
+ if (after_sec)
+ msec = after_sec->next;
+ else
+ msec = abfd->sections;
+
+ while (msec)
+ {
+ if (strcmp (msec->name, DWARF2_DEBUG_INFO) == 0)
+ return msec;
+
+ if (strncmp (msec->name, GNU_LINKONCE_INFO, strlen (GNU_LINKONCE_INFO)) == 0)
+ return msec;
+
+ msec = msec->next;
+ }
+
+ return NULL;
+}
+
/* The DWARF2 version of find_nearest line. Return true if the line
is found without error. ADDR_SIZE is the number of bytes in the
initial .debug_info length field and in the abbreviation offset.
We keep a list of all the previously read compilation units, and
a pointer to the next un-read compilation unit. Check the
- previously read units before reading more.
- */
-
+ previously read units before reading more. */
struct dwarf2_debug *stash = elf_tdata (abfd)->dwarf2_find_line_info;
- /* What address are we looking for? */
+ /* What address are we looking for? */
bfd_vma addr = offset + section->vma;
struct comp_unit* each;
if (! stash)
{
+ unsigned long total_size;
asection *msec;
- unsigned long size;
-
+
stash = elf_tdata (abfd)->dwarf2_find_line_info =
(struct dwarf2_debug*) bfd_zalloc (abfd, sizeof (struct dwarf2_debug));
-
if (! stash)
return false;
-
- msec = bfd_get_section_by_name (abfd, ".debug_info");
- if (! msec)
- {
- /* No dwarf2 info. Note that at this point the stash
- has been allocated, but contains zeros, this lets
- future calls to this function fail quicker. */
- return false;
- }
- size = msec->_raw_size;
- if (size == 0)
+ msec = find_debug_info (abfd, NULL);
+ if (! msec)
+ /* No dwarf2 info. Note that at this point the stash
+ has been allocated, but contains zeros, this lets
+ future calls to this function fail quicker. */
return false;
+
+ /* There can be more than one DWARF2 info section in a BFD these days.
+ Read them all in and produce one large stash. We do this in two
+ passes - in the first pass we just accumulate the section sizes.
+ In the second pass we read in the section's contents. The allows
+ us to avoid reallocing the data as we add sections to the stash. */
+ for (total_size = 0; msec; msec = find_debug_info (abfd, msec))
+ total_size += msec->_raw_size;
- stash->info_ptr = (char *) bfd_alloc (abfd, size);
-
- if (! stash->info_ptr)
+ stash->info_ptr = (char *) bfd_alloc (abfd, total_size);
+ if (stash->info_ptr == NULL)
return false;
- if (! bfd_get_section_contents (abfd, msec, stash->info_ptr, 0, size))
+ stash->info_ptr_end = stash->info_ptr;
+
+ for (msec = find_debug_info (abfd, NULL);
+ msec;
+ msec = find_debug_info (abfd, msec))
{
- stash->info_ptr = 0;
- return false;
+ unsigned long size;
+ unsigned long start;
+
+ size = msec->_raw_size;
+ if (size == 0)
+ continue;
+
+ start = stash->info_ptr_end - stash->info_ptr;
+
+ if (! bfd_get_section_contents (abfd, msec, stash->info_ptr + start, 0, size))
+ continue;
+
+ stash->info_ptr_end = stash->info_ptr + start + size;
}
- stash->info_ptr_end = stash->info_ptr + size;
-
- /* FIXME: There is a problem with the contents of the
- .debug_info section. The 'low' and 'high' addresses of the
- comp_units are computed by relocs against symbols in the
- .text segment. We need these addresses in order to determine
- the nearest line number, and so we have to resolve the
- relocs. There is a similar problem when the .debug_line
- section is processed as well (e.g., there may be relocs
- against the operand of the DW_LNE_set_address operator).
-
- Unfortunately getting hold of the reloc information is hard...
-
- For now, this means that disassembling object files (as
- opposed to fully executables) does not always work as well as
- we would like. */
+ BFD_ASSERT (stash->info_ptr_end = stash->info_ptr + total_size);
}
+
+ /* FIXME: There is a problem with the contents of the
+ .debug_info section. The 'low' and 'high' addresses of the
+ comp_units are computed by relocs against symbols in the
+ .text segment. We need these addresses in order to determine
+ the nearest line number, and so we have to resolve the
+ relocs. There is a similar problem when the .debug_line
+ section is processed as well (e.g., there may be relocs
+ against the operand of the DW_LNE_set_address operator).
+
+ Unfortunately getting hold of the reloc information is hard...
+
+ For now, this means that disassembling object files (as
+ opposed to fully executables) does not always work as well as
+ we would like. */
/* A null info_ptr indicates that there is no dwarf2 info
- (or that an error occured while setting up the stash). */
-
+ (or that an error occured while setting up the stash). */
if (! stash->info_ptr)
return false;
- /* Check the previously read comp. units first. */
-
+ /* Check the previously read comp. units first. */
for (each = stash->all_comp_units; each; each = each->next_unit)
if (comp_unit_contains_address (each, addr))
return comp_unit_find_nearest_line (each, addr, filename_ptr,
functionname_ptr, linenumber_ptr);
- /* Read each remaining comp. units checking each as they are read. */
+ /* Read each remaining comp. units checking each as they are read. */
while (stash->info_ptr < stash->info_ptr_end)
{
struct comp_unit* each;
compilation units. If we don't have them (i.e.,
unit->high == 0), we need to consult the line info
table to see if a compilation unit contains the given
- address. */
+ address. */
if (each->arange.high > 0)
{
if (comp_unit_contains_address (each, addr))
return false;
}
-
-/* end of file */