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;
}
diff --git a/libc/malloc_debug/MapData.h b/libc/malloc_debug/MapData.h
index b9b697c..5b08b90 100644
--- a/libc/malloc_debug/MapData.h
+++ b/libc/malloc_debug/MapData.h
@@ -37,17 +37,20 @@
#include <private/bionic_macros.h>
struct MapEntry {
- MapEntry(uintptr_t start, uintptr_t end, uintptr_t offset, const char* name, size_t name_len)
- : start(start), end(end), offset(offset), name(name, name_len) {}
+ MapEntry(uintptr_t start, uintptr_t end, uintptr_t offset, const char* name, size_t name_len, int flags)
+ : start(start), end(end), offset(offset), name(name, name_len), flags(flags) {}
explicit MapEntry(uintptr_t pc) : start(pc), end(pc) {}
uintptr_t start;
uintptr_t end;
uintptr_t offset;
- uintptr_t load_base;
- bool load_base_read = false;
+ uintptr_t load_bias;
+ uintptr_t elf_start_offset = 0;
std::string name;
+ int flags;
+ bool init = false;
+ bool valid = false;
};
// Ordering comparator that returns equivalence for overlapping entries
diff --git a/libc/malloc_debug/backtrace.cpp b/libc/malloc_debug/backtrace.cpp
index cd8c334..0e3a53f 100644
--- a/libc/malloc_debug/backtrace.cpp
+++ b/libc/malloc_debug/backtrace.cpp
@@ -156,8 +156,8 @@
}
char offset_buf[128];
- if (entry != nullptr && entry->offset != 0) {
- snprintf(offset_buf, sizeof(offset_buf), " (offset 0x%" PRIxPTR ")", entry->offset);
+ if (entry != nullptr && entry->elf_start_offset != 0) {
+ snprintf(offset_buf, sizeof(offset_buf), " (offset 0x%" PRIxPTR ")", entry->elf_start_offset);
} else {
offset_buf[0] = '\0';
}