Fix issue 235, properly handling included(inlined) code.

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@236 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
luly81 2007-12-13 06:56:23 +00:00
parent b3673d13a1
commit b801cd6d0f

View file

@ -54,6 +54,10 @@ namespace {
// Infomation of a line.
struct LineInfo {
// The index into string table for the name of the source file which
// this line belongs to.
// Load from stab symbol.
uint32_t source_name_index;
// Offset from start of the function.
// Load from stab symbol.
ElfW(Off) rva_to_func;
@ -65,6 +69,8 @@ struct LineInfo {
uint32_t size;
// Line number.
uint32_t line_num;
// Id of the source file for this line.
int source_id;
};
// Information of a function.
@ -82,14 +88,16 @@ struct FuncInfo {
uint32_t size;
// Total size of stack parameters.
uint32_t stack_param_size;
// Is the function defined in included function?
bool is_sol;
// Is there any lines included from other files?
bool has_sol;
// Line information array.
std::vector<struct LineInfo> line_info;
};
// Information of a source file.
struct SourceFileInfo {
// Name string index into the string table.
uint32_t name_index;
// Name of the source file.
const char *name;
// Starting address of the source file.
@ -104,17 +112,17 @@ struct SourceFileInfo {
// This is the root of all types of symbol.
struct SymbolInfo {
std::vector<struct SourceFileInfo> source_file_info;
// The next source id for newly found source file.
int next_source_id;
};
// Stab section name.
const char *kStabName = ".stab";
// Stab str section name.
const char *kStabStrName = ".stabstr";
static const char *kStabName = ".stab";
// Demangle using abi call.
// Older GCC may not support it.
std::string Demangle(const char *mangled) {
static std::string Demangle(const char *mangled) {
int status = 0;
char *demangled = abi::__cxa_demangle(mangled, NULL, NULL, &status);
if (status == 0 && demangled != NULL) {
@ -127,7 +135,7 @@ std::string Demangle(const char *mangled) {
// Fix offset into virtual address by adding the mapped base into offsets.
// Make life easier when want to find something by offset.
void FixAddress(void *obj_base) {
static void FixAddress(void *obj_base) {
ElfW(Word) base = reinterpret_cast<ElfW(Word)>(obj_base);
ElfW(Ehdr) *elf_header = static_cast<ElfW(Ehdr) *>(obj_base);
elf_header->e_phoff += base;
@ -138,7 +146,8 @@ void FixAddress(void *obj_base) {
}
// Find the prefered loading address of the binary.
ElfW(Addr) GetLoadingAddress(const ElfW(Phdr) *program_headers, int nheader) {
static ElfW(Addr) GetLoadingAddress(const ElfW(Phdr) *program_headers,
int nheader) {
for (int i = 0; i < nheader; ++i) {
const ElfW(Phdr) &header = program_headers[i];
// For executable, it is the PT_LOAD segment with offset to zero.
@ -150,7 +159,7 @@ ElfW(Addr) GetLoadingAddress(const ElfW(Phdr) *program_headers, int nheader) {
return 0;
}
bool WriteFormat(int fd, const char *fmt, ...) {
static bool WriteFormat(int fd, const char *fmt, ...) {
va_list list;
char buffer[4096];
ssize_t expected, written;
@ -162,14 +171,14 @@ bool WriteFormat(int fd, const char *fmt, ...) {
return expected == written;
}
bool IsValidElf(const ElfW(Ehdr) *elf_header) {
static bool IsValidElf(const ElfW(Ehdr) *elf_header) {
return memcmp(elf_header, ELFMAG, SELFMAG) == 0;
}
const ElfW(Shdr) *FindSectionByName(const char *name,
const ElfW(Shdr) *sections,
const ElfW(Shdr) *strtab,
int nsection) {
static const ElfW(Shdr) *FindSectionByName(const char *name,
const ElfW(Shdr) *sections,
const ElfW(Shdr) *strtab,
int nsection) {
assert(name != NULL);
assert(sections != NULL);
assert(nsection > 0);
@ -190,9 +199,9 @@ const ElfW(Shdr) *FindSectionByName(const char *name,
// TODO(liuli): Computer the stack parameter size.
// Expect parameter variables are immediately following the N_FUN symbol.
// Will need to parse the type information to get a correct size.
int LoadStackParamSize(struct nlist *list,
struct nlist *list_end,
struct FuncInfo *func_info) {
static int LoadStackParamSize(struct nlist *list,
struct nlist *list_end,
struct FuncInfo *func_info) {
struct nlist *cur_list = list;
assert(cur_list->n_type == N_FUN);
++cur_list;
@ -205,26 +214,45 @@ int LoadStackParamSize(struct nlist *list,
return step;
}
int LoadLineInfo(struct nlist *list,
struct nlist *list_end,
struct FuncInfo *func_info) {
static int LoadLineInfo(struct nlist *list,
struct nlist *list_end,
const struct SourceFileInfo &source_file_info,
struct FuncInfo *func_info) {
struct nlist *cur_list = list;
func_info->is_sol = false;
func_info->has_sol = false;
// Records which source file the following lines belongs. Default
// to the file we are handling. This helps us handling inlined source.
// When encountering N_SOL, we will change this to the source file
// specified by N_SOL.
int current_source_name_index = source_file_info.name_index;
do {
// Skip non line information.
while (cur_list < list_end && cur_list->n_type != N_SLINE) {
// Only exit when got another function, or source file.
if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO)
return cur_list - list;
if (cur_list->n_type == N_SOL)
func_info->is_sol = true;
// N_SOL means source lines following it will be from
// another source file.
if (cur_list->n_type == N_SOL) {
func_info->has_sol = true;
if (cur_list->n_un.n_strx > 0 &&
cur_list->n_un.n_strx != current_source_name_index) {
// The following lines will be from this source file.
current_source_name_index = cur_list->n_un.n_strx;
}
}
++cur_list;
}
struct LineInfo line;
while (cur_list < list_end && cur_list->n_type == N_SLINE) {
line.source_name_index = current_source_name_index;
line.rva_to_func = cur_list->n_value;
// n_desc is a signed short
line.line_num = (unsigned short)cur_list->n_desc;
// Don't set it here.
// Will be processed in later pass.
line.source_id = -1;
func_info->line_info.push_back(line);
++cur_list;
}
@ -233,10 +261,10 @@ int LoadLineInfo(struct nlist *list,
return cur_list - list;
}
int LoadFuncSymbols(struct nlist *list,
struct nlist *list_end,
const ElfW(Shdr) *stabstr_section,
struct SourceFileInfo *source_file_info) {
static int LoadFuncSymbols(struct nlist *list,
struct nlist *list_end,
const ElfW(Shdr) *stabstr_section,
struct SourceFileInfo *source_file_info) {
struct nlist *cur_list = list;
assert(cur_list->n_type == N_SO);
++cur_list;
@ -261,7 +289,10 @@ int LoadFuncSymbols(struct nlist *list,
// Stack parameter size.
cur_list += LoadStackParamSize(cur_list, list_end, &func_info);
// Line info.
cur_list += LoadLineInfo(cur_list, list_end, &func_info);
cur_list += LoadLineInfo(cur_list,
list_end,
*source_file_info,
&func_info);
// Functions in this module should have address bigger than the module
// startring address.
// There maybe a lot of duplicated entry for a function in the symbol,
@ -277,7 +308,7 @@ int LoadFuncSymbols(struct nlist *list,
// Comapre the address.
// The argument should have a memeber named "addr"
template<class T1, class T2>
bool CompareAddress(T1 *a, T2 *b) {
static bool CompareAddress(T1 *a, T2 *b) {
return a->addr < b->addr;
}
@ -285,7 +316,7 @@ bool CompareAddress(T1 *a, T2 *b) {
// Return vector of pointers to the elements in the incoming array. So caller
// should make sure the returned vector lives longer than the incoming vector.
template<class T>
std::vector<T *> SortByAddress(std::vector<T> *array) {
static std::vector<T *> SortByAddress(std::vector<T> *array) {
std::vector<T *> sorted_array_ptr;
sorted_array_ptr.reserve(array->size());
for (size_t i = 0; i < array->size(); ++i)
@ -299,9 +330,10 @@ std::vector<T *> SortByAddress(std::vector<T> *array) {
// Find the address of the next function or source file symbol in the symbol
// table. The address should be bigger than the current function's address.
ElfW(Addr) NextAddress(std::vector<struct FuncInfo *> *sorted_functions,
std::vector<struct SourceFileInfo *> *sorted_files,
const struct FuncInfo &func_info) {
static ElfW(Addr) NextAddress(
std::vector<struct FuncInfo *> *sorted_functions,
std::vector<struct SourceFileInfo *> *sorted_files,
const struct FuncInfo &func_info) {
std::vector<struct FuncInfo *>::iterator next_func_iter =
std::find_if(sorted_functions->begin(),
sorted_functions->end(),
@ -331,8 +363,68 @@ ElfW(Addr) NextAddress(std::vector<struct FuncInfo *> *sorted_functions,
return 0;
}
static int FindFileByNameIdx(uint32_t name_index,
const std::vector<SourceFileInfo> &files) {
for (size_t i = 0; i < files.size(); ++i) {
if (files[i].name_index == name_index)
return files[i].source_id;
}
return -1;
}
// Add included file information.
// Also fix the source id for the line info.
static void AddIncludedFiles(struct SymbolInfo *symbols,
const ElfW(Shdr) *stabstr_section) {
size_t source_file_size = symbols->source_file_info.size();
for (size_t i = 0; i < source_file_size; ++i) {
struct SourceFileInfo &source_file = symbols->source_file_info[i];
for (size_t j = 0; j < source_file.func_info.size(); ++j) {
struct FuncInfo &func_info = source_file.func_info[j];
for (size_t k = 0; k < func_info.line_info.size(); ++k) {
struct LineInfo &line_info = func_info.line_info[k];
assert(line_info.source_name_index > 0);
assert(source_file.name_index > 0);
// Check if the line belongs to the source file by comparing the
// name index into string table.
if (line_info.source_name_index != source_file.name_index) {
// This line is not from the current source file, check if this
// source file has been added before.
int found_source_id = FindFileByNameIdx(line_info.source_name_index,
symbols->source_file_info);
if (found_source_id < 0) {
// Got a new included file.
// Those included files don't have address or line information.
SourceFileInfo new_file;
new_file.name_index = line_info.source_name_index;
new_file.name = reinterpret_cast<char *>(new_file.name_index +
stabstr_section->sh_offset);
new_file.addr = 0;
new_file.source_id = symbols->next_source_id++;
line_info.source_id = new_file.source_id;
symbols->source_file_info.push_back(new_file);
} else {
// The file has been added.
line_info.source_id = found_source_id;
}
} else {
// The line belongs to the file.
line_info.source_id = source_file.source_id;
}
} // for each line.
} // for each function.
} // for each source file.
}
// Compute size and rva information based on symbols loaded from stab section.
bool ComputeSizeAndRVA(ElfW(Addr) loading_addr, struct SymbolInfo *symbols) {
static bool ComputeSizeAndRVA(ElfW(Addr) loading_addr,
struct SymbolInfo *symbols) {
std::vector<struct SourceFileInfo *> sorted_files =
SortByAddress(&(symbols->source_file_info));
for (size_t i = 0; i < sorted_files.size(); ++i) {
@ -410,17 +502,16 @@ bool ComputeSizeAndRVA(ElfW(Addr) loading_addr, struct SymbolInfo *symbols) {
return true;
}
bool LoadSymbols(const ElfW(Shdr) *stab_section,
const ElfW(Shdr) *stabstr_section,
ElfW(Addr) loading_addr,
struct SymbolInfo *symbols) {
static bool LoadSymbols(const ElfW(Shdr) *stab_section,
const ElfW(Shdr) *stabstr_section,
ElfW(Addr) loading_addr,
struct SymbolInfo *symbols) {
if (stab_section == NULL || stabstr_section == NULL)
return false;
struct nlist *lists =
reinterpret_cast<struct nlist *>(stab_section->sh_offset);
int nstab = stab_section->sh_size / sizeof(struct nlist);
int source_id = 0;
// First pass, load all symbols from the object file.
for (int i = 0; i < nstab; ) {
int step = 1;
@ -428,11 +519,12 @@ bool LoadSymbols(const ElfW(Shdr) *stab_section,
if (cur_list->n_type == N_SO) {
// FUNC <address> <length> <param_stack_size> <function>
struct SourceFileInfo source_file_info;
source_file_info.name_index = cur_list->n_un.n_strx;
source_file_info.name = reinterpret_cast<char *>(cur_list->n_un.n_strx +
stabstr_section->sh_offset);
source_file_info.addr = cur_list->n_value;
if (strchr(source_file_info.name, '.'))
source_file_info.source_id = source_id++;
source_file_info.source_id = symbols->next_source_id++;
else
source_file_info.source_id = -1;
step = LoadFuncSymbols(cur_list, lists + nstab,
@ -441,11 +533,19 @@ bool LoadSymbols(const ElfW(Shdr) *stab_section,
}
i += step;
}
// Second pass, compute the size of functions and lines.
return ComputeSizeAndRVA(loading_addr, symbols);
if (ComputeSizeAndRVA(loading_addr, symbols)) {
// Third pass, check for included source code, especially for header files.
// Until now, we only have compiling unit information, but they can
// have code from include files, add them here.
AddIncludedFiles(symbols, stabstr_section);
return true;
}
return false;
}
bool LoadSymbols(ElfW(Ehdr) *elf_header, struct SymbolInfo *symbols) {
static bool LoadSymbols(ElfW(Ehdr) *elf_header, struct SymbolInfo *symbols) {
// Translate all offsets in section headers into address.
FixAddress(elf_header);
ElfW(Addr) loading_addr = GetLoadingAddress(
@ -467,7 +567,9 @@ bool LoadSymbols(ElfW(Ehdr) *elf_header, struct SymbolInfo *symbols) {
return LoadSymbols(stab_section, stabstr_section, loading_addr, symbols);
}
bool WriteModuleInfo(int fd, ElfW(Half) arch, const std::string &obj_file) {
static bool WriteModuleInfo(int fd,
ElfW(Half) arch,
const std::string &obj_file) {
const char *arch_name = NULL;
if (arch == EM_386)
arch_name = "x86";
@ -494,13 +596,13 @@ bool WriteModuleInfo(int fd, ElfW(Half) arch, const std::string &obj_file) {
size_t slash_pos = obj_file.find_last_of("/");
if (slash_pos != std::string::npos)
filename = obj_file.substr(slash_pos + 1);
return WriteFormat(fd, "MODULE linux %s %s %s\n", arch_name,
return WriteFormat(fd, "MODULE Linux %s %s %s\n", arch_name,
id_no_dash, filename.c_str());
}
return false;
}
bool WriteSourceFileInfo(int fd, const struct SymbolInfo &symbols) {
static bool WriteSourceFileInfo(int fd, const struct SymbolInfo &symbols) {
for (size_t i = 0; i < symbols.source_file_info.size(); ++i) {
if (symbols.source_file_info[i].source_id != -1) {
const char *name = symbols.source_file_info[i].name;
@ -512,8 +614,8 @@ bool WriteSourceFileInfo(int fd, const struct SymbolInfo &symbols) {
return true;
}
bool WriteOneFunction(int fd, int source_id,
const struct FuncInfo &func_info){
static bool WriteOneFunction(int fd,
const struct FuncInfo &func_info){
// Discard the ending part of the name.
std::string func_name(func_info.name);
std::string::size_type last_colon = func_name.find_last_of(':');
@ -535,7 +637,7 @@ bool WriteOneFunction(int fd, int source_id,
line_info.rva_to_base,
line_info.size,
line_info.line_num,
source_id))
line_info.source_id))
return false;
}
return true;
@ -543,19 +645,19 @@ bool WriteOneFunction(int fd, int source_id,
return false;
}
bool WriteFunctionInfo(int fd, const struct SymbolInfo &symbols) {
static bool WriteFunctionInfo(int fd, const struct SymbolInfo &symbols) {
for (size_t i = 0; i < symbols.source_file_info.size(); ++i) {
const struct SourceFileInfo &file_info = symbols.source_file_info[i];
for (size_t j = 0; j < file_info.func_info.size(); ++j) {
const struct FuncInfo &func_info = file_info.func_info[j];
if (!WriteOneFunction(fd, file_info.source_id, func_info))
if (!WriteOneFunction(fd, func_info))
return false;
}
}
return true;
}
bool DumpStabSymbols(int fd, const struct SymbolInfo &symbols) {
static bool DumpStabSymbols(int fd, const struct SymbolInfo &symbols) {
return WriteSourceFileInfo(fd, symbols) &&
WriteFunctionInfo(fd, symbols);
}
@ -617,7 +719,7 @@ class MmapWrapper {
namespace google_breakpad {
bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
int sym_fd) {
int sym_fd) {
int obj_fd = open(obj_file.c_str(), O_RDONLY);
if (obj_fd < 0)
return false;
@ -634,6 +736,8 @@ bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
if (!IsValidElf(elf_header))
return false;
struct SymbolInfo symbols;
symbols.next_source_id = 0;
if (!LoadSymbols(elf_header, &symbols))
return false;
// Write to symbol file.