From Craig Silverstein: Support debug info for shared libraries.

This commit is contained in:
Ian Lance Taylor 2007-11-13 01:26:27 +00:00
parent f4c43811f7
commit af674d1d6c
2 changed files with 110 additions and 50 deletions

View File

@ -119,7 +119,7 @@ ResetLineStateMachine(struct LineStateMachine* lsm, bool default_is_stmt)
template<int size, bool big_endian> template<int size, bool big_endian>
Dwarf_line_info<size, big_endian>::Dwarf_line_info(Object* object) Dwarf_line_info<size, big_endian>::Dwarf_line_info(Object* object)
: data_valid_(false), buffer_(NULL), symtab_buffer_(NULL), : data_valid_(false), buffer_(NULL), symtab_buffer_(NULL),
directories_(1), files_(1) directories_(), files_(), current_header_index_(-1)
{ {
unsigned int debug_shndx; unsigned int debug_shndx;
for (debug_shndx = 0; debug_shndx < object->shnum(); ++debug_shndx) for (debug_shndx = 0; debug_shndx < object->shnum(); ++debug_shndx)
@ -135,6 +135,7 @@ Dwarf_line_info<size, big_endian>::Dwarf_line_info(Object* object)
return; return;
// Find the relocation section for ".debug_line". // Find the relocation section for ".debug_line".
// We expect these for relobjs (.o's) but not dynobjs (.so's).
bool got_relocs = false; bool got_relocs = false;
for (unsigned int reloc_shndx = 0; for (unsigned int reloc_shndx = 0;
reloc_shndx < object->shnum(); reloc_shndx < object->shnum();
@ -150,22 +151,21 @@ Dwarf_line_info<size, big_endian>::Dwarf_line_info(Object* object)
break; break;
} }
} }
if (!got_relocs)
return;
// Finally, we need the symtab section to interpret the relocs. // Finally, we need the symtab section to interpret the relocs.
unsigned int symtab_shndx; if (got_relocs)
for (symtab_shndx = 0; symtab_shndx < object->shnum(); ++symtab_shndx) {
if (object->section_type(symtab_shndx) == elfcpp::SHT_SYMTAB) unsigned int symtab_shndx;
{ for (symtab_shndx = 0; symtab_shndx < object->shnum(); ++symtab_shndx)
off_t symtab_size; if (object->section_type(symtab_shndx) == elfcpp::SHT_SYMTAB)
this->symtab_buffer_ = object->section_contents( {
symtab_shndx, &symtab_size, false); this->symtab_buffer_ = object->section_contents(
this->symtab_buffer_end_ = this->symtab_buffer_ + symtab_size; symtab_shndx, &this->symtab_buffer_size_, false);
break; break;
} }
if (this->symtab_buffer_ == NULL) if (this->symtab_buffer_ == NULL)
return; return;
}
// Now that we have successfully read all the data, parse the debug // Now that we have successfully read all the data, parse the debug
// info. // info.
@ -241,16 +241,29 @@ const unsigned char*
Dwarf_line_info<size, big_endian>::read_header_tables( Dwarf_line_info<size, big_endian>::read_header_tables(
const unsigned char* lineptr) const unsigned char* lineptr)
{ {
++this->current_header_index_;
// Create a new directories_ entry and a new files_ entry for our new
// header. We initialize each with a single empty element, because
// dwarf indexes directory and filenames starting at 1.
gold_assert(static_cast<int>(this->directories_.size())
== this->current_header_index_);
gold_assert(static_cast<int>(this->files_.size())
== this->current_header_index_);
this->directories_.push_back(std::vector<std::string>(1));
this->files_.push_back(std::vector<std::pair<int, std::string> >(1));
// It is legal for the directory entry table to be empty. // It is legal for the directory entry table to be empty.
if (*lineptr) if (*lineptr)
{ {
int dirindex = 1; int dirindex = 1;
while (*lineptr) while (*lineptr)
{ {
const unsigned char* dirname = lineptr; const char* dirname = reinterpret_cast<const char*>(lineptr);
gold_assert(dirindex == static_cast<int>(directories_.size())); gold_assert(dirindex
directories_.push_back(reinterpret_cast<const char*>(dirname)); == static_cast<int>(this->directories_.back().size()));
lineptr += directories_.back().size() + 1; this->directories_.back().push_back(dirname);
lineptr += this->directories_.back().back().size() + 1;
dirindex++; dirindex++;
} }
} }
@ -267,18 +280,21 @@ Dwarf_line_info<size, big_endian>::read_header_tables(
lineptr += strlen(filename) + 1; lineptr += strlen(filename) + 1;
uint64_t dirindex = read_unsigned_LEB_128(lineptr, &len); uint64_t dirindex = read_unsigned_LEB_128(lineptr, &len);
if (dirindex >= directories_.size())
dirindex = 0;
lineptr += len; lineptr += len;
if (dirindex >= this->directories_.back().size())
dirindex = 0;
int dirindexi = static_cast<int>(dirindex);
read_unsigned_LEB_128(lineptr, &len); // mod_time read_unsigned_LEB_128(lineptr, &len); // mod_time
lineptr += len; lineptr += len;
read_unsigned_LEB_128(lineptr, &len); // filelength read_unsigned_LEB_128(lineptr, &len); // filelength
lineptr += len; lineptr += len;
gold_assert(fileindex == static_cast<int>(files_.size())); gold_assert(fileindex
files_.push_back(std::pair<int, std::string>(dirindex, filename)); == static_cast<int>(this->files_.back().size()));
this->files_.back().push_back(std::make_pair(dirindexi, filename));
fileindex++; fileindex++;
} }
} }
@ -407,21 +423,21 @@ Dwarf_line_info<size, big_endian>::process_one_opcode(
case elfcpp::DW_LNE_set_address: case elfcpp::DW_LNE_set_address:
{ {
lsm->address = elfcpp::Swap<size, big_endian>::readval(start);
typename Reloc_map::const_iterator it typename Reloc_map::const_iterator it
= reloc_map_.find(start - this->buffer_); = reloc_map_.find(start - this->buffer_);
if (it != reloc_map_.end()) if (it != reloc_map_.end())
{ {
// value + addend. // value + addend.
lsm->address = lsm->address += it->second.second;
(elfcpp::Swap<size, big_endian>::readval(start)
+ it->second.second);
lsm->shndx = it->second.first; lsm->shndx = it->second.first;
} }
else else
{ {
// Every set_address should have an associated // If we're a normal .o file, with relocs, every
// relocation. // set_address should have an associated relocation.
this->data_valid_ = false; if (this->input_is_relobj())
this->data_valid_ = false;
} }
break; break;
} }
@ -432,17 +448,19 @@ Dwarf_line_info<size, big_endian>::process_one_opcode(
start += templen; start += templen;
uint64_t dirindex = read_unsigned_LEB_128(start, &templen); uint64_t dirindex = read_unsigned_LEB_128(start, &templen);
if (dirindex >= directories_.size())
dirindex = 0;
oplen += templen; oplen += templen;
if (dirindex >= this->directories_.back().size())
dirindex = 0;
int dirindexi = static_cast<int>(dirindex);
read_unsigned_LEB_128(start, &templen); // mod_time read_unsigned_LEB_128(start, &templen); // mod_time
oplen += templen; oplen += templen;
read_unsigned_LEB_128(start, &templen); // filelength read_unsigned_LEB_128(start, &templen); // filelength
oplen += templen; oplen += templen;
files_.push_back(std::pair<int, std::string>(dirindex, this->files_.back().push_back(std::make_pair(dirindexi,
filename)); filename));
} }
break; break;
@ -497,7 +515,8 @@ Dwarf_line_info<size, big_endian>::read_lines(unsigned const char* lineptr)
if (add_line) if (add_line)
{ {
Offset_to_lineno_entry entry Offset_to_lineno_entry entry
= { lsm.address, lsm.file_num, lsm.line_num }; = { lsm.address, this->current_header_index_,
lsm.file_num, lsm.line_num };
line_number_map_[lsm.shndx].push_back(entry); line_number_map_[lsm.shndx].push_back(entry);
} }
lineptr += oplength; lineptr += oplength;
@ -516,7 +535,7 @@ Dwarf_line_info<size, big_endian>::symbol_section(
typename elfcpp::Elf_types<size>::Elf_Addr* value) typename elfcpp::Elf_types<size>::Elf_Addr* value)
{ {
const int symsize = elfcpp::Elf_sizes<size>::sym_size; const int symsize = elfcpp::Elf_sizes<size>::sym_size;
gold_assert(this->symtab_buffer_ + sym * symsize < this->symtab_buffer_end_); gold_assert(sym * symsize < this->symtab_buffer_size_);
elfcpp::Sym<size, big_endian> elfsym(this->symtab_buffer_ + sym * symsize); elfcpp::Sym<size, big_endian> elfsym(this->symtab_buffer_ + sym * symsize);
*value = elfsym.get_st_value(); *value = elfsym.get_st_value();
return elfsym.get_st_shndx(); return elfsym.get_st_shndx();
@ -568,6 +587,21 @@ Dwarf_line_info<size, big_endian>::read_line_mappings()
std::sort(it->second.begin(), it->second.end()); std::sort(it->second.begin(), it->second.end());
} }
// Some processing depends on whether the input is a .o file or not.
// For instance, .o files have relocs, and have .debug_lines
// information on a per section basis. .so files, on the other hand,
// lack relocs, and offsets are unique, so we can ignore the section
// information.
template<int size, bool big_endian>
bool
Dwarf_line_info<size, big_endian>::input_is_relobj()
{
// Only .o files have relocs and the symtab buffer that goes with them.
return this->symtab_buffer_ != NULL;
}
// Return a string for a file name and line number. // Return a string for a file name and line number.
template<int size, bool big_endian> template<int size, bool big_endian>
@ -577,19 +611,26 @@ Dwarf_line_info<size, big_endian>::addr2line(unsigned int shndx, off_t offset)
if (this->data_valid_ == false) if (this->data_valid_ == false)
return ""; return "";
const Offset_to_lineno_entry lookup_key = { offset, 0, 0 }; const Offset_to_lineno_entry lookup_key = { offset, 0, 0, 0 };
std::vector<Offset_to_lineno_entry>& offsets = this->line_number_map_[shndx]; const std::vector<Offset_to_lineno_entry>* offsets;
if (offsets.empty()) // If we do not have reloc information, then our input is a .so or
// some similar data structure where all the information is held in
// the offset. In that case, we ignore the input shndx.
if (this->input_is_relobj())
offsets = &this->line_number_map_[shndx];
else
offsets = &this->line_number_map_[-1U];
if (offsets->empty())
return ""; return "";
typename std::vector<Offset_to_lineno_entry>::const_iterator it typename std::vector<Offset_to_lineno_entry>::const_iterator it
= std::lower_bound(offsets.begin(), offsets.end(), lookup_key); = std::lower_bound(offsets->begin(), offsets->end(), lookup_key);
// If we found an exact match, great, otherwise find the last entry // If we found an exact match, great, otherwise find the last entry
// before the passed-in offset. // before the passed-in offset.
if (it->offset > offset) if (it->offset > offset)
{ {
if (it == offsets.begin()) if (it == offsets->begin())
return ""; return "";
--it; --it;
gold_assert(it->offset < offset); gold_assert(it->offset < offset);
@ -597,11 +638,20 @@ Dwarf_line_info<size, big_endian>::addr2line(unsigned int shndx, off_t offset)
// Convert the file_num + line_num into a string. // Convert the file_num + line_num into a string.
std::string ret; std::string ret;
gold_assert(it->file_num < static_cast<int>(files_.size()));
const std::pair<int, std::string>& filename_pair = files_[it->file_num]; gold_assert(it->header_num < static_cast<int>(this->files_.size()));
gold_assert(filename_pair.first < static_cast<int>(directories_.size())); gold_assert(it->file_num
const std::string& dirname = directories_[filename_pair.first]; < static_cast<int>(this->files_[it->header_num].size()));
const std::pair<int, std::string>& filename_pair
= this->files_[it->header_num][it->file_num];
const std::string& filename = filename_pair.second; const std::string& filename = filename_pair.second;
gold_assert(it->header_num < static_cast<int>(this->directories_.size()));
gold_assert(filename_pair.first
< static_cast<int>(this->directories_[it->header_num].size()));
const std::string& dirname
= this->directories_[it->header_num][filename_pair.first];
if (!dirname.empty()) if (!dirname.empty())
{ {
ret += dirname; ret += dirname;

View File

@ -90,6 +90,10 @@ class Dwarf_line_info
process_one_opcode(const unsigned char* start, process_one_opcode(const unsigned char* start,
struct LineStateMachine* lsm, size_t* len); struct LineStateMachine* lsm, size_t* len);
// Some parts of processing differ depending on whether the input
// was a .o file or not.
bool input_is_relobj();
// If we saw anything amiss while parsing, we set this to false. // If we saw anything amiss while parsing, we set this to false.
// Then addr2line will always fail (rather than return possibly- // Then addr2line will always fail (rather than return possibly-
// corrupt data). // corrupt data).
@ -123,15 +127,20 @@ class Dwarf_line_info
// This is used to figure out what section to apply a relocation to. // This is used to figure out what section to apply a relocation to.
const unsigned char* symtab_buffer_; const unsigned char* symtab_buffer_;
const unsigned char* symtab_buffer_end_; off_t symtab_buffer_size_;
// Holds the directories and files as we see them. // Holds the directories and files as we see them. We have an array
std::vector<std::string> directories_; // of directory-lists, one for each .o file we're reading (usually
// there will just be one, but there may be more if input is a .so).
std::vector<std::vector<std::string> > directories_;
// The first part is an index into directories_, the second the filename. // The first part is an index into directories_, the second the filename.
std::vector< std::pair<int, std::string> > files_; std::vector<std::vector< std::pair<int, std::string> > > files_;
// A map from offset of the relocation target to the shndx and // An index into the current directories_ and files_ vectors.
// addend for the relocation. int current_header_index_;
// A sorted map from offset of the relocation target to the shndx
// and addend for the relocation.
typedef std::map<typename elfcpp::Elf_types<size>::Elf_Addr, typedef std::map<typename elfcpp::Elf_types<size>::Elf_Addr,
std::pair<unsigned int, std::pair<unsigned int,
typename elfcpp::Elf_types<size>::Elf_Swxword> > typename elfcpp::Elf_types<size>::Elf_Swxword> >
@ -143,8 +152,9 @@ class Dwarf_line_info
struct Offset_to_lineno_entry struct Offset_to_lineno_entry
{ {
off_t offset; off_t offset;
int header_num; // which file-list to use (i.e. which .o file are we in)
int file_num; // a pointer into files_ int file_num; // a pointer into files_
int line_num; int line_num; // the line number in the source file
// Offsets are unique within a section, so that's a sufficient sort key. // Offsets are unique within a section, so that's a sufficient sort key.
bool operator<(const Offset_to_lineno_entry& that) const bool operator<(const Offset_to_lineno_entry& that) const
{ return this->offset < that.offset; } { return this->offset < that.offset; }