Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

i#7046: Add register values to the output of X64 Linux dr_create_memory_dump(). #7088

Merged
merged 16 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion api/docs/release.dox
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ changes:

Further non-compatibility-affecting changes include:
- Added X64 Linux support to dr_create_memory_dump(). This API has the same
restriction as dr_suspend_all_other_threads_ex().
restriction as dr_suspend_all_other_threads_ex(). For X86_64 platform, the feature is
supported only when fast FP save and restore is supported. And mixed mode is not
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
supported.
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved

**************************************************
<hr>
Expand Down
4 changes: 3 additions & 1 deletion core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,9 @@ 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)
if (AARCH64 OR (X86 AND X64))
set(OS_SRCS ${OS_SRCS} unix/coredump.c)
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
endif ()
endif ()
set(OS_SRCS ${OS_SRCS} unix/memquery_linux.c)
set(OS_SRCS ${OS_SRCS} unix/memquery.c)
Expand Down
7 changes: 4 additions & 3 deletions core/lib/instrument.c
Original file line number Diff line number Diff line change
Expand Up @@ -2498,9 +2498,10 @@ 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();
#elif defined(LINUX) && ((defined(X64) && defined(X86)) || defined(AARCH64))
if (TEST(DR_MEMORY_DUMP_ELF, spec->flags)) {
return os_dump_core_live(get_thread_private_dcontext());
}
#endif
return false;
}
Expand Down
261 changes: 250 additions & 11 deletions core/unix/coredump.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,50 @@
#include <elf.h>
#include <stddef.h>
#include <sys/mman.h>
#include <sys/procfs.h>
#include "../globals.h"

#include "../hashtable.h"
#include "../os_shared.h"
#include "../synch.h"
#include "../utils.h"
#include "../ir/decode.h"
#include "../lib/dr_ir_utils.h"
#include "../lib/globals_api.h"
#include "../lib/globals_shared.h"
#include "../lib/instrument.h"

#include "dr_tools.h"
#include "elf_defines.h"
#include "memquery.h"

/* Only X64 is supported. */
#ifndef X64
# error Unsupported architecture
#endif

#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]"
/*
* The length of the name has to be a multiple of eight (for X64) to ensure the next
* field (descriptor) is 8-byte aligned. Null characters are added at the end as
* padding to increase the length to eight. The name CORE is used following the example
* of core dump files.
*/
#define NOTE_OWNER "CORE\0\0\0\0"
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
#define NOTE_OWNER_LENGTH 8
/*
* Two notes, NT_PRSTATUS (prstatus structure) and NT_FPREGSET (floating point registers),
* are written to the output file.
*/
#define PROGRAM_HEADER_NOTE_LENGTH \
(sizeof(ELF_NOTE_HEADER_TYPE) + NOTE_OWNER_LENGTH + sizeof(struct elf_prstatus) + \
sizeof(ELF_NOTE_HEADER_TYPE) + NOTE_OWNER_LENGTH + sizeof(elf_fpregset_t))
#define PROGRAM_HEADER_LENGTH \
(sizeof(ELF_PROGRAM_HEADER_TYPE) + PROGRAM_HEADER_NOTE_LENGTH)

