Skip to content

Commit

Permalink
Search code-id in ".note" ELF sections (#775)
Browse files Browse the repository at this point in the history
  • Loading branch information
supervacuus authored Dec 7, 2022
1 parent d380374 commit 4b546ac
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 88 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,16 @@ jobs:
os: ubuntu-22.04
RUN_ANALYZER: code-checker,valgrind
- name: macOS (xcode llvm)
os: macOs-latest
os: macOs-11
ERROR_ON_WARNINGS: 1
SYSTEM_VERSION_COMPAT: 0
- name: macOS (xcode llvm + universal)
os: macOs-latest
os: macOs-11
ERROR_ON_WARNINGS: 1
SYSTEM_VERSION_COMPAT: 0
CMAKE_DEFINES: -DCMAKE_OSX_ARCHITECTURES=arm64;x86_64
- name: macOS (clang + asan + llvm-cov)
os: macOs-latest
os: macOs-11
CC: clang
CXX: clang++
ERROR_ON_WARNINGS: 1
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

**Fixes**

- Linux module-finder now also searches for code-id in ".note" ELF sections ([#775](https://github.com/getsentry/sentry-native/pull/775))

**Internal**:

- CI: updated github actions to upgrade deprecated node runners. ([#767](https://github.com/getsentry/sentry-native/pull/767))
Expand Down
207 changes: 122 additions & 85 deletions src/modulefinder/sentry_modulefinder_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ get_code_id_from_notes(
}

static const uint8_t *
get_code_id_from_elf(const sentry_module_t *module, size_t *size_out)
get_code_id_from_program_header(const sentry_module_t *module, size_t *size_out)
{
*size_out = 0;

Expand Down Expand Up @@ -350,68 +350,90 @@ get_code_id_from_elf(const sentry_module_t *module, size_t *size_out)
return NULL;
}

static sentry_uuid_t
get_code_id_from_text_fallback(const sentry_module_t *module)
{
const uint8_t *text = NULL;
size_t text_size = 0;

// iterate over all the program headers, for 32/64 bit separately
unsigned char e_ident[EI_NIDENT];
ENSURE(sentry__module_read_safely(e_ident, module, 0, EI_NIDENT));
if (e_ident[EI_CLASS] == ELFCLASS64) {
Elf64_Ehdr elf;
ENSURE(sentry__module_read_safely(&elf, module, 0, sizeof(Elf64_Ehdr)));
#define ELF_SECTION_ITER(INNER) \
unsigned char e_ident[EI_NIDENT]; \
ENSURE(sentry__module_read_safely(e_ident, module, 0, EI_NIDENT)); \
if (e_ident[EI_CLASS] == ELFCLASS64) { \
Elf64_Ehdr elf; \
ENSURE( \
sentry__module_read_safely(&elf, module, 0, sizeof(Elf64_Ehdr))); \
\
Elf64_Shdr strheader; \
ENSURE(sentry__module_read_safely(&strheader, module, \
elf.e_shoff + elf.e_shentsize * elf.e_shstrndx, \
sizeof(Elf64_Shdr))); \
\
for (int i = 0; i < elf.e_shnum; i++) { \
Elf64_Shdr header; \
ENSURE(sentry__module_read_safely(&header, module, \
elf.e_shoff + elf.e_shentsize * i, sizeof(Elf64_Shdr))); \
\
char name[6]; \
ENSURE(sentry__module_read_safely(name, module, \
strheader.sh_offset + header.sh_name, sizeof(name))); \
name[5] = '\0'; \
\
INNER \
} \
} else { \
Elf32_Ehdr elf; \
ENSURE( \
sentry__module_read_safely(&elf, module, 0, sizeof(Elf32_Ehdr))); \
\
Elf32_Shdr strheader; \
ENSURE(sentry__module_read_safely(&strheader, module, \
elf.e_shoff + elf.e_shentsize * elf.e_shstrndx, \
sizeof(Elf32_Shdr))); \
\
for (int i = 0; i < elf.e_shnum; i++) { \
Elf32_Shdr header; \
ENSURE(sentry__module_read_safely(&header, module, \
elf.e_shoff + elf.e_shentsize * i, sizeof(Elf32_Shdr))); \
\
char name[6]; \
ENSURE(sentry__module_read_safely(name, module, \
strheader.sh_offset + header.sh_name, sizeof(name))); \
name[5] = '\0'; \
\
INNER \
} \
}

Elf64_Shdr strheader;
ENSURE(sentry__module_read_safely(&strheader, module,
elf.e_shoff + elf.e_shentsize * elf.e_shstrndx,
sizeof(Elf64_Shdr)));
static const uint8_t *
get_code_id_from_note_section(const sentry_module_t *module, size_t *size_out)
{
*size_out = 0;

for (int i = 0; i < elf.e_shnum; i++) {
Elf64_Shdr header;
ENSURE(sentry__module_read_safely(&header, module,
elf.e_shoff + elf.e_shentsize * i, sizeof(Elf64_Shdr)));

char name[6];
ENSURE(sentry__module_read_safely(name, module,
strheader.sh_offset + header.sh_name, sizeof(name)));
name[5] = '\0';
if (header.sh_type == SHT_PROGBITS && strcmp(name, ".text") == 0) {
text = sentry__module_get_addr(
module, header.sh_offset, header.sh_size);
ENSURE(text);
text_size = header.sh_size;
break;
ELF_SECTION_ITER(
if (header.sh_type == SHT_NOTE && strcmp(name, ".note") == 0) {
void *segment_addr = sentry__module_get_addr(
module, header.sh_offset, header.sh_size);
ENSURE(segment_addr);
const uint8_t *code_id = get_code_id_from_notes(header.sh_addralign,
segment_addr,
(void *)((uintptr_t)segment_addr + header.sh_size), size_out);
if (code_id) {
return code_id;
}
}
} else {
Elf32_Ehdr elf;
ENSURE(sentry__module_read_safely(&elf, module, 0, sizeof(Elf32_Ehdr)));
})
fail:
return NULL;
}

Elf32_Shdr strheader;
ENSURE(sentry__module_read_safely(&strheader, module,
elf.e_shoff + elf.e_shentsize * elf.e_shstrndx,
sizeof(Elf32_Shdr)));
static sentry_uuid_t
get_code_id_from_text_section(const sentry_module_t *module)
{
const uint8_t *text = NULL;
size_t text_size = 0;

for (int i = 0; i < elf.e_shnum; i++) {
Elf32_Shdr header;
ENSURE(sentry__module_read_safely(&header, module,
elf.e_shoff + elf.e_shentsize * i, sizeof(Elf32_Shdr)));

char name[6];
ENSURE(sentry__module_read_safely(name, module,
strheader.sh_offset + header.sh_name, sizeof(name)));
name[5] = '\0';
if (header.sh_type == SHT_PROGBITS && strcmp(name, ".text") == 0) {
text = sentry__module_get_addr(
module, header.sh_offset, header.sh_size);
ENSURE(text);
text_size = header.sh_size;
break;
}
}
}
ELF_SECTION_ITER(
if (header.sh_type == SHT_PROGBITS && strcmp(name, ".text") == 0) {
text = sentry__module_get_addr(
module, header.sh_offset, header.sh_size);
ENSURE(text);
text_size = header.sh_size;
break;
})

sentry_uuid_t uuid = sentry_uuid_nil();

Expand All @@ -427,28 +449,43 @@ get_code_id_from_text_fallback(const sentry_module_t *module)
return sentry_uuid_nil();
}

#undef ELF_SECTION_ITER

bool
sentry__procmaps_read_ids_from_elf(
sentry_value_t value, const sentry_module_t *module)
{
// and try to get the debug id from the elf headers of the loaded
// modules
// try to get the debug id from the elf headers of the loaded modules
size_t code_id_size;
const uint8_t *code_id = get_code_id_from_elf(module, &code_id_size);
const uint8_t *code_id
= get_code_id_from_program_header(module, &code_id_size);
sentry_uuid_t uuid = sentry_uuid_nil();

if (code_id) {
sentry_value_set_by_key(value, "code_id",
sentry__value_new_hexstring(code_id, code_id_size));

memcpy(uuid.bytes, code_id, MIN(code_id_size, 16));
} else {
uuid = get_code_id_from_text_fallback(module);
// no code-id found, try the ".note.gnu.build-id" section
code_id = get_code_id_from_note_section(module, &code_id_size);
if (code_id) {
sentry_value_set_by_key(value, "code_id",
sentry__value_new_hexstring(code_id, code_id_size));

memcpy(uuid.bytes, code_id, MIN(code_id_size, 16));
} else {
// We were not able to locate the code-id, so fall back to
// hashing the first page of the ".text" (program code)
// section.
uuid = get_code_id_from_text_section(module);
}
}

// the usage of these is described here:
// https://getsentry.github.io/symbolicator/advanced/symbol-server-compatibility/#identifiers
// in particular, the debug_id is a `little-endian GUID`, so we have to do
// appropriate byte-flipping
// in particular, the debug_id is a `little-endian GUID`, so we have
// to do appropriate byte-flipping
char *uuid_bytes = uuid.bytes;
uint32_t *a = (uint32_t *)uuid_bytes;
*a = htonl(*a);
Expand Down Expand Up @@ -480,14 +517,15 @@ sentry__procmaps_module_to_value(const sentry_module_t *module)
sentry_value_set_by_key(
mod_val, "image_size", sentry_value_new_int32(module_size));

// At least on the android API-16, x86 simulator, the linker apparently
// does not load the complete file into memory. Or at least, the section
// headers which are located at the end of the file are not loaded, and
// we would be poking into invalid memory. To be safe, we mmap the
// complete file from disk, so we have the on-disk layout, and are
// independent of how the runtime linker would load or re-order any
// sections. The exception here is the linux-gate, which is not an
// actual file on disk, so we actually poke at its memory.
// At least on the android API-16, x86 simulator, the linker
// apparently does not load the complete file into memory. Or at
// least, the section headers which are located at the end of the
// file are not loaded, and we would be poking into invalid memory.
// To be safe, we mmap the complete file from disk, so we have the
// on-disk layout, and are independent of how the runtime linker
// would load or re-order any sections. The exception here is the
// linux-gate, which is not an actual file on disk, so we actually
// poke at its memory.
if (sentry__slice_eq(module->file, LINUX_GATE)) {
sentry__procmaps_read_ids_from_elf(mod_val, module);
} else {
Expand Down Expand Up @@ -617,8 +655,9 @@ load_modules(sentry_value_t modules)

uint64_t linux_vdso = get_linux_vdso();

// we have multiple memory maps per file, and we need to merge their offsets
// based on the filename. Luckily, the maps are ordered by filename, so yay
// we have multiple memory maps per file, and we need to merge their
// offsets based on the filename. Luckily, the maps are ordered by
// filename, so yay
sentry_module_t last_module;
memset(&last_module, 0, sizeof(sentry_module_t));
while (true) {
Expand Down Expand Up @@ -649,19 +688,17 @@ load_modules(sentry_value_t modules)
}

if (is_valid_elf_header((void *)(size_t)module.start)) {
// On android, we sometimes have multiple mappings for the same
// inode at the same offset, such as this, excuse the auto-format
// here:
// 737b5570d000-737b5570e000 r--p 00000000 07:70 34
// /apex/com.android.runtime/lib64/bionic/libdl.so
// 737b5570e000-737b5570f000 r-xp 00000000 07:70 34
// /apex/com.android.runtime/lib64/bionic/libdl.so
// 737b5570f000-737b55710000 r--p 00000000 07:70 34
// /apex/com.android.runtime/lib64/bionic/libdl.so
// clang-format off
// On android, we sometimes have multiple mappings for the
// same inode at the same offset, such as this:
// 737b5570d000-737b5570e000 r--p 00000000 07:70 34 /apex/com.android.runtime/lib64/bionic/libdl.so
// 737b5570e000-737b5570f000 r-xp 00000000 07:70 34 /apex/com.android.runtime/lib64/bionic/libdl.so
// 737b5570f000-737b55710000 r--p 00000000 07:70 34 /apex/com.android.runtime/lib64/bionic/libdl.so
// clang-format on

if (!is_duplicated_mapping(&last_module, &module)) {
// try to append the module based on the mappings that we have
// found so far
// try to append the module based on the mappings that
// we have found so far
try_append_module(modules, &last_module);

// start a new module based on the current mapping
Expand Down

0 comments on commit 4b546ac

Please sign in to comment.