diff --git a/_drgn.pyi b/_drgn.pyi index 21bbcdd4..97665720 100644 --- a/_drgn.pyi +++ b/_drgn.pyi @@ -953,6 +953,12 @@ class Architecture(enum.Enum): PPC64 = ... """The 64-bit PowerPC architecture.""" + RISCV64 = ... + """The 64-bit RISC-V architecture.""" + + RISCV32 = ... + """The 32-bit RISC-V architecture.""" + UNKNOWN = ... """ An architecture which is not known to drgn. Certain features are not diff --git a/libdrgn/Makefile.am b/libdrgn/Makefile.am index f3eeb4ed..f444b7bb 100644 --- a/libdrgn/Makefile.am +++ b/libdrgn/Makefile.am @@ -30,6 +30,7 @@ libdrgnimpl_la_SOURCES = $(ARCH_DEFS:.defs=.c) \ arch_arm.c \ arch_i386.c \ arch_register_layout.h \ + arch_riscv.c \ array.h \ binary_buffer.c \ binary_buffer.h \ diff --git a/libdrgn/arch_riscv.c b/libdrgn/arch_riscv.c new file mode 100644 index 00000000..30b30661 --- /dev/null +++ b/libdrgn/arch_riscv.c @@ -0,0 +1,126 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "platform.h" // IWYU pragma: associated + +/* + * The ABI specification can be found at: + * https://github.com/riscv-non-isa/riscv-elf-psabi-doc + */ + +static struct drgn_error drgn_invalid_rel = { + .code = DRGN_ERROR_OTHER, + .message = "invalid relocation type for SHT_REL", +}; + +static struct drgn_error * +apply_elf_reloc_riscv(const struct drgn_relocating_section *relocating, + uint64_t r_offset, uint32_t r_type, const int64_t *r_addend, + uint64_t sym_value) +{ + switch (r_type) { + case R_RISCV_NONE: + return NULL; + case R_RISCV_32: + return drgn_reloc_add32(relocating, r_offset, r_addend, + sym_value); + case R_RISCV_64: + return drgn_reloc_add64(relocating, r_offset, r_addend, + sym_value); +#define CASE_R_RISCV_ADD_SUB(bits) \ + case R_RISCV_ADD##bits: { \ + if (!r_addend) \ + return &drgn_invalid_rel; \ + uint##bits##_t value; \ + if (r_offset > relocating->buf_size || \ + relocating->buf_size - r_offset < sizeof(value)) \ + return &drgn_invalid_relocation_offset; \ + memcpy(&value, relocating->buf + r_offset, sizeof(value)); \ + if (relocating->bswap) \ + value = bswap_##bits(value); \ + value += sym_value + *r_addend; \ + if (relocating->bswap) \ + value = bswap_##bits(value); \ + memcpy(relocating->buf + r_offset, &value, sizeof(value)); \ + return NULL; \ + } \ + case R_RISCV_SUB##bits: { \ + if (!r_addend) \ + return &drgn_invalid_rel; \ + uint##bits##_t value; \ + if (r_offset > relocating->buf_size || \ + relocating->buf_size - r_offset < sizeof(value)) \ + return &drgn_invalid_relocation_offset; \ + memcpy(&value, relocating->buf + r_offset, sizeof(value)); \ + if (relocating->bswap) \ + value = bswap_##bits(value); \ + value -= sym_value + *r_addend; \ + if (relocating->bswap) \ + value = bswap_##bits(value); \ + memcpy(relocating->buf + r_offset, &value, sizeof(value)); \ + return NULL; \ + } +#define bswap_8(x) (x) + CASE_R_RISCV_ADD_SUB(8) +#undef bswap_8 + CASE_R_RISCV_ADD_SUB(16) + CASE_R_RISCV_ADD_SUB(32) + CASE_R_RISCV_ADD_SUB(64) +#undef CASE_R_RISCV_ADD_SUB + case R_RISCV_SUB6: { + if (!r_addend) + return &drgn_invalid_rel; + uint8_t value; + if (r_offset > relocating->buf_size || + relocating->buf_size - r_offset < sizeof(value)) + return &drgn_invalid_relocation_offset; + memcpy(&value, relocating->buf + r_offset, sizeof(value)); + value = ((value & 0xc0) | + (((value & 0x3f) - (sym_value + *r_addend)) & 0x3f)); + memcpy(relocating->buf + r_offset, &value, sizeof(value)); + return NULL; + } + case R_RISCV_SET6: { + if (!r_addend) + return &drgn_invalid_rel; + uint8_t value; + if (r_offset > relocating->buf_size || + relocating->buf_size - r_offset < sizeof(value)) + return &drgn_invalid_relocation_offset; + memcpy(&value, relocating->buf + r_offset, sizeof(value)); + value = (value & 0xc0) | ((sym_value + *r_addend) & 0x3f); + memcpy(relocating->buf + r_offset, &value, sizeof(value)); + return NULL; + } + case R_RISCV_SET8: + return drgn_reloc_add8(relocating, r_offset, r_addend, + sym_value); + case R_RISCV_SET16: + return drgn_reloc_add16(relocating, r_offset, r_addend, + sym_value); + case R_RISCV_SET32: + return drgn_reloc_add32(relocating, r_offset, r_addend, + sym_value); + default: + return DRGN_UNKNOWN_RELOCATION_TYPE(r_type); + } +} + +const struct drgn_architecture_info arch_info_riscv64 = { + .name = "RISC-V 64", + .arch = DRGN_ARCH_RISCV64, + .default_flags = (DRGN_PLATFORM_IS_64_BIT | + DRGN_PLATFORM_IS_LITTLE_ENDIAN), + .register_by_name = drgn_register_by_name_unknown, + .apply_elf_reloc = apply_elf_reloc_riscv, +}; + +const struct drgn_architecture_info arch_info_riscv32 = { + .name = "RISC-V 32", + .arch = DRGN_ARCH_RISCV32, + .default_flags = DRGN_PLATFORM_IS_LITTLE_ENDIAN, + .register_by_name = drgn_register_by_name_unknown, + .apply_elf_reloc = apply_elf_reloc_riscv, +}; diff --git a/libdrgn/drgn.h.in b/libdrgn/drgn.h.in index 8ebaaa07..f26a9021 100644 --- a/libdrgn/drgn.h.in +++ b/libdrgn/drgn.h.in @@ -365,6 +365,8 @@ enum drgn_architecture { DRGN_ARCH_AARCH64, DRGN_ARCH_ARM, DRGN_ARCH_PPC64, + DRGN_ARCH_RISCV64, + DRGN_ARCH_RISCV32, }; /** Flags describing a @ref drgn_platform. */ diff --git a/libdrgn/kdump.c b/libdrgn/kdump.c index e30ec714..d1ec0c22 100644 --- a/libdrgn/kdump.c +++ b/libdrgn/kdump.c @@ -35,6 +35,7 @@ static struct drgn_error *drgn_platform_from_kdump(kdump_ctx_t *ctx, arch = &arch_info_arm; else if (strcmp(str, KDUMP_ARCH_PPC64) == 0) arch = &arch_info_ppc64; + /* libkdumpfile doesn't support RISC-V */ else arch = &arch_info_unknown; diff --git a/libdrgn/platform.c b/libdrgn/platform.c index 2fc463c5..452ddaa3 100644 --- a/libdrgn/platform.c +++ b/libdrgn/platform.c @@ -30,6 +30,14 @@ LIBDRGN_PUBLIC const struct drgn_platform drgn_host_platform = { .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 #else .arch = &arch_info_unknown, #endif @@ -63,6 +71,12 @@ drgn_platform_create(enum drgn_architecture arch, 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; default: return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, "invalid architecture"); @@ -142,6 +156,12 @@ void drgn_platform_from_elf(GElf_Ehdr *ehdr, struct drgn_platform *ret) 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; default: arch = &arch_info_unknown; break; @@ -176,7 +196,7 @@ drgn_register_names(const struct drgn_register *reg, size_t *num_names_ret) return reg->names; } -static struct drgn_error drgn_invalid_relocation_offset = { +struct drgn_error drgn_invalid_relocation_offset = { .code = DRGN_ERROR_OTHER, .message = "invalid relocation offset", }; @@ -207,4 +227,7 @@ drgn_reloc_add##bits(const struct drgn_relocating_section *relocating, \ 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 diff --git a/libdrgn/platform.h b/libdrgn/platform.h index ad6c9ac3..4d359fc5 100644 --- a/libdrgn/platform.h +++ b/libdrgn/platform.h @@ -34,6 +34,8 @@ struct drgn_relocating_section { bool bswap; }; +extern struct drgn_error drgn_invalid_relocation_offset; + /* * Apply an ELF relocation as: * @@ -54,6 +56,9 @@ drgn_reloc_add32(const struct drgn_relocating_section *relocating, struct drgn_error * drgn_reloc_add16(const struct drgn_relocating_section *relocating, uint64_t r_offset, const int64_t *r_addend, uint16_t addend); +struct drgn_error * +drgn_reloc_add8(const struct drgn_relocating_section *relocating, + uint64_t r_offset, const int64_t *r_addend, uint8_t addend); #define DRGN_UNKNOWN_RELOCATION_TYPE(r_type) \ drgn_error_format(DRGN_ERROR_OTHER, \ @@ -160,6 +165,8 @@ extern const struct drgn_architecture_info arch_info_i386; extern const struct drgn_architecture_info arch_info_aarch64; extern const struct drgn_architecture_info arch_info_arm; extern const struct drgn_architecture_info arch_info_ppc64; +extern const struct drgn_architecture_info arch_info_riscv64; +extern const struct drgn_architecture_info arch_info_riscv32; struct drgn_platform { const struct drgn_architecture_info *arch;