Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search code-id in ".note" ELF sections #775

Merged
merged 5 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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