/* DWARF 2 support.
- Copyright 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+ Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+ Free Software Foundation, Inc.
Adapted from gdb/dwarf2read.c by Gavin Koch of Cygnus Solutions
(gavin@cygnus.com).
#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;
- };
+ char *str;
+ struct dwarf_block *blk;
+ unsigned int unsnd;
+ int snd;
+ bfd_vma addr;
+ }
+ u;
+};
-/* Get at parts of an attribute structure */
+/* Get at parts of an attribute structure. */
#define DW_STRING(attr) ((attr)->u.str)
#define DW_UNSND(attr) ((attr)->u.unsnd)
#define DW_SND(attr) ((attr)->u.snd)
#define DW_ADDR(attr) ((attr)->u.addr)
-/* Blocks are a bunch of untyped bytes. */
+/* 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 section and address of the beginning of the
+ section. */
+ asection* sec;
+ char* sec_info_ptr;
+
+ /* Pointer to the symbol table. */
+ asymbol** syms;
+
+ /* 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. */
+ char *dwarf_line_buffer;
+
+ /* Length of the loaded .debug_line section. */
+ unsigned long dwarf_line_size;
};
+struct arange
+{
+ struct arange *next;
+ bfd_vma low;
+ bfd_vma high;
+};
-/* VERBATUM
- The following function up to the END VERBATUM mark are
- copied directly from dwarf2read.c. */
+/* A minimal decoding of DWARF2 compilation units. We only decode
+ what's needed to get to the line number information. */
-/* read dwarf information from a buffer */
+struct comp_unit
+{
+ /* Chain the previously read compilation units. */
+ struct comp_unit* next_unit;
+
+ /* 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. */
+ struct arange arange;
+
+ /* The DW_AT_name attribute (for error messages). */
+ char* name;
+
+ /* The abbrev hash table. */
+ struct abbrev_info** abbrevs;
+
+ /* Note that an error was found by comp_unit_find_nearest_line. */
+ int error;
+
+ /* The DW_AT_comp_dir attribute. */
+ char* comp_dir;
+
+ /* 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. */
+ unsigned long line_offset;
+
+ /* Pointer to the first child die for the comp unit. */
+ char *first_child_die_ptr;
+
+ /* The end of the comp unit. */
+ char *end_ptr;
+
+ /* 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. */
+ struct funcinfo* function_table;
+
+ /* Address size for this unit - from unit header. */
+ unsigned char addr_size;
+};
+
+/* 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. */
+};
+
+struct attr_abbrev
+{
+ enum dwarf_attribute name;
+ enum dwarf_form form;
+};
+
+#ifndef ABBREV_HASH_SIZE
+#define ABBREV_HASH_SIZE 121
+#endif
+#ifndef ATTR_ALLOC_CHUNK
+#define ATTR_ALLOC_CHUNK 4
+#endif
+
+static unsigned int read_1_byte PARAMS ((bfd *, char *));
+static int read_1_signed_byte PARAMS ((bfd *, char *));
+static unsigned int read_2_bytes PARAMS ((bfd *, char *));
+static unsigned int read_4_bytes PARAMS ((bfd *, char *));
+static unsigned int read_8_bytes PARAMS ((bfd *, char *));
+static char *read_n_bytes PARAMS ((bfd *, char *, unsigned int));
+static char *read_string PARAMS ((bfd *, char *, unsigned int *));
+static unsigned int read_unsigned_leb128
+ PARAMS ((bfd *, char *, unsigned int *));
+static int read_signed_leb128
+ PARAMS ((bfd *, char *, unsigned int *));
+static bfd_vma read_address PARAMS ((struct comp_unit *, char *));
+static struct abbrev_info *lookup_abbrev
+ PARAMS ((unsigned int, struct abbrev_info **));
+static struct abbrev_info **read_abbrevs
+ PARAMS ((bfd *, unsigned int, struct dwarf2_debug *));
+static char *read_attribute
+ PARAMS ((struct attribute *, struct attr_abbrev *,
+ struct comp_unit *, char *));
+static void add_line_info
+ PARAMS ((struct line_info_table *, bfd_vma, char *,
+ unsigned int, unsigned int, int));
+static char *concat_filename PARAMS ((struct line_info_table *, unsigned int));
+static void arange_add PARAMS ((struct comp_unit *, bfd_vma, bfd_vma));
+static struct line_info_table *decode_line_info
+ PARAMS ((struct comp_unit *, struct dwarf2_debug *));
+static boolean lookup_address_in_line_info_table
+ PARAMS ((struct line_info_table *, bfd_vma, const char **, unsigned int *));
+static boolean lookup_address_in_function_table
+ PARAMS ((struct funcinfo *, bfd_vma, const char **));
+static boolean scan_unit_for_functions PARAMS ((struct comp_unit *));
+static bfd_vma find_rela_addend
+ PARAMS ((bfd *, asection *, bfd_size_type, asymbol**));
+static struct comp_unit *parse_comp_unit
+ PARAMS ((bfd *, struct dwarf2_debug *, bfd_vma, unsigned int));
+static boolean comp_unit_contains_address
+ PARAMS ((struct comp_unit *, bfd_vma));
+static boolean comp_unit_find_nearest_line
+ PARAMS ((struct comp_unit *, bfd_vma, const char **, const char **,
+ unsigned int *, struct dwarf2_debug *));
+static asection *find_debug_info PARAMS ((bfd *, asection *));
+
+/* VERBATIM
+ The following function up to the END VERBATIM mark are
+ copied directly from dwarf2read.c. */
+
+/* Read dwarf information from a buffer. */
static unsigned int
read_1_byte (abfd, buf)
- bfd *abfd;
+ bfd *abfd ATTRIBUTE_UNUSED;
char *buf;
{
return bfd_get_8 (abfd, (bfd_byte *) buf);
static int
read_1_signed_byte (abfd, buf)
- bfd *abfd;
+ bfd *abfd ATTRIBUTE_UNUSED;
char *buf;
{
return bfd_get_signed_8 (abfd, (bfd_byte *) 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)
static char *
read_n_bytes (abfd, buf, size)
- bfd * abfd;
+ bfd *abfd ATTRIBUTE_UNUSED;
char *buf;
- unsigned int size;
+ unsigned int size ATTRIBUTE_UNUSED;
{
/* If the size of a host char is 8 bits, we can return a pointer
to the buffer, otherwise we have to copy the data to a buffer
static char *
read_string (abfd, buf, bytes_read_ptr)
- bfd *abfd;
+ bfd *abfd ATTRIBUTE_UNUSED;
char *buf;
unsigned int *bytes_read_ptr;
{
*bytes_read_ptr = 1;
return NULL;
}
+
*bytes_read_ptr = strlen (buf) + 1;
return buf;
}
static unsigned int
read_unsigned_leb128 (abfd, buf, bytes_read_ptr)
- bfd *abfd;
+ bfd *abfd ATTRIBUTE_UNUSED;
char *buf;
unsigned int *bytes_read_ptr;
{
- unsigned int result, num_read;
- int i, shift;
+ unsigned int result;
+ unsigned int num_read;
+ int shift;
unsigned char byte;
- result = 0;
- shift = 0;
+ result = 0;
+ shift = 0;
num_read = 0;
- i = 0;
- while (1)
+
+ do
{
byte = bfd_get_8 (abfd, (bfd_byte *) buf);
- buf++;
- num_read++;
- result |= ((byte & 127) << shift);
- if ((byte & 128) == 0)
- {
- break;
- }
+ buf ++;
+ num_read ++;
+ result |= ((byte & 0x7f) << shift);
shift += 7;
}
- *bytes_read_ptr = num_read;
+ while (byte & 0x80);
+
+ * bytes_read_ptr = num_read;
+
return result;
}
static int
read_signed_leb128 (abfd, buf, bytes_read_ptr)
- bfd *abfd;
+ bfd *abfd ATTRIBUTE_UNUSED;
char *buf;
- unsigned int *bytes_read_ptr;
+ unsigned int * bytes_read_ptr;
{
- int result;
- int i, shift, size, num_read;
+ int result;
+ int shift;
+ int num_read;
unsigned char byte;
result = 0;
shift = 0;
- size = 32;
num_read = 0;
- i = 0;
- while (1)
+
+ do
{
byte = bfd_get_8 (abfd, (bfd_byte *) buf);
- buf++;
- num_read++;
- result |= ((byte & 127) << shift);
+ buf ++;
+ num_read ++;
+ result |= ((byte & 0x7f) << shift);
shift += 7;
- if ((byte & 128) == 0)
- {
- break;
- }
}
- if ((shift < size) && (byte & 0x40))
- {
- result |= -(1 << shift);
- }
- *bytes_read_ptr = num_read;
+ while (byte & 0x80);
+
+ if ((shift < 32) && (byte & 0x40))
+ result |= -(1 << shift);
+
+ * bytes_read_ptr = num_read;
+
return result;
}
-/* END VERBATUM */
-
+/* END VERBATIM */
static bfd_vma
-read_address (abfd, buf)
- bfd *abfd;
+read_address (unit, buf)
+ struct comp_unit* unit;
char *buf;
{
- bfd_vma retval = 0;
-
- if (sizeof(retval) == 4)
+ switch (unit->addr_size)
{
- retval = bfd_get_32 (abfd, (bfd_byte *) buf);
- } else {
- retval = bfd_get_64 (abfd, (bfd_byte *) buf);
+ case 8:
+ return bfd_get_64 (unit->abfd, (bfd_byte *) buf);
+ case 4:
+ return bfd_get_32 (unit->abfd, (bfd_byte *) buf);
+ case 2:
+ return bfd_get_16 (unit->abfd, (bfd_byte *) buf);
+ default:
+ abort ();
}
- return retval;
}
-
-
-
-
-/* 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 */
- };
-
-struct attr_abbrev
- {
- enum dwarf_attribute name;
- enum dwarf_form form;
- };
-
-#ifndef ABBREV_HASH_SIZE
-#define ABBREV_HASH_SIZE 121
-#endif
-#ifndef ATTR_ALLOC_CHUNK
-#define ATTR_ALLOC_CHUNK 4
-#endif
-
/* Lookup an abbrev_info structure in the abbrev hash table. */
static struct abbrev_info *
else
abbrev = abbrev->next;
}
+
return NULL;
}
in a hash table. */
static struct abbrev_info**
-read_abbrevs (abfd, offset)
+read_abbrevs (abfd, offset, stash)
bfd * abfd;
unsigned int offset;
+ struct dwarf2_debug *stash;
{
struct abbrev_info **abbrevs;
char *abbrev_ptr;
struct abbrev_info *cur_abbrev;
unsigned int abbrev_number, bytes_read, abbrev_name;
unsigned int abbrev_form, hash_number;
- struct dwarf2_debug *stash;
-
- stash = elf_tdata(abfd)->dwarf2_find_line_info;
+ bfd_size_type amt;
if (! stash->dwarf_abbrev_buffer)
{
msec = bfd_get_section_by_name (abfd, ".debug_abbrev");
if (! msec)
{
- (*_bfd_error_handler) ("Dwarf Error: Can't find .debug_abbrev section.");
+ (*_bfd_error_handler) (_("Dwarf Error: Can't find .debug_abbrev section."));
bfd_set_error (bfd_error_bad_value);
return 0;
}
-
- stash->dwarf_abbrev_size = bfd_get_section_size_before_reloc (msec);
- stash->dwarf_abbrev_buffer = (unsigned char*) bfd_alloc (abfd, stash->dwarf_abbrev_size);
+
+ stash->dwarf_abbrev_size = msec->_raw_size;
+ stash->dwarf_abbrev_buffer = (char*) bfd_alloc (abfd, msec->_raw_size);
if (! stash->dwarf_abbrev_buffer)
return 0;
-
- if (! bfd_get_section_contents (abfd, msec,
- stash->dwarf_abbrev_buffer, 0,
- stash->dwarf_abbrev_size))
+
+ if (! bfd_get_section_contents (abfd, msec, stash->dwarf_abbrev_buffer,
+ (bfd_vma) 0, msec->_raw_size))
return 0;
}
- if (offset > stash->dwarf_abbrev_size)
+ if (offset >= stash->dwarf_abbrev_size)
{
- (*_bfd_error_handler) ("Dwarf Error: Abbrev offset (%u) bigger than abbrev size (%u).",
+ (*_bfd_error_handler) (_("Dwarf Error: Abbrev offset (%u) greater than or equal to abbrev size (%u)."),
offset, stash->dwarf_abbrev_size );
bfd_set_error (bfd_error_bad_value);
return 0;
}
- abbrevs = (struct abbrev_info**) bfd_zalloc (abfd, sizeof(struct abbrev_info*) * ABBREV_HASH_SIZE);
+ amt = sizeof (struct abbrev_info*) * ABBREV_HASH_SIZE;
+ abbrevs = (struct abbrev_info**) bfd_zalloc (abfd, amt);
abbrev_ptr = stash->dwarf_abbrev_buffer + offset;
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));
+ amt = sizeof (struct abbrev_info);
+ cur_abbrev = (struct abbrev_info *) bfd_zalloc (abfd, amt);
- /* 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)
{
- cur_abbrev->attrs = (struct attr_abbrev *)
- bfd_realloc (cur_abbrev->attrs,
- (cur_abbrev->num_attrs + ATTR_ALLOC_CHUNK)
- * sizeof (struct attr_abbrev));
+ amt = cur_abbrev->num_attrs + ATTR_ALLOC_CHUNK;
+ amt *= sizeof (struct attr_abbrev);
+ cur_abbrev->attrs = ((struct attr_abbrev *)
+ bfd_realloc (cur_abbrev->attrs, amt));
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);
/* Read an attribute described by an abbreviated attribute. */
static char *
-read_attribute (attr, abbrev, abfd, info_ptr)
- struct attribute *attr;
+read_attribute (attr, abbrev, unit, info_ptr)
+ struct attribute *attr;
struct attr_abbrev *abbrev;
- bfd *abfd;
- char *info_ptr;
+ struct comp_unit *unit;
+ char *info_ptr;
{
+ bfd *abfd = unit->abfd;
unsigned int bytes_read;
struct dwarf_block *blk;
+ bfd_size_type amt;
attr->name = abbrev->name;
attr->form = abbrev->form;
+
switch (abbrev->form)
{
case DW_FORM_addr:
case DW_FORM_ref_addr:
- DW_ADDR (attr) = read_address (abfd, info_ptr);
- info_ptr += sizeof(bfd_vma);
+ DW_ADDR (attr) = read_address (unit, info_ptr);
+ info_ptr += unit->addr_size;
break;
case DW_FORM_block2:
- blk = (struct dwarf_block *) bfd_alloc (abfd, sizeof (struct dwarf_block));
+ amt = sizeof (struct dwarf_block);
+ blk = (struct dwarf_block *) bfd_alloc (abfd, amt);
blk->size = read_2_bytes (abfd, info_ptr);
info_ptr += 2;
blk->data = read_n_bytes (abfd, info_ptr, blk->size);
DW_BLOCK (attr) = blk;
break;
case DW_FORM_block4:
- blk = (struct dwarf_block *) bfd_alloc (abfd, sizeof (struct dwarf_block));
+ amt = sizeof (struct dwarf_block);
+ blk = (struct dwarf_block *) bfd_alloc (abfd, amt);
blk->size = read_4_bytes (abfd, info_ptr);
info_ptr += 4;
blk->data = read_n_bytes (abfd, info_ptr, blk->size);
info_ptr += bytes_read;
break;
case DW_FORM_block:
- blk = (struct dwarf_block *) bfd_alloc (abfd, sizeof (struct dwarf_block));
+ amt = sizeof (struct dwarf_block);
+ blk = (struct dwarf_block *) bfd_alloc (abfd, amt);
blk->size = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
info_ptr += bytes_read;
blk->data = read_n_bytes (abfd, info_ptr, blk->size);
DW_BLOCK (attr) = blk;
break;
case DW_FORM_block1:
- blk = (struct dwarf_block *) bfd_alloc (abfd, sizeof (struct dwarf_block));
+ amt = sizeof (struct dwarf_block);
+ blk = (struct dwarf_block *) bfd_alloc (abfd, amt);
blk->size = read_1_byte (abfd, info_ptr);
info_ptr += 1;
blk->data = read_n_bytes (abfd, info_ptr, blk->size);
DW_UNSND (attr) = read_4_bytes (abfd, info_ptr);
info_ptr += 4;
break;
+ case DW_FORM_ref8:
+ DW_UNSND (attr) = read_8_bytes (abfd, info_ptr);
+ info_ptr += 8;
+ break;
case DW_FORM_ref_udata:
DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
info_ptr += bytes_read;
case DW_FORM_strp:
case DW_FORM_indirect:
default:
- (*_bfd_error_handler) ("Dwarf Error: Invalid or unhandled FORM value: %d.",
+ (*_bfd_error_handler) (_("Dwarf Error: Invalid or unhandled FORM value: %d."),
abbrev->form);
bfd_set_error (bfd_error_bad_value);
}
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. */
};
-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;
struct line_info* last_line;
};
-static void
-add_line_info (table, address, filename, line, column)
+static void
+add_line_info (table, address, filename, line, column, end_sequence)
struct line_info_table* table;
bfd_vma address;
char* filename;
unsigned int line;
unsigned int column;
+ int end_sequence;
{
- struct line_info* info = (struct line_info*)
- bfd_alloc (table->abfd, sizeof (struct line_info));
+ bfd_size_type amt = sizeof (struct line_info);
+ struct line_info* info = (struct line_info*) bfd_alloc (table->abfd, amt);
info->prev_line = table->last_line;
table->last_line = info;
info->filename = filename;
info->line = line;
info->column = column;
+ info->end_sequence = end_sequence;
}
-static char*
+static char *
concat_filename (table, file)
struct line_info_table* table;
unsigned int file;
{
- char* filename = table->files[file - 1].name;
- if (*filename == '/')
+ char* filename;
+
+ if (file - 1 >= table->num_files)
+ {
+ (*_bfd_error_handler)
+ (_("Dwarf Error: mangled line number section (bad file number)."));
+ return "<unknown>";
+ }
+
+ filename = table->files[file - 1].name;
+ if (IS_ABSOLUTE_PATH(filename))
return filename;
else
}
}
-/* Decode the line number information for the compilation unit whose
- line number info is at OFFSET in the .debug_line section.
- The compilation directory of the file is passed in COMP_DIR. */
+static void
+arange_add (unit, low_pc, high_pc)
+ struct comp_unit *unit;
+ bfd_vma low_pc;
+ bfd_vma high_pc;
+{
+ struct arange *arange;
+
+ /* First see if we can cheaply extend an existing range. */
+ arange = &unit->arange;
+
+ do
+ {
+ if (low_pc == arange->high)
+ {
+ arange->high = high_pc;
+ return;
+ }
+ if (high_pc == arange->low)
+ {
+ arange->low = low_pc;
+ return;
+ }
+ arange = arange->next;
+ }
+ while (arange);
+
+ if (unit->arange.high == 0)
+ {
+ /* 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. */
+ arange = bfd_zalloc (unit->abfd, (bfd_size_type) sizeof (*arange));
+ arange->low = low_pc;
+ arange->high = high_pc;
+
+ arange->next = unit->arange.next;
+ unit->arange.next = arange;
+}
+
+/* Decode the line number information for UNIT. */
static struct line_info_table*
-decode_line_info (abfd, offset, comp_dir)
- bfd *abfd;
- unsigned int offset;
- char *comp_dir;
+decode_line_info (unit, stash)
+ struct comp_unit *unit;
+ struct dwarf2_debug *stash;
{
- static char* dwarf_line_buffer = 0;
-
+ bfd *abfd = unit->abfd;
struct line_info_table* table;
-
char *line_ptr;
char *line_end;
struct line_head lh;
unsigned int i, bytes_read;
char *cur_file, *cur_dir;
unsigned char op_code, extended_op, adj_opcode;
+ bfd_size_type amt;
- if (! dwarf_line_buffer)
+ if (! stash->dwarf_line_buffer)
{
asection *msec;
- unsigned long size;
msec = bfd_get_section_by_name (abfd, ".debug_line");
if (! msec)
{
- (*_bfd_error_handler) ("Dwarf Error: Can't find .debug_line section.");
+ (*_bfd_error_handler) (_("Dwarf Error: Can't find .debug_line section."));
bfd_set_error (bfd_error_bad_value);
return 0;
}
-
- size = bfd_get_section_size_before_reloc (msec);
- dwarf_line_buffer = (unsigned char*) bfd_alloc (abfd, size);
- if (! dwarf_line_buffer)
+
+ stash->dwarf_line_size = msec->_raw_size;
+ stash->dwarf_line_buffer = (char *) bfd_alloc (abfd, msec->_raw_size);
+ if (! stash->dwarf_line_buffer)
return 0;
- if (! bfd_get_section_contents (abfd, msec,
- dwarf_line_buffer, 0,
- size))
+ if (! bfd_get_section_contents (abfd, msec, stash->dwarf_line_buffer,
+ (bfd_vma) 0, msec->_raw_size))
return 0;
+
+ /* FIXME: We ought to apply the relocs against this section before
+ we process it... */
}
- table = (struct line_info_table*) bfd_alloc (abfd,
- sizeof (struct line_info_table));
+ /* Since we are using un-relocated data, it is possible to get a bad value
+ for the line_offset. Validate it here so that we won't get a segfault
+ below. */
+ if (unit->line_offset >= stash->dwarf_line_size)
+ {
+ (*_bfd_error_handler) (_("Dwarf Error: Line offset (%u) greater than or equal to line size (%u)."),
+ unit->line_offset, stash->dwarf_line_size);
+ bfd_set_error (bfd_error_bad_value);
+ return 0;
+ }
+
+ amt = sizeof (struct line_info_table);
+ table = (struct line_info_table*) bfd_alloc (abfd, amt);
table->abfd = abfd;
- table->comp_dir = comp_dir;
+ table->comp_dir = unit->comp_dir;
table->num_files = 0;
table->files = NULL;
table->num_dirs = 0;
table->dirs = NULL;
- line_ptr = dwarf_line_buffer + offset;
+ table->files = NULL;
+ table->last_line = NULL;
- /* read in the prologue */
+ line_ptr = stash->dwarf_line_buffer + unit->line_offset;
+
+ /* Read in the prologue. */
lh.total_length = read_4_bytes (abfd, line_ptr);
line_ptr += 4;
line_end = line_ptr + lh.total_length;
line_ptr += 1;
lh.opcode_base = read_1_byte (abfd, line_ptr);
line_ptr += 1;
- lh.standard_opcode_lengths = (unsigned char *)
- bfd_alloc (abfd, lh.opcode_base * sizeof (unsigned char));
+ amt = lh.opcode_base * sizeof (unsigned char);
+ lh.standard_opcode_lengths = (unsigned char *) bfd_alloc (abfd, amt);
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 **)
- bfd_realloc (table->dirs,
- (table->num_dirs + DIR_ALLOC_CHUNK) * sizeof (char *));
+ amt = table->num_dirs + DIR_ALLOC_CHUNK;
+ amt *= sizeof (char *);
+ table->dirs = (char **) bfd_realloc (table->dirs, amt);
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 *)
- bfd_realloc (table->files,
- (table->num_files + FILE_ALLOC_CHUNK)
- * sizeof (struct fileinfo));
+ amt = table->num_files + FILE_ALLOC_CHUNK;
+ amt *= sizeof (struct fileinfo);
+ table->files = (struct fileinfo *) bfd_realloc (table->files, amt);
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;
unsigned int column = 0;
int is_stmt = lh.default_is_stmt;
int basic_block = 0;
- int end_sequence = 0;
+ 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)
{
case DW_LNE_end_sequence:
end_sequence = 1;
- add_line_info (table, address, filename, line, column);
+ add_line_info (table, address, filename, line, column,
+ end_sequence);
+ if (need_low_pc)
+ {
+ need_low_pc = 0;
+ low_pc = address;
+ }
+ arange_add (unit, low_pc, address);
break;
case DW_LNE_set_address:
- address = read_address (abfd, line_ptr);
- address &= 0xffffffff;
- line_ptr += sizeof (bfd_vma);
+ address = read_address (unit, line_ptr);
+ line_ptr += unit->addr_size;
break;
case DW_LNE_define_file:
cur_file = read_string (abfd, line_ptr, &bytes_read);
line_ptr += bytes_read;
if ((table->num_files % FILE_ALLOC_CHUNK) == 0)
{
- table->files = (struct fileinfo *)
- bfd_realloc (table->files,
- (table->num_files + FILE_ALLOC_CHUNK)
- * sizeof (struct fileinfo));
+ amt = table->num_files + FILE_ALLOC_CHUNK;
+ amt *= sizeof (struct fileinfo);
+ table->files =
+ (struct fileinfo *) bfd_realloc (table->files, amt);
if (! table->files)
return 0;
}
table->num_files++;
break;
default:
- (*_bfd_error_handler) ("Dwarf Error: mangled line number section.");
+ (*_bfd_error_handler) (_("Dwarf Error: mangled line number section."));
bfd_set_error (bfd_error_bad_value);
return 0;
}
break;
case DW_LNS_copy:
- add_line_info (table, address, filename, line, column);
+ add_line_info (table, address, filename, line, column, 0);
basic_block = 0;
+ if (need_low_pc)
+ {
+ need_low_pc = 0;
+ low_pc = address;
+ }
break;
case DW_LNS_advance_pc:
address += lh.minimum_instruction_length
basic_block = 1;
break;
case DW_LNS_const_add_pc:
- address += (255 - lh.opcode_base) / lh.line_range;
+ address += lh.minimum_instruction_length
+ * ((255 - lh.opcode_base) / lh.line_range);
break;
case DW_LNS_fixed_advance_pc:
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 */
- add_line_info (table, address, filename, line, column);
+ /* Append row to matrix using current values. */
+ add_line_info (table, address, filename, line, column, 0);
basic_block = 1;
+ if (need_low_pc)
+ {
+ need_low_pc = 0;
+ low_pc = address;
+ }
}
}
}
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,
+lookup_address_in_line_info_table (table,
addr,
- filename_ptr,
+ filename_ptr,
linenumber_ptr)
struct line_info_table* table;
bfd_vma addr;
const char **filename_ptr;
unsigned int *linenumber_ptr;
{
+ struct line_info* next_line = table->last_line;
struct line_info* each_line;
- struct line_info* next_line;
-
- for (next_line = 0, each_line = table->last_line;
- each_line;
- next_line = each_line, each_line = each_line->prev_line)
+
+ if (!next_line)
+ return false;
+
+ each_line = next_line->prev_line;
+
+ while (each_line && next_line)
{
- if (addr >= each_line->address
- && (next_line == 0
- || addr < next_line->address))
+ if (!each_line->end_sequence
+ && addr >= each_line->address && addr < next_line->address)
{
*filename_ptr = each_line->filename;
*linenumber_ptr = each_line->line;
return true;
}
+ next_line = each_line;
+ each_line = each_line->prev_line;
}
-
+
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. */
+/* If ADDR is within TABLE, set FUNCTIONNAME_PTR, and return true. */
static boolean
-lookup_address_in_function_table (table,
+lookup_address_in_function_table (table,
addr,
functionname_ptr)
struct funcinfo* table;
each_func;
each_func = each_func->prev_func)
{
- if (addr >= (each_func->low & 0xffffffff)
- && addr < (each_func->high & 0xffffffff))
+ if (addr >= each_func->low && addr < each_func->high)
{
*functionname_ptr = each_func->name;
return true;
}
}
-
+
return false;
}
-
-
-
-/* DWARF2 Compilation unit functions. */
-
-
-/* A minimal decoding of DWARF2 compilation units. We only decode
- what's needed to get to the line number information. */
-
-struct comp_unit {
-
- /* Chain the previously read compilation units. */
- struct comp_unit* next_unit;
-
- /* 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. */
- bfd_vma low;
- bfd_vma high;
-
- /* The DW_AT_name attribute (for error messages). */
- char* name;
-
- /* The abbrev hash table. */
- struct abbrev_info** abbrevs;
-
- /* Note that an error was found by comp_unit_find_nearest_line. */
- int error;
-
- /* The DW_AT_comp_dir attribute */
- char* comp_dir;
-
- /* 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. */
- unsigned long line_offset;
-
- /* Pointer to the first child die for the comp unit. */
- char *first_child_die_ptr;
-
- /* The end of the comp unit. */
- char *end_ptr;
-
- /* 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. */
- struct funcinfo* function_table;
-
-};
-
-
+/* 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)
nesting_level--;
continue;
}
-
+
abbrev = lookup_abbrev (abbrev_number,unit->abbrevs);
if (! abbrev)
{
- (*_bfd_error_handler) ("Dwarf Error: Could not find abbrev number %d.",
+ (*_bfd_error_handler) (_("Dwarf Error: Could not find abbrev number %d."),
abbrev_number);
bfd_set_error (bfd_error_bad_value);
return false;
}
-
+
if (abbrev->tag == DW_TAG_subprogram)
{
- func = (struct funcinfo*) bfd_zalloc (abfd, sizeof (struct funcinfo));
+ bfd_size_type amt = sizeof (struct funcinfo);
+ func = (struct funcinfo *) bfd_zalloc (abfd, amt);
func->prev_func = unit->function_table;
unit->function_table = func;
}
else
func = NULL;
-
+
for (i = 0; i < abbrev->num_attrs; ++i)
{
- info_ptr = read_attribute (&attr, &abbrev->attrs[i], abfd, info_ptr);
-
+ info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, info_ptr);
+
if (func)
{
switch (attr.name)
{
case DW_AT_name:
-
+
name = DW_STRING (&attr);
/* Prefer DW_AT_MIPS_linkage_name over DW_AT_name. */
if (func->name == NULL)
func->name = DW_STRING (&attr);
break;
-
+
case DW_AT_MIPS_linkage_name:
func->name = DW_STRING (&attr);
break;
case DW_AT_name:
name = DW_STRING (&attr);
break;
-
+
default:
break;
}
return true;
}
+/* Look for a RELA relocation to be applied on OFFSET of section SEC,
+ and return the addend if such a relocation is found. Since this is
+ only used to find relocations referring to the .debug_abbrev
+ section, we make sure the relocation refers to this section, but
+ this is not strictly necessary, and it can probably be safely
+ removed if needed. However, it is important to note that this
+ function only returns the addend, it doesn't serve the purpose of
+ applying a generic relocation.
+ If no suitable relocation is found, or if it is not a real RELA
+ relocation, this function returns 0. */
+static bfd_vma
+find_rela_addend (abfd, sec, offset, syms)
+ bfd* abfd;
+ asection* sec;
+ bfd_size_type offset;
+ asymbol** syms;
+{
+ long reloc_size = bfd_get_reloc_upper_bound (abfd, sec);
+ arelent **relocs = NULL;
+ long reloc_count, relc;
+ if (reloc_size <= 0)
+ return 0;
+ relocs = (arelent **) bfd_malloc ((bfd_size_type) reloc_size);
+ if (relocs == NULL)
+ return 0;
-/* 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 unit header.
- END_PTR points one past the end of this comp unit.
+ reloc_count = bfd_canonicalize_reloc (abfd, sec, relocs, syms);
+
+ if (reloc_count <= 0)
+ {
+ free (relocs);
+ return 0;
+ }
+
+ for (relc = 0; relc < reloc_count; relc++)
+ if (relocs[relc]->address == offset
+ && (*relocs[relc]->sym_ptr_ptr)->flags & BSF_SECTION_SYM
+ && strcmp ((*relocs[relc]->sym_ptr_ptr)->name,
+ ".debug_abbrev") == 0)
+ {
+ bfd_vma addend = (relocs[relc]->howto->partial_inplace
+ ? 0 : relocs[relc]->addend);
+ free (relocs);
+ return addend;
+ }
+
+ free (relocs);
+ return 0;
+}
+
+/* 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
+ unit header. END_PTR points one past the end of this comp unit.
+ If ABBREV_LENGTH is 0, then the length of the abbreviation offset
+ is assumed to be four bytes. Otherwise, it it is the size given.
This routine does not read the whole compilation unit; only enough
- to get to the line number information for the compilation unit.
- */
+ to get to the line number information for the compilation unit. */
-static struct comp_unit*
-parse_comp_unit (abfd, info_ptr, end_ptr)
+static struct comp_unit *
+parse_comp_unit (abfd, stash, unit_length, abbrev_length)
bfd* abfd;
- char* info_ptr;
- char* end_ptr;
+ struct dwarf2_debug *stash;
+ bfd_vma unit_length;
+ unsigned int abbrev_length;
{
struct comp_unit* unit;
unsigned short version;
- unsigned int abbrev_offset;
+ unsigned int abbrev_offset = 0;
unsigned char addr_size;
struct abbrev_info** abbrevs;
struct abbrev_info *abbrev;
struct attribute attr;
+ char *info_ptr = stash->info_ptr;
+ char *end_ptr = info_ptr + unit_length;
+ bfd_size_type amt;
+ bfd_size_type off;
+
version = read_2_bytes (abfd, info_ptr);
info_ptr += 2;
- abbrev_offset = read_4_bytes (abfd, info_ptr);
- info_ptr += 4;
+ BFD_ASSERT (abbrev_length == 0
+ || abbrev_length == 4
+ || abbrev_length == 8);
+ if (abbrev_length == 0 || abbrev_length == 4)
+ abbrev_offset = read_4_bytes (abfd, info_ptr);
+ else if (abbrev_length == 8)
+ abbrev_offset = read_8_bytes (abfd, info_ptr);
+ /* The abbrev offset is generally a relocation pointing to
+ .debug_abbrev+offset. On RELA targets, we have to find the
+ relocation and extract the addend to obtain the actual
+ abbrev_offset, so do it here. */
+ off = info_ptr - stash->sec_info_ptr;
+ abbrev_offset += find_rela_addend (abfd, stash->sec, off, stash->syms);
+ info_ptr += abbrev_length;
addr_size = read_1_byte (abfd, info_ptr);
info_ptr += 1;
if (version != 2)
{
- (*_bfd_error_handler) ("Dwarf Error: found dwarf version '%hu', this reader only handles version 2 information.", version );
+ (*_bfd_error_handler) (_("Dwarf Error: found dwarf version '%hu', this reader only handles version 2 information."), version );
bfd_set_error (bfd_error_bad_value);
return 0;
}
- if (addr_size != sizeof (bfd_vma))
+ if (addr_size > sizeof (bfd_vma))
{
- (*_bfd_error_handler) ("Dwarf Error: found address size '%u', this readers only handles address size '%u'.",
+ (*_bfd_error_handler) (_("Dwarf Error: found address size '%u', this reader can not handle sizes greater than '%u'."),
addr_size,
sizeof (bfd_vma));
bfd_set_error (bfd_error_bad_value);
return 0;
}
- /* Read the abbrevs for this compilation unit into a table */
- abbrevs = read_abbrevs (abfd, abbrev_offset);
+ if (addr_size != 2 && addr_size != 4 && addr_size != 8)
+ {
+ (*_bfd_error_handler) ("Dwarf Error: found address size '%u', this reader can only handle address sizes '2', '4' and '8'.", addr_size );
+ bfd_set_error (bfd_error_bad_value);
+ return 0;
+ }
+
+ /* Read the abbrevs for this compilation unit into a table. */
+ abbrevs = read_abbrevs (abfd, abbrev_offset, stash);
if (! abbrevs)
return 0;
info_ptr += bytes_read;
if (! abbrev_number)
{
- (*_bfd_error_handler) ("Dwarf Error: Bad abbrev number: %d.",
+ (*_bfd_error_handler) (_("Dwarf Error: Bad abbrev number: %d."),
abbrev_number);
bfd_set_error (bfd_error_bad_value);
return 0;
abbrev = lookup_abbrev (abbrev_number, abbrevs);
if (! abbrev)
{
- (*_bfd_error_handler) ("Dwarf Error: Could not find abbrev number %d.",
+ (*_bfd_error_handler) (_("Dwarf Error: Could not find abbrev number %d."),
abbrev_number);
bfd_set_error (bfd_error_bad_value);
return 0;
}
-
- unit = (struct comp_unit*) bfd_zalloc (abfd, sizeof (struct comp_unit));
+
+ amt = sizeof (struct comp_unit);
+ unit = (struct comp_unit*) bfd_zalloc (abfd, amt);
unit->abfd = abfd;
+ unit->addr_size = addr_size;
unit->abbrevs = abbrevs;
unit->end_ptr = end_ptr;
for (i = 0; i < abbrev->num_attrs; ++i)
{
- info_ptr = read_attribute (&attr, &abbrev->attrs[i], abfd, info_ptr);
+ info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, info_ptr);
/* Store the data if it is of an attribute we want to keep in a
partial symbol table. */
break;
case DW_AT_low_pc:
- unit->low = DW_ADDR (&attr);
+ unit->arange.low = DW_ADDR (&attr);
break;
case DW_AT_high_pc:
- unit->high = DW_ADDR (&attr);
+ unit->arange.high = DW_ADDR (&attr);
break;
case DW_AT_comp_dir:
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)
struct comp_unit* unit;
bfd_vma addr;
{
- return ! unit->error
- && ( addr >= (unit->low & 0xffffffff)
- && addr <= (unit->high & 0xffffffff));
-}
+ struct arange *arange;
+
+ if (unit->error)
+ return 0;
+ arange = &unit->arange;
+ do
+ {
+ if (addr >= arange->low && addr < arange->high)
+ return 1;
+ 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
- to be filled in.
+ to be filled in.
Return true of UNIT contains ADDR, and no errors were encountered;
false otherwise. */
static boolean
comp_unit_find_nearest_line (unit, addr,
- filename_ptr, functionname_ptr, linenumber_ptr)
+ filename_ptr, functionname_ptr, linenumber_ptr,
+ stash)
struct comp_unit* unit;
bfd_vma addr;
const char **filename_ptr;
const char **functionname_ptr;
unsigned int *linenumber_ptr;
+ struct dwarf2_debug *stash;
{
boolean line_p;
boolean func_p;
-
+
if (unit->error)
return false;
unit->error = 1;
return false;
}
-
- unit->line_table = decode_line_info (unit->abfd,
- unit->line_offset,
- unit->comp_dir);
+
+ unit->line_table = decode_line_info (unit, stash);
if (! unit->line_table)
{
unit->error = 1;
return false;
}
-
+
if (! scan_unit_for_functions (unit))
{
unit->error = 1;
line_p = lookup_address_in_line_info_table (unit->line_table,
addr,
- filename_ptr,
+ filename_ptr,
linenumber_ptr);
- func_p = lookup_address_in_function_table (unit->function_table,
+ func_p = lookup_address_in_function_table (unit->function_table,
addr,
functionname_ptr);
return line_p || func_p;
}
-/* The DWARF2 version of find_nearest line.
- Return true if the line is found without error. */
+/* 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.
+ You may use zero to indicate that the default value should be
+ used. */
boolean
_bfd_dwarf2_find_nearest_line (abfd, section, symbols, offset,
- filename_ptr, functionname_ptr, linenumber_ptr)
+ filename_ptr, functionname_ptr,
+ linenumber_ptr,
+ addr_size, pinfo)
bfd *abfd;
asection *section;
asymbol **symbols;
const char **filename_ptr;
const char **functionname_ptr;
unsigned int *linenumber_ptr;
+ unsigned int addr_size;
+ PTR *pinfo;
{
/* Read each compilation unit from the section .debug_info, and check
to see if it contains the address we are searching for. If yes,
lookup the address, and return the line number info. If no, go
- on to the next compilation unit.
+ on to the next compilation unit.
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.
- */
+ a pointer to the next un-read compilation unit. Check the
+ previously read units before reading more. */
+ struct dwarf2_debug *stash = (struct dwarf2_debug *) *pinfo;
- 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;
-
+
*filename_ptr = NULL;
*functionname_ptr = NULL;
*linenumber_ptr = 0;
+ /* The DWARF2 spec says that the initial length field, and the
+ offset of the abbreviation table, should both be 4-byte values.
+ However, some compilers do things differently. */
+ if (addr_size == 0)
+ addr_size = 4;
+ BFD_ASSERT (addr_size == 4 || addr_size == 8);
+
if (! stash)
{
+ bfd_size_type 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));
-
+ bfd_size_type amt = sizeof (struct dwarf2_debug);
+
+ stash = (struct dwarf2_debug*) bfd_zalloc (abfd, amt);
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 = bfd_get_section_size_before_reloc (msec);
- stash->info_ptr = (unsigned char*) bfd_alloc (abfd, size);
-
- if (! stash->info_ptr)
+ *pinfo = (PTR) stash;
+
+ 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, 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;
+ bfd_size_type size;
+ bfd_size_type 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,
+ (bfd_vma) 0, size))
+ continue;
+
+ stash->info_ptr_end = stash->info_ptr + start + size;
}
- stash->info_ptr_end = stash->info_ptr + size;
- }
+ BFD_ASSERT (stash->info_ptr_end == stash->info_ptr + total_size);
-
- /* A null info_ptr indicates that there is no dwarf2 info
- (or that an error occured while setting up the stash). */
+ stash->sec = find_debug_info (abfd, NULL);
+ stash->sec_info_ptr = stash->info_ptr;
+ stash->syms = symbols;
+ }
- if (! stash->info_ptr)
- return false;
+ /* 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. */
- /* Check the previously read comp. units first. */
+ /* A null info_ptr indicates that there is no dwarf2 info
+ (or that an error occured while setting up the stash). */
+ if (! stash->info_ptr)
+ return false;
+ /* 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);
- }
-
+ if (comp_unit_contains_address (each, addr))
+ return comp_unit_find_nearest_line (each, addr, filename_ptr,
+ functionname_ptr, linenumber_ptr,
+ stash);
- /* 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;
- unsigned int length;
+ bfd_vma length;
+ boolean found;
- length = read_4_bytes (abfd, stash->info_ptr);
- stash->info_ptr += 4;
+ if (addr_size == 4)
+ length = read_4_bytes (abfd, stash->info_ptr);
+ else
+ length = read_8_bytes (abfd, stash->info_ptr);
+ stash->info_ptr += addr_size;
if (length > 0)
{
- each = parse_comp_unit (abfd, stash->info_ptr,
- stash->info_ptr + length);
+ each = parse_comp_unit (abfd, stash, length, addr_size);
stash->info_ptr += length;
+ if ((bfd_vma) (stash->info_ptr - stash->sec_info_ptr)
+ == stash->sec->_raw_size)
+ {
+ stash->sec = find_debug_info (abfd, stash->sec);
+ stash->sec_info_ptr = stash->info_ptr;
+ }
+
if (each)
{
each->next_unit = stash->all_comp_units;
stash->all_comp_units = each;
-
- if (comp_unit_contains_address (each, addr))
- return comp_unit_find_nearest_line (each, addr,
- filename_ptr,
- functionname_ptr,
- linenumber_ptr);
+
+ /* DW_AT_low_pc and DW_AT_high_pc are optional for
+ 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. */
+ if (each->arange.high > 0)
+ {
+ if (comp_unit_contains_address (each, addr))
+ return comp_unit_find_nearest_line (each, addr,
+ filename_ptr,
+ functionname_ptr,
+ linenumber_ptr,
+ stash);
+ }
+ else
+ {
+ found = comp_unit_find_nearest_line (each, addr,
+ filename_ptr,
+ functionname_ptr,
+ linenumber_ptr,
+ stash);
+ if (found)
+ return true;
+ }
}
}
}
return false;
}
-
-/* end of file */