Fix handling of libs with a ro map and a rx map.
When the linker was changed so that it put a read-only map and
read-execute map, this code incorrectly computed the relative pc and
offset. Modify to look for the actual start map in this case.
Also, change the load_base name to load_bias.
Bug: 120613266
Test: Dumped logcat while dumping backtraces.
Change-Id: I6628694c5222ea34d63217af3d138707d4900004
diff --git a/libc/malloc_debug/MapData.cpp b/libc/malloc_debug/MapData.cpp
index 060425e..e8fbc54 100644
--- a/libc/malloc_debug/MapData.cpp
+++ b/libc/malloc_debug/MapData.cpp
@@ -33,6 +33,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <vector>
@@ -44,6 +45,7 @@
uintptr_t start;
uintptr_t end;
uintptr_t offset;
+ int flags;
char permissions[5];
int name_pos;
if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n", &start, &end,
@@ -57,18 +59,27 @@
name_len -= 1;
}
- MapEntry* entry = new MapEntry(start, end, offset, name, name_len);
- if (permissions[0] != 'r') {
- // Any unreadable map will just get a zero load base.
- entry->load_base = 0;
- entry->load_base_read = true;
+ flags = 0;
+ if (permissions[0] == 'r') {
+ flags |= PROT_READ;
+ }
+ if (permissions[2] == 'x') {
+ flags |= PROT_EXEC;
+ }
+
+ MapEntry* entry = new MapEntry(start, end, offset, name, name_len, flags);
+ if (!(flags & PROT_READ)) {
+ // Any unreadable map will just get a zero load bias.
+ entry->load_bias = 0;
+ entry->init = true;
+ entry->valid = false;
}
return entry;
}
template <typename T>
static inline bool get_val(MapEntry* entry, uintptr_t addr, T* store) {
- if (addr < entry->start || addr + sizeof(T) > entry->end) {
+ if (!(entry->flags & PROT_READ) || addr < entry->start || addr + sizeof(T) > entry->end) {
return false;
}
// Make sure the address is aligned properly.
@@ -79,9 +90,18 @@
return true;
}
-static void read_loadbase(MapEntry* entry) {
- entry->load_base = 0;
- entry->load_base_read = true;
+static bool valid_elf(MapEntry* entry) {
+ uintptr_t addr = entry->start;
+ uintptr_t end;
+ if (__builtin_add_overflow(addr, SELFMAG, &end) || end >= entry->end) {
+ return false;
+ }
+
+ return memcmp(reinterpret_cast<void*>(addr), ELFMAG, SELFMAG) == 0;
+}
+
+static void read_loadbias(MapEntry* entry) {
+ entry->load_bias = 0;
uintptr_t addr = entry->start;
ElfW(Ehdr) ehdr;
if (!get_val<ElfW(Half)>(entry, addr + offsetof(ElfW(Ehdr), e_phnum), &ehdr.e_phnum)) {
@@ -103,13 +123,24 @@
if (!get_val<ElfW(Addr)>(entry, addr + offsetof(ElfW(Phdr), p_vaddr), &phdr.p_vaddr)) {
return;
}
- entry->load_base = phdr.p_vaddr;
+ entry->load_bias = phdr.p_vaddr;
return;
}
addr += sizeof(phdr);
}
}
+static void inline init(MapEntry* entry) {
+ if (entry->init) {
+ return;
+ }
+ entry->init = true;
+ if (valid_elf(entry)) {
+ entry->valid = true;
+ read_loadbias(entry);
+ }
+}
+
bool MapData::ReadMaps() {
FILE* fp = fopen("/proc/self/maps", "re");
if (fp == nullptr) {
@@ -158,11 +189,25 @@
}
MapEntry* entry = *it;
- if (!entry->load_base_read) {
- read_loadbase(entry);
- }
- if (rel_pc) {
- *rel_pc = pc - entry->start + entry->load_base;
+ init(entry);
+
+ if (rel_pc != nullptr) {
+ // Need to check to see if this is a read-execute map and the read-only
+ // map is the previous one.
+ if (!entry->valid && it != entries_.begin()) {
+ MapEntry* prev_entry = *--it;
+ if (prev_entry->flags == PROT_READ && prev_entry->offset < entry->offset &&
+ prev_entry->name == entry->name) {
+ init(prev_entry);
+
+ if (prev_entry->valid) {
+ entry->elf_start_offset = prev_entry->offset;
+ *rel_pc = pc - entry->start + entry->offset + prev_entry->load_bias;
+ return entry;
+ }
+ }
+ }
+ *rel_pc = pc - entry->start + entry->load_bias;
}
return entry;
}