#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#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"
#include "common/linux/safe_readlink.h"
#include "google_breakpad/common/minidump_exception_linux.h"
#include "third_party/lss/linux_syscall_support.h"
FileID;
#if defined(__ANDROID__)
#ifndef DT_LOOS
#define DT_LOOS …
#endif
#ifndef DT_ANDROID_REL
static const int DT_ANDROID_REL = DT_LOOS + 2;
#endif
#ifndef DT_ANDROID_RELA
static const int DT_ANDROID_RELA = DT_LOOS + 4;
#endif
#endif
static const char kMappedFileUnsafePrefix[] = …;
static const char kDeletedSuffix[] = …;
inline static bool IsMappedFileOpenUnsafe(
const google_breakpad::MappingInfo& mapping) { … }
namespace google_breakpad {
namespace {
bool MappingContainsAddress(const MappingInfo& mapping, uintptr_t address) { … }
#if defined(__CHROMEOS__)
const unsigned int kHpageShift = 21;
const size_t kHpageSize = (1 << kHpageShift);
const size_t kHpageMask = (~(kHpageSize - 1));
void TryRecoverMappings(MappingInfo* curr, MappingInfo* next) {
if (curr->size == 0 || next->size == 0)
return;
if (curr->size >= kHpageSize &&
curr->exec &&
(curr->size & kHpageMask) == curr->size &&
(curr->start_addr & kHpageMask) == curr->start_addr &&
curr->name[0] == '\0' &&
next->name[0] != '\0' &&
curr->start_addr + curr->size == next->start_addr &&
curr->size == next->offset) {
my_strlcpy(curr->name, next->name, NAME_MAX);
if (next->exec) {
curr->size += next->size;
next->size = 0;
}
}
}
void TryRecoverMappings(MappingInfo* prev, MappingInfo* curr,
MappingInfo* next) {
if (prev->size == 0 || curr->size == 0 || next->size == 0)
return;
if (curr->size >= kHpageSize &&
curr->exec &&
(curr->size & kHpageMask) == curr->size &&
(curr->start_addr & kHpageMask) == curr->start_addr &&
curr->name[0] == '\0' &&
next->name[0] != '\0' &&
curr->start_addr + curr->size == next->start_addr &&
prev->start_addr + prev->size == curr->start_addr &&
my_strncmp(prev->name, next->name, NAME_MAX) == 0 &&
next->offset == prev->offset + prev->size + curr->size) {
my_strlcpy(curr->name, prev->name, NAME_MAX);
if (prev->exec) {
curr->offset = prev->offset;
curr->start_addr = prev->start_addr;
if (next->exec) {
curr->size += prev->size + next->size;
prev->size = 0;
next->size = 0;
} else {
curr->size += prev->size;
prev->size = 0;
}
} else {
curr->offset = prev->offset + prev->size;
if (next->exec) {
curr->size += next->size;
next->size = 0;
} else {
}
}
}
}
void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
size_t l = 1;
size_t r = mappings.size();
size_t next = mappings.size();
while (l < r) {
int m = (l + r) / 2;
if (mappings[m]->start_addr > mappings[0]->start_addr)
r = next = m;
else
l = m + 1;
}
size_t first_start_addr = mappings[0]->start_addr;
size_t first_end_addr = mappings[0]->start_addr + mappings[0]->size;
std::rotate(mappings.begin(), mappings.begin() + 1, mappings.begin() + next);
for (size_t i = 0; i < mappings.size() - 1; i++)
TryRecoverMappings(mappings[i], mappings[i + 1]);
for (size_t i = 0; i < mappings.size() - 2; i++)
TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]);
size_t f, e;
for (f = e = 0; e < mappings.size(); e++)
if (mappings[e]->size > 0)
mappings[f++] = mappings[e];
mappings.resize(f);
for (l = 0; l < mappings.size(); l++) {
if (mappings[l]->start_addr <= first_start_addr
&& (mappings[l]->start_addr + mappings[l]->size >= first_end_addr))
break;
}
if (l > 0) {
r = mappings.size();
std::rotate(mappings.rbegin() + r - l - 1, mappings.rbegin() + r - l,
mappings.rend());
}
}
#endif
}
#define AT_MAX …
LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix)
: … { … }
LinuxDumper::~LinuxDumper() { … }
bool LinuxDumper::Init() { … }
bool LinuxDumper::LateInit() { … }
bool
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
bool member,
unsigned int mapping_id,
wasteful_vector<uint8_t>& identifier) { … }
void LinuxDumper::SetCrashInfoFromSigInfo(const siginfo_t& siginfo) { … }
const char* LinuxDumper::GetCrashSignalString() const { … }
bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping,
char path[PATH_MAX]) const { … }
namespace {
bool ElfFileSoName(const LinuxDumper& dumper,
const MappingInfo& mapping, char* soname, size_t soname_size) { … }
}
void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
char* file_path,
size_t file_path_size,
char* file_name,
size_t file_name_size) { … }
bool LinuxDumper::ReadAuxv() { … }
bool LinuxDumper::EnumerateMappings() { … }
#if defined(__ANDROID__)
bool LinuxDumper::GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr) {
CopyFromProcess(ehdr, pid_,
reinterpret_cast<const void*>(start_addr),
sizeof(*ehdr));
return my_memcmp(&ehdr->e_ident, ELFMAG, SELFMAG) == 0;
}
void LinuxDumper::ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr,
uintptr_t start_addr,
uintptr_t* min_vaddr_ptr,
uintptr_t* dyn_vaddr_ptr,
size_t* dyn_count_ptr) {
uintptr_t phdr_addr = start_addr + ehdr->e_phoff;
const uintptr_t max_addr = UINTPTR_MAX;
uintptr_t min_vaddr = max_addr;
uintptr_t dyn_vaddr = 0;
size_t dyn_count = 0;
for (size_t i = 0; i < ehdr->e_phnum; ++i) {
ElfW(Phdr) phdr;
CopyFromProcess(&phdr, pid_,
reinterpret_cast<const void*>(phdr_addr),
sizeof(phdr));
if (phdr.p_type == PT_LOAD && phdr.p_vaddr < min_vaddr) {
min_vaddr = phdr.p_vaddr;
}
if (phdr.p_type == PT_DYNAMIC) {
dyn_vaddr = phdr.p_vaddr;
dyn_count = phdr.p_memsz / sizeof(ElfW(Dyn));
}
phdr_addr += sizeof(phdr);
}
*min_vaddr_ptr = min_vaddr;
*dyn_vaddr_ptr = dyn_vaddr;
*dyn_count_ptr = dyn_count;
}
bool LinuxDumper::HasAndroidPackedRelocations(uintptr_t load_bias,
uintptr_t dyn_vaddr,
size_t dyn_count) {
uintptr_t dyn_addr = load_bias + dyn_vaddr;
for (size_t i = 0; i < dyn_count; ++i) {
ElfW(Dyn) dyn;
CopyFromProcess(&dyn, pid_,
reinterpret_cast<const void*>(dyn_addr),
sizeof(dyn));
if (dyn.d_tag == DT_ANDROID_REL || dyn.d_tag == DT_ANDROID_RELA) {
return true;
}
dyn_addr += sizeof(dyn);
}
return false;
}
uintptr_t LinuxDumper::GetEffectiveLoadBias(ElfW(Ehdr)* ehdr,
uintptr_t start_addr) {
uintptr_t min_vaddr = 0;
uintptr_t dyn_vaddr = 0;
size_t dyn_count = 0;
ParseLoadedElfProgramHeaders(ehdr, start_addr,
&min_vaddr, &dyn_vaddr, &dyn_count);
if (min_vaddr != 0) {
const uintptr_t load_bias = start_addr - min_vaddr;
if (HasAndroidPackedRelocations(load_bias, dyn_vaddr, dyn_count)) {
return load_bias;
}
}
return start_addr;
}
void LinuxDumper::LatePostprocessMappings() {
for (size_t i = 0; i < mappings_.size(); ++i) {
MappingInfo* mapping = mappings_[i];
if (!(mapping->exec && mapping->name[0] == '/')) {
continue;
}
ElfW(Ehdr) ehdr;
if (!GetLoadedElfHeader(mapping->start_addr, &ehdr)) {
continue;
}
if (ehdr.e_type == ET_DYN) {
const uintptr_t load_bias = GetEffectiveLoadBias(&ehdr,
mapping->start_addr);
mapping->size += mapping->start_addr - load_bias;
mapping->start_addr = load_bias;
}
}
}
#endif
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
uintptr_t int_stack_pointer) { … }
void LinuxDumper::SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
uintptr_t stack_pointer,
uintptr_t sp_offset) { … }
bool LinuxDumper::StackHasPointerToMapping(const uint8_t* stack_copy,
size_t stack_len,
uintptr_t sp_offset,
const MappingInfo& mapping) { … }
const MappingInfo* LinuxDumper::FindMapping(const void* address) const { … }
const MappingInfo* LinuxDumper::FindMappingNoBias(uintptr_t address) const { … }
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { … }
}