diff options
Diffstat (limited to 'src/client/linux/minidump_writer')
4 files changed, 123 insertions, 9 deletions
diff --git a/src/client/linux/minidump_writer/linux_core_dumper.cc b/src/client/linux/minidump_writer/linux_core_dumper.cc index aa6aa70f..3eab44db 100644 --- a/src/client/linux/minidump_writer/linux_core_dumper.cc +++ b/src/client/linux/minidump_writer/linux_core_dumper.cc @@ -124,7 +124,7 @@ bool LinuxCoreDumper::ThreadsResume() { } bool LinuxCoreDumper::EnumerateThreads() { - if (!mapped_core_file_.Map(core_path_)) { + if (!mapped_core_file_.Map(core_path_, 0)) { fprintf(stderr, "Could not map core dump file into memory\n"); return false; } diff --git a/src/client/linux/minidump_writer/linux_dumper.cc b/src/client/linux/minidump_writer/linux_dumper.cc index e09da916..c1e77c96 100644 --- a/src/client/linux/minidump_writer/linux_dumper.cc +++ b/src/client/linux/minidump_writer/linux_dumper.cc @@ -38,12 +38,14 @@ #include "client/linux/minidump_writer/linux_dumper.h" #include <assert.h> +#include <elf.h> #include <fcntl.h> #include <limits.h> #include <stddef.h> #include <string.h> #include "client/linux/minidump_writer/line_reader.h" +#include "common/linux/elfutils.h" #include "common/linux/file_id.h" #include "common/linux/linux_libc_support.h" #include "common/linux/memory_mapped_file.h" @@ -115,15 +117,16 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, char filename[NAME_MAX]; size_t filename_len = my_strlen(mapping.name); - assert(filename_len < NAME_MAX); - if (filename_len >= NAME_MAX) + if (filename_len >= NAME_MAX) { + assert(false); return false; + } my_memcpy(filename, mapping.name, filename_len); filename[filename_len] = '\0'; bool filename_modified = HandleDeletedFileInMapping(filename); - MemoryMappedFile mapped_file(filename); - if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)? + MemoryMappedFile mapped_file(filename, mapping.offset); + if (!mapped_file.data() || mapped_file.size() < SELFMAG) return false; bool success = @@ -136,6 +139,80 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, return success; } +namespace { +bool ElfFileSoNameFromMappedFile( + const void* elf_base, char* soname, size_t soname_size) { + if (!IsValidElf(elf_base)) { + // Not ELF + return false; + } + + const void* segment_start; + size_t segment_size; + int elf_class; + if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC, + &segment_start, &segment_size, &elf_class)) { + // No dynamic section + return false; + } + + const void* dynstr_start; + size_t dynstr_size; + if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB, + &dynstr_start, &dynstr_size, &elf_class)) { + // No dynstr section + return false; + } + + const ElfW(Dyn)* dynamic = static_cast<const ElfW(Dyn)*>(segment_start); + size_t dcount = segment_size / sizeof(ElfW(Dyn)); + for (const ElfW(Dyn)* dyn = dynamic; dyn < dynamic + dcount; ++dyn) { + if (dyn->d_tag == DT_SONAME) { + const char* dynstr = static_cast<const char*>(dynstr_start); + if (dyn->d_un.d_val >= dynstr_size) { + // Beyond the end of the dynstr section + return false; + } + const char* str = dynstr + dyn->d_un.d_val; + const size_t maxsize = dynstr_size - dyn->d_un.d_val; + my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size); + return true; + } + } + + // Did not find SONAME + return false; +} +} // namespace + +// static +bool LinuxDumper::ElfFileSoName( + const MappingInfo& mapping, char* soname, size_t soname_size) { + if (IsMappedFileOpenUnsafe(mapping)) { + // Not safe + return false; + } + + char filename[NAME_MAX]; + size_t filename_len = my_strlen(mapping.name); + if (filename_len >= NAME_MAX) { + assert(false); + // name too long + return false; + } + + my_memcpy(filename, mapping.name, filename_len); + filename[filename_len] = '\0'; + + MemoryMappedFile mapped_file(filename, mapping.offset); + if (!mapped_file.data() || mapped_file.size() < SELFMAG) { + // mmap failed + return false; + } + + return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size); +} + bool LinuxDumper::ReadAuxv() { char auxv_path[NAME_MAX]; if (!BuildProcPath(auxv_path, pid_, "auxv")) { @@ -195,6 +272,7 @@ bool LinuxDumper::EnumerateMappings() { if (*i1 == '-') { const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); if (*i2 == ' ') { + bool exec = (*(i2 + 3) == 'x'); const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); if (*i3 == ' ') { const char* name = NULL; @@ -223,6 +301,7 @@ bool LinuxDumper::EnumerateMappings() { module->start_addr = start_addr; module->size = end_addr - start_addr; module->offset = offset; + module->exec = exec; if (name != NULL) { const unsigned l = my_strlen(name); if (l < sizeof(module->name)) diff --git a/src/client/linux/minidump_writer/linux_dumper.h b/src/client/linux/minidump_writer/linux_dumper.h index 335a2ce9..ad2af018 100644 --- a/src/client/linux/minidump_writer/linux_dumper.h +++ b/src/client/linux/minidump_writer/linux_dumper.h @@ -107,6 +107,7 @@ struct MappingInfo { uintptr_t start_addr; size_t size; size_t offset; // offset into the backed file. + bool exec; // true if the mapping has the execute bit set. char name[NAME_MAX]; }; @@ -162,6 +163,13 @@ class LinuxDumper { unsigned int mapping_id, uint8_t identifier[sizeof(MDGUID)]); + // Find the shared object name (SONAME) by examining the ELF information + // for |mapping|. If the SONAME is found copy it into the passed buffer + // |soname| and return true. The size of the buffer is |soname_size|. + // The SONAME will be truncated if it is too long to fit in the buffer. + static bool ElfFileSoName( + const MappingInfo& mapping, char* soname, size_t soname_size); + uintptr_t crash_address() const { return crash_address_; } void set_crash_address(uintptr_t crash_address) { crash_address_ = crash_address; diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc index fa2ad9e1..86cec55b 100644 --- a/src/client/linux/minidump_writer/minidump_writer.cc +++ b/src/client/linux/minidump_writer/minidump_writer.cc @@ -938,7 +938,9 @@ class MinidumpWriter { static bool ShouldIncludeMapping(const MappingInfo& mapping) { if (mapping.name[0] == 0 || // only want modules with filenames. - mapping.offset || // only want to include one mapping per shared lib. + // Only want to include one mapping per shared lib. + // Avoid filtering executable mappings. + (mapping.offset != 0 && !mapping.exec) || mapping.size < 4096) { // too small to get a signature for. return false; } @@ -1029,7 +1031,8 @@ class MinidumpWriter { mod.base_of_image = mapping.start_addr; mod.size_of_image = mapping.size; - const size_t filepath_len = my_strlen(mapping.name); + const char* filepath_ptr = mapping.name; + size_t filepath_len = my_strlen(mapping.name); // Figure out file name from path const char* filename_ptr = mapping.name + filepath_len - 1; @@ -1040,7 +1043,31 @@ class MinidumpWriter { } filename_ptr++; - const size_t filename_len = mapping.name + filepath_len - filename_ptr; + size_t filename_len = mapping.name + filepath_len - filename_ptr; + + // If an executable is mapped from a non-zero offset, this is likely + // because the executable was loaded directly from inside an archive + // file. We try to find the name of the shared object (SONAME) by + // looking in the file for ELF sections. + + char soname[NAME_MAX]; + char pathname[NAME_MAX]; + if (mapping.exec && mapping.offset != 0 && + LinuxDumper::ElfFileSoName(mapping, soname, sizeof(soname))) { + filename_ptr = soname; + filename_len = my_strlen(soname); + + if (filepath_len + filename_len + 1 < NAME_MAX) { + // It doesn't have a real pathname, but tools such as stackwalk + // extract the basename, so simulating a pathname is helpful. + my_memcpy(pathname, filepath_ptr, filepath_len); + pathname[filepath_len] = '/'; + my_memcpy(pathname + filepath_len + 1, filename_ptr, filename_len); + pathname[filepath_len + filename_len + 1] = '\0'; + filepath_ptr = pathname; + filepath_len = filepath_len + filename_len + 1; + } + } uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX]; uint8_t* cv_ptr = cv_buf; @@ -1070,7 +1097,7 @@ class MinidumpWriter { mod.cv_record = cv.location(); MDLocationDescriptor ld; - if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld)) + if (!minidump_writer_.WriteString(filepath_ptr, filepath_len, &ld)) return false; mod.module_name_rva = ld.rva; return true; |