OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2006, 2009, Google Inc. |
| 2 // All rights reserved. |
| 3 // |
| 4 // Redistribution and use in source and binary forms, with or without |
| 5 // modification, are permitted provided that the following conditions are |
| 6 // met: |
| 7 // |
| 8 // * Redistributions of source code must retain the above copyright |
| 9 // notice, this list of conditions and the following disclaimer. |
| 10 // * Redistributions in binary form must reproduce the above |
| 11 // copyright notice, this list of conditions and the following disclaimer |
| 12 // in the documentation and/or other materials provided with the |
| 13 // distribution. |
| 14 // * Neither the name of Google Inc. nor the names of its |
| 15 // contributors may be used to endorse or promote products derived from |
| 16 // this software without specific prior written permission. |
| 17 // |
| 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 |
| 30 #include "breakpad/linux/dump_symbols.h" |
| 31 |
| 32 #include <a.out.h> |
| 33 #include <assert.h> |
| 34 #include <cxxabi.h> |
| 35 #include <elf.h> |
| 36 #include <errno.h> |
| 37 #include <fcntl.h> |
| 38 #include <link.h> |
| 39 #include <stab.h> |
| 40 #include <string.h> |
| 41 #include <sys/mman.h> |
| 42 #include <sys/stat.h> |
| 43 #include <sys/types.h> |
| 44 #include <unistd.h> |
| 45 |
| 46 #include <algorithm> |
| 47 #include <cstdarg> |
| 48 #include <cstdio> |
| 49 #include <cstdlib> |
| 50 #include <functional> |
| 51 #include <list> |
| 52 #include <vector> |
| 53 |
| 54 #include "breakpad/linux/file_id.h" |
| 55 #include "common/linux/guid_creator.h" |
| 56 |
| 57 // This namespace contains helper functions. |
| 58 namespace { |
| 59 |
| 60 // Infomation of a line. |
| 61 struct LineInfo { |
| 62 // The index into string table for the name of the source file which |
| 63 // this line belongs to. |
| 64 // Load from stab symbol. |
| 65 uint32_t source_name_index; |
| 66 // Offset from start of the function. |
| 67 // Load from stab symbol. |
| 68 ElfW(Off) rva_to_func; |
| 69 // Offset from base of the loading binary. |
| 70 ElfW(Off) rva_to_base; |
| 71 // Size of the line. |
| 72 // It is the difference of the starting address of the line and starting |
| 73 // address of the next N_SLINE, N_FUN or N_SO. |
| 74 uint32_t size; |
| 75 // Line number. |
| 76 uint32_t line_num; |
| 77 // Id of the source file for this line. |
| 78 int source_id; |
| 79 }; |
| 80 |
| 81 typedef std::list<struct LineInfo> LineInfoList; |
| 82 |
| 83 // Information of a function. |
| 84 struct FuncInfo { |
| 85 // Name of the function. |
| 86 const char* name; |
| 87 // Offset from the base of the loading address. |
| 88 ElfW(Off) rva_to_base; |
| 89 // Virtual address of the function. |
| 90 // Load from stab symbol. |
| 91 ElfW(Addr) addr; |
| 92 // Size of the function. |
| 93 // It is the difference of the starting address of the function and starting |
| 94 // address of the next N_FUN or N_SO. |
| 95 uint32_t size; |
| 96 // Total size of stack parameters. |
| 97 uint32_t stack_param_size; |
| 98 // Is there any lines included from other files? |
| 99 bool has_sol; |
| 100 // Line information array. |
| 101 LineInfoList line_info; |
| 102 }; |
| 103 |
| 104 typedef std::list<struct FuncInfo> FuncInfoList; |
| 105 |
| 106 // Information of a source file. |
| 107 struct SourceFileInfo { |
| 108 // Name string index into the string table. |
| 109 uint32_t name_index; |
| 110 // Name of the source file. |
| 111 const char* name; |
| 112 // Starting address of the source file. |
| 113 ElfW(Addr) addr; |
| 114 // Id of the source file. |
| 115 int source_id; |
| 116 // Functions information. |
| 117 FuncInfoList func_info; |
| 118 }; |
| 119 |
| 120 typedef std::list<struct SourceFileInfo> SourceFileInfoList; |
| 121 |
| 122 // Information of a symbol table. |
| 123 // This is the root of all types of symbol. |
| 124 struct SymbolInfo { |
| 125 SourceFileInfoList source_file_info; |
| 126 |
| 127 // The next source id for newly found source file. |
| 128 int next_source_id; |
| 129 }; |
| 130 |
| 131 // Stab section name. |
| 132 static const char* kStabName = ".stab"; |
| 133 |
| 134 // Demangle using abi call. |
| 135 // Older GCC may not support it. |
| 136 static std::string Demangle(const char* mangled) { |
| 137 int status = 0; |
| 138 char* demangled = abi::__cxa_demangle(mangled, NULL, NULL, &status); |
| 139 if (status == 0 && demangled != NULL) { |
| 140 std::string str(demangled); |
| 141 free(demangled); |
| 142 return str; |
| 143 } |
| 144 return std::string(mangled); |
| 145 } |
| 146 |
| 147 // Fix offset into virtual address by adding the mapped base into offsets. |
| 148 // Make life easier when want to find something by offset. |
| 149 static void FixAddress(void* obj_base) { |
| 150 ElfW(Word) base = reinterpret_cast<ElfW(Word)>(obj_base); |
| 151 ElfW(Ehdr)* elf_header = static_cast<ElfW(Ehdr)* >(obj_base); |
| 152 elf_header->e_phoff += base; |
| 153 elf_header->e_shoff += base; |
| 154 ElfW(Shdr)* sections = reinterpret_cast<ElfW(Shdr)* >(elf_header->e_shoff); |
| 155 for (int i = 0; i < elf_header->e_shnum; ++i) |
| 156 sections[i].sh_offset += base; |
| 157 } |
| 158 |
| 159 // Find the prefered loading address of the binary. |
| 160 static ElfW(Addr) GetLoadingAddress(const ElfW(Phdr)* program_headers, |
| 161 int nheader) { |
| 162 for (int i = 0; i < nheader; ++i) { |
| 163 const ElfW(Phdr)& header = program_headers[i]; |
| 164 // For executable, it is the PT_LOAD segment with offset to zero. |
| 165 if (header.p_type == PT_LOAD && |
| 166 header.p_offset == 0) |
| 167 return header.p_vaddr; |
| 168 } |
| 169 // For other types of ELF, return 0. |
| 170 return 0; |
| 171 } |
| 172 |
| 173 static bool WriteFormat(int fd, const char* fmt, ...) { |
| 174 va_list list; |
| 175 char buffer[4096]; |
| 176 ssize_t expected, written; |
| 177 va_start(list, fmt); |
| 178 vsnprintf(buffer, sizeof(buffer), fmt, list); |
| 179 expected = strlen(buffer); |
| 180 written = write(fd, buffer, strlen(buffer)); |
| 181 va_end(list); |
| 182 return expected == written; |
| 183 } |
| 184 |
| 185 static bool IsValidElf(const ElfW(Ehdr)* elf_header) { |
| 186 return memcmp(elf_header, ELFMAG, SELFMAG) == 0; |
| 187 } |
| 188 |
| 189 static const ElfW(Shdr)* FindSectionByName(const char* name, |
| 190 const ElfW(Shdr)* sections, |
| 191 const ElfW(Shdr)* strtab, |
| 192 int nsection) { |
| 193 assert(name != NULL); |
| 194 assert(sections != NULL); |
| 195 assert(nsection > 0); |
| 196 |
| 197 int name_len = strlen(name); |
| 198 if (name_len == 0) |
| 199 return NULL; |
| 200 |
| 201 for (int i = 0; i < nsection; ++i) { |
| 202 const char* section_name = |
| 203 reinterpret_cast<char*>(strtab->sh_offset + sections[i].sh_name); |
| 204 if (!strncmp(name, section_name, name_len)) |
| 205 return sections + i; |
| 206 } |
| 207 return NULL; |
| 208 } |
| 209 |
| 210 // TODO(liuli): Computer the stack parameter size. |
| 211 // Expect parameter variables are immediately following the N_FUN symbol. |
| 212 // Will need to parse the type information to get a correct size. |
| 213 static int LoadStackParamSize(struct nlist* list, |
| 214 struct nlist* list_end, |
| 215 struct FuncInfo* func_info) { |
| 216 struct nlist* cur_list = list; |
| 217 assert(cur_list->n_type == N_FUN); |
| 218 ++cur_list; |
| 219 int step = 1; |
| 220 while (cur_list < list_end && cur_list->n_type == N_PSYM) { |
| 221 ++cur_list; |
| 222 ++step; |
| 223 } |
| 224 func_info->stack_param_size = 0; |
| 225 return step; |
| 226 } |
| 227 |
| 228 static int LoadLineInfo(struct nlist* list, |
| 229 struct nlist* list_end, |
| 230 const struct SourceFileInfo& source_file_info, |
| 231 struct FuncInfo* func_info) { |
| 232 struct nlist* cur_list = list; |
| 233 func_info->has_sol = false; |
| 234 // Records which source file the following lines belongs. Default |
| 235 // to the file we are handling. This helps us handling inlined source. |
| 236 // When encountering N_SOL, we will change this to the source file |
| 237 // specified by N_SOL. |
| 238 int current_source_name_index = source_file_info.name_index; |
| 239 do { |
| 240 // Skip non line information. |
| 241 while (cur_list < list_end && cur_list->n_type != N_SLINE) { |
| 242 // Only exit when got another function, or source file. |
| 243 if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO) |
| 244 return cur_list - list; |
| 245 // N_SOL means source lines following it will be from |
| 246 // another source file. |
| 247 if (cur_list->n_type == N_SOL) { |
| 248 func_info->has_sol = true; |
| 249 |
| 250 if (cur_list->n_un.n_strx > 0 && |
| 251 cur_list->n_un.n_strx != current_source_name_index) { |
| 252 // The following lines will be from this source file. |
| 253 current_source_name_index = cur_list->n_un.n_strx; |
| 254 } |
| 255 } |
| 256 ++cur_list; |
| 257 } |
| 258 struct LineInfo line; |
| 259 while (cur_list < list_end && cur_list->n_type == N_SLINE) { |
| 260 line.source_name_index = current_source_name_index; |
| 261 line.rva_to_func = cur_list->n_value; |
| 262 // n_desc is a signed short |
| 263 line.line_num = (unsigned short)cur_list->n_desc; |
| 264 // Don't set it here. |
| 265 // Will be processed in later pass. |
| 266 line.source_id = -1; |
| 267 func_info->line_info.push_back(line); |
| 268 ++cur_list; |
| 269 } |
| 270 } while (list < list_end); |
| 271 |
| 272 return cur_list - list; |
| 273 } |
| 274 |
| 275 static int LoadFuncSymbols(struct nlist* list, |
| 276 struct nlist* list_end, |
| 277 const ElfW(Shdr)* stabstr_section, |
| 278 struct SourceFileInfo* source_file_info) { |
| 279 struct nlist* cur_list = list; |
| 280 assert(cur_list->n_type == N_SO); |
| 281 ++cur_list; |
| 282 source_file_info->func_info.clear(); |
| 283 while (cur_list < list_end) { |
| 284 // Go until the function symbol. |
| 285 while (cur_list < list_end && cur_list->n_type != N_FUN) { |
| 286 if (cur_list->n_type == N_SO) { |
| 287 return cur_list - list; |
| 288 } |
| 289 ++cur_list; |
| 290 continue; |
| 291 } |
| 292 if (cur_list->n_type == N_FUN) { |
| 293 struct FuncInfo func_info; |
| 294 func_info.name = |
| 295 reinterpret_cast<char* >(cur_list->n_un.n_strx + |
| 296 stabstr_section->sh_offset); |
| 297 func_info.addr = cur_list->n_value; |
| 298 func_info.rva_to_base = 0; |
| 299 func_info.size = 0; |
| 300 func_info.stack_param_size = 0; |
| 301 func_info.has_sol = 0; |
| 302 |
| 303 // Stack parameter size. |
| 304 cur_list += LoadStackParamSize(cur_list, list_end, &func_info); |
| 305 // Line info. |
| 306 cur_list += LoadLineInfo(cur_list, |
| 307 list_end, |
| 308 *source_file_info, |
| 309 &func_info); |
| 310 |
| 311 // Functions in this module should have address bigger than the module |
| 312 // startring address. |
| 313 // There maybe a lot of duplicated entry for a function in the symbol, |
| 314 // only one of them can met this. |
| 315 if (func_info.addr >= source_file_info->addr) { |
| 316 source_file_info->func_info.push_back(func_info); |
| 317 } |
| 318 } |
| 319 } |
| 320 return cur_list - list; |
| 321 } |
| 322 |
| 323 // Comapre the address. |
| 324 // The argument should have a memeber named "addr" |
| 325 template<class T1, class T2> |
| 326 static bool CompareAddress(T1* a, T2* b) { |
| 327 return a->addr < b->addr; |
| 328 } |
| 329 |
| 330 // Sort the array into increasing ordered array based on the virtual address. |
| 331 // Return vector of pointers to the elements in the incoming array. So caller |
| 332 // should make sure the returned vector lives longer than the incoming vector. |
| 333 template<class Container> |
| 334 static std::vector<typename Container::value_type* > SortByAddress( |
| 335 Container* container) { |
| 336 typedef typename Container::iterator It; |
| 337 typedef typename Container::value_type T; |
| 338 std::vector<T* > sorted_array_ptr; |
| 339 sorted_array_ptr.reserve(container->size()); |
| 340 for (It it = container->begin(); it != container->end(); it++) |
| 341 sorted_array_ptr.push_back(&(*it)); |
| 342 std::sort(sorted_array_ptr.begin(), |
| 343 sorted_array_ptr.end(), |
| 344 std::ptr_fun(CompareAddress<T, T>)); |
| 345 |
| 346 return sorted_array_ptr; |
| 347 } |
| 348 |
| 349 // Find the address of the next function or source file symbol in the symbol |
| 350 // table. The address should be bigger than the current function's address. |
| 351 static ElfW(Addr) NextAddress( |
| 352 std::vector<struct FuncInfo* >* sorted_functions, |
| 353 std::vector<struct SourceFileInfo* >* sorted_files, |
| 354 const struct FuncInfo& func_info) { |
| 355 std::vector<struct FuncInfo* >::iterator next_func_iter = |
| 356 std::find_if(sorted_functions->begin(), |
| 357 sorted_functions->end(), |
| 358 std::bind1st( |
| 359 std::ptr_fun( |
| 360 CompareAddress<struct FuncInfo, |
| 361 struct FuncInfo>), |
| 362 &func_info)); |
| 363 if (next_func_iter != sorted_functions->end()) |
| 364 return (*next_func_iter)->addr; |
| 365 |
| 366 std::vector<struct SourceFileInfo* >::iterator next_file_iter = |
| 367 std::find_if(sorted_files->begin(), |
| 368 sorted_files->end(), |
| 369 std::bind1st( |
| 370 std::ptr_fun( |
| 371 CompareAddress<struct FuncInfo, |
| 372 struct SourceFileInfo>), |
| 373 &func_info)); |
| 374 if (next_file_iter != sorted_files->end()) { |
| 375 return (*next_file_iter)->addr; |
| 376 } |
| 377 return 0; |
| 378 } |
| 379 |
| 380 static int FindFileByNameIdx(uint32_t name_index, |
| 381 SourceFileInfoList& files) { |
| 382 for (SourceFileInfoList::iterator it = files.begin(); |
| 383 it != files.end(); it++) { |
| 384 if (it->name_index == name_index) |
| 385 return it->source_id; |
| 386 } |
| 387 |
| 388 return -1; |
| 389 } |
| 390 |
| 391 // Add included file information. |
| 392 // Also fix the source id for the line info. |
| 393 static void AddIncludedFiles(struct SymbolInfo* symbols, |
| 394 const ElfW(Shdr)* stabstr_section) { |
| 395 for (SourceFileInfoList::iterator source_file_it = |
| 396 symbols->source_file_info.begin(); |
| 397 source_file_it != symbols->source_file_info.end(); |
| 398 ++source_file_it) { |
| 399 struct SourceFileInfo& source_file = *source_file_it; |
| 400 |
| 401 for (FuncInfoList::iterator func_info_it = source_file.func_info.begin(); |
| 402 func_info_it != source_file.func_info.end(); |
| 403 ++func_info_it) { |
| 404 struct FuncInfo& func_info = *func_info_it; |
| 405 |
| 406 for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); |
| 407 line_info_it != func_info.line_info.end(); ++line_info_it) { |
| 408 struct LineInfo& line_info = *line_info_it; |
| 409 |
| 410 assert(line_info.source_name_index > 0); |
| 411 assert(source_file.name_index > 0); |
| 412 |
| 413 // Check if the line belongs to the source file by comparing the |
| 414 // name index into string table. |
| 415 if (line_info.source_name_index != source_file.name_index) { |
| 416 // This line is not from the current source file, check if this |
| 417 // source file has been added before. |
| 418 int found_source_id = FindFileByNameIdx(line_info.source_name_index, |
| 419 symbols->source_file_info); |
| 420 if (found_source_id < 0) { |
| 421 // Got a new included file. |
| 422 // Those included files don't have address or line information. |
| 423 SourceFileInfo new_file; |
| 424 new_file.name_index = line_info.source_name_index; |
| 425 new_file.name = |
| 426 reinterpret_cast<char* >(new_file.name_index + |
| 427 stabstr_section->sh_offset); |
| 428 new_file.addr = 0; |
| 429 new_file.source_id = symbols->next_source_id++; |
| 430 line_info.source_id = new_file.source_id; |
| 431 symbols->source_file_info.push_back(new_file); |
| 432 } else { |
| 433 // The file has been added. |
| 434 line_info.source_id = found_source_id; |
| 435 } |
| 436 } else { |
| 437 // The line belongs to the file. |
| 438 line_info.source_id = source_file.source_id; |
| 439 } |
| 440 } // for each line. |
| 441 } // for each function. |
| 442 } // for each source file. |
| 443 } |
| 444 |
| 445 // Compute size and rva information based on symbols loaded from stab section. |
| 446 static bool ComputeSizeAndRVA(ElfW(Addr) loading_addr, |
| 447 struct SymbolInfo* symbols) { |
| 448 std::vector<struct SourceFileInfo* > sorted_files = |
| 449 SortByAddress(&(symbols->source_file_info)); |
| 450 for (size_t i = 0; i < sorted_files.size(); ++i) { |
| 451 struct SourceFileInfo& source_file =* sorted_files[i]; |
| 452 std::vector<struct FuncInfo* > sorted_functions = |
| 453 SortByAddress(&(source_file.func_info)); |
| 454 for (size_t j = 0; j < sorted_functions.size(); ++j) { |
| 455 struct FuncInfo& func_info = *sorted_functions[j]; |
| 456 assert(func_info.addr >= loading_addr); |
| 457 func_info.rva_to_base = func_info.addr - loading_addr; |
| 458 func_info.size = 0; |
| 459 ElfW(Addr) next_addr = NextAddress(&sorted_functions, |
| 460 &sorted_files, |
| 461 func_info); |
| 462 // I've noticed functions with an address bigger than any other functions |
| 463 // and source files modules, this is probably the last function in the |
| 464 // module, due to limitions of Linux stab symbol, it is impossible to get |
| 465 // the exact size of this kind of function, thus we give it a default |
| 466 // very big value. This should be safe since this is the last function. |
| 467 // But it is a ugly hack..... |
| 468 // The following code can reproduce the case: |
| 469 // template<class T> |
| 470 // void Foo(T value) { |
| 471 // } |
| 472 // |
| 473 // int main(void) { |
| 474 // Foo(10); |
| 475 // Foo(std::string("hello")); |
| 476 // return 0; |
| 477 // } |
| 478 // TODO(liuli): Find a better solution. |
| 479 static const int kDefaultSize = 0x10000000; |
| 480 static int no_next_addr_count = 0; |
| 481 if (next_addr != 0) { |
| 482 func_info.size = next_addr - func_info.addr; |
| 483 } else { |
| 484 if (no_next_addr_count > 1) { |
| 485 fprintf(stderr, "Got more than one function without the following "); |
| 486 fprintf(stderr, "symbol. Ignore this function.\n"); |
| 487 fprintf(stderr, "The dumped symbol may not correct.\n"); |
| 488 assert(!"This should not happen!\n"); |
| 489 func_info.size = 0; |
| 490 continue; |
| 491 } |
| 492 |
| 493 no_next_addr_count++; |
| 494 func_info.size = kDefaultSize; |
| 495 } |
| 496 // Compute line size. |
| 497 for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); |
| 498 line_info_it != func_info.line_info.end(); line_info_it++) { |
| 499 struct LineInfo& line_info = *line_info_it; |
| 500 LineInfoList::iterator next_line_info_it = line_info_it; |
| 501 next_line_info_it++; |
| 502 line_info.size = 0; |
| 503 if (next_line_info_it != func_info.line_info.end()) { |
| 504 line_info.size = |
| 505 next_line_info_it->rva_to_func - line_info.rva_to_func; |
| 506 } else { |
| 507 // The last line in the function. |
| 508 // If we can find a function or source file symbol immediately |
| 509 // following the line, we can get the size of the line by computing |
| 510 // the difference of the next address to the starting address of this |
| 511 // line. |
| 512 // Otherwise, we need to set a default big enough value. This occurs |
| 513 // mostly because the this function is the last one in the module. |
| 514 if (next_addr != 0) { |
| 515 ElfW(Off) next_addr_offset = next_addr - func_info.addr; |
| 516 line_info.size = next_addr_offset - line_info.rva_to_func; |
| 517 } else { |
| 518 line_info.size = kDefaultSize; |
| 519 } |
| 520 } |
| 521 line_info.rva_to_base = line_info.rva_to_func + func_info.rva_to_base; |
| 522 } // for each line. |
| 523 } // for each function. |
| 524 } // for each source file. |
| 525 return true; |
| 526 } |
| 527 |
| 528 static bool LoadSymbols(const ElfW(Shdr)* stab_section, |
| 529 const ElfW(Shdr)* stabstr_section, |
| 530 ElfW(Addr) loading_addr, |
| 531 struct SymbolInfo* symbols) { |
| 532 if (stab_section == NULL || stabstr_section == NULL) |
| 533 return false; |
| 534 |
| 535 struct nlist* lists = |
| 536 reinterpret_cast<struct nlist* >(stab_section->sh_offset); |
| 537 int nstab = stab_section->sh_size / sizeof(struct nlist); |
| 538 // First pass, load all symbols from the object file. |
| 539 for (int i = 0; i < nstab;) { |
| 540 int step = 1; |
| 541 struct nlist* cur_list = lists + i; |
| 542 if (cur_list->n_type == N_SO) { |
| 543 // FUNC <address> <length> <param_stack_size> <function> |
| 544 struct SourceFileInfo source_file_info; |
| 545 source_file_info.name_index = cur_list->n_un.n_strx; |
| 546 source_file_info.name = reinterpret_cast<char* >(cur_list->n_un.n_strx + |
| 547 stabstr_section->sh_offset); |
| 548 source_file_info.addr = cur_list->n_value; |
| 549 if (strchr(source_file_info.name, '.')) |
| 550 source_file_info.source_id = symbols->next_source_id++; |
| 551 else |
| 552 source_file_info.source_id = -1; |
| 553 step = LoadFuncSymbols(cur_list, lists + nstab, |
| 554 stabstr_section, &source_file_info); |
| 555 symbols->source_file_info.push_back(source_file_info); |
| 556 } |
| 557 i += step; |
| 558 } |
| 559 |
| 560 // Second pass, compute the size of functions and lines. |
| 561 if (ComputeSizeAndRVA(loading_addr, symbols)) { |
| 562 // Third pass, check for included source code, especially for header files. |
| 563 // Until now, we only have compiling unit information, but they can |
| 564 // have code from include files, add them here. |
| 565 AddIncludedFiles(symbols, stabstr_section); |
| 566 return true; |
| 567 } |
| 568 return false; |
| 569 } |
| 570 |
| 571 static bool LoadSymbols(ElfW(Ehdr)* elf_header, struct SymbolInfo* symbols) { |
| 572 // Translate all offsets in section headers into address. |
| 573 FixAddress(elf_header); |
| 574 ElfW(Addr) loading_addr = GetLoadingAddress( |
| 575 reinterpret_cast<ElfW(Phdr)* >(elf_header->e_phoff), |
| 576 elf_header->e_phnum); |
| 577 |
| 578 const ElfW(Shdr)* sections = |
| 579 reinterpret_cast<ElfW(Shdr)* >(elf_header->e_shoff); |
| 580 const ElfW(Shdr)* strtab = sections + elf_header->e_shstrndx; |
| 581 const ElfW(Shdr)* stab_section = |
| 582 FindSectionByName(kStabName, sections, strtab, elf_header->e_shnum); |
| 583 if (stab_section == NULL) { |
| 584 fprintf(stderr, "Stab section not found.\n"); |
| 585 return false; |
| 586 } |
| 587 const ElfW(Shdr)* stabstr_section = stab_section->sh_link + sections; |
| 588 |
| 589 // Load symbols. |
| 590 return LoadSymbols(stab_section, stabstr_section, loading_addr, symbols); |
| 591 } |
| 592 |
| 593 static bool WriteModuleInfo(int fd, |
| 594 ElfW(Half) arch, |
| 595 const std::string& obj_file) { |
| 596 const char* arch_name = NULL; |
| 597 if (arch == EM_386) |
| 598 arch_name = "x86"; |
| 599 else if (arch == EM_X86_64) |
| 600 arch_name = "x86_64"; |
| 601 else |
| 602 return false; |
| 603 |
| 604 uint8_t identifier[google_breakpad::kMDGUIDSize]; |
| 605 google_breakpad::FileID file_id(obj_file.c_str()); |
| 606 if (file_id.ElfFileIdentifier(identifier)) { |
| 607 char identifier_str[40]; |
| 608 file_id.ConvertIdentifierToString(identifier, |
| 609 identifier_str, sizeof(identifier_str)); |
| 610 char id_no_dash[40]; |
| 611 int id_no_dash_len = 0; |
| 612 memset(id_no_dash, 0, sizeof(id_no_dash)); |
| 613 for (int i = 0; identifier_str[i] != '\0'; ++i) |
| 614 if (identifier_str[i] != '-') |
| 615 id_no_dash[id_no_dash_len++] = identifier_str[i]; |
| 616 // Add an extra "0" by the end. |
| 617 id_no_dash[id_no_dash_len++] = '0'; |
| 618 std::string filename = obj_file; |
| 619 size_t slash_pos = obj_file.find_last_of("/"); |
| 620 if (slash_pos != std::string::npos) |
| 621 filename = obj_file.substr(slash_pos + 1); |
| 622 return WriteFormat(fd, "MODULE Linux %s %s %s\n", arch_name, |
| 623 id_no_dash, filename.c_str()); |
| 624 } |
| 625 return false; |
| 626 } |
| 627 |
| 628 static bool WriteSourceFileInfo(int fd, const struct SymbolInfo& symbols) { |
| 629 for (SourceFileInfoList::const_iterator it = |
| 630 symbols.source_file_info.begin(); |
| 631 it != symbols.source_file_info.end(); it++) { |
| 632 if (it->source_id != -1) { |
| 633 const char* name = it->name; |
| 634 if (!WriteFormat(fd, "FILE %d %s\n", it->source_id, name)) |
| 635 return false; |
| 636 } |
| 637 } |
| 638 return true; |
| 639 } |
| 640 |
| 641 static bool WriteOneFunction(int fd, |
| 642 const struct FuncInfo& func_info) { |
| 643 // Discard the ending part of the name. |
| 644 std::string func_name(func_info.name); |
| 645 std::string::size_type last_colon = func_name.find_last_of(':'); |
| 646 if (last_colon != std::string::npos) |
| 647 func_name = func_name.substr(0, last_colon); |
| 648 func_name = Demangle(func_name.c_str()); |
| 649 |
| 650 if (func_info.size <= 0) |
| 651 return true; |
| 652 |
| 653 if (WriteFormat(fd, "FUNC %lx %lx %d %s\n", |
| 654 func_info.rva_to_base, |
| 655 func_info.size, |
| 656 func_info.stack_param_size, |
| 657 func_name.c_str())) { |
| 658 for (LineInfoList::const_iterator it = func_info.line_info.begin(); |
| 659 it != func_info.line_info.end(); it++) { |
| 660 const struct LineInfo& line_info =* it; |
| 661 if (!WriteFormat(fd, "%lx %lx %d %d\n", |
| 662 line_info.rva_to_base, |
| 663 line_info.size, |
| 664 line_info.line_num, |
| 665 line_info.source_id)) |
| 666 return false; |
| 667 } |
| 668 return true; |
| 669 } |
| 670 return false; |
| 671 } |
| 672 |
| 673 static bool WriteFunctionInfo(int fd, const struct SymbolInfo& symbols) { |
| 674 for (SourceFileInfoList::const_iterator it = |
| 675 symbols.source_file_info.begin(); |
| 676 it != symbols.source_file_info.end(); it++) { |
| 677 const struct SourceFileInfo& file_info =* it; |
| 678 for (FuncInfoList::const_iterator fiIt = file_info.func_info.begin(); |
| 679 fiIt != file_info.func_info.end(); fiIt++) { |
| 680 const struct FuncInfo& func_info =* fiIt; |
| 681 if (!WriteOneFunction(fd, func_info)) |
| 682 return false; |
| 683 } |
| 684 } |
| 685 return true; |
| 686 } |
| 687 |
| 688 static bool DumpStabSymbols(int fd, const struct SymbolInfo& symbols) { |
| 689 return WriteSourceFileInfo(fd, symbols) && |
| 690 WriteFunctionInfo(fd, symbols); |
| 691 } |
| 692 |
| 693 // |
| 694 // FDWrapper |
| 695 // |
| 696 // Wrapper class to make sure opened file is closed. |
| 697 // |
| 698 class FDWrapper { |
| 699 public: |
| 700 explicit FDWrapper(int fd) : |
| 701 fd_(fd) { |
| 702 } |
| 703 ~FDWrapper() { |
| 704 if (fd_ != -1) |
| 705 close(fd_); |
| 706 } |
| 707 int get() { |
| 708 return fd_; |
| 709 } |
| 710 int release() { |
| 711 int fd = fd_; |
| 712 fd_ = -1; |
| 713 return fd; |
| 714 } |
| 715 private: |
| 716 int fd_; |
| 717 }; |
| 718 |
| 719 // |
| 720 // MmapWrapper |
| 721 // |
| 722 // Wrapper class to make sure mapped regions are unmapped. |
| 723 // |
| 724 class MmapWrapper { |
| 725 public: |
| 726 MmapWrapper(void* mapped_address, size_t mapped_size) : |
| 727 base_(mapped_address), size_(mapped_size) { |
| 728 } |
| 729 ~MmapWrapper() { |
| 730 if (base_ != NULL) { |
| 731 assert(size_ > 0); |
| 732 munmap(base_, size_); |
| 733 } |
| 734 } |
| 735 void release() { |
| 736 base_ = NULL; |
| 737 size_ = 0; |
| 738 } |
| 739 |
| 740 private: |
| 741 void* base_; |
| 742 size_t size_; |
| 743 }; |
| 744 |
| 745 } // namespace |
| 746 |
| 747 namespace google_breakpad { |
| 748 |
| 749 bool DumpSymbols::WriteSymbolFile(const std::string& obj_file, |
| 750 int sym_fd) { |
| 751 int obj_fd = open(obj_file.c_str(), O_RDONLY); |
| 752 if (obj_fd < 0) |
| 753 return false; |
| 754 FDWrapper obj_fd_wrapper(obj_fd); |
| 755 struct stat st; |
| 756 if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) |
| 757 return false; |
| 758 void* obj_base = mmap(NULL, st.st_size, |
| 759 PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0); |
| 760 if (obj_base == MAP_FAILED) |
| 761 return false; |
| 762 MmapWrapper map_wrapper(obj_base, st.st_size); |
| 763 ElfW(Ehdr)* elf_header = reinterpret_cast<ElfW(Ehdr)* >(obj_base); |
| 764 if (!IsValidElf(elf_header)) |
| 765 return false; |
| 766 struct SymbolInfo symbols; |
| 767 symbols.next_source_id = 0; |
| 768 |
| 769 if (!LoadSymbols(elf_header, &symbols)) |
| 770 return false; |
| 771 // Write to symbol file. |
| 772 if (WriteModuleInfo(sym_fd, elf_header->e_machine, obj_file) && |
| 773 DumpStabSymbols(sym_fd, symbols)) |
| 774 return true; |
| 775 |
| 776 return false; |
| 777 } |
| 778 |
| 779 } // namespace google_breakpad |
OLD | NEW |