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. // Infomation of a line.
struct LineInfo { 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. // Offset from start of the function.
// Load from stab symbol. // Load from stab symbol.
ElfW(Off) rva_to_func; ElfW(Off) rva_to_func;
@ -65,6 +69,8 @@ struct LineInfo {
uint32_t size; uint32_t size;
// Line number. // Line number.
uint32_t line_num; uint32_t line_num;
// Id of the source file for this line.
int source_id;
}; };
// Information of a function. // Information of a function.
@ -82,14 +88,16 @@ struct FuncInfo {
uint32_t size; uint32_t size;
// Total size of stack parameters. // Total size of stack parameters.
uint32_t stack_param_size; uint32_t stack_param_size;
// Is the function defined in included function? // Is there any lines included from other files?
bool is_sol; bool has_sol;
// Line information array. // Line information array.
std::vector<struct LineInfo> line_info; std::vector<struct LineInfo> line_info;
}; };
// Information of a source file. // Information of a source file.
struct SourceFileInfo { struct SourceFileInfo {
// Name string index into the string table.
uint32_t name_index;
// Name of the source file. // Name of the source file.
const char *name; const char *name;
// Starting address of the source file. // Starting address of the source file.
@ -104,17 +112,17 @@ struct SourceFileInfo {
// This is the root of all types of symbol. // This is the root of all types of symbol.
struct SymbolInfo { struct SymbolInfo {
std::vector<struct SourceFileInfo> source_file_info; std::vector<struct SourceFileInfo> source_file_info;
// The next source id for newly found source file.
int next_source_id;
}; };
// Stab section name. // Stab section name.
const char *kStabName = ".stab"; static const char *kStabName = ".stab";
// Stab str section name.
const char *kStabStrName = ".stabstr";
// Demangle using abi call. // Demangle using abi call.
// Older GCC may not support it. // Older GCC may not support it.
std::string Demangle(const char *mangled) { static std::string Demangle(const char *mangled) {
int status = 0; int status = 0;
char *demangled = abi::__cxa_demangle(mangled, NULL, NULL, &status); char *demangled = abi::__cxa_demangle(mangled, NULL, NULL, &status);
if (status == 0 && demangled != NULL) { 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. // Fix offset into virtual address by adding the mapped base into offsets.
// Make life easier when want to find something by offset. // 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(Word) base = reinterpret_cast<ElfW(Word)>(obj_base);
ElfW(Ehdr) *elf_header = static_cast<ElfW(Ehdr) *>(obj_base); ElfW(Ehdr) *elf_header = static_cast<ElfW(Ehdr) *>(obj_base);
elf_header->e_phoff += base; elf_header->e_phoff += base;
@ -138,7 +146,8 @@ void FixAddress(void *obj_base) {
} }
// Find the prefered loading address of the binary. // 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) { for (int i = 0; i < nheader; ++i) {
const ElfW(Phdr) &header = program_headers[i]; const ElfW(Phdr) &header = program_headers[i];
// For executable, it is the PT_LOAD segment with offset to zero. // 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; return 0;
} }
bool WriteFormat(int fd, const char *fmt, ...) { static bool WriteFormat(int fd, const char *fmt, ...) {
va_list list; va_list list;
char buffer[4096]; char buffer[4096];
ssize_t expected, written; ssize_t expected, written;
@ -162,14 +171,14 @@ bool WriteFormat(int fd, const char *fmt, ...) {
return expected == written; 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; return memcmp(elf_header, ELFMAG, SELFMAG) == 0;
} }
const ElfW(Shdr) *FindSectionByName(const char *name, static const ElfW(Shdr) *FindSectionByName(const char *name,
const ElfW(Shdr) *sections, const ElfW(Shdr) *sections,
const ElfW(Shdr) *strtab, const ElfW(Shdr) *strtab,
int nsection) { int nsection) {
assert(name != NULL); assert(name != NULL);
assert(sections != NULL); assert(sections != NULL);
assert(nsection > 0); assert(nsection > 0);
@ -190,9 +199,9 @@ const ElfW(Shdr) *FindSectionByName(const char *name,
// TODO(liuli): Computer the stack parameter size. // TODO(liuli): Computer the stack parameter size.
// Expect parameter variables are immediately following the N_FUN symbol. // Expect parameter variables are immediately following the N_FUN symbol.
// Will need to parse the type information to get a correct size. // Will need to parse the type information to get a correct size.
int LoadStackParamSize(struct nlist *list, static int LoadStackParamSize(struct nlist *list,
struct nlist *list_end, struct nlist *list_end,
struct FuncInfo *func_info) { struct FuncInfo *func_info) {
struct nlist *cur_list = list; struct nlist *cur_list = list;
assert(cur_list->n_type == N_FUN); assert(cur_list->n_type == N_FUN);
++cur_list; ++cur_list;
@ -205,26 +214,45 @@ int LoadStackParamSize(struct nlist *list,
return step; return step;
} }
int LoadLineInfo(struct nlist *list, static int LoadLineInfo(struct nlist *list,
struct nlist *list_end, struct nlist *list_end,
struct FuncInfo *func_info) { const struct SourceFileInfo &source_file_info,
struct FuncInfo *func_info) {
struct nlist *cur_list = list; 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 { do {
// Skip non line information. // Skip non line information.
while (cur_list < list_end && cur_list->n_type != N_SLINE) { while (cur_list < list_end && cur_list->n_type != N_SLINE) {
// Only exit when got another function, or source file. // Only exit when got another function, or source file.
if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO) if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO)
return cur_list - list; return cur_list - list;
if (cur_list->n_type == N_SOL) // N_SOL means source lines following it will be from
func_info->is_sol = true; // 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; ++cur_list;
} }
struct LineInfo line; struct LineInfo line;
while (cur_list < list_end && cur_list->n_type == N_SLINE) { 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; line.rva_to_func = cur_list->n_value;
// n_desc is a signed short // n_desc is a signed short
line.line_num = (unsigned short)cur_list->n_desc; 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); func_info->line_info.push_back(line);
++cur_list; ++cur_list;
} }
@ -233,10 +261,10 @@ int LoadLineInfo(struct nlist *list,
return cur_list - list; return cur_list - list;
} }
int LoadFuncSymbols(struct nlist *list, static int LoadFuncSymbols(struct nlist *list,
struct nlist *list_end, struct nlist *list_end,
const ElfW(Shdr) *stabstr_section, const ElfW(Shdr) *stabstr_section,
struct SourceFileInfo *source_file_info) { struct SourceFileInfo *source_file_info) {
struct nlist *cur_list = list; struct nlist *cur_list = list;
assert(cur_list->n_type == N_SO); assert(cur_list->n_type == N_SO);
++cur_list; ++cur_list;
@ -261,7 +289,10 @@ int LoadFuncSymbols(struct nlist *list,
// Stack parameter size. // Stack parameter size.
cur_list += LoadStackParamSize(cur_list, list_end, &func_info); cur_list += LoadStackParamSize(cur_list, list_end, &func_info);
// Line 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 // Functions in this module should have address bigger than the module
// startring address. // startring address.
// There maybe a lot of duplicated entry for a function in the symbol, // 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. // Comapre the address.
// The argument should have a memeber named "addr" // The argument should have a memeber named "addr"
template<class T1, class T2> template<class T1, class T2>
bool CompareAddress(T1 *a, T2 *b) { static bool CompareAddress(T1 *a, T2 *b) {
return a->addr < b->addr; 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 // 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. // should make sure the returned vector lives longer than the incoming vector.
template<class T> 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; std::vector<T *> sorted_array_ptr;
sorted_array_ptr.reserve(array->size()); sorted_array_ptr.reserve(array->size());
for (size_t i = 0; i < array->size(); ++i) 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 // 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. // table. The address should be bigger than the current function's address.
ElfW(Addr) NextAddress(std::vector<struct FuncInfo *> *sorted_functions, static ElfW(Addr) NextAddress(
std::vector<struct SourceFileInfo *> *sorted_files, std::vector<struct FuncInfo *> *sorted_functions,
const struct FuncInfo &func_info) { std::vector<struct SourceFileInfo *> *sorted_files,
const struct FuncInfo &func_info) {
std::vector<struct FuncInfo *>::iterator next_func_iter = std::vector<struct FuncInfo *>::iterator next_func_iter =
std::find_if(sorted_functions->begin(), std::find_if(sorted_functions->begin(),
sorted_functions->end(), sorted_functions->end(),
@ -331,8 +363,68 @@ ElfW(Addr) NextAddress(std::vector<struct FuncInfo *> *sorted_functions,
return 0; 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. // 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 = std::vector<struct SourceFileInfo *> sorted_files =
SortByAddress(&(symbols->source_file_info)); SortByAddress(&(symbols->source_file_info));
for (size_t i = 0; i < sorted_files.size(); ++i) { 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; return true;
} }
bool LoadSymbols(const ElfW(Shdr) *stab_section, static bool LoadSymbols(const ElfW(Shdr) *stab_section,
const ElfW(Shdr) *stabstr_section, const ElfW(Shdr) *stabstr_section,
ElfW(Addr) loading_addr, ElfW(Addr) loading_addr,
struct SymbolInfo *symbols) { struct SymbolInfo *symbols) {
if (stab_section == NULL || stabstr_section == NULL) if (stab_section == NULL || stabstr_section == NULL)
return false; return false;
struct nlist *lists = struct nlist *lists =
reinterpret_cast<struct nlist *>(stab_section->sh_offset); reinterpret_cast<struct nlist *>(stab_section->sh_offset);
int nstab = stab_section->sh_size / sizeof(struct nlist); int nstab = stab_section->sh_size / sizeof(struct nlist);
int source_id = 0;
// First pass, load all symbols from the object file. // First pass, load all symbols from the object file.
for (int i = 0; i < nstab; ) { for (int i = 0; i < nstab; ) {
int step = 1; int step = 1;
@ -428,11 +519,12 @@ bool LoadSymbols(const ElfW(Shdr) *stab_section,
if (cur_list->n_type == N_SO) { if (cur_list->n_type == N_SO) {
// FUNC <address> <length> <param_stack_size> <function> // FUNC <address> <length> <param_stack_size> <function>
struct SourceFileInfo source_file_info; 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 + source_file_info.name = reinterpret_cast<char *>(cur_list->n_un.n_strx +
stabstr_section->sh_offset); stabstr_section->sh_offset);
source_file_info.addr = cur_list->n_value; source_file_info.addr = cur_list->n_value;
if (strchr(source_file_info.name, '.')) if (strchr(source_file_info.name, '.'))
source_file_info.source_id = source_id++; source_file_info.source_id = symbols->next_source_id++;
else else
source_file_info.source_id = -1; source_file_info.source_id = -1;
step = LoadFuncSymbols(cur_list, lists + nstab, step = LoadFuncSymbols(cur_list, lists + nstab,
@ -441,11 +533,19 @@ bool LoadSymbols(const ElfW(Shdr) *stab_section,
} }
i += step; i += step;
} }
// Second pass, compute the size of functions and lines. // 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. // Translate all offsets in section headers into address.
FixAddress(elf_header); FixAddress(elf_header);
ElfW(Addr) loading_addr = GetLoadingAddress( 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); 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; const char *arch_name = NULL;
if (arch == EM_386) if (arch == EM_386)
arch_name = "x86"; 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("/"); size_t slash_pos = obj_file.find_last_of("/");
if (slash_pos != std::string::npos) if (slash_pos != std::string::npos)
filename = obj_file.substr(slash_pos + 1); 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()); id_no_dash, filename.c_str());
} }
return false; 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) { for (size_t i = 0; i < symbols.source_file_info.size(); ++i) {
if (symbols.source_file_info[i].source_id != -1) { if (symbols.source_file_info[i].source_id != -1) {
const char *name = symbols.source_file_info[i].name; const char *name = symbols.source_file_info[i].name;
@ -512,8 +614,8 @@ bool WriteSourceFileInfo(int fd, const struct SymbolInfo &symbols) {
return true; return true;
} }
bool WriteOneFunction(int fd, int source_id, static bool WriteOneFunction(int fd,
const struct FuncInfo &func_info){ const struct FuncInfo &func_info){
// Discard the ending part of the name. // Discard the ending part of the name.
std::string func_name(func_info.name); std::string func_name(func_info.name);
std::string::size_type last_colon = func_name.find_last_of(':'); 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.rva_to_base,
line_info.size, line_info.size,
line_info.line_num, line_info.line_num,
source_id)) line_info.source_id))
return false; return false;
} }
return true; return true;
@ -543,19 +645,19 @@ bool WriteOneFunction(int fd, int source_id,
return false; 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) { for (size_t i = 0; i < symbols.source_file_info.size(); ++i) {
const struct SourceFileInfo &file_info = symbols.source_file_info[i]; const struct SourceFileInfo &file_info = symbols.source_file_info[i];
for (size_t j = 0; j < file_info.func_info.size(); ++j) { for (size_t j = 0; j < file_info.func_info.size(); ++j) {
const struct FuncInfo &func_info = file_info.func_info[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 false;
} }
} }
return true; return true;
} }
bool DumpStabSymbols(int fd, const struct SymbolInfo &symbols) { static bool DumpStabSymbols(int fd, const struct SymbolInfo &symbols) {
return WriteSourceFileInfo(fd, symbols) && return WriteSourceFileInfo(fd, symbols) &&
WriteFunctionInfo(fd, symbols); WriteFunctionInfo(fd, symbols);
} }
@ -617,7 +719,7 @@ class MmapWrapper {
namespace google_breakpad { namespace google_breakpad {
bool DumpSymbols::WriteSymbolFile(const std::string &obj_file, bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
int sym_fd) { int sym_fd) {
int obj_fd = open(obj_file.c_str(), O_RDONLY); int obj_fd = open(obj_file.c_str(), O_RDONLY);
if (obj_fd < 0) if (obj_fd < 0)
return false; return false;
@ -634,6 +736,8 @@ bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
if (!IsValidElf(elf_header)) if (!IsValidElf(elf_header))
return false; return false;
struct SymbolInfo symbols; struct SymbolInfo symbols;
symbols.next_source_id = 0;
if (!LoadSymbols(elf_header, &symbols)) if (!LoadSymbols(elf_header, &symbols))
return false; return false;
// Write to symbol file. // Write to symbol file.