Switch the Linux minidump writer to use MDCVInfoELF for CV data.

This preserves full build ids in minidumps, which are useful for
tracking down the right version of system libraries from Linux
distributions.

The default build id produced by GNU binutils' ld is a 160-bit SHA-1
hash of some parts of the binary, which is exactly 20 bytes:
https://sourceware.org/binutils/docs-2.26/ld/Options.html#index-g_t_002d_002dbuild_002did-292

The bulk of the changes here are to change the signatures of the
FileID methods to use a wasteful_vector instead of raw pointers, since
build ids can be of arbitrary length.

The previous change that added support for this in the processor code
preserved the return value of `Minidump::debug_identifier()` as the
current `GUID+age` treatment for backwards-compatibility, and exposed
the full build id from `Minidump::code_identifier()`, which was
previously stubbed out for Linux dumps. This change keeps the debug ID
in the `dump_syms` output the same to match.

R=mark@chromium.org, thestig@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1688743002 .
This commit is contained in:
Ted Mielczarek 2016-04-05 09:34:20 -04:00
parent c1848484e1
commit 6c8f80aa8b
16 changed files with 460 additions and 227 deletions

View file

@ -39,6 +39,7 @@
#include <string.h>
#include <algorithm>
#include <string>
#include "common/linux/elf_gnu_compat.h"
#include "common/linux/elfutils.h"
@ -46,8 +47,13 @@
#include "common/linux/memory_mapped_file.h"
#include "third_party/lss/linux_syscall_support.h"
using std::string;
namespace google_breakpad {
// Used in a few places for backwards-compatibility.
const size_t kMDGUIDSize = sizeof(MDGUID);
FileID::FileID(const char* path) : path_(path) {}
// ELF note name and desc are 32-bits word padded.
@ -58,7 +64,7 @@ FileID::FileID(const char* path) : path_(path) {}
template<typename ElfClass>
static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
uint8_t identifier[kMDGUIDSize]) {
wasteful_vector<uint8_t>& identifier) {
typedef typename ElfClass::Nhdr Nhdr;
const void* section_end = reinterpret_cast<const char*>(section) + length;
@ -76,21 +82,19 @@ static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
return false;
}
const char* build_id = reinterpret_cast<const char*>(note_header) +
const uint8_t* build_id = reinterpret_cast<const uint8_t*>(note_header) +
sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz);
// Copy as many bits of the build ID as will fit
// into the GUID space.
my_memset(identifier, 0, kMDGUIDSize);
memcpy(identifier, build_id,
std::min(kMDGUIDSize, (size_t)note_header->n_descsz));
identifier.insert(identifier.end(),
build_id,
build_id + note_header->n_descsz);
return true;
}
// Attempt to locate a .note.gnu.build-id section in an ELF binary
// and copy as many bytes of it as will fit into |identifier|.
static bool FindElfBuildIDNote(const void *elf_mapped_base,
uint8_t identifier[kMDGUIDSize]) {
// and copy it into |identifier|.
static bool FindElfBuildIDNote(const void* elf_mapped_base,
wasteful_vector<uint8_t>& identifier) {
void* note_section;
size_t note_size;
int elfclass;
@ -116,8 +120,10 @@ static bool FindElfBuildIDNote(const void *elf_mapped_base,
// Attempt to locate the .text section of an ELF binary and generate
// a simple hash by XORing the first page worth of bytes into |identifier|.
static bool HashElfTextSection(const void *elf_mapped_base,
uint8_t identifier[kMDGUIDSize]) {
static bool HashElfTextSection(const void* elf_mapped_base,
wasteful_vector<uint8_t>& identifier) {
identifier.resize(kMDGUIDSize);
void* text_section;
size_t text_size;
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
@ -126,7 +132,9 @@ static bool HashElfTextSection(const void *elf_mapped_base,
return false;
}
my_memset(identifier, 0, kMDGUIDSize);
// Only provide |kMDGUIDSize| bytes to keep identifiers produced by this
// function backwards-compatible.
my_memset(&identifier[0], 0, kMDGUIDSize);
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
const uint8_t* ptr_end = ptr + std::min(text_size, static_cast<size_t>(4096));
while (ptr < ptr_end) {
@ -139,7 +147,7 @@ static bool HashElfTextSection(const void *elf_mapped_base,
// static
bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
uint8_t identifier[kMDGUIDSize]) {
wasteful_vector<uint8_t>& identifier) {
// Look for a build id note first.
if (FindElfBuildIDNote(base, identifier))
return true;
@ -148,7 +156,7 @@ bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
return HashElfTextSection(base, identifier);
}
bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
bool FileID::ElfFileIdentifier(wasteful_vector<uint8_t>& identifier) {
MemoryMappedFile mapped_file(path_.c_str(), 0);
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
return false;
@ -156,13 +164,16 @@ bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
}
// This function is not ever called in an unsafe context, so it's OK
// to allocate memory and use libc.
// static
void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
char* buffer, int buffer_length) {
uint8_t identifier_swapped[kMDGUIDSize];
string FileID::ConvertIdentifierToUUIDString(
const wasteful_vector<uint8_t>& identifier) {
uint8_t identifier_swapped[kMDGUIDSize] = { 0 };
// Endian-ness swap to match dump processor expectation.
memcpy(identifier_swapped, identifier, kMDGUIDSize);
memcpy(identifier_swapped, &identifier[0],
std::min(kMDGUIDSize, identifier.size()));
uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
*data1 = htonl(*data1);
uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
@ -170,22 +181,13 @@ void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
*data3 = htons(*data3);
int buffer_idx = 0;
for (unsigned int idx = 0;
(buffer_idx < buffer_length) && (idx < kMDGUIDSize);
++idx) {
int hi = (identifier_swapped[idx] >> 4) & 0x0F;
int lo = (identifier_swapped[idx]) & 0x0F;
if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
buffer[buffer_idx++] = '-';
buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
string result;
for (unsigned int idx = 0; idx < kMDGUIDSize; ++idx) {
char buf[3];
snprintf(buf, sizeof(buf), "%02X", identifier_swapped[idx]);
result.append(buf);
}
// NULL terminate
buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0;
return result;
}
} // namespace google_breakpad