OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 // Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | |
31 | |
32 // dump_symbols.cc: implement google_breakpad::WriteSymbolFile: | |
33 // Find all the debugging info in a file and dump it as a Breakpad symbol file. | |
34 | |
35 #include <elf.h> | |
36 #include <fcntl.h> | |
37 //#include <link.h> | |
38 //#include <sys/mman.h> | |
39 #include <sys/stat.h> | |
40 //#include <unistd.h> | |
41 | |
42 #include <cassert> | |
43 #include <cerrno> | |
44 #include <cstdio> | |
45 #include <cstdlib> | |
46 #include <cstring> | |
47 #include <string> | |
48 | |
49 #include "common/dwarf/bytereader-inl.h" | |
50 #include "common/dwarf/dwarf2diehandler.h" | |
51 #include "common/dump_stabs.h" | |
52 #include "common/linux/dump_symbols.h" | |
53 #include "common/dwarf_cfi_to_module.h" | |
54 #include "common/dwarf_cu_to_module.h" | |
55 #include "common/dwarf_line_to_module.h" | |
56 #include "common/linux/file_id.h" | |
57 #include "common/module.h" | |
58 #include "common/stabs_reader.h" | |
59 | |
60 // This namespace contains helper functions. | |
61 namespace { | |
62 | |
63 using google_breakpad::DumpStabsHandler; | |
64 using google_breakpad::DwarfCFIToModule; | |
65 using google_breakpad::DwarfCUToModule; | |
66 using google_breakpad::DwarfLineToModule; | |
67 using google_breakpad::Module; | |
68 | |
69 // Fix offset into virtual address by adding the mapped base into offsets. | |
70 // Make life easier when want to find something by offset. | |
71 static void FixAddress(void *obj_base) { | |
72 ElfW(Addr) base = reinterpret_cast<ElfW(Addr)>(obj_base); | |
73 ElfW(Ehdr) *elf_header = static_cast<ElfW(Ehdr) *>(obj_base); | |
74 elf_header->e_phoff += base; | |
75 elf_header->e_shoff += base; | |
76 ElfW(Shdr) *sections = reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff); | |
77 for (int i = 0; i < elf_header->e_shnum; ++i) | |
78 sections[i].sh_offset += base; | |
79 } | |
80 | |
81 // Find the prefered loading address of the binary. | |
82 static ElfW(Addr) GetLoadingAddress(const ElfW(Phdr) *program_headers, | |
83 int nheader) { | |
84 for (int i = 0; i < nheader; ++i) { | |
85 const ElfW(Phdr) &header = program_headers[i]; | |
86 // For executable, it is the PT_LOAD segment with offset to zero. | |
87 if (header.p_type == PT_LOAD && | |
88 header.p_offset == 0) | |
89 return header.p_vaddr; | |
90 } | |
91 // For other types of ELF, return 0. | |
92 return 0; | |
93 } | |
94 | |
95 static bool IsValidElf(const ElfW(Ehdr) *elf_header) { | |
96 return memcmp(elf_header, ELFMAG, SELFMAG) == 0; | |
97 } | |
98 | |
99 static const ElfW(Shdr) *FindSectionByName(const char *name, | |
100 const ElfW(Shdr) *sections, | |
101 const ElfW(Shdr) *section_names, | |
102 int nsection) { | |
103 assert(name != NULL); | |
104 assert(sections != NULL); | |
105 assert(nsection > 0); | |
106 | |
107 int name_len = strlen(name); | |
108 if (name_len == 0) | |
109 return NULL; | |
110 | |
111 // Find the end of the section name section, to make sure that | |
112 // comparisons don't run off the end of the section. | |
113 const char *names_end = | |
114 reinterpret_cast<char*>(section_names->sh_offset + section_names->sh_size); | |
115 | |
116 for (int i = 0; i < nsection; ++i) { | |
117 const char *section_name = | |
118 reinterpret_cast<char*>(section_names->sh_offset + sections[i].sh_name); | |
119 if (names_end - section_name >= name_len + 1 && | |
120 strcmp(name, section_name) == 0) | |
121 return sections + i; | |
122 } | |
123 return NULL; | |
124 } | |
125 | |
126 static bool LoadStabs(const ElfW(Shdr) *stab_section, | |
127 const ElfW(Shdr) *stabstr_section, | |
128 Module *module) { | |
129 // A callback object to handle data from the STABS reader. | |
130 DumpStabsHandler handler(module); | |
131 // Find the addresses of the STABS data, and create a STABS reader object. | |
132 uint8_t *stabs = reinterpret_cast<uint8_t *>(stab_section->sh_offset); | |
133 uint8_t *stabstr = reinterpret_cast<uint8_t *>(stabstr_section->sh_offset); | |
134 google_breakpad::StabsReader reader(stabs, stab_section->sh_size, | |
135 stabstr, stabstr_section->sh_size, | |
136 &handler); | |
137 // Read the STABS data, and do post-processing. | |
138 if (!reader.Process()) | |
139 return false; | |
140 handler.Finalize(); | |
141 return true; | |
142 } | |
143 | |
144 // A line-to-module loader that accepts line number info parsed by | |
145 // dwarf2reader::LineInfo and populates a Module and a line vector | |
146 // with the results. | |
147 class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor { | |
148 public: | |
149 // Create a line-to-module converter using BYTE_READER. | |
150 DumperLineToModule(dwarf2reader::ByteReader *byte_reader) | |
151 : byte_reader_(byte_reader) { } | |
152 void operator()(const char *program, uint64 length, | |
153 Module *module, vector<Module::Line> *lines) { | |
154 DwarfLineToModule handler(module, lines); | |
155 dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); | |
156 parser.Start(); | |
157 } | |
158 private: | |
159 dwarf2reader::ByteReader *byte_reader_; | |
160 }; | |
161 | |
162 static bool LoadDwarf(const string &dwarf_filename, | |
163 const ElfW(Ehdr) *elf_header, | |
164 Module *module) { | |
165 // Figure out what endianness this file is. | |
166 dwarf2reader::Endianness endianness; | |
167 if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) | |
168 endianness = dwarf2reader::ENDIANNESS_LITTLE; | |
169 else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) | |
170 endianness = dwarf2reader::ENDIANNESS_BIG; | |
171 else { | |
172 fprintf(stderr, "%s: bad data encoding in ELF header: %d\n", | |
173 dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]); | |
174 return false; | |
175 } | |
176 dwarf2reader::ByteReader byte_reader(endianness); | |
177 | |
178 // Construct a context for this file. | |
179 DwarfCUToModule::FileContext file_context(dwarf_filename, module); | |
180 | |
181 // Build a map of the ELF file's sections. | |
182 const ElfW(Shdr) *sections | |
183 = reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff); | |
184 int num_sections = elf_header->e_shnum; | |
185 const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx; | |
186 for (int i = 0; i < num_sections; i++) { | |
187 const ElfW(Shdr) *section = §ions[i]; | |
188 string name = reinterpret_cast<const char *>(section_names->sh_offset | |
189 + section->sh_name); | |
190 const char *contents = reinterpret_cast<const char *>(section->sh_offset); | |
191 uint64 length = section->sh_size; | |
192 file_context.section_map[name] = std::make_pair(contents, length); | |
193 } | |
194 | |
195 // Parse all the compilation units in the .debug_info section. | |
196 DumperLineToModule line_to_module(&byte_reader); | |
197 std::pair<const char *, uint64> debug_info_section | |
198 = file_context.section_map[".debug_info"]; | |
199 // We should never have been called if the file doesn't have a | |
200 // .debug_info section. | |
201 assert(debug_info_section.first); | |
202 uint64 debug_info_length = debug_info_section.second; | |
203 for (uint64 offset = 0; offset < debug_info_length;) { | |
204 // Make a handler for the root DIE that populates MODULE with the | |
205 // data we find. | |
206 DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset); | |
207 DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); | |
208 // Make a Dwarf2Handler that drives our DIEHandler. | |
209 dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); | |
210 // Make a DWARF parser for the compilation unit at OFFSET. | |
211 dwarf2reader::CompilationUnit reader(file_context.section_map, | |
212 offset, | |
213 &byte_reader, | |
214 &die_dispatcher); | |
215 // Process the entire compilation unit; get the offset of the next. | |
216 offset += reader.Start(); | |
217 } | |
218 return true; | |
219 } | |
220 | |
221 // Fill REGISTER_NAMES with the register names appropriate to the | |
222 // machine architecture given in HEADER, indexed by the register | |
223 // numbers used in DWARF call frame information. Return true on | |
224 // success, or false if we don't recognize HEADER's machine | |
225 // architecture. | |
226 static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header, | |
227 vector<string> *register_names) | |
228 { | |
229 static const char *const i386_names[] = { | |
230 "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi", | |
231 "$eip", "$eflags", "$unused1", | |
232 "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", | |
233 "$unused2", "$unused3", | |
234 "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", | |
235 "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", | |
236 "$fcw", "$fsw", "$mxcsr", | |
237 "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5", | |
238 "$tr", "$ldtr", | |
239 NULL | |
240 }; | |
241 | |
242 static const char *const x86_64_names[] = { | |
243 "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp", | |
244 "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", | |
245 "$rip", | |
246 "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", | |
247 "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15", | |
248 "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", | |
249 "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", | |
250 "$rflags", | |
251 "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2", | |
252 "$fs.base", "$gs.base", "$unused3", "$unused4", | |
253 "$tr", "$ldtr", | |
254 "$mxcsr", "$fcw", "$fsw", | |
255 NULL | |
256 }; | |
257 | |
258 static const char *const arm_names[] = { | |
259 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", | |
260 "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", | |
261 "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", | |
262 "fps", "cpsr", | |
263 NULL | |
264 }; | |
265 | |
266 const char * const *name_table; | |
267 switch (elf_header->e_machine) { | |
268 case EM_386: name_table = i386_names; break; | |
269 case EM_ARM: name_table = arm_names; break; | |
270 case EM_X86_64: name_table = x86_64_names; break; | |
271 default: | |
272 return false; | |
273 } | |
274 | |
275 register_names->clear(); | |
276 for (int i = 0; name_table[i]; i++) | |
277 register_names->push_back(name_table[i]); | |
278 return true; | |
279 } | |
280 | |
281 static bool LoadDwarfCFI(const string &dwarf_filename, | |
282 const ElfW(Ehdr) *elf_header, | |
283 const char *section_name, | |
284 const ElfW(Shdr) *section, | |
285 bool eh_frame, | |
286 const ElfW(Shdr) *got_section, | |
287 const ElfW(Shdr) *text_section, | |
288 Module *module) { | |
289 // Find the appropriate set of register names for this file's | |
290 // architecture. | |
291 vector<string> register_names; | |
292 if (!DwarfCFIRegisterNames(elf_header, ®ister_names)) { | |
293 fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';" | |
294 " cannot convert DWARF call frame information\n", | |
295 dwarf_filename.c_str(), elf_header->e_machine); | |
296 return false; | |
297 } | |
298 | |
299 // Figure out what endianness this file is. | |
300 dwarf2reader::Endianness endianness; | |
301 if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) | |
302 endianness = dwarf2reader::ENDIANNESS_LITTLE; | |
303 else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) | |
304 endianness = dwarf2reader::ENDIANNESS_BIG; | |
305 else { | |
306 fprintf(stderr, "%s: bad data encoding in ELF header: %d\n", | |
307 dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]); | |
308 return false; | |
309 } | |
310 | |
311 // Find the call frame information and its size. | |
312 const char *cfi = reinterpret_cast<const char *>(section->sh_offset); | |
313 size_t cfi_size = section->sh_size; | |
314 | |
315 // Plug together the parser, handler, and their entourages. | |
316 DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name); | |
317 DwarfCFIToModule handler(module, register_names, &module_reporter); | |
318 dwarf2reader::ByteReader byte_reader(endianness); | |
319 // Since we're using the ElfW macro, we're not actually capable of | |
320 // processing both ELF32 and ELF64 files with the same program; that | |
321 // would take a bit more work. But this will work out well enough. | |
322 if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) | |
323 byte_reader.SetAddressSize(4); | |
324 else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) | |
325 byte_reader.SetAddressSize(8); | |
326 else { | |
327 fprintf(stderr, "%s: bad file class in ELF header: %d\n", | |
328 dwarf_filename.c_str(), elf_header->e_ident[EI_CLASS]); | |
329 return false; | |
330 } | |
331 // Provide the base addresses for .eh_frame encoded pointers, if | |
332 // possible. | |
333 byte_reader.SetCFIDataBase(section->sh_addr, cfi); | |
334 if (got_section) | |
335 byte_reader.SetDataBase(got_section->sh_addr); | |
336 if (text_section) | |
337 byte_reader.SetTextBase(text_section->sh_addr); | |
338 | |
339 dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, | |
340 section_name); | |
341 dwarf2reader::CallFrameInfo parser(cfi, cfi_size, | |
342 &byte_reader, &handler, &dwarf_reporter, | |
343 eh_frame); | |
344 parser.Start(); | |
345 return true; | |
346 } | |
347 | |
348 static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header, | |
349 Module *module) { | |
350 // Translate all offsets in section headers into address. | |
351 FixAddress(elf_header); | |
352 ElfW(Addr) loading_addr = GetLoadingAddress( | |
353 reinterpret_cast<ElfW(Phdr) *>(elf_header->e_phoff), | |
354 elf_header->e_phnum); | |
355 module->SetLoadAddress(loading_addr); | |
356 | |
357 const ElfW(Shdr) *sections = | |
358 reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff); | |
359 const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx; | |
360 bool found_debug_info_section = false; | |
361 | |
362 // Look for STABS debugging information, and load it if present. | |
363 const ElfW(Shdr) *stab_section | |
364 = FindSectionByName(".stab", sections, section_names, | |
365 elf_header->e_shnum); | |
366 if (stab_section) { | |
367 const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections; | |
368 if (stabstr_section) { | |
369 found_debug_info_section = true; | |
370 if (!LoadStabs(stab_section, stabstr_section, module)) | |
371 fprintf(stderr, "%s: \".stab\" section found, but failed to load STABS" | |
372 " debugging information\n", obj_file.c_str()); | |
373 } | |
374 } | |
375 | |
376 // Look for DWARF debugging information, and load it if present. | |
377 const ElfW(Shdr) *dwarf_section | |
378 = FindSectionByName(".debug_info", sections, section_names, | |
379 elf_header->e_shnum); | |
380 if (dwarf_section) { | |
381 found_debug_info_section = true; | |
382 if (!LoadDwarf(obj_file, elf_header, module)) | |
383 fprintf(stderr, "%s: \".debug_info\" section found, but failed to load " | |
384 "DWARF debugging information\n", obj_file.c_str()); | |
385 } | |
386 | |
387 // Dwarf Call Frame Information (CFI) is actually independent from | |
388 // the other DWARF debugging information, and can be used alone. | |
389 const ElfW(Shdr) *dwarf_cfi_section = | |
390 FindSectionByName(".debug_frame", sections, section_names, | |
391 elf_header->e_shnum); | |
392 if (dwarf_cfi_section) { | |
393 // Ignore the return value of this function; even without call frame | |
394 // information, the other debugging information could be perfectly | |
395 // useful. | |
396 LoadDwarfCFI(obj_file, elf_header, ".debug_frame", | |
397 dwarf_cfi_section, false, 0, 0, module); | |
398 } | |
399 | |
400 // Linux C++ exception handling information can also provide | |
401 // unwinding data. | |
402 const ElfW(Shdr) *eh_frame_section = | |
403 FindSectionByName(".eh_frame", sections, section_names, | |
404 elf_header->e_shnum); | |
405 if (eh_frame_section) { | |
406 // Pointers in .eh_frame data may be relative to the base addresses of | |
407 // certain sections. Provide those sections if present. | |
408 const ElfW(Shdr) *got_section = | |
409 FindSectionByName(".got", sections, section_names, elf_header->e_shnum); | |
410 const ElfW(Shdr) *text_section = | |
411 FindSectionByName(".text", sections, section_names, | |
412 elf_header->e_shnum); | |
413 // As above, ignore the return value of this function. | |
414 LoadDwarfCFI(obj_file, elf_header, ".eh_frame", | |
415 eh_frame_section, true, got_section, text_section, module); | |
416 } | |
417 | |
418 if (!found_debug_info_section) { | |
419 fprintf(stderr, "%s: file contains no debugging information" | |
420 " (no \".stab\" or \".debug_info\" sections)\n", | |
421 obj_file.c_str()); | |
422 return false; | |
423 } | |
424 return true; | |
425 } | |
426 | |
427 // | |
428 // FDWrapper | |
429 // | |
430 // Wrapper class to make sure opened file is closed. | |
431 // | |
432 class FDWrapper { | |
433 public: | |
434 explicit FDWrapper(int fd) : | |
435 fd_(fd) { | |
436 } | |
437 ~FDWrapper() { | |
438 if (fd_ != -1) | |
439 close(fd_); | |
440 } | |
441 int get() { | |
442 return fd_; | |
443 } | |
444 int release() { | |
445 int fd = fd_; | |
446 fd_ = -1; | |
447 return fd; | |
448 } | |
449 private: | |
450 int fd_; | |
451 }; | |
452 | |
453 // | |
454 // MmapWrapper | |
455 // | |
456 // Wrapper class to make sure mapped regions are unmapped. | |
457 // | |
458 class MmapWrapper { | |
459 public: | |
460 MmapWrapper(void *mapped_address, size_t mapped_size) : | |
461 base_(mapped_address), size_(mapped_size) { | |
462 } | |
463 ~MmapWrapper() { | |
464 if (base_ != NULL) { | |
465 assert(size_ > 0); | |
466 munmap(base_, size_); | |
467 } | |
468 } | |
469 void release() { | |
470 base_ = NULL; | |
471 size_ = 0; | |
472 } | |
473 | |
474 private: | |
475 void *base_; | |
476 size_t size_; | |
477 }; | |
478 | |
479 // Return the breakpad symbol file identifier for the architecture of | |
480 // ELF_HEADER. | |
481 const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) { | |
482 ElfW(Half) arch = elf_header->e_machine; | |
483 switch (arch) { | |
484 case EM_386: return "x86"; | |
485 case EM_ARM: return "arm"; | |
486 case EM_MIPS: return "mips"; | |
487 case EM_PPC64: return "ppc64"; | |
488 case EM_PPC: return "ppc"; | |
489 case EM_S390: return "s390"; | |
490 case EM_SPARC: return "sparc"; | |
491 case EM_SPARCV9: return "sparcv9"; | |
492 case EM_X86_64: return "x86_64"; | |
493 default: return NULL; | |
494 } | |
495 } | |
496 | |
497 // Format the Elf file identifier in IDENTIFIER as a UUID with the | |
498 // dashes removed. | |
499 std::string FormatIdentifier(unsigned char identifier[16]) { | |
500 char identifier_str[40]; | |
501 google_breakpad::FileID::ConvertIdentifierToString( | |
502 identifier, | |
503 identifier_str, | |
504 sizeof(identifier_str)); | |
505 std::string id_no_dash; | |
506 for (int i = 0; identifier_str[i] != '\0'; ++i) | |
507 if (identifier_str[i] != '-') | |
508 id_no_dash += identifier_str[i]; | |
509 // Add an extra "0" by the end. PDB files on Windows have an 'age' | |
510 // number appended to the end of the file identifier; this isn't | |
511 // really used or necessary on other platforms, but let's preserve | |
512 // the pattern. | |
513 id_no_dash += '0'; | |
514 return id_no_dash; | |
515 } | |
516 | |
517 // Return the non-directory portion of FILENAME: the portion after the | |
518 // last slash, or the whole filename if there are no slashes. | |
519 std::string BaseFileName(const std::string &filename) { | |
520 // Lots of copies! basename's behavior is less than ideal. | |
521 char *c_filename = strdup(filename.c_str()); | |
522 std::string base = basename(c_filename); | |
523 free(c_filename); | |
524 return base; | |
525 } | |
526 | |
527 } // namespace | |
528 | |
529 namespace google_breakpad { | |
530 | |
531 bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file) { | |
532 int obj_fd = open(obj_file.c_str(), O_RDONLY); | |
533 if (obj_fd < 0) { | |
534 fprintf(stderr, "Failed to open ELF file '%s': %s\n", | |
535 obj_file.c_str(), strerror(errno)); | |
536 return false; | |
537 } | |
538 FDWrapper obj_fd_wrapper(obj_fd); | |
539 struct stat st; | |
540 if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) { | |
541 fprintf(stderr, "Unable to fstat ELF file '%s': %s\n", | |
542 obj_file.c_str(), strerror(errno)); | |
543 return false; | |
544 } | |
545 void *obj_base = mmap(NULL, st.st_size, | |
546 PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0); | |
547 if (obj_base == MAP_FAILED) { | |
548 fprintf(stderr, "Failed to mmap ELF file '%s': %s\n", | |
549 obj_file.c_str(), strerror(errno)); | |
550 return false; | |
551 } | |
552 MmapWrapper map_wrapper(obj_base, st.st_size); | |
553 ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_base); | |
554 if (!IsValidElf(elf_header)) { | |
555 fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); | |
556 return false; | |
557 } | |
558 | |
559 unsigned char identifier[16]; | |
560 google_breakpad::FileID file_id(obj_file.c_str()); | |
561 if (!file_id.ElfFileIdentifier(identifier)) { | |
562 fprintf(stderr, "%s: unable to generate file identifier\n", | |
563 obj_file.c_str()); | |
564 return false; | |
565 } | |
566 | |
567 const char *architecture = ElfArchitecture(elf_header); | |
568 if (!architecture) { | |
569 fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", | |
570 obj_file.c_str(), elf_header->e_machine); | |
571 return false; | |
572 } | |
573 | |
574 std::string name = BaseFileName(obj_file); | |
575 std::string os = "Linux"; | |
576 std::string id = FormatIdentifier(identifier); | |
577 | |
578 Module module(name, os, architecture, id); | |
579 if (!LoadSymbols(obj_file, elf_header, &module)) | |
580 return false; | |
581 if (!module.Write(sym_file)) | |
582 return false; | |
583 | |
584 return true; | |
585 } | |
586 | |
587 } // namespace google_breakpad | |
OLD | NEW |