diff --git a/api/docs/release.dox b/api/docs/release.dox index d9d352a946d..4674a8648dd 100644 --- a/api/docs/release.dox +++ b/api/docs/release.dox @@ -126,7 +126,8 @@ clients. The changes between version \DR_VERSION and 11.0.0 include the following compatibility changes: - - No compatibility changes. + - Added X64 Linux support to dr_create_memory_dump(). This API has the same + restriction as dr_suspend_all_other_threads_ex(). Further non-compatibility-affecting changes include: - No changes yet. diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 55a12a82c84..b23ebf77a66 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -390,6 +390,7 @@ if (UNIX) set(OS_SRCS ${OS_SRCS} unix/loader_android.c) else () set(OS_SRCS ${OS_SRCS} unix/loader_linux.c) + set(OS_SRCS ${OS_SRCS} unix/coredump.c) endif () set(OS_SRCS ${OS_SRCS} unix/memquery_linux.c) set(OS_SRCS ${OS_SRCS} unix/memquery.c) diff --git a/core/lib/dr_tools.h b/core/lib/dr_tools.h index 57563a4f383..3f453407bf3 100644 --- a/core/lib/dr_tools.h +++ b/core/lib/dr_tools.h @@ -341,6 +341,13 @@ typedef enum { * \note Windows only. */ DR_MEMORY_DUMP_LDMP = 0x0001, + /** + * Memory dump in Executable and Linkable Format. This API has the same + * restrictions in where it can be called from as dr_suspend_all_other_threads_ex(). + * + * \note X64 Linux only. + */ + DR_MEMORY_DUMP_ELF = 0x0002, } dr_memory_dump_flags_t; /** Indicates the type of memory dump for dr_create_memory_dump(). */ diff --git a/core/lib/instrument.c b/core/lib/instrument.c index e7c2f9b1446..3b3590b42d3 100644 --- a/core/lib/instrument.c +++ b/core/lib/instrument.c @@ -2497,6 +2497,9 @@ dr_create_memory_dump(dr_memory_dump_spec_t *spec) #ifdef WINDOWS if (TEST(DR_MEMORY_DUMP_LDMP, spec->flags)) return os_dump_core_live(spec->label, spec->ldmp_path, spec->ldmp_path_size); +#elif defined(LINUX) && defined(X64) + if (TEST(DR_MEMORY_DUMP_ELF, spec->flags)) + return os_dump_core_live(); #endif return false; } diff --git a/core/unix/coredump.c b/core/unix/coredump.c new file mode 100644 index 00000000000..f77f0fa9849 --- /dev/null +++ b/core/unix/coredump.c @@ -0,0 +1,364 @@ +/* ********************************************************** + * Copyright (c) 2024 Google, Inc. All rights reserved. + * **********************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include +#include +#include "../globals.h" + +#include "../hashtable.h" +#include "../os_shared.h" +#include "../synch.h" +#include "dr_tools.h" +#include "elf_defines.h" +#include "memquery.h" + +#define MAX_SECTION_HEADERS 300 +#define MAX_SECTION_NAME_BUFFER_SIZE 8192 +#define SECTION_HEADER_TABLE ".shstrtab" +#define VVAR_SECTION "[vvar]" +#define VSYSCALL_SECTION "[vsyscall]" + +typedef struct _section_header_info_t { + app_pc vm_start; + app_pc vm_end; + uint prot; + ELF_ADDR name_offset; +} section_header_info_t; + +/* + * Writes an ELF header to the file. Returns true if the ELF header is written to the core + * dump file, false otherwise. + */ +static bool +write_elf_header(DR_PARAM_IN file_t elf_file, DR_PARAM_IN ELF_ADDR entry_point, + DR_PARAM_IN ELF_OFF section_header_table_offset, + DR_PARAM_IN ELF_OFF flags, DR_PARAM_IN ELF_OFF program_header_count, + DR_PARAM_IN ELF_OFF section_header_count, + DR_PARAM_IN ELF_OFF section_string_table_index) +{ + ELF_HEADER_TYPE ehdr; + ehdr.e_ident[0] = ELFMAG0; + ehdr.e_ident[1] = ELFMAG1; + ehdr.e_ident[2] = ELFMAG2; + ehdr.e_ident[3] = ELFMAG3; + ehdr.e_ident[EI_CLASS] = ELFCLASS64; + ehdr.e_ident[EI_DATA] = ELFDATA2LSB; + ehdr.e_ident[EI_VERSION] = EV_CURRENT; + ehdr.e_ident[EI_OSABI] = ELFOSABI_LINUX; + ehdr.e_ident[EI_ABIVERSION] = 0; + ehdr.e_type = ET_CORE; + ehdr.e_machine = IF_AARCHXX_ELSE(EM_AARCH64, EM_X86_64); + ehdr.e_version = EV_CURRENT; + /* This is the memory address of the entry point from where the process starts + * executing. */ + ehdr.e_entry = entry_point; + /* Points to the start of the program header table. */ + ehdr.e_phoff = sizeof(ehdr); + /* Points to the start of the section header table. */ + ehdr.e_shoff = section_header_table_offset; + ehdr.e_flags = 0; + /* Contains the size of this header */ + ehdr.e_ehsize = sizeof(ehdr); + /* Contains the size of a program header table entry. As explained below, this will + * typically be 0x20 (32 bit) or 0x38 (64 bit). */ + ehdr.e_phentsize = sizeof(ELF_PROGRAM_HEADER_TYPE); + /* Contains the number of entries in the program header table. */ + ehdr.e_phnum = program_header_count; + ehdr.e_shentsize = sizeof(ELF_SECTION_HEADER_TYPE); + /* Contains the number of entries in the section header table. */ + ehdr.e_shnum = section_header_count; + /* Contains index of the section header table entry that contains the section names. + */ + ehdr.e_shstrndx = section_string_table_index; + + return os_write(elf_file, (void *)&ehdr, sizeof(ehdr)) == sizeof(ehdr); +} + +/* + * Writes a program header to the file. Returns true if the program header is written to + * the core dump file, false otherwise. + */ +static bool +write_program_header(DR_PARAM_IN file_t elf_file, DR_PARAM_IN ELF_WORD type, + DR_PARAM_IN ELF_WORD flags, DR_PARAM_IN ELF_OFF offset, + DR_PARAM_IN ELF_ADDR virtual_address, + DR_PARAM_IN ELF_ADDR physical_address, + DR_PARAM_IN ELF_WORD file_size, DR_PARAM_IN ELF_WORD memory_size, + DR_PARAM_IN ELF_WORD alignment) +{ + ELF_PROGRAM_HEADER_TYPE phdr; + phdr.p_type = type; /* Segment type. */ + phdr.p_flags = flags; /* Segment flags. */ + phdr.p_offset = offset; /* Segment file offset. */ + phdr.p_vaddr = virtual_address; /* Segment virtual address. */ + phdr.p_paddr = physical_address; /* Segment physical address. */ + phdr.p_filesz = file_size; /* Segment size in file. */ + phdr.p_memsz = memory_size; /* Segment size in memory. */ + phdr.p_align = alignment; /* Segment alignment. */ + + return os_write(elf_file, (void *)&phdr, sizeof(phdr)) == sizeof(phdr); +} + +/* + * Write a section header to the file. Returns true if the section header is written to + * the core dump file, false otherwise. + */ +static bool +write_section_header(DR_PARAM_IN file_t elf_file, + DR_PARAM_IN ELF_WORD string_table_offset, DR_PARAM_IN ELF_WORD type, + DR_PARAM_IN ELF_WORD flags, DR_PARAM_IN ELF_ADDR virtual_address, + DR_PARAM_IN ELF_OFF offset, DR_PARAM_IN ELF_WORD section_size, + DR_PARAM_IN ELF_WORD link, DR_PARAM_IN ELF_WORD info, + DR_PARAM_IN ELF_WORD alignment, DR_PARAM_IN ELF_WORD entry_size) +{ + ELF_SECTION_HEADER_TYPE shdr; + shdr.sh_name = string_table_offset; /* Section name (string tbl index). */ + shdr.sh_type = type; /* Section type. */ + shdr.sh_flags = flags; /* Section flags. */ + shdr.sh_addr = virtual_address; /* Section virtual addr at execution. */ + shdr.sh_offset = offset; /* Section file offset. */ + shdr.sh_size = section_size; /* Section size in bytes. */ + shdr.sh_link = link; /* Link to another section. */ + shdr.sh_info = info; /* Additional section information. */ + shdr.sh_addralign = alignment; /* Section alignment. */ + shdr.sh_entsize = entry_size; /* Entry size if section holds table. */ + + return os_write(elf_file, (void *)&shdr, sizeof(shdr)) == sizeof(shdr); +} + +/* + * Writes a memory dump file in ELF format. Returns true if a core dump file is written, + * false otherwise. + */ +static bool +os_dump_core_internal(void) +{ + // Insert a null string at the beginning for sections with no comment. + char string_table[MAX_SECTION_NAME_BUFFER_SIZE]; + // Reserve the first byte for sections without a name. + string_table[0] = '\0'; + string_table[1] = '\0'; + ELF_ADDR string_table_offset = 1; + ELF_OFF section_count = 0; + ELF_OFF section_data_size = 0; + // Reserve a section for the section name string table. + section_header_info_t section_header_info[MAX_SECTION_HEADERS + 1]; + + memquery_iter_t iter; + if (!memquery_iterator_start(&iter, NULL, /*may_alloc=*/true)) { + SYSLOG_INTERNAL_ERROR("memquery_iterator_start failed."); + return false; + } + + ASSERT(d_r_get_num_threads() == 1); + // When GLOBAL_DCONTEXT is used to create a hash table, the HASHTABLE_SHARED + // flag has to be set. With the HASHTABLE_SHARED flag, a lock has to be used even + // though all other threads have been suspended. + strhash_table_t *string_htable = strhash_hash_create( + GLOBAL_DCONTEXT, /*bits=*/8, /*load_factor_percent=*/80, + /*table_flags=*/HASHTABLE_SHARED, NULL _IF_DEBUG("mmap-string-table")); + + // Iterate through memory regions to store the start, end, protection, and + // the offset to the section name string table. The first byte of the + // section name table is a null character for regions without a comment. The + // section name table is built based on the name of the memory region stored + // in iter.comment. Region names are stored in the section name table + // without duplications. An offset is used in the section header to locate + // the section name in the section name table. + while (memquery_iterator_next(&iter)) { + // Skip non-readable section. + if (iter.prot == MEMPROT_NONE || strcmp(iter.comment, VVAR_SECTION) == 0 || + strcmp(iter.comment, VSYSCALL_SECTION) == 0) { + continue; + } + ELF_ADDR offset = 0; + if (iter.comment != NULL && iter.comment[0] != '\0') { + TABLE_RWLOCK(string_htable, write, lock); + offset = (ELF_ADDR)strhash_hash_lookup(GLOBAL_DCONTEXT, string_htable, + iter.comment); + if (offset == 0) { + strhash_hash_add(GLOBAL_DCONTEXT, string_htable, iter.comment, + (void *)string_table_offset); + const size_t comment_len = d_r_strlen(iter.comment); + if (comment_len + string_table_offset > MAX_SECTION_NAME_BUFFER_SIZE) { + SYSLOG_INTERNAL_ERROR("Section name table is too small to store " + "all the section names."); + return false; + } + offset = string_table_offset; + d_r_strncpy(&string_table[string_table_offset], iter.comment, + comment_len); + string_table_offset += comment_len + 1; + string_table[string_table_offset - 1] = '\0'; + } + TABLE_RWLOCK(string_htable, write, unlock); + } + section_header_info[section_count].vm_start = iter.vm_start; + section_header_info[section_count].vm_end = iter.vm_end; + section_header_info[section_count].prot = iter.prot; + section_header_info[section_count].name_offset = offset; + section_data_size += iter.vm_end - iter.vm_start; + ++section_count; + if (section_count > MAX_SECTION_HEADERS) { + SYSLOG_INTERNAL_ERROR("Too many section headers."); + return false; + } + } + strhash_hash_destroy(GLOBAL_DCONTEXT, string_htable); + memquery_iterator_stop(&iter); + + // Add the string table section. Append the section name ".shstrtab" to the + // section name table. + const size_t section_header_table_len = d_r_strlen(SECTION_HEADER_TABLE) + 1; + d_r_strncpy(&string_table[string_table_offset], SECTION_HEADER_TABLE, + section_header_table_len); + string_table_offset += section_header_table_len; + ++section_count; + section_data_size += string_table_offset; + + file_t elf_file; + char dump_core_file_name[MAXIMUM_PATH]; + if (!get_unique_logfile(".elf", dump_core_file_name, sizeof(dump_core_file_name), + false, &elf_file) || + elf_file == INVALID_FILE) { + SYSLOG_INTERNAL_ERROR("Unable to open the core dump file."); + return false; + } + + if (!write_elf_header(elf_file, /*entry_point=*/0, + /*section_header_table_offset*/ sizeof(ELF_HEADER_TYPE) + + 1 /*program_header_count*/ * + sizeof(ELF_PROGRAM_HEADER_TYPE) + + section_data_size, + /*flags=*/0, + /*program_header_count=*/1, + /*section_header_count=*/section_count, + /*section_string_table_index=*/section_count - 1)) { + os_close(elf_file); + return false; + } + // TODO i#7046: Fill the program header with valid data. + if (!write_program_header(elf_file, PT_NULL, PF_X, /*offset=*/0, + /*virtual_address=*/0, + /*physical_address=*/0, + /*file_size=*/0, /*memory_size=*/0, /*alignment=*/0)) { + os_close(elf_file); + return false; + } + // Write memory content to the core dump file. + for (int section_index = 0; section_index < section_count - 1; ++section_index) { + const size_t length = section_header_info[section_index].vm_end - + section_header_info[section_index].vm_start; + const size_t written = os_write( + elf_file, (void *)section_header_info[section_index].vm_start, length); + if (written != length) { + SYSLOG_INTERNAL_ERROR("Failed to write the requested memory content into " + "the core dump file."); + SYSLOG_INTERNAL_ERROR( + "section: %s, prot: %x, length: %d, written: %d\n", + &string_table[section_header_info[section_index].name_offset], + section_header_info[section_index].prot, length, written); + os_close(elf_file); + return false; + } + } + // Write the section name section. + if (os_write(elf_file, (void *)string_table, string_table_offset) != + string_table_offset) { + SYSLOG_INTERNAL_ERROR("Failed to write section name string table into the " + "core dump file."); + os_close(elf_file); + 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) + + 1 /*program_header_count*/ * sizeof(ELF_PROGRAM_HEADER_TYPE); + // 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 + // this loop. + for (int section_index = 0; section_index < section_count - 1; ++section_index) { + ELF_WORD flags = SHF_ALLOC | SHF_MERGE; + if (TEST(PROT_WRITE, section_header_info[section_index].prot)) { + flags |= SHF_WRITE; + } + 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, + section_header_info[section_index].vm_end - + section_header_info[section_index].vm_start, + /*link=*/0, + /*info=*/0, /*alignment=*/sizeof(ELF_WORD), + /*entry_size=*/0)) { + os_close(elf_file); + return false; + } + file_offset += section_header_info[section_index].vm_end - + section_header_info[section_index].vm_start; + } + // Write the section name section. + if (!write_section_header( + elf_file, string_table_offset - strlen(SECTION_HEADER_TABLE), SHT_STRTAB, + /*flags=*/0, /*virtual_address=*/0, file_offset, + /*section_size=*/string_table_offset, /*link=*/0, + /*info=*/0, /*alignment=*/1, + /*entry_size=*/0)) { + os_close(elf_file); + return false; + } + + os_close(elf_file); + return true; +} + +/* + * Returns true if a core dump file is written, false otherwise. + */ +bool +os_dump_core_live(void) +{ + // Suspend all threads including native threads to ensure the memory regions + // do not change in the middle of the core dump. + int num_threads; + thread_record_t **threads; + if (!synch_with_all_threads(THREAD_SYNCH_SUSPENDED_VALID_MCONTEXT_OR_NO_XFER, + &threads, &num_threads, THREAD_SYNCH_NO_LOCKS_NO_XFER, + /* If we fail to suspend a thread, there is a + * risk of deadlock in the child, so it's worth + * retrying on failure. + */ + THREAD_SYNCH_SUSPEND_FAILURE_IGNORE)) { + return false; + } + + const bool ret = os_dump_core_internal(); + + end_synch_with_all_threads(threads, num_threads, + /*resume=*/true); + return ret; +} diff --git a/core/unix/elf_defines.h b/core/unix/elf_defines.h new file mode 100644 index 00000000000..b9a51f3cde5 --- /dev/null +++ b/core/unix/elf_defines.h @@ -0,0 +1,237 @@ +/* ********************************************************** + * Copyright (c) 2024 Google, Inc. All rights reserved. + * **********************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of VMware, Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#ifndef ELF_DEFINES_H +#define ELF_DEFINES_H + +#include /* for ELF types */ + +#ifndef DT_RELRSZ +# define DT_RELRSZ 35 +# define DT_RELR 36 +#endif + +/* Workaround for EM_RISCV not being defined in elf.h on RHEL-7. */ +#ifndef EM_RISCV +# define EM_RISCV 243 +#endif + +/* XXX i#1345: support mixed-mode 32-bit and 64-bit in one process. + * There is no official support for that on Linux or Mac and for now we do + * not support it either, especially not mixing libraries. + * Update: We want this for i#1684 for multi-arch support in drdecode. + */ +#ifdef X64 +# define ELF_HEADER_TYPE Elf64_Ehdr +# define ELF_ALTARCH_HEADER_TYPE Elf32_Ehdr +# define ELF_PROGRAM_HEADER_TYPE Elf64_Phdr +# define ELF_SECTION_HEADER_TYPE Elf64_Shdr +# define ELF_DYNAMIC_ENTRY_TYPE Elf64_Dyn +# define ELF_ADDR Elf64_Addr +# define ELF_WORD Elf64_Xword +# define ELF_SWORD Elf64_Sxword +# define ELF_HALF Elf64_Half +# define ELF_SYM_TYPE Elf64_Sym +# define ELF_WORD_SIZE 64 /* __ELF_NATIVE_CLASS */ +# define ELF_ST_VISIBILITY ELF64_ST_VISIBILITY +# define ELF_REL_TYPE Elf64_Rel +# define ELF_RELA_TYPE Elf64_Rela +# define ELF_AUXV_TYPE Elf64_auxv_t +# define ELF_OFF Elf64_Word +/* system like android has ELF_ST_TYPE and ELF_ST_BIND */ +# ifndef ELF_ST_TYPE +# define ELF_ST_TYPE ELF64_ST_TYPE +# define ELF_ST_BIND ELF64_ST_BIND +# endif +#else +# define ELF_HEADER_TYPE Elf32_Ehdr +# define ELF_ALTARCH_HEADER_TYPE Elf64_Ehdr +# define ELF_PROGRAM_HEADER_TYPE Elf32_Phdr +# define ELF_SECTION_HEADER_TYPE Elf32_Shdr +# define ELF_DYNAMIC_ENTRY_TYPE Elf32_Dyn +# define ELF_ADDR Elf32_Addr +# define ELF_WORD Elf32_Word +# define ELF_SWORD Elf32_Sword +# define ELF_HALF Elf32_Half +# define ELF_SYM_TYPE Elf32_Sym +# define ELF_WORD_SIZE 32 /* __ELF_NATIVE_CLASS */ +# define ELF_ST_VISIBILITY ELF32_ST_VISIBILITY +# define ELF_REL_TYPE Elf32_Rel +# define ELF_RELA_TYPE Elf32_Rela +# define ELF_AUXV_TYPE Elf32_auxv_t +# define ELF_OFF Elf32_Word +/* system like android has ELF_ST_TYPE and ELF_ST_BIND */ +# ifndef ELF_ST_TYPE +# define ELF_ST_TYPE ELF32_ST_TYPE +# define ELF_ST_BIND ELF32_ST_BIND +# endif +#endif + +#ifdef X86 +# ifdef X64 +/* AMD x86-64 relocations. */ +# define ELF_R_TYPE ELF64_R_TYPE +# define ELF_R_SYM ELF64_R_SYM +# define ELF_R_INFO ELF64_R_INFO +/* relocation type */ +# define ELF_R_NONE R_X86_64_NONE /* No reloc */ +# define ELF_R_DIRECT R_X86_64_64 /* Direct 64 bit */ +# define ELF_R_PC32 R_X86_64_PC32 /* PC relative 32-bit signed */ +# define ELF_R_COPY R_X86_64_COPY /* copy symbol at runtime */ +# define ELF_R_GLOB_DAT R_X86_64_GLOB_DAT /* GOT entry */ +# define ELF_R_JUMP_SLOT R_X86_64_JUMP_SLOT /* PLT entry */ +# define ELF_R_RELATIVE R_X86_64_RELATIVE /* Adjust by program delta */ +# ifndef R_X86_64_IRELATIVE +# define R_X86_64_IRELATIVE 37 +# endif +# define ELF_R_IRELATIVE \ + R_X86_64_IRELATIVE /* Adjust indirectly by program base \ + */ + /* TLS hanlding */ +# define ELF_R_TLS_DTPMOD R_X86_64_DTPMOD64 /* Module ID */ +# define ELF_R_TLS_TPOFF R_X86_64_TPOFF64 /* Offset in module's TLS block */ +# define ELF_R_TLS_DTPOFF R_X86_64_DTPOFF64 /* Offset in initial TLS block */ +# ifndef R_X86_64_TLSDESC +# define R_X86_64_TLSDESC 36 +# endif +# define ELF_R_TLS_DESC \ + R_X86_64_TLSDESC /* TLS descriptor containing \ + * pointer to code and to \ + * argument, returning the TLS \ + * offset for the symbol. \ + */ +# else /* 32-bit */ +# define ELF_R_TYPE ELF32_R_TYPE +# define ELF_R_SYM ELF32_R_SYM +# define ELF_R_INFO ELF32_R_INFO +/* relocation type */ +# define ELF_R_NONE R_386_NONE /* No reloc */ +# define ELF_R_DIRECT R_386_32 /* Direct 32 bit */ +# define ELF_R_PC32 R_386_PC32 /* PC relative 32 bit */ +# define ELF_R_COPY R_386_COPY /* Copy symbol at runtime */ +# define ELF_R_GLOB_DAT R_386_GLOB_DAT /* GOT entry */ +# define ELF_R_JUMP_SLOT R_386_JMP_SLOT /* PLT entry */ +# define ELF_R_RELATIVE R_386_RELATIVE /* Adjust by program delta */ +# ifndef R_386_IRELATIVE +# define R_386_IRELATIVE 42 +# endif +# define ELF_R_IRELATIVE R_386_IRELATIVE /* Adjust indirectly by program base */ +/* tls related */ +# define ELF_R_TLS_DTPMOD R_386_TLS_DTPMOD32 /* Module ID */ +# define ELF_R_TLS_TPOFF \ + R_386_TLS_TPOFF /* Negated offsets in static TLS block \ + */ +# define ELF_R_TLS_DTPOFF R_386_TLS_DTPOFF32 /* Offset in TLS block */ +# ifndef R_386_TLS_DESC +# define R_386_TLS_DESC 41 +# endif +# define ELF_R_TLS_DESC \ + R_386_TLS_DESC /* TLS descriptor containing \ + * pointer to code and to \ + * argument, returning the TLS \ + * offset for the symbol. \ + */ +# endif +#elif defined(AARCH64) +# define ELF_R_TYPE ELF64_R_TYPE +# define ELF_R_SYM ELF64_R_SYM +/* relocation type */ +# define ELF_R_NONE R_AARCH64_NONE /* No relocation. */ +# define ELF_R_DIRECT R_AARCH64_ABS64 /* Direct 64 bit. */ +# define ELF_R_COPY R_AARCH64_COPY /* Copy symbol at runtime. */ +# define ELF_R_GLOB_DAT R_AARCH64_GLOB_DAT /* Create GOT entry. */ +# define ELF_R_JUMP_SLOT R_AARCH64_JUMP_SLOT /* Create PLT entry. */ +# define ELF_R_RELATIVE R_AARCH64_RELATIVE /* Adjust by program base. */ +# define ELF_R_IRELATIVE R_AARCH64_IRELATIVE /* STT_GNU_IFUNC relocation. */ +/* tls related */ +# define ELF_R_TLS_DTPMOD 1028 /* R_AARCH64_TLS_DTPMOD64 Module number. */ +# define ELF_R_TLS_TPOFF 1030 /* R_AARCH64_TLS_TPREL64 TP-relative offset. */ +# define ELF_R_TLS_DTPOFF 1029 /* R_AARCH64_TLS_DTPREL64 Module-relative offset. */ +# define ELF_R_TLS_DESC 1031 /* R_AARCH64_TLSDESC TLS Descriptor. */ +#elif defined(ARM) +# define ELF_R_TYPE ELF32_R_TYPE +# define ELF_R_SYM ELF32_R_SYM +# define ELF_R_INFO ELF32_R_INFO +/* relocation type */ +# define ELF_R_NONE R_ARM_NONE /* No reloc */ +# define ELF_R_DIRECT R_ARM_ABS32 /* Direct 32 bit */ +# define ELF_R_COPY R_ARM_COPY /* Copy symbol at runtime */ +# define ELF_R_GLOB_DAT R_ARM_GLOB_DAT /* GOT entry */ +# define ELF_R_JUMP_SLOT R_ARM_JUMP_SLOT /* PLT entry */ +# define ELF_R_RELATIVE R_ARM_RELATIVE /* Adjust by program delta */ +# define ELF_R_IRELATIVE R_ARM_IRELATIVE /* Adjust indirectly by program base */ +/* tls related */ +# define ELF_R_TLS_DTPMOD R_ARM_TLS_DTPMOD32 /* Module ID */ +# define ELF_R_TLS_TPOFF R_ARM_TLS_TPOFF32 /* Negated offsets in static TLS block */ +# define ELF_R_TLS_DTPOFF R_ARM_TLS_DTPOFF32 /* Offset in TLS block */ +# ifndef ANDROID +# define ELF_R_TLS_DESC \ + R_ARM_TLS_DESC /* TLS descriptor containing \ + * pointer to code and to \ + * argument, returning the TLS \ + * offset for the symbol. \ + */ +# endif /* ANDROID */ +#elif defined(RISCV64) +# define ELF_R_TYPE ELF64_R_TYPE +# define ELF_R_SYM ELF64_R_SYM +/* relocation type */ +# define ELF_R_NONE R_RISCV_NONE /* No relocation. */ +# define ELF_R_DIRECT R_RISCV_64 /* Direct 64 bit. */ +# define ELF_R_COPY R_RISCV_COPY /* Copy symbol at runtime. */ +/* XXX i#3544: GOT and direct 64 bit both use R_RISCV_64. */ +# define ELF_R_GLOB_DAT R_RISCV_64 /* Create GOT entry. */ +# define ELF_R_JUMP_SLOT R_RISCV_JUMP_SLOT /* Create PLT entry. */ +# define ELF_R_RELATIVE R_RISCV_RELATIVE /* Adjust by program base. */ +/* XXX i#3544: R_RISCV_IRELATIVE was added after libc 2.31 and some distros + * don't have it yet (i.e. Ubuntu 20.04). The official number has been defined + * here: https://github.com/riscv/riscv-elf-psabi-doc/commit/d21ca40a. + */ +# ifndef R_RISCV_IRELATIVE +# define R_RISCV_IRELATIVE 58 +# endif +# define ELF_R_IRELATIVE R_RISCV_IRELATIVE /* STT_GNU_IFUNC relocation. */ +/* tls related */ +# define ELF_R_TLS_DTPMOD R_RISCV_TLS_DTPMOD64 /* Module ID. */ +# define ELF_R_TLS_TPOFF R_RISCV_TLS_TPREL64 /* TP-relative offset. */ +# define ELF_R_TLS_DTPOFF R_RISCV_TLS_DTPREL64 /* Module-relative offset. */ +#endif /* X86/ARM/RISCV64 */ + +/* Define ARM ELF machine types to support compiling on old Linux distros. */ +#ifndef EM_ARM +# define EM_ARM 40 +#endif +#ifndef EM_AARCH64 +# define EM_AARCH64 183 +#endif + +#endif /* ELF_DEFINES_H */ diff --git a/core/unix/module_elf.h b/core/unix/module_elf.h index 08e1b292143..1db03e55939 100644 --- a/core/unix/module_elf.h +++ b/core/unix/module_elf.h @@ -36,203 +36,7 @@ #include /* for ELF types */ #include "../module_shared.h" - -#ifndef DT_RELRSZ -# define DT_RELRSZ 35 -# define DT_RELR 36 -#endif - -/* Workaround for EM_RISCV not being defined in elf.h on RHEL-7. */ -#ifndef EM_RISCV -# define EM_RISCV 243 -#endif - -/* XXX i#1345: support mixed-mode 32-bit and 64-bit in one process. - * There is no official support for that on Linux or Mac and for now we do - * not support it either, especially not mixing libraries. - * Update: We want this for i#1684 for multi-arch support in drdecode. - */ -#ifdef X64 -# define ELF_HEADER_TYPE Elf64_Ehdr -# define ELF_ALTARCH_HEADER_TYPE Elf32_Ehdr -# define ELF_PROGRAM_HEADER_TYPE Elf64_Phdr -# define ELF_SECTION_HEADER_TYPE Elf64_Shdr -# define ELF_DYNAMIC_ENTRY_TYPE Elf64_Dyn -# define ELF_ADDR Elf64_Addr -# define ELF_WORD Elf64_Xword -# define ELF_SWORD Elf64_Sxword -# define ELF_HALF Elf64_Half -# define ELF_SYM_TYPE Elf64_Sym -# define ELF_WORD_SIZE 64 /* __ELF_NATIVE_CLASS */ -# define ELF_ST_VISIBILITY ELF64_ST_VISIBILITY -# define ELF_REL_TYPE Elf64_Rel -# define ELF_RELA_TYPE Elf64_Rela -# define ELF_AUXV_TYPE Elf64_auxv_t -/* system like android has ELF_ST_TYPE and ELF_ST_BIND */ -# ifndef ELF_ST_TYPE -# define ELF_ST_TYPE ELF64_ST_TYPE -# define ELF_ST_BIND ELF64_ST_BIND -# endif -#else -# define ELF_HEADER_TYPE Elf32_Ehdr -# define ELF_ALTARCH_HEADER_TYPE Elf64_Ehdr -# define ELF_PROGRAM_HEADER_TYPE Elf32_Phdr -# define ELF_SECTION_HEADER_TYPE Elf32_Shdr -# define ELF_DYNAMIC_ENTRY_TYPE Elf32_Dyn -# define ELF_ADDR Elf32_Addr -# define ELF_WORD Elf32_Word -# define ELF_SWORD Elf32_Sword -# define ELF_HALF Elf32_Half -# define ELF_SYM_TYPE Elf32_Sym -# define ELF_WORD_SIZE 32 /* __ELF_NATIVE_CLASS */ -# define ELF_ST_VISIBILITY ELF32_ST_VISIBILITY -# define ELF_REL_TYPE Elf32_Rel -# define ELF_RELA_TYPE Elf32_Rela -# define ELF_AUXV_TYPE Elf32_auxv_t -/* system like android has ELF_ST_TYPE and ELF_ST_BIND */ -# ifndef ELF_ST_TYPE -# define ELF_ST_TYPE ELF32_ST_TYPE -# define ELF_ST_BIND ELF32_ST_BIND -# endif -#endif - -#ifdef X86 -# ifdef X64 -/* AMD x86-64 relocations. */ -# define ELF_R_TYPE ELF64_R_TYPE -# define ELF_R_SYM ELF64_R_SYM -# define ELF_R_INFO ELF64_R_INFO -/* relocation type */ -# define ELF_R_NONE R_X86_64_NONE /* No reloc */ -# define ELF_R_DIRECT R_X86_64_64 /* Direct 64 bit */ -# define ELF_R_PC32 R_X86_64_PC32 /* PC relative 32-bit signed */ -# define ELF_R_COPY R_X86_64_COPY /* copy symbol at runtime */ -# define ELF_R_GLOB_DAT R_X86_64_GLOB_DAT /* GOT entry */ -# define ELF_R_JUMP_SLOT R_X86_64_JUMP_SLOT /* PLT entry */ -# define ELF_R_RELATIVE R_X86_64_RELATIVE /* Adjust by program delta */ -# ifndef R_X86_64_IRELATIVE -# define R_X86_64_IRELATIVE 37 -# endif -# define ELF_R_IRELATIVE \ - R_X86_64_IRELATIVE /* Adjust indirectly by program base \ - */ - /* TLS hanlding */ -# define ELF_R_TLS_DTPMOD R_X86_64_DTPMOD64 /* Module ID */ -# define ELF_R_TLS_TPOFF R_X86_64_TPOFF64 /* Offset in module's TLS block */ -# define ELF_R_TLS_DTPOFF R_X86_64_DTPOFF64 /* Offset in initial TLS block */ -# ifndef R_X86_64_TLSDESC -# define R_X86_64_TLSDESC 36 -# endif -# define ELF_R_TLS_DESC \ - R_X86_64_TLSDESC /* TLS descriptor containing \ - * pointer to code and to \ - * argument, returning the TLS \ - * offset for the symbol. \ - */ -# else /* 32-bit */ -# define ELF_R_TYPE ELF32_R_TYPE -# define ELF_R_SYM ELF32_R_SYM -# define ELF_R_INFO ELF32_R_INFO -/* relocation type */ -# define ELF_R_NONE R_386_NONE /* No reloc */ -# define ELF_R_DIRECT R_386_32 /* Direct 32 bit */ -# define ELF_R_PC32 R_386_PC32 /* PC relative 32 bit */ -# define ELF_R_COPY R_386_COPY /* Copy symbol at runtime */ -# define ELF_R_GLOB_DAT R_386_GLOB_DAT /* GOT entry */ -# define ELF_R_JUMP_SLOT R_386_JMP_SLOT /* PLT entry */ -# define ELF_R_RELATIVE R_386_RELATIVE /* Adjust by program delta */ -# ifndef R_386_IRELATIVE -# define R_386_IRELATIVE 42 -# endif -# define ELF_R_IRELATIVE R_386_IRELATIVE /* Adjust indirectly by program base */ -/* tls related */ -# define ELF_R_TLS_DTPMOD R_386_TLS_DTPMOD32 /* Module ID */ -# define ELF_R_TLS_TPOFF \ - R_386_TLS_TPOFF /* Negated offsets in static TLS block \ - */ -# define ELF_R_TLS_DTPOFF R_386_TLS_DTPOFF32 /* Offset in TLS block */ -# ifndef R_386_TLS_DESC -# define R_386_TLS_DESC 41 -# endif -# define ELF_R_TLS_DESC \ - R_386_TLS_DESC /* TLS descriptor containing \ - * pointer to code and to \ - * argument, returning the TLS \ - * offset for the symbol. \ - */ -# endif -#elif defined(AARCH64) -# define ELF_R_TYPE ELF64_R_TYPE -# define ELF_R_SYM ELF64_R_SYM -/* relocation type */ -# define ELF_R_NONE R_AARCH64_NONE /* No relocation. */ -# define ELF_R_DIRECT R_AARCH64_ABS64 /* Direct 64 bit. */ -# define ELF_R_COPY R_AARCH64_COPY /* Copy symbol at runtime. */ -# define ELF_R_GLOB_DAT R_AARCH64_GLOB_DAT /* Create GOT entry. */ -# define ELF_R_JUMP_SLOT R_AARCH64_JUMP_SLOT /* Create PLT entry. */ -# define ELF_R_RELATIVE R_AARCH64_RELATIVE /* Adjust by program base. */ -# define ELF_R_IRELATIVE R_AARCH64_IRELATIVE /* STT_GNU_IFUNC relocation. */ -/* tls related */ -# define ELF_R_TLS_DTPMOD 1028 /* R_AARCH64_TLS_DTPMOD64 Module number. */ -# define ELF_R_TLS_TPOFF 1030 /* R_AARCH64_TLS_TPREL64 TP-relative offset. */ -# define ELF_R_TLS_DTPOFF 1029 /* R_AARCH64_TLS_DTPREL64 Module-relative offset. */ -# define ELF_R_TLS_DESC 1031 /* R_AARCH64_TLSDESC TLS Descriptor. */ -#elif defined(ARM) -# define ELF_R_TYPE ELF32_R_TYPE -# define ELF_R_SYM ELF32_R_SYM -# define ELF_R_INFO ELF32_R_INFO -/* relocation type */ -# define ELF_R_NONE R_ARM_NONE /* No reloc */ -# define ELF_R_DIRECT R_ARM_ABS32 /* Direct 32 bit */ -# define ELF_R_COPY R_ARM_COPY /* Copy symbol at runtime */ -# define ELF_R_GLOB_DAT R_ARM_GLOB_DAT /* GOT entry */ -# define ELF_R_JUMP_SLOT R_ARM_JUMP_SLOT /* PLT entry */ -# define ELF_R_RELATIVE R_ARM_RELATIVE /* Adjust by program delta */ -# define ELF_R_IRELATIVE R_ARM_IRELATIVE /* Adjust indirectly by program base */ -/* tls related */ -# define ELF_R_TLS_DTPMOD R_ARM_TLS_DTPMOD32 /* Module ID */ -# define ELF_R_TLS_TPOFF R_ARM_TLS_TPOFF32 /* Negated offsets in static TLS block */ -# define ELF_R_TLS_DTPOFF R_ARM_TLS_DTPOFF32 /* Offset in TLS block */ -# ifndef ANDROID -# define ELF_R_TLS_DESC \ - R_ARM_TLS_DESC /* TLS descriptor containing \ - * pointer to code and to \ - * argument, returning the TLS \ - * offset for the symbol. \ - */ -# endif /* ANDROID */ -#elif defined(RISCV64) -# define ELF_R_TYPE ELF64_R_TYPE -# define ELF_R_SYM ELF64_R_SYM -/* relocation type */ -# define ELF_R_NONE R_RISCV_NONE /* No relocation. */ -# define ELF_R_DIRECT R_RISCV_64 /* Direct 64 bit. */ -# define ELF_R_COPY R_RISCV_COPY /* Copy symbol at runtime. */ -/* FIXME i#3544: GOT and direct 64 bit both use R_RISCV_64. */ -# define ELF_R_GLOB_DAT R_RISCV_64 /* Create GOT entry. */ -# define ELF_R_JUMP_SLOT R_RISCV_JUMP_SLOT /* Create PLT entry. */ -# define ELF_R_RELATIVE R_RISCV_RELATIVE /* Adjust by program base. */ -/* FIXME i#3544: R_RISCV_IRELATIVE was added after libc 2.31 and some distros - * don't have it yet (i.e. Ubuntu 20.04). The official number has been defined - * here: https://github.com/riscv/riscv-elf-psabi-doc/commit/d21ca40a. - */ -# ifndef R_RISCV_IRELATIVE -# define R_RISCV_IRELATIVE 58 -# endif -# define ELF_R_IRELATIVE R_RISCV_IRELATIVE /* STT_GNU_IFUNC relocation. */ -/* tls related */ -# define ELF_R_TLS_DTPMOD R_RISCV_TLS_DTPMOD64 /* Module ID. */ -# define ELF_R_TLS_TPOFF R_RISCV_TLS_TPREL64 /* TP-relative offset. */ -# define ELF_R_TLS_DTPOFF R_RISCV_TLS_DTPREL64 /* Module-relative offset. */ -#endif /* X86/ARM/RISCV64 */ - -/* Define ARM ELF machine types to support compiling on old Linux distros. */ -#ifndef EM_ARM -# define EM_ARM 40 -#endif -#ifndef EM_AARCH64 -# define EM_AARCH64 183 -#endif +#include "elf_defines.h" bool get_elf_platform(file_t f, dr_platform_t *platform); diff --git a/core/unix/os_exports.h b/core/unix/os_exports.h index 7626556d0ca..d66c67111b1 100644 --- a/core/unix/os_exports.h +++ b/core/unix/os_exports.h @@ -246,6 +246,8 @@ ushort os_get_app_tls_reg_offset(reg_id_t seg); void * os_get_app_tls_base(dcontext_t *dcontext, reg_id_t seg); +bool +os_dump_core_live(void); #if defined(AARCHXX) || defined(RISCV64) bool diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index 5bb52650edc..c7cd5363549 100644 --- a/suite/tests/CMakeLists.txt +++ b/suite/tests/CMakeLists.txt @@ -2646,6 +2646,9 @@ if (NOT ANDROID) # TODO i#38: Port test to Android. torunonly_ci(client.attach_blocking linux.infloop client.attach_test.dll client-interface/attach_blocking.runall "" "" "") endif () + if (X64 AND (LINUX OR WIN32)) + tobuild_ci(client.memory_dump_test client-interface/memory_dump_test.c "" "" "") + endif () endif () if (UNIX) diff --git a/suite/tests/client-interface/memory_dump_test.dll.c b/suite/tests/client-interface/memory_dump_test.dll.c new file mode 100644 index 00000000000..0113dc4ad42 --- /dev/null +++ b/suite/tests/client-interface/memory_dump_test.dll.c @@ -0,0 +1,98 @@ +/* ********************************************************** + * Copyright (c) 2024 Google, Inc. All rights reserved. + * **********************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of VMware, Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include "dr_api.h" +#include "client_tools.h" +#ifdef WINDOWS +# include +#endif + +#define NUDGE_ARG_DUMP_MEMORY 1 + +static bool saw_thread_init_event = false; +static client_id_t client_id = 0; +static thread_id_t thread_id = 0; + +static void +event_nudge(void *drcontext, uint64 arg) +{ + dr_fprintf(STDERR, "nudge delivered %d\n", (uint)arg); + if (arg == NUDGE_ARG_DUMP_MEMORY) { + dr_memory_dump_spec_t spec; + spec.size = sizeof(dr_memory_dump_spec_t); +#ifdef WINDOWS + spec.flags = DR_MEMORY_DUMP_LDMP; +#else + spec.flags = DR_MEMORY_DUMP_ELF; +#endif + if (!dr_create_memory_dump(&spec)) + dr_fprintf(STDERR, "Error: failed to create memory dump.\n"); + // TODO i#7046: Verify the content of the output file. + return; + } + dr_fprintf(STDERR, "Error: unexpected nudge event!\n"); +} + +static void +dr_exit(void) +{ + if (!saw_thread_init_event) + dr_fprintf(STDERR, "Error: never saw thread init event!\n"); +} + +static void +dr_thread_init(void *drcontext) +{ + thread_id_t tid = dr_get_thread_id(drcontext); + if (tid != thread_id) + return; + + dr_fprintf(STDERR, "thread init\n"); + saw_thread_init_event = true; + + if (!dr_nudge_client(client_id, NUDGE_ARG_DUMP_MEMORY)) + dr_fprintf(STDERR, "Error: failed to nudge client!\n"); +} + +DR_EXPORT +void +dr_init(client_id_t id) +{ + void *drcontext = dr_get_current_drcontext(); + thread_id = dr_get_thread_id(drcontext); + + client_id = id; + dr_register_exit_event(dr_exit); + dr_register_thread_init_event(dr_thread_init); + dr_register_nudge_event(event_nudge, id); + dr_fprintf(STDERR, "thank you for testing memory dump\n"); +} diff --git a/suite/tests/client-interface/memory_dump_test.expect b/suite/tests/client-interface/memory_dump_test.expect new file mode 100644 index 00000000000..903cc0a1840 --- /dev/null +++ b/suite/tests/client-interface/memory_dump_test.expect @@ -0,0 +1,4 @@ +thank you for testing memory dump +thread init +nudge delivered 1 +Hello, world!