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>
Dwarf_line_info<size, big_endian>::Dwarf_line_info(Object* object)
: data_valid_(false), buffer_(NULL), symtab_buffer_(NULL),
directories_(1), files_(1)
directories_(), files_(), current_header_index_(-1)
{
unsigned int 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;
// Find the relocation section for ".debug_line".
// We expect these for relobjs (.o's) but not dynobjs (.so's).
bool got_relocs = false;
for (unsigned int reloc_shndx = 0;
reloc_shndx < object->shnum();
@ -150,22 +151,21 @@ Dwarf_line_info<size, big_endian>::Dwarf_line_info(Object* object)
break;
}
}
if (!got_relocs)
return;
// Finally, we need the symtab section to interpret the relocs.
unsigned int symtab_shndx;
for (symtab_shndx = 0; symtab_shndx < object->shnum(); ++symtab_shndx)
if (object->section_type(symtab_shndx) == elfcpp::SHT_SYMTAB)
{
off_t symtab_size;
this->symtab_buffer_ = object->section_contents(
symtab_shndx, &symtab_size, false);
this->symtab_buffer_end_ = this->symtab_buffer_ + symtab_size;
break;
}
if (this->symtab_buffer_ == NULL)
return;
if (got_relocs)
{
unsigned int symtab_shndx;
for (symtab_shndx = 0; symtab_shndx < object->shnum(); ++symtab_shndx)
if (object->section_type(symtab_shndx) == elfcpp::SHT_SYMTAB)
{
this->symtab_buffer_ = object->section_contents(
symtab_shndx, &this->symtab_buffer_size_, false);
break;
}
if (this->symtab_buffer_ == NULL)
return;
}
// Now that we have successfully read all the data, parse the debug
// info.
@ -241,16 +241,29 @@ const unsigned char*
Dwarf_line_info<size, big_endian>::read_header_tables(
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.
if (*lineptr)
{
int dirindex = 1;
while (*lineptr)
{
const unsigned char* dirname = lineptr;
gold_assert(dirindex == static_cast<int>(directories_.size()));
directories_.push_back(reinterpret_cast<const char*>(dirname));
lineptr += directories_.back().size() + 1;
const char* dirname = reinterpret_cast<const char*>(lineptr);
gold_assert(dirindex
== static_cast<int>(this->directories_.back().size()));
this->directories_.back().push_back(dirname);
lineptr += this->directories_.back().back().size() + 1;
dirindex++;
}
}
@ -267,18 +280,21 @@ Dwarf_line_info<size, big_endian>::read_header_tables(
lineptr += strlen(filename) + 1;
uint64_t dirindex = read_unsigned_LEB_128(lineptr, &len);
if (dirindex >= directories_.size())
dirindex = 0;
lineptr += len;
if (dirindex >= this->directories_.back().size())
dirindex = 0;
int dirindexi = static_cast<int>(dirindex);
read_unsigned_LEB_128(lineptr, &len); // mod_time
lineptr += len;
read_unsigned_LEB_128(lineptr, &len); // filelength
lineptr += len;
gold_assert(fileindex == static_cast<int>(files_.size()));
files_.push_back(std::pair<int, std::string>(dirindex, filename));
gold_assert(fileindex
== static_cast<int>(this->files_.back().size()));
this->files_.back().push_back(std::make_pair(dirindexi, filename));
fileindex++;
}
}
@ -407,21 +423,21 @@ Dwarf_line_info<size, big_endian>::process_one_opcode(
case elfcpp::DW_LNE_set_address:
{
lsm->address = elfcpp::Swap<size, big_endian>::readval(start);
typename Reloc_map::const_iterator it
= reloc_map_.find(start - this->buffer_);
if (it != reloc_map_.end())
{
// value + addend.
lsm->address =
(elfcpp::Swap<size, big_endian>::readval(start)
+ it->second.second);
lsm->address += it->second.second;
lsm->shndx = it->second.first;
}
else
{
// Every set_address should have an associated
// relocation.
this->data_valid_ = false;
// If we're a normal .o file, with relocs, every
// set_address should have an associated relocation.
if (this->input_is_relobj())
this->data_valid_ = false;
}
break;
}
@ -432,17 +448,19 @@ Dwarf_line_info<size, big_endian>::process_one_opcode(
start += templen;
uint64_t dirindex = read_unsigned_LEB_128(start, &templen);
if (dirindex >= directories_.size())
dirindex = 0;
oplen += templen;
if (dirindex >= this->directories_.back().size())
dirindex = 0;
int dirindexi = static_cast<int>(dirindex);
read_unsigned_LEB_128(start, &templen); // mod_time
oplen += templen;
read_unsigned_LEB_128(start, &templen); // filelength
oplen += templen;
files_.push_back(std::pair<int, std::string>(dirindex,
this->files_.back().push_back(std::make_pair(dirindexi,
filename));
}
break;
@ -497,7 +515,8 @@ Dwarf_line_info<size, big_endian>::read_lines(unsigned const char* lineptr)
if (add_line)
{
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);
}
lineptr += oplength;
@ -516,7 +535,7 @@ Dwarf_line_info<size, big_endian>::symbol_section(
typename elfcpp::Elf_types<size>::Elf_Addr* value)
{
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);
*value = elfsym.get_st_value();
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());
}
// 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.
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)
return "";
const Offset_to_lineno_entry lookup_key = { offset, 0, 0 };
std::vector<Offset_to_lineno_entry>& offsets = this->line_number_map_[shndx];
if (offsets.empty())
const Offset_to_lineno_entry lookup_key = { offset, 0, 0, 0 };
const std::vector<Offset_to_lineno_entry>* offsets;
// 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 "";
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
// before the passed-in offset.
if (it->offset > offset)
{
if (it == offsets.begin())
if (it == offsets->begin())
return "";
--it;
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.
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(filename_pair.first < static_cast<int>(directories_.size()));
const std::string& dirname = directories_[filename_pair.first];
gold_assert(it->header_num < static_cast<int>(this->files_.size()));
gold_assert(it->file_num
< 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;
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())
{
ret += dirname;

View File

@ -90,6 +90,10 @@ class Dwarf_line_info
process_one_opcode(const unsigned char* start,
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.
// Then addr2line will always fail (rather than return possibly-
// corrupt data).
@ -123,15 +127,20 @@ class Dwarf_line_info
// This is used to figure out what section to apply a relocation to.
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.
std::vector<std::string> directories_;
// Holds the directories and files as we see them. We have an array
// 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.
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
// addend for the relocation.
// An index into the current directories_ and files_ vectors.
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,
std::pair<unsigned int,
typename elfcpp::Elf_types<size>::Elf_Swxword> >
@ -143,8 +152,9 @@ class Dwarf_line_info
struct Offset_to_lineno_entry
{
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 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.
bool operator<(const Offset_to_lineno_entry& that) const
{ return this->offset < that.offset; }