2019-10-18 10:03:32 +01:00
|
|
|
%{
|
2019-07-29 08:57:28 +01:00
|
|
|
// Copyright 2019 - Omar Sandoval
|
|
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
|
2019-07-29 22:41:33 +01:00
|
|
|
#include "internal.h"
|
2019-07-29 08:57:28 +01:00
|
|
|
#include "platform.h"
|
2020-04-09 22:59:42 +01:00
|
|
|
#include "program.h"
|
2019-10-18 10:03:32 +01:00
|
|
|
%}
|
|
|
|
|
|
|
|
x86-64
|
|
|
|
%%
|
|
|
|
rax
|
|
|
|
rdx
|
|
|
|
rcx
|
|
|
|
rbx
|
|
|
|
rsi
|
|
|
|
rdi
|
|
|
|
rbp
|
|
|
|
rsp
|
|
|
|
r8
|
|
|
|
r9
|
|
|
|
r10
|
|
|
|
r11
|
|
|
|
r12
|
|
|
|
r13
|
|
|
|
r14
|
|
|
|
r15
|
|
|
|
# The System V ABI calls this the return address (RA) register, but it's
|
|
|
|
# effectively the instruction pointer.
|
|
|
|
rip
|
|
|
|
xmm0
|
|
|
|
xmm1
|
|
|
|
xmm2
|
|
|
|
xmm3
|
|
|
|
xmm4
|
|
|
|
xmm5
|
|
|
|
xmm6
|
|
|
|
xmm7
|
|
|
|
xmm8
|
|
|
|
xmm9
|
|
|
|
xmm10
|
|
|
|
xmm11
|
|
|
|
xmm12
|
|
|
|
xmm13
|
|
|
|
xmm14
|
|
|
|
xmm15
|
|
|
|
st0
|
|
|
|
st1
|
|
|
|
st2
|
|
|
|
st3
|
|
|
|
st4
|
|
|
|
st5
|
|
|
|
st6
|
|
|
|
st7
|
|
|
|
mm0
|
|
|
|
mm1
|
|
|
|
mm2
|
|
|
|
mm3
|
|
|
|
mm4
|
|
|
|
mm5
|
|
|
|
mm6
|
|
|
|
mm7
|
|
|
|
rFLAGS
|
|
|
|
es
|
|
|
|
cs
|
|
|
|
ss
|
|
|
|
ds
|
|
|
|
fs
|
|
|
|
gs
|
|
|
|
fs.base, 58
|
|
|
|
gs.base
|
|
|
|
tr, 62
|
|
|
|
ldtr
|
|
|
|
mxcsr
|
|
|
|
fcw
|
|
|
|
fsw
|
|
|
|
xmm16
|
|
|
|
xmm17
|
|
|
|
xmm18
|
|
|
|
xmm19
|
|
|
|
xmm20
|
|
|
|
xmm21
|
|
|
|
xmm22
|
|
|
|
xmm23
|
|
|
|
xmm24
|
|
|
|
xmm25
|
|
|
|
xmm26
|
|
|
|
xmm27
|
|
|
|
xmm28
|
|
|
|
xmm29
|
|
|
|
xmm30
|
|
|
|
xmm31
|
|
|
|
k0, 118
|
|
|
|
k1
|
|
|
|
k2
|
|
|
|
k3
|
|
|
|
k4
|
|
|
|
k5
|
|
|
|
k6
|
|
|
|
k7
|
|
|
|
bnd0
|
|
|
|
bnd1
|
|
|
|
bnd2
|
|
|
|
bnd3
|
|
|
|
%%
|
2019-07-29 08:57:28 +01:00
|
|
|
|
2019-10-24 22:26:45 +01:00
|
|
|
static const struct drgn_frame_register frame_registers_x86_64[] = {
|
2019-10-28 18:36:18 +00:00
|
|
|
{ DRGN_REGISTER_X86_64_rax, 8, 192, "ax", "rax", },
|
|
|
|
{ DRGN_REGISTER_X86_64_rdx, 8, 208, "dx", "rdx", },
|
|
|
|
{ DRGN_REGISTER_X86_64_rcx, 8, 200, "cx", "rcx", },
|
|
|
|
{ DRGN_REGISTER_X86_64_rbx, 8, 152, "bx", "rbx", },
|
|
|
|
{ DRGN_REGISTER_X86_64_rsi, 8, 216, "si", "rsi", },
|
|
|
|
{ DRGN_REGISTER_X86_64_rdi, 8, 224, "di", "rdi", },
|
|
|
|
{ DRGN_REGISTER_X86_64_rbp, 8, 144, "bp", "rbp", },
|
|
|
|
{ DRGN_REGISTER_X86_64_rsp, 8, 264, "sp", "rsp", },
|
|
|
|
{ DRGN_REGISTER_X86_64_r8, 8, 184, "r8", },
|
|
|
|
{ DRGN_REGISTER_X86_64_r9, 8, 176, "r9", },
|
|
|
|
{ DRGN_REGISTER_X86_64_r10, 8, 168, "r10", },
|
|
|
|
{ DRGN_REGISTER_X86_64_r11, 8, 160, "r11", },
|
|
|
|
{ DRGN_REGISTER_X86_64_r12, 8, 136, "r12", },
|
|
|
|
{ DRGN_REGISTER_X86_64_r13, 8, 128, "r13", },
|
|
|
|
{ DRGN_REGISTER_X86_64_r14, 8, 120, "r14", },
|
|
|
|
{ DRGN_REGISTER_X86_64_r15, 8, 112, "r15", },
|
|
|
|
{ DRGN_REGISTER_X86_64_rip, 8, 240, "ip", "rip", },
|
2019-10-24 22:26:45 +01:00
|
|
|
};
|
|
|
|
|
2019-07-29 22:41:33 +01:00
|
|
|
static inline struct drgn_error *read_register(struct drgn_object *reg_obj,
|
|
|
|
struct drgn_object *frame_obj,
|
|
|
|
const char *name,
|
|
|
|
Dwarf_Addr *ret)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
uint64_t reg;
|
|
|
|
|
|
|
|
err = drgn_object_member_dereference(reg_obj, frame_obj, name);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
err = drgn_object_read_unsigned(reg_obj, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
*ret = reg;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct drgn_error *
|
2019-11-22 19:57:51 +00:00
|
|
|
set_initial_registers_inactive_task_frame(Dwfl_Thread *thread,
|
|
|
|
struct drgn_object *frame_obj)
|
2019-07-29 22:41:33 +01:00
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
2019-11-22 19:57:51 +00:00
|
|
|
struct drgn_object reg_obj;
|
2019-07-29 22:41:33 +01:00
|
|
|
Dwarf_Word dwarf_regs[5];
|
|
|
|
uint64_t sp;
|
|
|
|
|
2019-11-22 19:57:51 +00:00
|
|
|
drgn_object_init(®_obj, frame_obj->prog);
|
2019-07-29 22:41:33 +01:00
|
|
|
|
2019-11-22 19:57:51 +00:00
|
|
|
err = read_register(®_obj, frame_obj, "bx", &dwarf_regs[0]);
|
2019-07-29 22:41:33 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
/* rbx is register 3. */
|
|
|
|
if (!dwfl_thread_state_registers(thread, 3, 1, dwarf_regs)) {
|
|
|
|
err = drgn_error_libdwfl();
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2019-11-22 19:57:51 +00:00
|
|
|
err = read_register(®_obj, frame_obj, "bp", &dwarf_regs[0]);
|
2019-07-29 22:41:33 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2019-11-22 19:57:51 +00:00
|
|
|
err = drgn_object_read_unsigned(frame_obj, &sp);
|
2019-07-29 22:41:33 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2019-11-22 19:57:51 +00:00
|
|
|
/* rbp is register 6. */
|
|
|
|
if (!dwfl_thread_state_registers(thread, 6, 1, dwarf_regs)) {
|
2019-07-29 22:41:33 +01:00
|
|
|
err = drgn_error_libdwfl();
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2019-11-22 19:57:51 +00:00
|
|
|
err = read_register(®_obj, frame_obj, "r12", &dwarf_regs[0]);
|
2019-07-29 22:41:33 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2019-11-22 19:57:51 +00:00
|
|
|
err = read_register(®_obj, frame_obj, "r13", &dwarf_regs[1]);
|
2019-07-29 22:41:33 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2019-11-22 19:57:51 +00:00
|
|
|
err = read_register(®_obj, frame_obj, "r14", &dwarf_regs[2]);
|
2019-07-29 22:41:33 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2019-11-22 19:57:51 +00:00
|
|
|
err = read_register(®_obj, frame_obj, "r15", &dwarf_regs[3]);
|
2019-07-29 22:41:33 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2019-11-22 19:57:51 +00:00
|
|
|
err = read_register(®_obj, frame_obj, "ret_addr", &dwarf_regs[4]);
|
2019-07-29 22:41:33 +01:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
/* r12-r15 are registers 12-15; register 16 is the return address. */
|
2019-11-22 19:57:51 +00:00
|
|
|
if (!dwfl_thread_state_registers(thread, 12, 5, dwarf_regs)) {
|
2019-07-29 22:41:33 +01:00
|
|
|
err = drgn_error_libdwfl();
|
2019-11-22 19:57:51 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2019-07-29 22:41:33 +01:00
|
|
|
|
2019-11-22 19:57:51 +00:00
|
|
|
err = NULL;
|
2019-07-29 22:41:33 +01:00
|
|
|
out:
|
|
|
|
drgn_object_deinit(®_obj);
|
2019-11-22 19:57:51 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct drgn_error *
|
|
|
|
linux_kernel_set_initial_registers_x86_64(Dwfl_Thread *thread,
|
|
|
|
const struct drgn_object *task_obj)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_program *prog = task_obj->prog;
|
|
|
|
struct drgn_object sp_obj;
|
|
|
|
struct drgn_qualified_type frame_type;
|
|
|
|
uint64_t sp;
|
|
|
|
Dwarf_Word dwarf_reg;
|
|
|
|
|
|
|
|
drgn_object_init(&sp_obj, prog);
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
err = drgn_object_member_dereference(&sp_obj, task_obj, "thread");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_member(&sp_obj, &sp_obj, "sp");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_unsigned(&sp_obj, &sp);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
dwarf_reg = sp;
|
|
|
|
/* rsp is register 7. */
|
|
|
|
if (!dwfl_thread_state_registers(thread, 7, 1, &dwarf_reg)) {
|
|
|
|
err = drgn_error_libdwfl();
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since Linux kernel commit 0100301bfdf5 ("sched/x86: Rewrite the
|
|
|
|
* switch_to() code") (in v4.9), sp points to a struct
|
|
|
|
* inactive_task_frame, which we can use to get most registers. Before
|
|
|
|
* that, it points to bp.
|
|
|
|
*/
|
|
|
|
err = drgn_program_find_type(prog, "struct inactive_task_frame *", NULL,
|
|
|
|
&frame_type);
|
|
|
|
if (!err) {
|
|
|
|
err = drgn_object_cast(&sp_obj, frame_type, &sp_obj);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = set_initial_registers_inactive_task_frame(thread,
|
|
|
|
&sp_obj);
|
|
|
|
|
|
|
|
} else if (err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
uint64_t bp;
|
|
|
|
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
err = drgn_program_find_type(prog, "unsigned long", NULL,
|
|
|
|
&frame_type);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_set_reference(&sp_obj, frame_type, sp, 0, 0,
|
|
|
|
DRGN_PROGRAM_ENDIAN);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_read_unsigned(&sp_obj, &bp);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
dwarf_reg = bp;
|
|
|
|
/* rbp is register 6. */
|
|
|
|
if (!dwfl_thread_state_registers(thread, 6, 1, &dwarf_reg)) {
|
|
|
|
err = drgn_error_libdwfl();
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
err = NULL;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
drgn_object_deinit(&sp_obj);
|
2019-07-29 22:41:33 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-04-09 22:59:42 +01:00
|
|
|
static struct drgn_error *
|
|
|
|
linux_kernel_get_page_offset_x86_64(struct drgn_program *prog, uint64_t *ret)
|
|
|
|
{
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_object obj;
|
|
|
|
bool nonzero;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If KASLR is enabled, PAGE_OFFSET is easily available via
|
|
|
|
* page_offset_base.
|
|
|
|
*/
|
|
|
|
drgn_object_init(&obj, prog);
|
|
|
|
err = drgn_program_find_object(prog, "page_offset_base", NULL,
|
|
|
|
DRGN_FIND_OBJECT_VARIABLE, &obj);
|
|
|
|
if (!err) {
|
|
|
|
err = drgn_object_read_unsigned(&obj, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (err->code == DRGN_ERROR_LOOKUP)
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
else
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If not, we determine PAGE_OFFSET based on the kernel page table. On
|
|
|
|
* x86_64, swapper_pg_dir is a macro defined to init_top_pgt, which
|
|
|
|
* itself is defined in assembly. They're both available in VMCOREINFO,
|
|
|
|
* but it's easier to get it from init_mm.pgd.
|
|
|
|
*/
|
|
|
|
err = drgn_program_find_object(prog, "init_mm", NULL,
|
|
|
|
DRGN_FIND_OBJECT_VARIABLE, &obj);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_member(&obj, &obj, "pgd");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Before Linux kernel commit d52888aa2753 ("x86/mm: Move LDT remap out
|
|
|
|
* of KASLR region on 5-level paging") (in v4.20), PAGE_OFFSET was pgd
|
|
|
|
* slot 272. After that, it is pgd slot 273, and slot 272 is empty
|
|
|
|
* (reserved for Local Descriptor Table mappings for userspace tasks).
|
|
|
|
*/
|
|
|
|
err = drgn_object_subscript(&obj, &obj, 272);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_member(&obj, &obj, "pgd");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = drgn_object_bool(&obj, &nonzero);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
if (nonzero) {
|
|
|
|
if (prog->vmcoreinfo.pgtable_l5_enabled)
|
|
|
|
*ret = UINT64_C(0xff10000000000000);
|
|
|
|
else
|
|
|
|
*ret = UINT64_C(0xffff880000000000);
|
|
|
|
} else {
|
|
|
|
if (prog->vmcoreinfo.pgtable_l5_enabled)
|
|
|
|
*ret = UINT64_C(0xff11000000000000);
|
|
|
|
else
|
|
|
|
*ret = UINT64_C(0xffff888000000000);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
drgn_object_deinit(&obj);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-04-09 23:37:46 +01:00
|
|
|
static struct drgn_error *
|
|
|
|
linux_kernel_get_vmemmap_x86_64(struct drgn_program *prog, uint64_t *ret)
|
|
|
|
{
|
|
|
|
|
|
|
|
struct drgn_error *err;
|
|
|
|
struct drgn_object obj;
|
|
|
|
|
|
|
|
/* If KASLR is enabled, vmemmap is vmemmap_base. */
|
|
|
|
drgn_object_init(&obj, prog);
|
|
|
|
err = drgn_program_find_object(prog, "vmemmap_base", NULL,
|
|
|
|
DRGN_FIND_OBJECT_VARIABLE, &obj);
|
|
|
|
if (!err) {
|
|
|
|
err = drgn_object_read_unsigned(&obj, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (err->code == DRGN_ERROR_LOOKUP) {
|
|
|
|
drgn_error_destroy(err);
|
|
|
|
err = NULL;
|
|
|
|
} else {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, it depends on whether we have 5-level page tables. */
|
|
|
|
if (prog->vmcoreinfo.pgtable_l5_enabled)
|
|
|
|
*ret = UINT64_C(0xffd4000000000000);
|
|
|
|
else
|
|
|
|
*ret = UINT64_C(0xffffea0000000000);
|
|
|
|
|
|
|
|
out:
|
|
|
|
drgn_object_deinit(&obj);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-07-29 08:57:28 +01:00
|
|
|
const struct drgn_architecture_info arch_info_x86_64 = {
|
2019-10-18 10:03:32 +01:00
|
|
|
ARCHITECTURE_INFO,
|
2019-07-29 08:57:28 +01:00
|
|
|
.default_flags = (DRGN_PLATFORM_IS_64_BIT |
|
|
|
|
DRGN_PLATFORM_IS_LITTLE_ENDIAN),
|
2019-10-24 22:26:45 +01:00
|
|
|
.frame_registers = frame_registers_x86_64,
|
|
|
|
.num_frame_registers = ARRAY_SIZE(frame_registers_x86_64),
|
2019-07-29 22:41:33 +01:00
|
|
|
.linux_kernel_set_initial_registers = linux_kernel_set_initial_registers_x86_64,
|
2020-04-09 22:59:42 +01:00
|
|
|
.linux_kernel_get_page_offset = linux_kernel_get_page_offset_x86_64,
|
2020-04-09 23:37:46 +01:00
|
|
|
.linux_kernel_get_vmemmap = linux_kernel_get_vmemmap_x86_64,
|
2019-07-29 08:57:28 +01:00
|
|
|
};
|