From 1a4bb014422cb45fcf192fb8c577fcfb5913dfaf Mon Sep 17 00:00:00 2001 From: Kaiyeung Date: Wed, 4 Dec 2024 16:48:17 -0800 Subject: [PATCH 1/2] i#7046: Add loadable segment program headers. --- core/unix/coredump.c | 73 ++++++++++++++++--- .../memory_dump_test.templatex | 5 +- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/core/unix/coredump.c b/core/unix/coredump.c index 2c1aa99e406..f77befa31a6 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -71,8 +71,7 @@ #define PROGRAM_HEADER_NOTE_LENGTH \ (sizeof(ELF_NOTE_HEADER_TYPE) + NOTE_OWNER_LENGTH + sizeof(struct elf_prstatus) + \ sizeof(ELF_NOTE_HEADER_TYPE) + NOTE_OWNER_LENGTH + sizeof(elf_fpregset_t)) -#define PROGRAM_HEADER_LENGTH \ - (sizeof(ELF_PROGRAM_HEADER_TYPE) + PROGRAM_HEADER_NOTE_LENGTH) +#define PROGRAM_HEADER_LOAD_ALIGNMENT 0x1000 typedef struct _section_header_info_t { app_pc vm_start; @@ -274,6 +273,23 @@ mcontext_to_user_regs(DR_PARAM_IN priv_mcontext_t *mcontext, #endif } +/* + * Return the entry point of the program. + */ +uint64_t +get_entry_point(DR_PARAM_IN priv_mcontext_t *mcontext) +{ +#ifdef DR_HOST_NOT_TARGET + return 0; +#elif defined(X86) + return (uint64_t)mcontext->rip; +#elif defined(AARCH64) + return (uint64_t)mcontext->pc; +#else +# error Unsupported architecture +#endif +} + /* * Write prstatus structure to the file. Returns true if the note is written to * the file, false otherwise. @@ -479,26 +495,62 @@ os_dump_core_internal(dcontext_t *dcontext) return false; } - if (!write_elf_header(elf_file, /*entry_point=*/0, + if (!write_elf_header(elf_file, /*entry_point=*/get_entry_point(&mc), /*section_header_table_offset*/ sizeof(ELF_HEADER_TYPE) + - PROGRAM_HEADER_LENGTH + section_data_size, + PROGRAM_HEADER_NOTE_LENGTH + + sizeof(ELF_PROGRAM_HEADER_TYPE) * section_count + + section_data_size, /*flags=*/0, - /*program_header_count=*/1, + /*program_header_count=*/section_count, /*section_header_count=*/section_count, /*section_string_table_index=*/section_count - 1)) { os_close(elf_file); return false; } + // Write program header table entry. if (!write_program_header(elf_file, PT_NOTE, /*flags=*/0, /*offset=*/sizeof(ELF_HEADER_TYPE) + - sizeof(ELF_PROGRAM_HEADER_TYPE), + sizeof(ELF_PROGRAM_HEADER_TYPE) * section_count, /*virtual_address=*/0, /*physical_address=*/0, /*file_size=*/PROGRAM_HEADER_NOTE_LENGTH, - /*memory_size=*/0, /*alignment=*/4)) { + /*memory_size=*/PROGRAM_HEADER_NOTE_LENGTH, + /*alignment=*/sizeof(ELF_HALF))) { os_close(elf_file); return false; } + // Write loadable program segment program headers. + ELF_OFF file_offset = sizeof(ELF_HEADER_TYPE) + + sizeof(ELF_PROGRAM_HEADER_TYPE) * section_count + PROGRAM_HEADER_NOTE_LENGTH; + // TODO i#7046: Merge adjacent sections with the same prot values. + for (int section_index = 0; section_index < section_count - 1; ++section_index) { + ELF_WORD flags = 0; + if (TEST(PROT_EXEC, section_header_info[section_index].prot)) { + flags |= PF_X; + } + if (TEST(PROT_WRITE, section_header_info[section_index].prot)) { + flags |= PF_W; + } + if (TEST(PROT_READ, section_header_info[section_index].prot)) { + flags |= PF_R; + } + const ELF_WORD size = section_header_info[section_index].vm_end - + section_header_info[section_index].vm_start; + if (!write_program_header( + elf_file, PT_LOAD, flags, + /*offset=*/file_offset, + /*virtual_address=*/(ELF_ADDR)section_header_info[section_index].vm_start, + /*physical_address=*/ + (ELF_ADDR)section_header_info[section_index].vm_start, + /*file_size=*/size, + /*memory_size=*/size, + /*alignment=*/PROGRAM_HEADER_LOAD_ALIGNMENT)) { + os_close(elf_file); + return false; + } + file_offset += section_header_info[section_index].vm_end - + section_header_info[section_index].vm_start; + } if (!write_prstatus_note(&mc, elf_file)) { os_close(elf_file); return false; @@ -533,8 +585,8 @@ os_dump_core_internal(dcontext_t *dcontext) return false; } // Write section headers to the core dump file. - // TODO i#7046: Handle multiple program headers. - ELF_OFF file_offset = sizeof(ELF_HEADER_TYPE) + PROGRAM_HEADER_LENGTH; + file_offset = sizeof(ELF_HEADER_TYPE) + + sizeof(ELF_PROGRAM_HEADER_TYPE) * section_count + PROGRAM_HEADER_NOTE_LENGTH; // The section_count includes the section name section, so we need to skip // it in the loop. The section name section is handled differently after @@ -544,6 +596,9 @@ os_dump_core_internal(dcontext_t *dcontext) if (TEST(PROT_WRITE, section_header_info[section_index].prot)) { flags |= SHF_WRITE; } + if (TEST(PROT_EXEC, section_header_info[section_index].prot)) { + flags |= SHF_EXECINSTR; + } if (!write_section_header( elf_file, section_header_info[section_index].name_offset, SHT_PROGBITS, flags, (ELF_ADDR)section_header_info[section_index].vm_start, file_offset, diff --git a/suite/tests/client-interface/memory_dump_test.templatex b/suite/tests/client-interface/memory_dump_test.templatex index 3171bf9510b..829163c3393 100644 --- a/suite/tests/client-interface/memory_dump_test.templatex +++ b/suite/tests/client-interface/memory_dump_test.templatex @@ -14,13 +14,13 @@ ELF Header: Type: CORE \(Core file\) Machine: .* Version: 0x1 - Entry point address: 0x0 + Entry point address: 0x[0-9a-f]+ Start of program headers: 64 \(bytes into file\) Start of section headers: [0-9]+ \(bytes into file\) Flags: 0x0 Size of this header: 64 \(bytes\) Size of program headers: 56 \(bytes\) - Number of program headers: 1 + Number of program headers: [0-9]+ Size of section headers: 64 \(bytes\) Number of section headers: [0-9]+ Section header string table index: [0-9]+ @@ -33,6 +33,7 @@ Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align NOTE.* + LOAD.* .* Displaying notes found at file offset 0x[0-9a-f]+ with length 0x[0-9a-f]+: Owner.*Data size.*Description From 41c7bf2b29fc4af1ff3d6412a301671f9eca8771 Mon Sep 17 00:00:00 2001 From: Kaiyeung Date: Thu, 5 Dec 2024 11:13:17 -0800 Subject: [PATCH 2/2] Incorporate review comments. --- core/unix/coredump.c | 45 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/core/unix/coredump.c b/core/unix/coredump.c index f77befa31a6..a14701890e8 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -71,7 +71,6 @@ #define PROGRAM_HEADER_NOTE_LENGTH \ (sizeof(ELF_NOTE_HEADER_TYPE) + NOTE_OWNER_LENGTH + sizeof(struct elf_prstatus) + \ sizeof(ELF_NOTE_HEADER_TYPE) + NOTE_OWNER_LENGTH + sizeof(elf_fpregset_t)) -#define PROGRAM_HEADER_LOAD_ALIGNMENT 0x1000 typedef struct _section_header_info_t { app_pc vm_start; @@ -273,23 +272,6 @@ mcontext_to_user_regs(DR_PARAM_IN priv_mcontext_t *mcontext, #endif } -/* - * Return the entry point of the program. - */ -uint64_t -get_entry_point(DR_PARAM_IN priv_mcontext_t *mcontext) -{ -#ifdef DR_HOST_NOT_TARGET - return 0; -#elif defined(X86) - return (uint64_t)mcontext->rip; -#elif defined(AARCH64) - return (uint64_t)mcontext->pc; -#else -# error Unsupported architecture -#endif -} - /* * Write prstatus structure to the file. Returns true if the note is written to * the file, false otherwise. @@ -494,14 +476,21 @@ os_dump_core_internal(dcontext_t *dcontext) SYSLOG_INTERNAL_ERROR("Unable to open the core dump file."); return false; } - - if (!write_elf_header(elf_file, /*entry_point=*/get_entry_point(&mc), + // We use two types of program headers. NOTE is used to store prstatus + // structure and floating point registers. LOAD is used to specify loadable + // segments. All but one section (shstrtab which stores section names) + // require a corresponding LOAD program header. The total number of + // program headers equals the number of NOTE program header (1) and the number + // of LOAD program header (number of sections minus one since shstrtab does not + // require a LOAD header). + const ELF_OFF program_header_count = section_count; + if (!write_elf_header(elf_file, /*entry_point=*/(uint64_t)mc.pc, /*section_header_table_offset*/ sizeof(ELF_HEADER_TYPE) + PROGRAM_HEADER_NOTE_LENGTH + - sizeof(ELF_PROGRAM_HEADER_TYPE) * section_count + + sizeof(ELF_PROGRAM_HEADER_TYPE) * program_header_count + section_data_size, /*flags=*/0, - /*program_header_count=*/section_count, + /*program_header_count=*/program_header_count, /*section_header_count=*/section_count, /*section_string_table_index=*/section_count - 1)) { os_close(elf_file); @@ -510,7 +499,7 @@ os_dump_core_internal(dcontext_t *dcontext) // Write program header table entry. if (!write_program_header(elf_file, PT_NOTE, /*flags=*/0, /*offset=*/sizeof(ELF_HEADER_TYPE) + - sizeof(ELF_PROGRAM_HEADER_TYPE) * section_count, + sizeof(ELF_PROGRAM_HEADER_TYPE) * program_header_count, /*virtual_address=*/0, /*physical_address=*/0, /*file_size=*/PROGRAM_HEADER_NOTE_LENGTH, @@ -521,8 +510,11 @@ os_dump_core_internal(dcontext_t *dcontext) } // Write loadable program segment program headers. ELF_OFF file_offset = sizeof(ELF_HEADER_TYPE) + - sizeof(ELF_PROGRAM_HEADER_TYPE) * section_count + PROGRAM_HEADER_NOTE_LENGTH; + sizeof(ELF_PROGRAM_HEADER_TYPE) * program_header_count + + PROGRAM_HEADER_NOTE_LENGTH; // TODO i#7046: Merge adjacent sections with the same prot values. + // The last section is shstrtab which stores the section names and it does + // not require a LOAD program header. for (int section_index = 0; section_index < section_count - 1; ++section_index) { ELF_WORD flags = 0; if (TEST(PROT_EXEC, section_header_info[section_index].prot)) { @@ -544,7 +536,7 @@ os_dump_core_internal(dcontext_t *dcontext) (ELF_ADDR)section_header_info[section_index].vm_start, /*file_size=*/size, /*memory_size=*/size, - /*alignment=*/PROGRAM_HEADER_LOAD_ALIGNMENT)) { + /*alignment=*/os_page_size())) { os_close(elf_file); return false; } @@ -586,7 +578,8 @@ os_dump_core_internal(dcontext_t *dcontext) } // Write section headers to the core dump file. file_offset = sizeof(ELF_HEADER_TYPE) + - sizeof(ELF_PROGRAM_HEADER_TYPE) * section_count + PROGRAM_HEADER_NOTE_LENGTH; + sizeof(ELF_PROGRAM_HEADER_TYPE) * program_header_count + + PROGRAM_HEADER_NOTE_LENGTH; // The section_count includes the section name section, so we need to skip // it in the loop. The section name section is handled differently after