From 162c90204b3ef66453f25582515f42b2e242cb0b Mon Sep 17 00:00:00 2001 From: Kai Date: Fri, 18 Oct 2024 20:42:29 +0000 Subject: [PATCH 01/19] Add Linux support to dr_create_memory_dump. --- core/CMakeLists.txt | 1 + core/lib/dr_tools.h | 6 + core/lib/instrument.c | 3 + core/unix/coredump.c | 336 +++++++++++++++++++++++++++++++++++++++++ core/unix/os_exports.h | 1 + 5 files changed, 347 insertions(+) create mode 100644 core/unix/coredump.c diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 55a12a82c84..8d739a44568 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -391,6 +391,7 @@ if (UNIX) else () set(OS_SRCS ${OS_SRCS} unix/loader_linux.c) endif () + set(OS_SRCS ${OS_SRCS} unix/coredump.c) set(OS_SRCS ${OS_SRCS} unix/memquery_linux.c) set(OS_SRCS ${OS_SRCS} unix/memquery.c) set(OS_SRCS ${OS_SRCS} unix/memcache.c) diff --git a/core/lib/dr_tools.h b/core/lib/dr_tools.h index 57563a4f383..51ae8a321e0 100644 --- a/core/lib/dr_tools.h +++ b/core/lib/dr_tools.h @@ -341,6 +341,12 @@ typedef enum { * \note Windows only. */ DR_MEMORY_DUMP_LDMP = 0x0001, + /** + * Memory dump in Executable and Linkable Format. + * + * \note Linux and X64 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..56a3a2b0941 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..eec54a1f526 --- /dev/null +++ b/core/unix/coredump.c @@ -0,0 +1,336 @@ +/* ********************************************************** + * 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 "../os_shared.h" +#include "../hashtable.h" +#include "memquery.h" + +#define MAX_SECTION_NAME_BUFFER_SIZE 8192 +#define SECTION_HEADER_TABLE ".shstrtab" +#define VVAR_SECTION "[vvar]" + +DECLARE_CXTSWPROT_VAR(static mutex_t dump_core_lock, INIT_LOCK_FREE(dump_core_lock)); + +/* + * Returns true if the ELF header is written to the core dump file, false + * otherwise. + */ +static bool +write_elf_header(file_t elf_file, Elf64_Addr entry_point, + Elf64_Off section_header_table_offset, Elf64_Half flags, + Elf64_Half program_header_count, Elf64_Half section_header_count, + Elf64_Half section_string_table_index) +{ + Elf64_Ehdr 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] = IF_AARCHXX_ELSE(ELFDATA2MSB, 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(Elf64_Phdr); + /* Contains the number of entries in the program header table. */ + ehdr.e_phnum = program_header_count; + ehdr.e_shentsize = sizeof(Elf64_Shdr); + /* 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(Elf64_Ehdr)) == sizeof(Elf64_Ehdr); +} + +/* + * Returns true if the program header is written to the core dump file, false + * otherwise. + */ +static bool +write_phdrs(file_t elf_file, Elf64_Word type, Elf64_Word flags, Elf64_Off offset, + Elf64_Addr virtual_address, Elf64_Addr physical_address, + Elf64_Xword file_size, Elf64_Xword memory_size, Elf64_Xword alignment) +{ + Elf64_Phdr 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(Elf64_Phdr)) == sizeof(Elf64_Phdr); +} + +/* + * Returns true if the section header is written to the core dump file, false + * otherwise. + */ +static bool +write_shdr(file_t elf_file, Elf64_Word string_table_offset, Elf64_Word type, + Elf64_Xword flags, Elf64_Addr virtual_address, Elf64_Off offset, + Elf64_Xword section_size, Elf64_Word link, Elf64_Word info, + Elf64_Xword alignment, Elf64_Xword entry_size) +{ + Elf64_Shdr 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(Elf64_Shdr)) == sizeof(Elf64_Shdr); +} + +/* + * Returns true if a core dump file is written, false otherwise. + */ +static bool +os_dump_core_internal(void) +{ + strhash_table_t *string_htable = + strhash_hash_create(GLOBAL_DCONTEXT, /*bits=*/8, /*load_factor_percent=*/80, + /*table_flags=*/0, NULL _IF_DEBUG("mmap-string-table")); + + // Insert a null string at the beginning for sections with no comment. + char string_table[MAX_SECTION_NAME_BUFFER_SIZE]; + string_table[0] = '\0'; + string_table[1] = '\0'; + int64 string_table_offset = 1; + int64 section_count = 0; + int64 seciion_data_size = 0; + memquery_iter_t iter; + if (memquery_iterator_start(&iter, NULL, /*may_alloc=*/false)) { + while (memquery_iterator_next(&iter)) { + // Skip non-readable section. + if (iter.prot == MEMPROT_NONE || strcmp(iter.comment, VVAR_SECTION) == 0) { + continue; + } + int64 offset = 0; + if (iter.comment != NULL && iter.comment[0] != '\0') { + offset = (int64)strhash_hash_lookup(GLOBAL_DCONTEXT, string_htable, + iter.comment); + if (offset == 0 && + (string_table[1] == '\0' || + d_r_strcmp(string_table, iter.comment) != 0)) { + strhash_hash_add(GLOBAL_DCONTEXT, string_htable, iter.comment, + (void *)string_table_offset); + offset = 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; + } + 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'; + } + } + seciion_data_size += iter.vm_end - iter.vm_start; + ++section_count; + } + // Add the string table section. Append the section name ".shstrtab" to the + // section names table. + const int 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; + seciion_data_size += string_table_offset; + memquery_iterator_stop(&iter); + } + + 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(Elf64_Ehdr) + + 1 /*program_header_count*/ * sizeof(Elf64_Phdr) + + seciion_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#xxxx: Fill the program header with valid data. + if (!write_phdrs(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; + } + int total = 0; + if (memquery_iterator_start(&iter, NULL, /*may_alloc=*/false)) { + while (memquery_iterator_next(&iter)) { + // Skip non-readable sections. + if (iter.prot == MEMPROT_NONE || strcmp(iter.comment, VVAR_SECTION) == 0) { + continue; + } + const size_t length = iter.vm_end - iter.vm_start; + const int written = os_write(elf_file, (void *)iter.vm_start, length); + if (written != length) { + SYSLOG_INTERNAL_ERROR("Failed to write the requested memory content into " + "the core dump file."); + os_close(elf_file); + return false; + } + total += length; + } + // Write the section names section. + if (os_write(elf_file, (void *)string_table, string_table_offset) != + string_table_offset) { + os_close(elf_file); + return false; + } + memquery_iterator_stop(&iter); + } + + if (memquery_iterator_start(&iter, NULL, /*may_alloc=*/false)) { + // TODO i#xxxx: Handle multiple program headers. + int64 file_offset = + sizeof(Elf64_Ehdr) + 1 /*program_header_count*/ * sizeof(Elf64_Phdr); + while (memquery_iterator_next(&iter)) { + // Skip non-readable section. + if (iter.prot == MEMPROT_NONE || strcmp(iter.comment, VVAR_SECTION) == 0) { + continue; + } + Elf64_Xword flags = SHF_ALLOC | SHF_MERGE; + if (iter.prot & PROT_WRITE) { + flags |= SHF_WRITE; + } + int64 name_offset = 0; + if (iter.comment != NULL && iter.comment[0] != '\0') { + name_offset = (int64)strhash_hash_lookup(GLOBAL_DCONTEXT, string_htable, + iter.comment); + } + if (!write_shdr(elf_file, name_offset, SHT_PROGBITS, flags, + (Elf64_Addr)iter.vm_start, file_offset, + iter.vm_end - iter.vm_start, /*link=*/0, + /*info=*/0, /*alignment=*/sizeof(Elf64_Xword), + /*entry_size=*/0)) { + os_close(elf_file); + return false; + } + file_offset += iter.vm_end - iter.vm_start; + } + memquery_iterator_stop(&iter); + // Write the section names section. + if (!write_shdr(elf_file, string_table_offset - strlen(".shstrtab"), 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); + strhash_hash_destroy(GLOBAL_DCONTEXT, string_htable); + return true; +} + +/* + * Returns true if a core dump file is written, false otherwise. + */ +bool +os_dump_core_live(void) +{ + static thread_id_t current_dumping_thread_id VAR_IN_SECTION(NEVER_PROTECTED_SECTION) = + 0; + thread_id_t current_id = d_r_get_thread_id(); +#ifdef DEADLOCK_AVOIDANCE + dcontext_t *dcontext = get_thread_private_dcontext(); + thread_locks_t *old_thread_owned_locks = NULL; +#endif + + if (current_id == current_dumping_thread_id) { + return false; /* avoid infinite loop */ + } + +#ifdef DEADLOCK_AVOIDANCE + /* first turn off deadlock avoidance for this thread (needed for live dump + * to try to grab all_threads and thread_initexit locks) */ + if (dcontext != NULL) { + old_thread_owned_locks = dcontext->thread_owned_locks; + dcontext->thread_owned_locks = NULL; + } +#endif + /* only allow one thread to dumpcore at a time, also protects static + * buffers and current_dumping_thread_id */ + d_r_mutex_lock(&dump_core_lock); + current_dumping_thread_id = current_id; + const bool ret = os_dump_core_internal(); + current_dumping_thread_id = 0; + d_r_mutex_unlock(&dump_core_lock); + +#ifdef DEADLOCK_AVOIDANCE + /* restore deadlock avoidance for this thread */ + if (dcontext != NULL) { + dcontext->thread_owned_locks = old_thread_owned_locks; + } +#endif + return ret; +} diff --git a/core/unix/os_exports.h b/core/unix/os_exports.h index 7626556d0ca..26f1b6cd8e5 100644 --- a/core/unix/os_exports.h +++ b/core/unix/os_exports.h @@ -246,6 +246,7 @@ 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 From 13f26abf01075a0e41be9f860f28d6f8c08b7ced Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 30 Oct 2024 17:05:48 +0000 Subject: [PATCH 02/19] Incorporate review comments. --- api/docs/release.dox | 1 + clients/drcachesim/tracer/tracer.cpp | 7 + core/lib/dr_tools.h | 2 +- core/unix/coredump.c | 310 +++++++++++++-------------- core/unix/elf_defines.h | 237 ++++++++++++++++++++ core/unix/module_elf.h | 198 +---------------- core/utils.c | 1 + 7 files changed, 402 insertions(+), 354 deletions(-) create mode 100644 core/unix/elf_defines.h diff --git a/api/docs/release.dox b/api/docs/release.dox index a4a7d9f5049..62e2ce8e95c 100644 --- a/api/docs/release.dox +++ b/api/docs/release.dox @@ -163,6 +163,7 @@ changes: signature between AArch64 and RISC-V. - Changed the drcachesim -LL_miss_file option by adding a process ID field to the output. This helps in better analyzing cache misses in multi-process environments. + - Added X64 Linux only support to dr_create_memory_dump. Further non-compatibility-affecting changes include: - Added DWARF-5 support to the drsyms library by linking in 4 static libraries diff --git a/clients/drcachesim/tracer/tracer.cpp b/clients/drcachesim/tracer/tracer.cpp index 88db853deb3..ea698a13eaf 100644 --- a/clients/drcachesim/tracer/tracer.cpp +++ b/clients/drcachesim/tracer/tracer.cpp @@ -1825,6 +1825,13 @@ init_thread_in_process(void *drcontext) } #endif // XXX i#1729: gather and store an initial callstack for the thread. +#ifdef LINUX + // kyluk: DO NOT COMMIT + dr_memory_dump_spec_t dump_spec; + dump_spec.flags = DR_MEMORY_DUMP_ELF; + dump_spec.size = sizeof(dump_spec); + dr_create_memory_dump(&dump_spec); +#endif } static void diff --git a/core/lib/dr_tools.h b/core/lib/dr_tools.h index 51ae8a321e0..b48e7623d18 100644 --- a/core/lib/dr_tools.h +++ b/core/lib/dr_tools.h @@ -344,7 +344,7 @@ typedef enum { /** * Memory dump in Executable and Linkable Format. * - * \note Linux and X64 only. + * \note X64 Linux only. */ DR_MEMORY_DUMP_ELF = 0x0002, } dr_memory_dump_flags_t; diff --git a/core/unix/coredump.c b/core/unix/coredump.c index 3c26d45691b..d2b0b87f2d8 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -29,32 +29,28 @@ #include #include #include "../globals.h" + +#include + #include "../os_shared.h" #include "../hashtable.h" #include "memquery.h" +#include "elf_defines.h" +#define MAX_SECTION_HEADERS 200 #define MAX_SECTION_NAME_BUFFER_SIZE 8192 #define SECTION_HEADER_TABLE ".shstrtab" #define VVAR_SECTION "[vvar]" -#ifdef X64 -# define ELF_HEADER_TYPE Elf64_Ehdr -# define ELF_PROGRAM_HEADER_TYPE Elf64_Phdr -# define ELF_SECTION_HEADER_TYPE Elf64_Shdr -# define ELF_ADDR Elf64_Addr -# define ELF_WORD Elf64_Xword -# define ELF_OFF Elf64_Word -#else -# define ELF_HEADER_TYPE Elf32_Ehdr -# define ELF_PROGRAM_HEADER_TYPE Elf32_Phdr -# define ELF_SECTION_HEADER_TYPE Elf32_Shdr -# define ELF_ADDR Elf32_Addr -# define ELF_WORD Elf32_Word -# define ELF_OFF Elf32_Word -#endif - DECLARE_CXTSWPROT_VAR(static mutex_t dump_core_lock, INIT_LOCK_FREE(dump_core_lock)); +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. @@ -72,7 +68,7 @@ write_elf_header(DR_PARAM_IN file_t elf_file, DR_PARAM_IN ELF_ADDR entry_point, ehdr.e_ident[2] = ELFMAG2; ehdr.e_ident[3] = ELFMAG3; ehdr.e_ident[EI_CLASS] = ELFCLASS64; - ehdr.e_ident[EI_DATA] = IF_AARCHXX_ELSE(ELFDATA2MSB, ELFDATA2LSB); + 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; @@ -101,8 +97,7 @@ write_elf_header(DR_PARAM_IN file_t elf_file, DR_PARAM_IN ELF_ADDR entry_point, */ ehdr.e_shstrndx = section_string_table_index; - return os_write(elf_file, (void *)&ehdr, sizeof(ELF_HEADER_TYPE)) == - sizeof(ELF_HEADER_TYPE); + return os_write(elf_file, (void *)&ehdr, sizeof(ehdr)) == sizeof(ehdr); } /* @@ -110,24 +105,24 @@ write_elf_header(DR_PARAM_IN file_t elf_file, DR_PARAM_IN ELF_ADDR entry_point, * the core dump file, false otherwise. */ static bool -write_phdrs(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) +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 */ + 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(ELF_PROGRAM_HEADER_TYPE)) == - sizeof(ELF_PROGRAM_HEADER_TYPE); + return os_write(elf_file, (void *)&phdr, sizeof(phdr)) == sizeof(phdr); } /* @@ -135,27 +130,26 @@ write_phdrs(DR_PARAM_IN file_t elf_file, DR_PARAM_IN ELF_WORD type, * the core dump file, false otherwise. */ static bool -write_shdr(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) +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 */ + 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(ELF_SECTION_HEADER_TYPE)) == - sizeof(ELF_SECTION_HEADER_TYPE); + return os_write(elf_file, (void *)&shdr, sizeof(shdr)) == sizeof(shdr); } /* @@ -171,54 +165,65 @@ 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 seciion_data_size = 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=*/false)) { - while (memquery_iterator_next(&iter)) { - // Skip non-readable section. - if (iter.prot == MEMPROT_NONE || strcmp(iter.comment, VVAR_SECTION) == 0) { - continue; - } - ELF_ADDR offset = 0; - if (iter.comment != NULL && iter.comment[0] != '\0') { - offset = (ELF_ADDR)strhash_hash_lookup(GLOBAL_DCONTEXT, string_htable, - iter.comment); - if (offset == 0 && - (string_table[1] == '\0' || - d_r_strcmp(string_table, iter.comment) != 0)) { - strhash_hash_add(GLOBAL_DCONTEXT, string_htable, iter.comment, - (void *)string_table_offset); - offset = 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; - } - 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'; + if (!memquery_iterator_start(&iter, NULL, /*may_alloc=*/false)) { + SYSLOG_INTERNAL_ERROR("Too many section headers."); + return false; + } + while (memquery_iterator_next(&iter)) { + // Skip non-readable section. + if (iter.prot == MEMPROT_NONE || strcmp(iter.comment, VVAR_SECTION) == 0) { + continue; + } + ELF_ADDR offset = 0; + if (iter.comment != NULL && iter.comment[0] != '\0') { + 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'; } - seciion_data_size += iter.vm_end - iter.vm_start; - ++section_count; } - // Add the string table section. Append the section name ".shstrtab" to the - // section names table. - const int 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_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; - seciion_data_size += string_table_offset; - memquery_iterator_stop(&iter); + if (section_count > MAX_SECTION_HEADERS) { + SYSLOG_INTERNAL_ERROR("Too many section headers."); + return false; + } } + memquery_iterator_stop(&iter); + + // Add the string table section. Append the section name ".shstrtab" to the + // section names 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]; @@ -233,7 +238,7 @@ os_dump_core_internal(void) /*section_header_table_offset*/ sizeof(ELF_HEADER_TYPE) + 1 /*program_header_count*/ * sizeof(ELF_PROGRAM_HEADER_TYPE) + - seciion_data_size, + section_data_size, /*flags=*/0, /*program_header_count=*/1, /*section_header_count=*/section_count, @@ -242,77 +247,68 @@ os_dump_core_internal(void) return false; } // TODO i#7046: Fill the program header with valid data. - if (!write_phdrs(elf_file, PT_NULL, PF_X, /*offset=*/0, /*virtual_address=*/0, - /*physical_address=*/0, - /*file_size=*/0, /*memory_size=*/0, /*alignment=*/0)) { + 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; } - int total = 0; - if (memquery_iterator_start(&iter, NULL, /*may_alloc=*/false)) { - while (memquery_iterator_next(&iter)) { - // Skip non-readable sections. - if (iter.prot == MEMPROT_NONE || strcmp(iter.comment, VVAR_SECTION) == 0) { - continue; - } - const size_t length = iter.vm_end - iter.vm_start; - const int written = os_write(elf_file, (void *)iter.vm_start, length); - if (written != length) { - SYSLOG_INTERNAL_ERROR("Failed to write the requested memory content into " - "the core dump file."); - os_close(elf_file); - return false; - } - total += length; - } - // Write the section names section. - if (os_write(elf_file, (void *)string_table, string_table_offset) != - string_table_offset) { + // 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."); os_close(elf_file); return false; } - memquery_iterator_stop(&iter); } - - if (memquery_iterator_start(&iter, NULL, /*may_alloc=*/false)) { - // TODO i#7046: Handle multiple program headers. - ELF_OFF file_offset = sizeof(ELF_HEADER_TYPE) + - 1 /*program_header_count*/ * sizeof(ELF_PROGRAM_HEADER_TYPE); - while (memquery_iterator_next(&iter)) { - // Skip non-readable section. - if (iter.prot == MEMPROT_NONE || strcmp(iter.comment, VVAR_SECTION) == 0) { - continue; - } - ELF_WORD flags = SHF_ALLOC | SHF_MERGE; - if (iter.prot & PROT_WRITE) { - flags |= SHF_WRITE; - } - ELF_ADDR name_offset = 0; - if (iter.comment != NULL && iter.comment[0] != '\0') { - name_offset = (ELF_ADDR)strhash_hash_lookup(GLOBAL_DCONTEXT, - string_htable, iter.comment); - } - if (!write_shdr(elf_file, name_offset, SHT_PROGBITS, flags, - (ELF_ADDR)iter.vm_start, file_offset, - iter.vm_end - iter.vm_start, /*link=*/0, - /*info=*/0, /*alignment=*/sizeof(ELF_WORD), - /*entry_size=*/0)) { - os_close(elf_file); - return false; - } - file_offset += iter.vm_end - iter.vm_start; + // Write the section names 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); + for (int section_index = 0; section_index < section_count - 1; ++section_index) { + ELF_WORD flags = SHF_ALLOC | SHF_MERGE; + if (section_header_info[section_index].prot & PROT_WRITE) { + flags |= SHF_WRITE; } - memquery_iterator_stop(&iter); - // Write the section names section. - if (!write_shdr(elf_file, string_table_offset - strlen(".shstrtab"), SHT_STRTAB, - /*flags=*/0, /*virtual_address=*/0, file_offset, - /*section_size=*/string_table_offset, /*link=*/0, - /*info=*/0, /*alignment=*/1, - /*entry_size=*/0)) { + 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 names 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); strhash_hash_destroy(GLOBAL_DCONTEXT, string_htable); return true; @@ -337,15 +333,17 @@ os_dump_core_live(void) } #ifdef DEADLOCK_AVOIDANCE - /* first turn off deadlock avoidance for this thread (needed for live dump - * to try to grab all_threads and thread_initexit locks) */ + /* First turn off deadlock avoidance for this thread (needed for live dump + * to try to grab all_threads and thread_initexit locks). + */ if (dcontext != NULL) { old_thread_owned_locks = dcontext->thread_owned_locks; dcontext->thread_owned_locks = NULL; } #endif - /* only allow one thread to dumpcore at a time, also protects static - * buffers and current_dumping_thread_id */ + /* Only allow one thread to dumpcore at a time, also protects static + * buffers and current_dumping_thread_id. + */ d_r_mutex_lock(&dump_core_lock); current_dumping_thread_id = current_id; const bool ret = os_dump_core_internal(); @@ -353,7 +351,7 @@ os_dump_core_live(void) d_r_mutex_unlock(&dump_core_lock); #ifdef DEADLOCK_AVOIDANCE - /* restore deadlock avoidance for this thread */ + /* Restore deadlock avoidance for this thread. */ if (dcontext != NULL) { dcontext->thread_owned_locks = old_thread_owned_locks; } diff --git a/core/unix/elf_defines.h b/core/unix/elf_defines.h new file mode 100644 index 00000000000..1ff3d106923 --- /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. */ +/* 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 + +#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/utils.c b/core/utils.c index 3217387cdf9..29a71cc1ba3 100644 --- a/core/utils.c +++ b/core/utils.c @@ -2947,6 +2947,7 @@ get_unique_logfile(const char *file_type, char *filename_buffer, uint maxlen, if (NULL != filename_buffer) { strncpy(filename_buffer, buf, maxlen); filename_buffer[maxlen - 1] = '\0'; /* NULL terminate */ + LOG(GLOBAL, LOG_ALL, 0, "dump core file name is %s\n", buf); } return success; From 728ad875821863f4bcabb85c2055d07239766962 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 30 Oct 2024 17:17:13 +0000 Subject: [PATCH 03/19] Remove debug code in tracer.cpp. --- clients/drcachesim/tracer/tracer.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/clients/drcachesim/tracer/tracer.cpp b/clients/drcachesim/tracer/tracer.cpp index ea698a13eaf..88db853deb3 100644 --- a/clients/drcachesim/tracer/tracer.cpp +++ b/clients/drcachesim/tracer/tracer.cpp @@ -1825,13 +1825,6 @@ init_thread_in_process(void *drcontext) } #endif // XXX i#1729: gather and store an initial callstack for the thread. -#ifdef LINUX - // kyluk: DO NOT COMMIT - dr_memory_dump_spec_t dump_spec; - dump_spec.flags = DR_MEMORY_DUMP_ELF; - dump_spec.size = sizeof(dump_spec); - dr_create_memory_dump(&dump_spec); -#endif } static void From 838c2767f7de9300608b3b429703fc86e148ee35 Mon Sep 17 00:00:00 2001 From: Kai Date: Fri, 1 Nov 2024 17:48:37 +0000 Subject: [PATCH 04/19] Incorporate review comments. Use dr_suspend_all_other_threads_ex() instead of a lock. --- api/docs/release.dox | 12 ++++++-- core/unix/coredump.c | 71 +++++++++++++++++--------------------------- core/utils.c | 4 +-- core/utils.h | 2 +- 4 files changed, 40 insertions(+), 49 deletions(-) diff --git a/api/docs/release.dox b/api/docs/release.dox index 62e2ce8e95c..7bead1e9cd3 100644 --- a/api/docs/release.dox +++ b/api/docs/release.dox @@ -124,7 +124,16 @@ Dr. Memory Framework (DRMF) in the same package as DynamoRIO. DRMF provides the umbra, drsyscall, and drsymcache Extensions for use by clients. -The changes between version \DR_VERSION and 10.0.0 include the following compatibility +The changes between version \DR_VERSION and 11.0.0 include the following compatibility +changes: + - Added X64 Linux support to dr_create_memory_dump(). + +Further non-compatibility-affecting changes include: + +************************************************** +
+ +The changes between version 11.0.0 and 10.0.0 include the following compatibility changes: - Marked x86 rep instructions as predicated. - The #dr_instr_category_t enum underwent changes to support new categories @@ -163,7 +172,6 @@ changes: signature between AArch64 and RISC-V. - Changed the drcachesim -LL_miss_file option by adding a process ID field to the output. This helps in better analyzing cache misses in multi-process environments. - - Added X64 Linux only support to dr_create_memory_dump. Further non-compatibility-affecting changes include: - Added DWARF-5 support to the drsyms library by linking in 4 static libraries diff --git a/core/unix/coredump.c b/core/unix/coredump.c index d2b0b87f2d8..57abbe63554 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -32,18 +32,17 @@ #include -#include "../os_shared.h" #include "../hashtable.h" -#include "memquery.h" +#include "../os_shared.h" +#include "dr_tools.h" #include "elf_defines.h" +#include "memquery.h" -#define MAX_SECTION_HEADERS 200 +#define MAX_SECTION_HEADERS 300 #define MAX_SECTION_NAME_BUFFER_SIZE 8192 #define SECTION_HEADER_TABLE ".shstrtab" #define VVAR_SECTION "[vvar]" -DECLARE_CXTSWPROT_VAR(static mutex_t dump_core_lock, INIT_LOCK_FREE(dump_core_lock)); - typedef struct _section_header_info_t { app_pc vm_start; app_pc vm_end; @@ -175,9 +174,16 @@ os_dump_core_internal(void) memquery_iter_t iter; if (!memquery_iterator_start(&iter, NULL, /*may_alloc=*/false)) { - SYSLOG_INTERNAL_ERROR("Too many section headers."); + SYSLOG_INTERNAL_ERROR("memquery_iterator_start failed."); return false; } + // 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) { @@ -217,7 +223,7 @@ os_dump_core_internal(void) memquery_iterator_stop(&iter); // Add the string table section. Append the section name ".shstrtab" to the - // section names table. + // 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); @@ -267,7 +273,7 @@ os_dump_core_internal(void) return false; } } - // Write the section names section. + // 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 " @@ -279,9 +285,12 @@ os_dump_core_internal(void) // 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 (section_header_info[section_index].prot & PROT_WRITE) { + if (TEST(PROT_WRITE, section_header_info[section_index].prot)) { flags |= SHF_WRITE; } if (!write_section_header( @@ -298,7 +307,7 @@ os_dump_core_internal(void) file_offset += section_header_info[section_index].vm_end - section_header_info[section_index].vm_start; } - // Write the section names section. + // 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, @@ -320,41 +329,17 @@ os_dump_core_internal(void) bool os_dump_core_live(void) { - static thread_id_t current_dumping_thread_id VAR_IN_SECTION(NEVER_PROTECTED_SECTION) = - 0; - thread_id_t current_id = d_r_get_thread_id(); -#ifdef DEADLOCK_AVOIDANCE - dcontext_t *dcontext = get_thread_private_dcontext(); - thread_locks_t *old_thread_owned_locks = NULL; -#endif - - if (current_id == current_dumping_thread_id) { - return false; /* avoid infinite loop */ - } - -#ifdef DEADLOCK_AVOIDANCE - /* First turn off deadlock avoidance for this thread (needed for live dump - * to try to grab all_threads and thread_initexit locks). - */ - if (dcontext != NULL) { - old_thread_owned_locks = dcontext->thread_owned_locks; - dcontext->thread_owned_locks = NULL; + uint num_threads; + void **drcontexts = NULL; + // Suspend all threads including native threads to ensure the memory regions + // do not change in the middle of the core dump. + if (!dr_suspend_all_other_threads_ex(&drcontexts, &num_threads, NULL, + DR_SUSPEND_NATIVE)) { + return false; } -#endif - /* Only allow one thread to dumpcore at a time, also protects static - * buffers and current_dumping_thread_id. - */ - d_r_mutex_lock(&dump_core_lock); - current_dumping_thread_id = current_id; const bool ret = os_dump_core_internal(); - current_dumping_thread_id = 0; - d_r_mutex_unlock(&dump_core_lock); - -#ifdef DEADLOCK_AVOIDANCE - /* Restore deadlock avoidance for this thread. */ - if (dcontext != NULL) { - dcontext->thread_owned_locks = old_thread_owned_locks; + if (!dr_resume_all_other_threads(drcontexts, num_threads)) { + return false; } -#endif return ret; } diff --git a/core/utils.c b/core/utils.c index 29a71cc1ba3..50846eb6ae9 100644 --- a/core/utils.c +++ b/core/utils.c @@ -417,8 +417,7 @@ locks_not_closed() cur_lock->rank == LOCK_RANK(options_lock) /* This lock can be used parallel to detach cleanup. */ - IF_UNIX(|| cur_lock->rank == LOCK_RANK(detached_sigact_lock)) - IF_LINUX(|| cur_lock->rank == LOCK_RANK(dump_core_lock)))) { + IF_UNIX(|| cur_lock->rank == LOCK_RANK(detached_sigact_lock)))) { /* i#1058: curiosities during exit re-acquire these locks. */ ignored++; } else { @@ -2947,7 +2946,6 @@ get_unique_logfile(const char *file_type, char *filename_buffer, uint maxlen, if (NULL != filename_buffer) { strncpy(filename_buffer, buf, maxlen); filename_buffer[maxlen - 1] = '\0'; /* NULL terminate */ - LOG(GLOBAL, LOG_ALL, 0, "dump core file name is %s\n", buf); } return success; diff --git a/core/utils.h b/core/utils.h index 8d8517a628a..f78f5790a0f 100644 --- a/core/utils.h +++ b/core/utils.h @@ -618,7 +618,7 @@ enum { # elif defined(MACOS) LOCK_RANK(memquery_backing_lock), # endif -# if defined(WINDOWS) || defined(LINUX) +# ifdef WINDOWS LOCK_RANK(dump_core_lock), # endif From ed8e3dae93ac329681e71679bbbc0a35fad9edfd Mon Sep 17 00:00:00 2001 From: Kai Date: Fri, 1 Nov 2024 18:00:58 +0000 Subject: [PATCH 05/19] Change FIXME in elf_defines.h to XXX. --- core/unix/elf_defines.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/unix/elf_defines.h b/core/unix/elf_defines.h index 1ff3d106923..b9a51f3cde5 100644 --- a/core/unix/elf_defines.h +++ b/core/unix/elf_defines.h @@ -208,11 +208,11 @@ # 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. */ +/* 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. */ -/* FIXME i#3544: R_RISCV_IRELATIVE was added after libc 2.31 and some distros +/* 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. */ From 78d97af38437f84052da5e6c6f326f147dc5d1fb Mon Sep 17 00:00:00 2001 From: Kai Date: Tue, 5 Nov 2024 17:59:32 +0000 Subject: [PATCH 06/19] Add unit test to cover dr_create_memory_dump for X64 Linux. --- .../client-interface/memory_dump_test.dll.c | 69 +++++++++++++++++++ .../client-interface/memory_dump_test.expect | 3 + .../client-interface/memroy_dump_test.runall | 1 + 3 files changed, 73 insertions(+) create mode 100644 suite/tests/client-interface/memory_dump_test.dll.c create mode 100644 suite/tests/client-interface/memory_dump_test.expect create mode 100644 suite/tests/client-interface/memroy_dump_test.runall 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..9eb8d27e7e1 --- /dev/null +++ b/suite/tests/client-interface/memory_dump_test.dll.c @@ -0,0 +1,69 @@ +/* ********************************************************** + * 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" + +#include "dr_api.h" +#include "client_tools.h" + +static bool saw_thread_init_event = false; + +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) +{ + dr_fprintf(STDERR, "thread init\n"); + saw_thread_init_event = true; + + dr_memory_dump_spec_t spec; + spec.size = sizeof(dr_memory_dump_spec_t); + spec.flags = DR_MEMORY_DUMP_ELF; + if (!dr_create_memory_dump(&spec)) + dr_fprintf(STDERR, "Error: failed to create memory dump.\n"); +} + +DR_EXPORT +void +dr_init(client_id_t id) +{ + void *drcontext = dr_get_current_drcontext(); + dr_register_exit_event(dr_exit); + dr_register_thread_init_event(dr_thread_init); + 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..1586392cb28 --- /dev/null +++ b/suite/tests/client-interface/memory_dump_test.expect @@ -0,0 +1,3 @@ +thank you for testing memory dump +thread init +Hello, world! diff --git a/suite/tests/client-interface/memroy_dump_test.runall b/suite/tests/client-interface/memroy_dump_test.runall new file mode 100644 index 00000000000..23b651e0b70 --- /dev/null +++ b/suite/tests/client-interface/memroy_dump_test.runall @@ -0,0 +1 @@ + From 26edb929f9af3787d58adfc6de482af7c35b2f07 Mon Sep 17 00:00:00 2001 From: Kai Date: Tue, 5 Nov 2024 23:34:18 +0000 Subject: [PATCH 07/19] Remove strhash_table_t which triggers lock rack errors in debug build. Add suite/tests/CMakeLists.txt which was missed in the last commit. Use synch_with_all_threads instead of dr_suspend_all_other_threads_ex. --- core/unix/coredump.c | 38 +++++++++---------- suite/tests/CMakeLists.txt | 3 ++ .../client-interface/memory_dump_test.dll.c | 1 - .../client-interface/memroy_dump_test.runall | 1 - 4 files changed, 22 insertions(+), 21 deletions(-) delete mode 100644 suite/tests/client-interface/memroy_dump_test.runall diff --git a/core/unix/coredump.c b/core/unix/coredump.c index 57abbe63554..daa919b9f63 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -30,10 +30,8 @@ #include #include "../globals.h" -#include - -#include "../hashtable.h" #include "../os_shared.h" +#include "../synch.h" #include "dr_tools.h" #include "elf_defines.h" #include "memquery.h" @@ -158,14 +156,11 @@ write_section_header(DR_PARAM_IN file_t elf_file, static bool os_dump_core_internal(void) { - strhash_table_t *string_htable = - strhash_hash_create(GLOBAL_DCONTEXT, /*bits=*/8, /*load_factor_percent=*/80, - /*table_flags=*/0, NULL _IF_DEBUG("mmap-string-table")); - // 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; @@ -191,11 +186,11 @@ os_dump_core_internal(void) } ELF_ADDR offset = 0; if (iter.comment != NULL && iter.comment[0] != '\0') { - offset = (ELF_ADDR)strhash_hash_lookup(GLOBAL_DCONTEXT, string_htable, - iter.comment); + const char *pos = strstr(&string_table[1], iter.comment); + if (pos != NULL) { + offset = pos - string_table; + } 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 " @@ -319,7 +314,6 @@ os_dump_core_internal(void) } os_close(elf_file); - strhash_hash_destroy(GLOBAL_DCONTEXT, string_htable); return true; } @@ -329,17 +323,23 @@ os_dump_core_internal(void) bool os_dump_core_live(void) { - uint num_threads; - void **drcontexts = NULL; // Suspend all threads including native threads to ensure the memory regions // do not change in the middle of the core dump. - if (!dr_suspend_all_other_threads_ex(&drcontexts, &num_threads, NULL, - DR_SUSPEND_NATIVE)) { + 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(); - if (!dr_resume_all_other_threads(drcontexts, num_threads)) { - return false; - } + + end_synch_with_all_threads(threads, num_threads, + /*resume=*/true); return ret; } diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index 5bb52650edc..b6b0016798a 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 (LINUX AND X64) + 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 index 9eb8d27e7e1..3c0df5cb7a1 100644 --- a/suite/tests/client-interface/memory_dump_test.dll.c +++ b/suite/tests/client-interface/memory_dump_test.dll.c @@ -62,7 +62,6 @@ DR_EXPORT void dr_init(client_id_t id) { - void *drcontext = dr_get_current_drcontext(); dr_register_exit_event(dr_exit); dr_register_thread_init_event(dr_thread_init); dr_fprintf(STDERR, "thank you for testing memory dump\n"); diff --git a/suite/tests/client-interface/memroy_dump_test.runall b/suite/tests/client-interface/memroy_dump_test.runall deleted file mode 100644 index 23b651e0b70..00000000000 --- a/suite/tests/client-interface/memroy_dump_test.runall +++ /dev/null @@ -1 +0,0 @@ - From db46f71fcf8c0dbababc777822a0edb770d14e94 Mon Sep 17 00:00:00 2001 From: Kai Date: Tue, 5 Nov 2024 23:37:08 +0000 Subject: [PATCH 08/19] Update release note. --- api/docs/release.dox | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/docs/release.dox b/api/docs/release.dox index 7bead1e9cd3..dd2e87fedcd 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: - - Added X64 Linux support to dr_create_memory_dump(). + - 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: From fdaf5444520b1421ee41c7cc2ebc4a08e11f8430 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 6 Nov 2024 01:39:18 +0000 Subject: [PATCH 09/19] Adding more details to error message. --- core/unix/coredump.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/unix/coredump.c b/core/unix/coredump.c index daa919b9f63..05fafd97d6e 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -264,6 +264,10 @@ os_dump_core_internal(void) 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; } From d8a517395e6ab40ea1c1c1a9203ddaaf0eb487f0 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 6 Nov 2024 02:11:07 +0000 Subject: [PATCH 10/19] Skip vsyscall region. --- core/unix/coredump.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/unix/coredump.c b/core/unix/coredump.c index 05fafd97d6e..e4ebdecbdb3 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -40,6 +40,7 @@ #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; @@ -181,7 +182,8 @@ os_dump_core_internal(void) // 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) { + if (iter.prot == MEMPROT_NONE || strcmp(iter.comment, VVAR_SECTION) == 0 || + strcmp(iter.comment, VSYSCALL_SECTION) == 0) { continue; } ELF_ADDR offset = 0; From 9d5a96cec1230dcc82a9be9a2c7f5728e438bbb7 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 6 Nov 2024 16:00:03 +0000 Subject: [PATCH 11/19] Add a TODO to check the content of the memory dump file. --- suite/tests/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index b6b0016798a..5e83018e035 100644 --- a/suite/tests/CMakeLists.txt +++ b/suite/tests/CMakeLists.txt @@ -2647,6 +2647,7 @@ if (NOT ANDROID) # TODO i#38: Port test to Android. client-interface/attach_blocking.runall "" "" "") endif () if (LINUX AND X64) + # TODO i#7046: Verify the content of the output file. tobuild_ci(client.memory_dump_test client-interface/memory_dump_test.c "" "" "") endif () endif () From 0e674a00cd83a7183a4a282055348279b7e552b5 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 6 Nov 2024 20:37:09 +0000 Subject: [PATCH 12/19] Use nudge event for memory dump test. Enable Windows memory dump test. --- core/lib/dr_tools.h | 3 +- suite/tests/CMakeLists.txt | 1 - .../client-interface/memory_dump_test.dll.c | 33 +++++++++++++++---- .../client-interface/memory_dump_test.expect | 1 + 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/core/lib/dr_tools.h b/core/lib/dr_tools.h index b48e7623d18..7170e8d83c2 100644 --- a/core/lib/dr_tools.h +++ b/core/lib/dr_tools.h @@ -342,7 +342,8 @@ typedef enum { */ DR_MEMORY_DUMP_LDMP = 0x0001, /** - * Memory dump in Executable and Linkable Format. + * Memory dump in Executable and Linkable Format. This API has the same + * restriction as dr_suspend_all_other_threads_ex(). * * \note X64 Linux only. */ diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index 5e83018e035..b6b0016798a 100644 --- a/suite/tests/CMakeLists.txt +++ b/suite/tests/CMakeLists.txt @@ -2647,7 +2647,6 @@ if (NOT ANDROID) # TODO i#38: Port test to Android. client-interface/attach_blocking.runall "" "" "") endif () if (LINUX AND X64) - # TODO i#7046: Verify the content of the output file. tobuild_ci(client.memory_dump_test client-interface/memory_dump_test.c "" "" "") endif () endif () diff --git a/suite/tests/client-interface/memory_dump_test.dll.c b/suite/tests/client-interface/memory_dump_test.dll.c index 3c0df5cb7a1..8c31745428f 100644 --- a/suite/tests/client-interface/memory_dump_test.dll.c +++ b/suite/tests/client-interface/memory_dump_test.dll.c @@ -33,10 +33,30 @@ #include "dr_api.h" #include "client_tools.h" -#include "dr_api.h" -#include "client_tools.h" +#define NUDGE_ARG_DUMP_MEMORY 1 static bool saw_thread_init_event = false; +static client_id_t client_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) @@ -51,18 +71,17 @@ dr_thread_init(void *drcontext) dr_fprintf(STDERR, "thread init\n"); saw_thread_init_event = true; - dr_memory_dump_spec_t spec; - spec.size = sizeof(dr_memory_dump_spec_t); - spec.flags = DR_MEMORY_DUMP_ELF; - if (!dr_create_memory_dump(&spec)) - dr_fprintf(STDERR, "Error: failed to create memory dump.\n"); + 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) { + 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 index 1586392cb28..903cc0a1840 100644 --- a/suite/tests/client-interface/memory_dump_test.expect +++ b/suite/tests/client-interface/memory_dump_test.expect @@ -1,3 +1,4 @@ thank you for testing memory dump thread init +nudge delivered 1 Hello, world! From 5444f3002fd2754782f8bca1bff2da0827c20dec Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 6 Nov 2024 20:53:07 +0000 Subject: [PATCH 13/19] Enable client.memory_dump_test for WIN32. --- suite/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index b6b0016798a..e0effbc1274 100644 --- a/suite/tests/CMakeLists.txt +++ b/suite/tests/CMakeLists.txt @@ -2646,7 +2646,7 @@ 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 (LINUX AND X64) + if ((LINUX AND X64) OR WIN32) tobuild_ci(client.memory_dump_test client-interface/memory_dump_test.c "" "" "") endif () endif () From f014ac02c23de9117b11e1bf6474355ed712c605 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 6 Nov 2024 21:39:48 +0000 Subject: [PATCH 14/19] Add Windows specific code to memory_dump_test.dll.c. --- .../client-interface/memory_dump_test.dll.c | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/suite/tests/client-interface/memory_dump_test.dll.c b/suite/tests/client-interface/memory_dump_test.dll.c index 8c31745428f..68d554c7700 100644 --- a/suite/tests/client-interface/memory_dump_test.dll.c +++ b/suite/tests/client-interface/memory_dump_test.dll.c @@ -32,12 +32,20 @@ #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; +#ifdef WINDOWS +static thread_id_t injection_tid; +static bool first_thread = true; +#endif + static void event_nudge(void *drcontext, uint64 arg) { @@ -63,12 +71,26 @@ dr_exit(void) { if (!saw_thread_init_event) dr_fprintf(STDERR, "Error: never saw thread init event!\n"); +#ifdef WINDOWS + dr_fprintf(STDERR, "done\n"); +#endif } static void dr_thread_init(void *drcontext) { +#ifdef WINDOWS + thread_id_t tid = dr_get_thread_id(drcontext); + // On Windows there is an additional thread used for attach injection. + if (tid != injection_tid && first_thread) { + first_thread = false; + dr_fprintf(STDERR, "thread init\n"); + } else { + return; + } +#else dr_fprintf(STDERR, "thread init\n"); +#endif saw_thread_init_event = true; if (!dr_nudge_client(client_id, NUDGE_ARG_DUMP_MEMORY)) @@ -79,6 +101,10 @@ DR_EXPORT void dr_init(client_id_t id) { +#ifdef WINDOWS + void *drcontext = dr_get_current_drcontext(); + injection_tid = dr_get_thread_id(drcontext); +#endif client_id = id; dr_register_exit_event(dr_exit); dr_register_thread_init_event(dr_thread_init); From 4543ca4c49266a06511b1621c0a6972a9dcb060d Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 6 Nov 2024 22:48:21 +0000 Subject: [PATCH 15/19] Rewrite the memory dump test for win32. --- .../client-interface/memory_dump_test.dll.c | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/suite/tests/client-interface/memory_dump_test.dll.c b/suite/tests/client-interface/memory_dump_test.dll.c index 68d554c7700..b1077773713 100644 --- a/suite/tests/client-interface/memory_dump_test.dll.c +++ b/suite/tests/client-interface/memory_dump_test.dll.c @@ -40,11 +40,7 @@ static bool saw_thread_init_event = false; static client_id_t client_id = 0; - -#ifdef WINDOWS -static thread_id_t injection_tid; -static bool first_thread = true; -#endif +static thread_id_t thread_id = 0; static void event_nudge(void *drcontext, uint64 arg) @@ -79,18 +75,11 @@ dr_exit(void) static void dr_thread_init(void *drcontext) { -#ifdef WINDOWS thread_id_t tid = dr_get_thread_id(drcontext); - // On Windows there is an additional thread used for attach injection. - if (tid != injection_tid && first_thread) { - first_thread = false; - dr_fprintf(STDERR, "thread init\n"); - } else { + if (tid != thread_id) return; - } -#else + dr_fprintf(STDERR, "thread init\n"); -#endif saw_thread_init_event = true; if (!dr_nudge_client(client_id, NUDGE_ARG_DUMP_MEMORY)) @@ -101,10 +90,9 @@ DR_EXPORT void dr_init(client_id_t id) { -#ifdef WINDOWS void *drcontext = dr_get_current_drcontext(); - injection_tid = dr_get_thread_id(drcontext); -#endif + thread_id = dr_get_thread_id(drcontext); + client_id = id; dr_register_exit_event(dr_exit); dr_register_thread_init_event(dr_thread_init); From 5ad051a6783b62528faf33d366d234879e290949 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 6 Nov 2024 23:21:55 +0000 Subject: [PATCH 16/19] Limit the memory dump test to X64 only. --- suite/tests/CMakeLists.txt | 2 +- suite/tests/client-interface/memory_dump_test.dll.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index e0effbc1274..c7cd5363549 100644 --- a/suite/tests/CMakeLists.txt +++ b/suite/tests/CMakeLists.txt @@ -2646,7 +2646,7 @@ 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 ((LINUX AND X64) OR WIN32) + if (X64 AND (LINUX OR WIN32)) tobuild_ci(client.memory_dump_test client-interface/memory_dump_test.c "" "" "") endif () endif () diff --git a/suite/tests/client-interface/memory_dump_test.dll.c b/suite/tests/client-interface/memory_dump_test.dll.c index b1077773713..0113dc4ad42 100644 --- a/suite/tests/client-interface/memory_dump_test.dll.c +++ b/suite/tests/client-interface/memory_dump_test.dll.c @@ -67,9 +67,6 @@ dr_exit(void) { if (!saw_thread_init_event) dr_fprintf(STDERR, "Error: never saw thread init event!\n"); -#ifdef WINDOWS - dr_fprintf(STDERR, "done\n"); -#endif } static void From 73fa0fd7cb42cd0549bb77b6c1aef4ffeffd9bc1 Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 7 Nov 2024 02:03:50 +0000 Subject: [PATCH 17/19] Use a hashtable instead of strstr() to look up existing section names. --- core/unix/coredump.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/core/unix/coredump.c b/core/unix/coredump.c index e4ebdecbdb3..a9368fa58a5 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -30,6 +30,7 @@ #include #include "../globals.h" +#include "../hashtable.h" #include "../os_shared.h" #include "../synch.h" #include "dr_tools.h" @@ -169,10 +170,18 @@ os_dump_core_internal(void) section_header_info_t section_header_info[MAX_SECTION_HEADERS + 1]; memquery_iter_t iter; - if (!memquery_iterator_start(&iter, NULL, /*may_alloc=*/false)) { + 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); + // All other threads have been suspended, we can make it a shared table with + // lock. + 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 @@ -188,11 +197,12 @@ os_dump_core_internal(void) } ELF_ADDR offset = 0; if (iter.comment != NULL && iter.comment[0] != '\0') { - const char *pos = strstr(&string_table[1], iter.comment); - if (pos != NULL) { - offset = pos - string_table; - } + 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 " @@ -205,6 +215,7 @@ os_dump_core_internal(void) 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; @@ -217,6 +228,7 @@ os_dump_core_internal(void) 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 From 5b5a3fdced8a577c8ba7943afbbfdfb7e87d8294 Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 7 Nov 2024 02:08:09 +0000 Subject: [PATCH 18/19] Fix the format. --- core/unix/coredump.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/unix/coredump.c b/core/unix/coredump.c index a9368fa58a5..dcd78d52da6 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -178,9 +178,9 @@ os_dump_core_internal(void) ASSERT(d_r_get_num_threads() == 1); // All other threads have been suspended, we can make it a shared table with // lock. - 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")); + 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 From 0e9584b69926197e11c86040c7f040a2d34e95fa Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 7 Nov 2024 17:47:59 +0000 Subject: [PATCH 19/19] Incorporate review comments. --- core/lib/dr_tools.h | 2 +- core/unix/coredump.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/lib/dr_tools.h b/core/lib/dr_tools.h index 7170e8d83c2..3f453407bf3 100644 --- a/core/lib/dr_tools.h +++ b/core/lib/dr_tools.h @@ -343,7 +343,7 @@ typedef enum { DR_MEMORY_DUMP_LDMP = 0x0001, /** * Memory dump in Executable and Linkable Format. This API has the same - * restriction as dr_suspend_all_other_threads_ex(). + * restrictions in where it can be called from as dr_suspend_all_other_threads_ex(). * * \note X64 Linux only. */ diff --git a/core/unix/coredump.c b/core/unix/coredump.c index dcd78d52da6..f77f0fa9849 100644 --- a/core/unix/coredump.c +++ b/core/unix/coredump.c @@ -176,8 +176,9 @@ os_dump_core_internal(void) } ASSERT(d_r_get_num_threads() == 1); - // All other threads have been suspended, we can make it a shared table with - // lock. + // 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"));