typedef struct _section_header_info_t {
app_pc vm_start;
Expand All @@ -53,6 +81,31 @@ typedef struct _section_header_info_t {
ELF_ADDR name_offset;
} section_header_info_t;

/*
* Please reference
* https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/
* 2021-8/intrinsics-to-save-and-restore-ext-proc-states.html for the memory layout
* used by fxsave64.
*/
typedef struct _fxsave64_map_t {
unsigned short fcw;
unsigned short fsw;
unsigned short ftw;
unsigned short reserved1;
unsigned short fop;
unsigned int fip;
unsigned short fcs;
unsigned short reserved2;
unsigned int fdp;
unsigned int fds;
unsigned short reserved3;
unsigned short mxcsr;
unsigned short mxcsr_mask;
unsigned int st_space[32];
unsigned int xmm_space[64];
unsigned int padding[24];
} fxsave64_map_t;
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved

/*
* Writes an ELF header to the file. Returns true if the ELF header is written to the core
* dump file, false otherwise.
Expand Down Expand Up @@ -154,13 +207,186 @@ write_section_header(DR_PARAM_IN file_t elf_file,
return os_write(elf_file, (void *)&shdr, sizeof(shdr)) == sizeof(shdr);
}

/*
* Copy dr_mcontext_t register values to user_regs_struct.
*/
static void
mcontext_to_user_regs(DR_PARAM_IN priv_mcontext_t *mcontext,
DR_PARAM_OUT struct user_regs_struct *regs)
{
#ifdef DR_HOST_NOT_TARGET
return;
#elif defined(X86)
regs->rax = mcontext->rax;
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
regs->rcx = mcontext->rcx;
regs->rdx = mcontext->rdx;
regs->rbx = mcontext->rbx;
regs->rsp = mcontext->rsp;
regs->rbp = mcontext->rbp;
regs->rsi = mcontext->rsi;
regs->rdi = mcontext->rdi;
regs->r8 = mcontext->r8;
regs->r9 = mcontext->r9;
regs->r10 = mcontext->r10;
regs->r11 = mcontext->r11;
regs->r12 = mcontext->r12;
regs->r13 = mcontext->r13;
regs->r14 = mcontext->r14;
regs->r15 = mcontext->r15;
regs->rip = (uint64_t)mcontext->rip;
regs->eflags = mcontext->rflags;
#elif defined(AARCH64)
regs->regs[0] = mcontext->r0;
regs->regs[1] = mcontext->r1;
regs->regs[2] = mcontext->r2;
regs->regs[3] = mcontext->r3;
regs->regs[4] = mcontext->r4;
regs->regs[5] = mcontext->r5;
regs->regs[6] = mcontext->r6;
regs->regs[7] = mcontext->r7;
regs->regs[8] = mcontext->r8;
regs->regs[9] = mcontext->r9;
regs->regs[10] = mcontext->r10;
regs->regs[11] = mcontext->r11;
regs->regs[12] = mcontext->r12;
regs->regs[13] = mcontext->r13;
regs->regs[14] = mcontext->r14;
regs->regs[15] = mcontext->r15;
regs->regs[16] = mcontext->r16;
regs->regs[17] = mcontext->r17;
regs->regs[18] = mcontext->r18;
regs->regs[19] = mcontext->r19;
regs->regs[20] = mcontext->r20;
regs->regs[21] = mcontext->r21;
regs->regs[22] = mcontext->r22;
regs->regs[23] = mcontext->r23;
regs->regs[24] = mcontext->r24;
regs->regs[25] = mcontext->r25;
regs->regs[26] = mcontext->r26;
regs->regs[27] = mcontext->r27;
regs->regs[28] = mcontext->r28;
regs->regs[29] = mcontext->r29;
regs->regs[30] = mcontext->r30;
regs->sp = mcontext->sp;
regs->pc = (uint64_t)mcontext->pc;
#else
# error Unsupported architecture
#endif
}

/*
* Write prstatus structure to the file. Returns true if the note is written to
* the file, false otherwise.
*/
static bool
write_prstatus_note(DR_PARAM_IN priv_mcontext_t *mc, DR_PARAM_IN file_t elf_file)
{
struct elf_prstatus prstatus;
struct user_regs_struct *regs = (struct user_regs_struct *)&prstatus.pr_reg;
mcontext_to_user_regs(mc, regs);

ELF_NOTE_HEADER_TYPE nhdr;
// Add one to include the terminating null character.
nhdr.n_namesz = strlen(NOTE_OWNER) + 1;
nhdr.n_descsz = sizeof(prstatus);
nhdr.n_type = NT_PRSTATUS;
if (os_write(elf_file, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) {
return false;
}
if (os_write(elf_file, NOTE_OWNER, NOTE_OWNER_LENGTH) != NOTE_OWNER_LENGTH) {
return false;
}
return os_write(elf_file, &prstatus, sizeof(prstatus)) == sizeof(prstatus);
}

/*
* Copy register values from dr_mcontext_t to elf_fpregset_t for AARCH64. For
* X86, copy the floating point registers from the output of fxsave64.
*/
static bool
get_floating_point_registers(DR_PARAM_IN dcontext_t *dcontext,
DR_PARAM_IN priv_mcontext_t *mc,
DR_PARAM_OUT elf_fpregset_t *regs)
{
#ifdef DR_HOST_NOT_TARGET
return false;
#elif defined(X86)
// Fast FP save and restore support is required.
ASSERT(proc_has_feature(FEATURE_FXSR));
// Mixed mode is not supported.
ASSERT(X64_MODE_DC(dcontext));
byte fpstate_buf[MAX_FP_STATE_SIZE];
fxsave64_map_t *fpstate =
(fxsave64_map_t *)ALIGN_FORWARD(fpstate_buf, DR_FPSTATE_ALIGN);
if (proc_save_fpstate((byte *)fpstate) != DR_FPSTATE_BUF_SIZE) {
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
ivankyluk marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

regs->cwd = fpstate->fcw;
regs->swd = fpstate->fsw;
regs->ftw = fpstate->ftw;
regs->fop = fpstate->fop;
regs->mxcsr = fpstate->mxcsr;
regs->mxcr_mask = fpstate->mxcsr;
/* 8*16 bytes for each FP-reg = 128 bytes */
for (int i = 0; i < 32; ++i) {
regs->st_space[i] = fpstate->st_space[i];
}
/* 16*16 bytes for each XMM-reg = 256 bytes */
for (int i = 0; i < 64; ++i) {
regs->xmm_space[i] = fpstate->xmm_space[i];
}
return true;
#elif defined(AARCH64)
regs->fpsr = mc->fpsr;
regs->fpcr = mc->fpcr;
for (int i = 0; i < MCXT_NUM_SIMD_SVE_SLOTS; ++i) {
memcpy(&regs->vregs[i], &mc->simd[i].q, sizeof(mc->simd[i].q));
}
return true;
#else
return false;
#endif
}

/*
* Write floating point registers to the file. Returns true if the note is written to
* the file, false otherwise.
*/
static bool
write_fpregset_note(DR_PARAM_IN dcontext_t *dcontext, DR_PARAM_IN priv_mcontext_t *mc,
DR_PARAM_IN file_t elf_file)
{
elf_fpregset_t fpregset;
if (!get_floating_point_registers(dcontext, mc, &fpregset)) {
return false;
}

ELF_NOTE_HEADER_TYPE nhdr;
// Add one to include the terminating null character.
nhdr.n_namesz = strlen(NOTE_OWNER) + 1;
nhdr.n_descsz = sizeof(fpregset);
nhdr.n_type = NT_FPREGSET;
if (os_write(elf_file, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) {
return false;
}
if (os_write(elf_file, NOTE_OWNER, NOTE_OWNER_LENGTH) != NOTE_OWNER_LENGTH) {
return false;
}
return os_write(elf_file, &fpregset, sizeof(fpregset)) == sizeof(fpregset);
}

/*
* 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)
os_dump_core_internal(dcontext_t *dcontext)
{
priv_mcontext_t mc;
if (!dr_get_mcontext_priv(dcontext, NULL, &mc))
return false;

// 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.
Expand Down Expand Up @@ -255,21 +481,29 @@ os_dump_core_internal(void)

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,
PROGRAM_HEADER_LENGTH + 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,
if (!write_program_header(elf_file, PT_NOTE, /*flags=*/0,
/*offset=*/sizeof(ELF_HEADER_TYPE) +
sizeof(ELF_PROGRAM_HEADER_TYPE),
/*virtual_address=*/0,
/*physical_address=*/0,
/*file_size=*/0, /*memory_size=*/0, /*alignment=*/0)) {
/*file_size=*/PROGRAM_HEADER_NOTE_LENGTH,
/*memory_size=*/0, /*alignment=*/4)) {
os_close(elf_file);
return false;
}
if (!write_prstatus_note(&mc, elf_file)) {
os_close(elf_file);
return false;
}
if (!write_fpregset_note(dcontext, &mc, elf_file)) {
os_close(elf_file);
return false;
}
Expand Down Expand Up @@ -300,8 +534,8 @@ os_dump_core_internal(void)
}
// 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);
ELF_OFF file_offset = sizeof(ELF_HEADER_TYPE) + PROGRAM_HEADER_LENGTH;

