mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-23 09:43:06 +00:00
b5ed892481
Bear 3 changed the CLI arguments, so update scripts/iwyu.py for it and clean up some new warnings. Signed-off-by: Omar Sandoval <osandov@osandov.com>
258 lines
5.5 KiB
C
258 lines
5.5 KiB
C
%{
|
|
// (C) Copyright IBM Corp. 2020
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
#include <byteswap.h>
|
|
#include <elfutils/libdw.h>
|
|
#include <elfutils/libdwfl.h>
|
|
#include <string.h>
|
|
|
|
#include "drgn.h"
|
|
#include "error.h"
|
|
#include "platform.h"
|
|
#include "program.h"
|
|
%}
|
|
|
|
ppc64
|
|
%%
|
|
r0
|
|
r1
|
|
r2
|
|
r3
|
|
r4
|
|
r5
|
|
r6
|
|
r7
|
|
r8
|
|
r9
|
|
r10
|
|
r11
|
|
r12
|
|
r13
|
|
r14
|
|
r15
|
|
r16
|
|
r17
|
|
r18
|
|
r19
|
|
r20
|
|
r21
|
|
r22
|
|
r23
|
|
r24
|
|
r25
|
|
r26
|
|
r27
|
|
r28
|
|
r29
|
|
r30
|
|
r31
|
|
f0
|
|
f1
|
|
f2
|
|
f3
|
|
f4
|
|
f5
|
|
f6
|
|
f7
|
|
f8
|
|
f9
|
|
f10
|
|
f11
|
|
f12
|
|
f13
|
|
f14
|
|
f15
|
|
f16
|
|
f17
|
|
f18
|
|
f19
|
|
f20
|
|
f21
|
|
f22
|
|
f23
|
|
f24
|
|
f25
|
|
f26
|
|
f27
|
|
f28
|
|
f29
|
|
f30
|
|
f31
|
|
# There are two numbering schemes for the remaining registers. The original
|
|
# definition appears to be "64-bit PowerPC ELF Application Binary Interface
|
|
# Supplement" [1]. The GNU toolchain instead uses its own that was later
|
|
# codified in "Power Architecture 64-Bit ELF V2 ABI Specification" [2]. We use
|
|
# the latter.
|
|
#
|
|
# What the ABI calls the link register (register 65) is used as the DWARF CFI
|
|
# return_address_register column in practice, which is actually the program
|
|
# counter. We omit it to avoid confusion.
|
|
#
|
|
# 1: https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html
|
|
# 2: https://openpowerfoundation.org/?resource_lib=64-bit-elf-v2-abi-specification-power-architecture
|
|
cr0, 68
|
|
cr1
|
|
cr2
|
|
cr3
|
|
cr4
|
|
cr5
|
|
cr6
|
|
cr7
|
|
%%
|
|
|
|
static struct drgn_error *
|
|
set_initial_registers_from_struct_ppc64(struct drgn_program *prog,
|
|
Dwfl_Thread *thread, const void *regs,
|
|
size_t size, bool linux_kernel_prstatus,
|
|
bool linux_kernel_switched_out)
|
|
{
|
|
if (size < 312) {
|
|
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
|
|
"registers are truncated");
|
|
}
|
|
|
|
bool bswap = drgn_platform_bswap(&prog->platform);
|
|
|
|
Dwarf_Word dwarf_regs[32];
|
|
|
|
#define READ_REGISTER(n) ({ \
|
|
uint64_t reg; \
|
|
memcpy(®, (uint64_t *)regs + (n), sizeof(reg)); \
|
|
bswap ? bswap_64(reg) : reg; \
|
|
})
|
|
|
|
/*
|
|
* The NT_PRSTATUS note in Linux kernel vmcores is odd. Since Linux
|
|
* kernel commit d16a58f8854b ("powerpc: Improve ppc_save_regs()") (in
|
|
* v5.7), the saved stack pointer (r1) is for the caller of the program
|
|
* counter saved in nip. Before that, the saved nip is set to the same
|
|
* as the link register. So, use the link register instead of nip.
|
|
*/
|
|
uint64_t nip = READ_REGISTER(32);
|
|
uint64_t link = READ_REGISTER(36);
|
|
if (linux_kernel_prstatus) {
|
|
dwfl_thread_state_register_pc(thread, link);
|
|
} else {
|
|
dwfl_thread_state_register_pc(thread, nip);
|
|
/*
|
|
* Switched out tasks in the Linux kernel don't save the link
|
|
* register.
|
|
*/
|
|
if (!linux_kernel_switched_out) {
|
|
dwarf_regs[0] = link;
|
|
if (!dwfl_thread_state_registers(thread, 65, 1,
|
|
dwarf_regs))
|
|
return drgn_error_libdwfl();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Switched out tasks in the Linux kernel only save the callee-saved
|
|
* general purpose registers (14-31).
|
|
*/
|
|
int min_gpr = linux_kernel_switched_out ? 14 : 0;
|
|
for (int i = min_gpr; i < 32; i++)
|
|
dwarf_regs[i] = READ_REGISTER(i);
|
|
if (!dwfl_thread_state_registers(thread, min_gpr, 32 - min_gpr,
|
|
dwarf_regs))
|
|
return drgn_error_libdwfl();
|
|
|
|
/* cr0 - cr7 */
|
|
uint64_t ccr = READ_REGISTER(38);
|
|
for (int i = 0; i < 8; i++)
|
|
dwarf_regs[i] = ccr & (UINT64_C(0xf) << (28 - 4 * i));
|
|
if (!dwfl_thread_state_registers(thread, 68, 8, dwarf_regs))
|
|
return drgn_error_libdwfl();
|
|
|
|
#undef READ_REGISTER
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct drgn_error *
|
|
pt_regs_set_initial_registers_ppc64(Dwfl_Thread *thread,
|
|
const struct drgn_object *obj)
|
|
{
|
|
return set_initial_registers_from_struct_ppc64(drgn_object_program(obj),
|
|
thread,
|
|
drgn_object_buffer(obj),
|
|
drgn_object_size(obj),
|
|
false, false);
|
|
}
|
|
|
|
static struct drgn_error *
|
|
prstatus_set_initial_registers_ppc64(struct drgn_program *prog,
|
|
Dwfl_Thread *thread, const void *prstatus,
|
|
size_t size)
|
|
{
|
|
if (size < 112) {
|
|
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
|
|
"NT_PRSTATUS is truncated");
|
|
}
|
|
bool is_linux_kernel = prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL;
|
|
return set_initial_registers_from_struct_ppc64(prog, thread,
|
|
(char *)prstatus + 112,
|
|
size - 112,
|
|
is_linux_kernel, false);
|
|
}
|
|
|
|
static struct drgn_error *
|
|
linux_kernel_set_initial_registers_ppc64(Dwfl_Thread *thread,
|
|
const struct drgn_object *task_obj)
|
|
{
|
|
static const uint64_t STACK_FRAME_OVERHEAD = 112;
|
|
static const uint64_t SWITCH_FRAME_SIZE = STACK_FRAME_OVERHEAD + 368;
|
|
|
|
struct drgn_error *err;
|
|
struct drgn_program *prog = drgn_object_program(task_obj);
|
|
|
|
struct drgn_object sp_obj;
|
|
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, "ksp");
|
|
if (err)
|
|
goto out;
|
|
uint64_t ksp;
|
|
err = drgn_object_read_unsigned(&sp_obj, &ksp);
|
|
if (err)
|
|
goto out;
|
|
|
|
char regs[312];
|
|
err = drgn_program_read_memory(prog, regs, ksp + STACK_FRAME_OVERHEAD,
|
|
sizeof(regs), false);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = set_initial_registers_from_struct_ppc64(prog, thread, regs,
|
|
sizeof(regs), false,
|
|
true);
|
|
if (err)
|
|
goto out;
|
|
|
|
/* r1 */
|
|
Dwarf_Word dwarf_reg = ksp + SWITCH_FRAME_SIZE;
|
|
if (!dwfl_thread_state_registers(thread, 1, 1, &dwarf_reg)) {
|
|
err = drgn_error_libdwfl();
|
|
goto out;
|
|
}
|
|
|
|
err = NULL;
|
|
out:
|
|
drgn_object_deinit(&sp_obj);
|
|
return err;
|
|
}
|
|
|
|
const struct drgn_architecture_info arch_info_ppc64 = {
|
|
ARCHITECTURE_INFO,
|
|
.default_flags = (DRGN_PLATFORM_IS_64_BIT |
|
|
DRGN_PLATFORM_IS_LITTLE_ENDIAN),
|
|
.pt_regs_set_initial_registers = pt_regs_set_initial_registers_ppc64,
|
|
.prstatus_set_initial_registers = prstatus_set_initial_registers_ppc64,
|
|
.linux_kernel_set_initial_registers =
|
|
linux_kernel_set_initial_registers_ppc64,
|
|
};
|