mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 09:13:06 +00:00
9ee1ccff98
The only relocation type I saw in Debian's kernel module debug info was R_390_32. R_390_8, R_390_16, R_390_64, R_390_PC16, R_390_PC32, and R_390_PC64 are trivial to support, as well. The Linux kernel supports many more, but hopefully they won't show up for debug info. Signed-off-by: Omar Sandoval <osandov@osandov.com>
251 lines
6.2 KiB
C
251 lines
6.2 KiB
C
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
#include <byteswap.h>
|
|
#include <elf.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "platform.h"
|
|
|
|
const struct drgn_register *drgn_register_by_name_unknown(const char *name)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
const struct drgn_architecture_info arch_info_unknown = {
|
|
.name = "unknown",
|
|
.arch = DRGN_ARCH_UNKNOWN,
|
|
.register_by_name = drgn_register_by_name_unknown,
|
|
};
|
|
|
|
LIBDRGN_PUBLIC const struct drgn_platform drgn_host_platform = {
|
|
#if __x86_64__
|
|
.arch = &arch_info_x86_64,
|
|
#elif __i386__
|
|
.arch = &arch_info_i386,
|
|
#elif __aarch64__
|
|
.arch = &arch_info_aarch64,
|
|
#elif __arm__
|
|
.arch = &arch_info_arm,
|
|
#elif __powerpc64__
|
|
.arch = &arch_info_ppc64,
|
|
#elif __riscv
|
|
#if __riscv_xlen == 64
|
|
.arch = &arch_info_riscv64,
|
|
#elif __riscv_xlen == 32
|
|
.arch = &arch_info_riscv32,
|
|
#else
|
|
#error "unknown __riscv_xlen"
|
|
#endif
|
|
#elif __s390x__
|
|
.arch = &arch_info_s390x,
|
|
// __s390__ is also defined for s390x, so the order is important.
|
|
#elif __s390__
|
|
.arch = &arch_info_s390,
|
|
#else
|
|
.arch = &arch_info_unknown,
|
|
#endif
|
|
.flags = ((sizeof(void *) == 8 ? DRGN_PLATFORM_IS_64_BIT : 0) |
|
|
(HOST_LITTLE_ENDIAN ? DRGN_PLATFORM_IS_LITTLE_ENDIAN : 0)),
|
|
};
|
|
|
|
LIBDRGN_PUBLIC struct drgn_error *
|
|
drgn_platform_create(enum drgn_architecture arch,
|
|
enum drgn_platform_flags flags, struct drgn_platform **ret)
|
|
{
|
|
const struct drgn_architecture_info *arch_info;
|
|
struct drgn_platform *platform;
|
|
|
|
SWITCH_ENUM_DEFAULT(arch,
|
|
case DRGN_ARCH_UNKNOWN:
|
|
arch_info = &arch_info_unknown;
|
|
break;
|
|
case DRGN_ARCH_X86_64:
|
|
arch_info = &arch_info_x86_64;
|
|
break;
|
|
case DRGN_ARCH_I386:
|
|
arch_info = &arch_info_i386;
|
|
break;
|
|
case DRGN_ARCH_AARCH64:
|
|
arch_info = &arch_info_aarch64;
|
|
break;
|
|
case DRGN_ARCH_ARM:
|
|
arch_info = &arch_info_arm;
|
|
break;
|
|
case DRGN_ARCH_PPC64:
|
|
arch_info = &arch_info_ppc64;
|
|
break;
|
|
case DRGN_ARCH_RISCV64:
|
|
arch_info = &arch_info_riscv64;
|
|
break;
|
|
case DRGN_ARCH_RISCV32:
|
|
arch_info = &arch_info_riscv32;
|
|
break;
|
|
case DRGN_ARCH_S390X:
|
|
arch_info = &arch_info_s390x;
|
|
break;
|
|
case DRGN_ARCH_S390:
|
|
arch_info = &arch_info_s390;
|
|
break;
|
|
default:
|
|
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
|
|
"invalid architecture");
|
|
)
|
|
if (flags == DRGN_PLATFORM_DEFAULT_FLAGS) {
|
|
if (arch == DRGN_ARCH_UNKNOWN) {
|
|
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
|
|
"cannot get default platform flags of unknown architecture");
|
|
}
|
|
flags = arch_info->default_flags;
|
|
} else if (flags & ~DRGN_ALL_PLATFORM_FLAGS) {
|
|
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
|
|
"invalid platform flags");
|
|
}
|
|
platform = malloc(sizeof(*platform));
|
|
if (!platform)
|
|
return &drgn_enomem;
|
|
platform->arch = arch_info;
|
|
platform->flags = flags;
|
|
*ret = platform;
|
|
return NULL;
|
|
}
|
|
|
|
LIBDRGN_PUBLIC void drgn_platform_destroy(struct drgn_platform *platform)
|
|
{
|
|
free(platform);
|
|
}
|
|
|
|
LIBDRGN_PUBLIC enum drgn_architecture
|
|
drgn_platform_arch(const struct drgn_platform *platform)
|
|
{
|
|
return platform->arch->arch;
|
|
}
|
|
|
|
LIBDRGN_PUBLIC enum drgn_platform_flags
|
|
drgn_platform_flags(const struct drgn_platform *platform)
|
|
{
|
|
return platform->flags;
|
|
}
|
|
|
|
LIBDRGN_PUBLIC bool drgn_platform_eq(struct drgn_platform *a,
|
|
struct drgn_platform *b)
|
|
{
|
|
return a->arch == b->arch && a->flags == b->flags;
|
|
}
|
|
|
|
void drgn_platform_from_arch(const struct drgn_architecture_info *arch,
|
|
bool is_64_bit, bool is_little_endian,
|
|
struct drgn_platform *ret)
|
|
{
|
|
ret->arch = arch;
|
|
ret->flags = (arch->default_flags &
|
|
~(DRGN_PLATFORM_IS_64_BIT | DRGN_PLATFORM_IS_LITTLE_ENDIAN));
|
|
if (is_64_bit)
|
|
ret->flags |= DRGN_PLATFORM_IS_64_BIT;
|
|
if (is_little_endian)
|
|
ret->flags |= DRGN_PLATFORM_IS_LITTLE_ENDIAN;
|
|
}
|
|
|
|
void drgn_platform_from_elf(GElf_Ehdr *ehdr, struct drgn_platform *ret)
|
|
{
|
|
const struct drgn_architecture_info *arch;
|
|
|
|
switch (ehdr->e_machine) {
|
|
case EM_X86_64:
|
|
arch = &arch_info_x86_64;
|
|
break;
|
|
case EM_386:
|
|
arch = &arch_info_i386;
|
|
break;
|
|
case EM_AARCH64:
|
|
arch = &arch_info_aarch64;
|
|
break;
|
|
case EM_ARM:
|
|
arch = &arch_info_arm;
|
|
break;
|
|
case EM_PPC64:
|
|
arch = &arch_info_ppc64;
|
|
break;
|
|
case EM_RISCV:
|
|
if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
|
|
arch = &arch_info_riscv64;
|
|
else
|
|
arch = &arch_info_riscv32;
|
|
break;
|
|
case EM_S390:
|
|
if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
|
|
arch = &arch_info_s390x;
|
|
else
|
|
arch = &arch_info_s390;
|
|
break;
|
|
default:
|
|
arch = &arch_info_unknown;
|
|
break;
|
|
}
|
|
drgn_platform_from_arch(arch, ehdr->e_ident[EI_CLASS] == ELFCLASS64,
|
|
ehdr->e_ident[EI_DATA] == ELFDATA2LSB, ret);
|
|
}
|
|
|
|
LIBDRGN_PUBLIC size_t
|
|
drgn_platform_num_registers(const struct drgn_platform *platform)
|
|
{
|
|
return platform->arch->num_registers;
|
|
}
|
|
|
|
LIBDRGN_PUBLIC const struct drgn_register *
|
|
drgn_platform_register(const struct drgn_platform *platform, size_t n)
|
|
{
|
|
return &platform->arch->registers[n];
|
|
}
|
|
|
|
LIBDRGN_PUBLIC const struct drgn_register *
|
|
drgn_platform_register_by_name(const struct drgn_platform *platform,
|
|
const char *name)
|
|
{
|
|
return platform->arch->register_by_name(name);
|
|
}
|
|
|
|
LIBDRGN_PUBLIC const char * const *
|
|
drgn_register_names(const struct drgn_register *reg, size_t *num_names_ret)
|
|
{
|
|
*num_names_ret = reg->num_names;
|
|
return reg->names;
|
|
}
|
|
|
|
struct drgn_error drgn_invalid_relocation_offset = {
|
|
.code = DRGN_ERROR_OTHER,
|
|
.message = "invalid relocation offset",
|
|
};
|
|
|
|
#define DEFINE_DRGN_RELOC_ADD(bits) \
|
|
struct drgn_error * \
|
|
drgn_reloc_add##bits(const struct drgn_relocating_section *relocating, \
|
|
uint64_t r_offset, const int64_t *r_addend, \
|
|
uint##bits##_t addend) \
|
|
{ \
|
|
uint##bits##_t value; \
|
|
if (r_offset > relocating->buf_size || \
|
|
relocating->buf_size - r_offset < sizeof(value)) \
|
|
return &drgn_invalid_relocation_offset; \
|
|
if (r_addend) { \
|
|
value = *r_addend; \
|
|
} else { \
|
|
memcpy(&value, relocating->buf + r_offset, sizeof(value)); \
|
|
if (relocating->bswap) \
|
|
value = bswap_##bits(value); \
|
|
} \
|
|
value += addend; \
|
|
if (relocating->bswap) \
|
|
value = bswap_##bits(value); \
|
|
memcpy(relocating->buf + r_offset, &value, sizeof(value)); \
|
|
return NULL; \
|
|
}
|
|
DEFINE_DRGN_RELOC_ADD(64)
|
|
DEFINE_DRGN_RELOC_ADD(32)
|
|
DEFINE_DRGN_RELOC_ADD(16)
|
|
#define bswap_8(x) (x)
|
|
DEFINE_DRGN_RELOC_ADD(8)
|
|
#undef bswap_8
|
|
#undef DEFINE_DRGN_RELOC_ADD
|