// The section_count includes the section name section, so we need to skip
// it in the loop. The section name section is handled differently after
// this loop.
Expand Down Expand Up @@ -343,8 +577,12 @@ os_dump_core_internal(void)
* Returns true if a core dump file is written, false otherwise.
*/
bool
os_dump_core_live(void)
os_dump_core_live(dcontext_t *dcontext)
{
#ifdef DR_HOST_NOT_TARGET
// Memory dump is supported only when the host and the target are the same.
return false;
#endif
// Suspend all threads including native threads to ensure the memory regions
// do not change in the middle of the core dump.
int num_threads;
Expand All @@ -359,7 +597,8 @@ os_dump_core_live(void)
return false;
}

const bool ret = os_dump_core_internal();
// TODO i#7046: Add support to save register values for all threads.
const bool ret = os_dump_core_internal(dcontext);

end_synch_with_all_threads(threads, num_threads,
/*resume=*/true);
Expand Down
2 changes: 2 additions & 0 deletions core/unix/elf_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
# define ELF_HEADER_TYPE Elf64_Ehdr
# define ELF_ALTARCH_HEADER_TYPE Elf32_Ehdr
# define ELF_PROGRAM_HEADER_TYPE Elf64_Phdr
# define ELF_NOTE_HEADER_TYPE Elf64_Nhdr
# define ELF_SECTION_HEADER_TYPE Elf64_Shdr
# define ELF_DYNAMIC_ENTRY_TYPE Elf64_Dyn
# define ELF_ADDR Elf64_Addr
Expand All @@ -76,6 +77,7 @@
# define ELF_HEADER_TYPE Elf32_Ehdr
# define ELF_ALTARCH_HEADER_TYPE Elf64_Ehdr
# define ELF_PROGRAM_HEADER_TYPE Elf32_Phdr
# define ELF_NOTE_HEADER_TYPE Elf32_Nhdr
# define ELF_SECTION_HEADER_TYPE Elf32_Shdr
# define ELF_DYNAMIC_ENTRY_TYPE Elf32_Dyn
# define ELF_ADDR Elf32_Addr
Expand Down
Loading
Loading