Add initial support to loongarch 32/64 (#4810)

This commit is contained in:
Giovanni 2025-01-01 15:36:23 +08:00 committed by GitHub
parent cf6da4ff2b
commit b019d9d4ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 2870 additions and 5 deletions

View file

@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2024 deroad <deroad@kumo.xn--q9jyb4c>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_types.h>
#include <capstone.h>
#ifndef RZ_LOONGARCH_H
#define RZ_LOONGARCH_H
#include <capstone/capstone.h>
#include <capstone/loongarch.h>
typedef struct {
csh h;
cs_mode mode;
cs_insn *insn;
ut32 count;
ut32 word;
RzPVector /*<RzAsmTokenPattern *>*/ *token_patterns;
} RzAsmLoongArchContext;
#endif // RZ_LOONGARCH_H

View file

@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: 2024 deroad <deroad@kumo.xn--q9jyb4c>
// SPDX-License-Identifier: LGPL-3.0-only
#include <capstone/capstone.h>
#include <capstone/loongarch.h>
#include "loongarch.h"
#define LOONGARCH_CPUS "loongarch32,loongarch64"
static inline bool
loongarch_setup_cs_handle(RzAsmLoongArchContext *ctx, bool is_64_bits, bool big_endian) {
cs_mode mode = big_endian ? CS_MODE_BIG_ENDIAN : CS_MODE_LITTLE_ENDIAN;
if (mode != ctx->mode) {
cs_close(&ctx->h);
ctx->h = 0;
ctx->mode = mode;
}
if (is_64_bits) {
mode |= CS_MODE_LOONGARCH64;
} else {
mode |= CS_MODE_LOONGARCH32;
}
if (ctx->h != 0) {
return true;
}
cs_err err = cs_open(CS_ARCH_LOONGARCH, mode, &ctx->h);
if (err) {
const char *error_str = cs_strerror(err);
RZ_LOG_ERROR("Failed on cs_open() with error returned: %u: %s\n", err, error_str);
return false;
}
err = cs_option(ctx->h, CS_OPT_DETAIL, CS_OPT_ON);
if (err) {
const char *error_str = cs_strerror(err);
RZ_LOG_ERROR("Failed on cs_option() with error returned: %u: %s\n", err, error_str);
return false;
}
return true;
}
static inline ut8 loongarch_op_count(cs_insn *insn) {
return insn->detail->loongarch.op_count;
}
static inline cs_loongarch_op *loongarch_op_get(cs_insn *insn, int idx) {
if (idx >= loongarch_op_count(insn)) {
RZ_LOG_WARN("Failed to get operand%d [%d]: \"%s %s\"\n",
idx, loongarch_op_count(insn), insn->mnemonic, insn->op_str);
rz_warn_if_reached();
return NULL;
}
return &insn->detail->loongarch.operands[idx];
}
static inline bool loongarch_op_is_reg(RzAsmLoongArchContext *ctx, int idx, loongarch_reg reg_no) {
const cs_loongarch_op *op = loongarch_op_get(ctx->insn, idx);
if (!op || op->type != LOONGARCH_OP_REG) {
RZ_LOG_WARN("Failed to get operand%d [%d]: \"%s %s\" [reg]\n",
idx, loongarch_op_count(ctx->insn), ctx->insn->mnemonic, ctx->insn->op_str);
rz_warn_if_reached();
return NULL;
}
return op->reg == reg_no;
}
static inline const char *loongarch_op_as_reg(RzAsmLoongArchContext *ctx, int idx) {
const cs_loongarch_op *op = loongarch_op_get(ctx->insn, idx);
if (!op || op->type != LOONGARCH_OP_REG) {
RZ_LOG_WARN("Failed to get operand%d [%d]: \"%s %s\" [reg]\n",
idx, loongarch_op_count(ctx->insn), ctx->insn->mnemonic, ctx->insn->op_str);
rz_warn_if_reached();
return NULL;
}
return cs_reg_name(ctx->h, op->reg);
}
static inline st64 loongarch_op_as_imm(RzAsmLoongArchContext *ctx, int idx) {
const cs_loongarch_op *op = loongarch_op_get(ctx->insn, idx);
if (!op || op->type != LOONGARCH_OP_IMM) {
RZ_LOG_WARN("Failed to get operand%d [%d]: \"%s %s\" [imm]\n",
idx, loongarch_op_count(ctx->insn), ctx->insn->mnemonic, ctx->insn->op_str);
rz_warn_if_reached();
return 0;
}
return op->imm;
}

View file

@ -326,12 +326,14 @@ if capstone_dep.version() == 'next'
# plugins
arch_plugins_list += [
'alpha_cs',
'loongarch_cs',
'xtensa_cs',
]
# plugins sources
arch_plugin_sources += [
'p/arch_alpha.c',
'p/arch_loongarch_cs.c',
'p/arch_xtensa_cs.c',
]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2024 RizinOrg <info@rizin.re>
// SPDX-License-Identifier: LGPL-3.0-only
#include <deprecated_arch_helper.h>
#include <capstone/capstone.h>
#include <capstone/loongarch.h>
#include <loongarch/loongarch.inc>
#include "analysis/analysis_loongarch_cs.c"
#include "asm/asm_loongarch_cs.c"
RZ_ARCH_PLUGIN_DEFINE_DEPRECATED(loongarch_cs);

View file

@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: 2024 deroad <deroad@kumo.xn--q9jyb4c>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_asm.h>
#include <rz_lib.h>
static int disassemble(RzAsm *a, RzAsmOp *op, const ut8 *buf, int len) {
if (!buf || !op || !a->plugin_data) {
return -1;
}
RzAsmLoongArchContext *ctx = a->plugin_data;
if (!loongarch_setup_cs_handle(ctx, a->bits == 64, a->big_endian)) {
return -1;
}
op->size = 4;
ctx->insn = NULL;
ctx->count = cs_disasm(ctx->h, (ut8 *)buf, len, a->pc, 1, &ctx->insn);
if (ctx->count < 1) {
rz_asm_op_set_asm(op, "invalid");
goto beach;
}
if (ctx->insn->size < 1) {
return op->size;
}
op->size = ctx->insn->size;
rz_asm_op_setf_asm(op, "%s%s%s",
ctx->insn->mnemonic, RZ_STR_ISNOTEMPTY(ctx->insn->op_str) ? " " : "", ctx->insn->op_str);
char *str = rz_asm_op_get_asm(op);
if (str) {
// remove the '$'<registername> in the string
rz_str_replace_char(str, '$', 0);
}
beach:
cs_free(ctx->insn, ctx->count);
ctx->insn = NULL;
ctx->count = 0;
return op->size;
}
#define TOKEN(_type, _pat) \
do { \
RzAsmTokenPattern *pat = RZ_NEW0(RzAsmTokenPattern); \
pat->type = RZ_ASM_TOKEN_##_type; \
pat->pattern = strdup(_pat); \
rz_pvector_push(pvec, pat); \
} while (0)
static bool loongarch_asm_init(void **u) {
if (!u) {
return false;
}
// u = RzAsm.plugin_data
RzAsmLoongArchContext *ctx = NULL;
if (*u) {
rz_mem_memzero(*u, sizeof(RzAsmLoongArchContext));
ctx = *u;
} else {
ctx = RZ_NEW0(RzAsmLoongArchContext);
if (!ctx) {
return false;
}
*u = ctx;
}
return true;
}
static bool loongarch_asm_fini(void *u) {
if (!u) {
return true;
}
RzAsmLoongArchContext *ctx = u;
cs_close(&ctx->h);
free(u);
return true;
}
RzAsmPlugin rz_asm_plugin_loongarch_cs = {
.name = "loongarch",
.desc = "Capstone Alpha disassembler",
.license = "LGPL3",
.arch = "loongarch",
.bits = 32 | 64,
.endian = RZ_SYS_ENDIAN_LITTLE | RZ_SYS_ENDIAN_BIG,
.disassemble = &disassemble,
.init = &loongarch_asm_init,
.fini = &loongarch_asm_fini,
};
#ifndef RZ_PLUGIN_INCORE
RZ_API RzLibStruct rizin_plugin = {
.type = RZ_LIB_TYPE_ASM,
.data = &rz_asm_plugin_loongarch_cs,
.version = RZ_VERSION
};
#endif

View file

@ -7,6 +7,9 @@
#include "elf.h"
#define MIPS_PLT_OFFSET 0x20
#define LOONGARCH_PLT_OFFSET 0x20
#define LOONGARCH_PLT_ENTRY_SIZE 0x10
#define RISCV_PLT_OFFSET 0x20
#define RISCV_PLT_ENTRY_SIZE 0x10
#define RISCV_PLT_OFFSET 0x20
#define SPARC_OFFSET_PLT_ENTRY_FROM_GOT_ADDR -0x6
@ -89,6 +92,22 @@ static ut64 get_import_addr_hexagon(ELFOBJ *eo, RzBinElfReloc *rel) {
}
}
static ut64 get_import_addr_loongarch(ELFOBJ *bin, RzBinElfReloc *rel) {
ut64 got_addr;
if (!Elf_(rz_bin_elf_get_dt_info)(bin, DT_PLTGOT, &got_addr)) {
return UT64_MAX;
}
ut64 plt_addr = get_got_entry(bin, rel);
if (plt_addr == UT64_MAX) {
return UT64_MAX;
}
ut64 pos = COMPUTE_PLTGOT_POSITION(rel, got_addr, 0x2);
return plt_addr + LOONGARCH_PLT_OFFSET + pos * LOONGARCH_PLT_ENTRY_SIZE;
}
static ut64 get_import_addr_riscv(ELFOBJ *bin, RzBinElfReloc *rel) {
ut64 got_addr;
@ -323,6 +342,8 @@ static ut64 get_import_addr_aux(ELFOBJ *bin, RzBinElfReloc *reloc) {
return get_import_addr_x86(bin, reloc);
case EM_QDSP6:
return get_import_addr_hexagon(bin, reloc);
case EM_LOONGARCH:
return get_import_addr_loongarch(bin, reloc);
default:
RZ_LOG_WARN("Unsupported relocs type %" PFMT64u " for arch %d\n",
(ut64)reloc->type, bin->ehdr.e_machine);

View file

@ -32,6 +32,11 @@ cs_files = [
'arch/HPPA/HPPAInstPrinter.c',
'arch/HPPA/HPPAMapping.c',
'arch/HPPA/HPPAModule.c',
'arch/LoongArch/LoongArchInstPrinter.c',
'arch/LoongArch/LoongArchDisassembler.c',
'arch/LoongArch/LoongArchMapping.c',
'arch/LoongArch/LoongArchDisassemblerExtension.c',
'arch/LoongArch/LoongArchModule.c',
'arch/M680X/M680XDisassembler.c',
'arch/M680X/M680XInstPrinter.c',
'arch/M680X/M680XModule.c',
@ -96,20 +101,21 @@ libcapstone_c_args = [
'-DCAPSTONE_X86_REDUCE_NO',
'-DCAPSTONE_USE_SYS_DYN_MEM',
'-DCAPSTONE_DIET_NO',
'-DCAPSTONE_HAS_AARCH64',
'-DCAPSTONE_HAS_ALPHA',
'-DCAPSTONE_HAS_ARM',
'-DCAPSTONE_HAS_AARCH64',
'-DCAPSTONE_HAS_HPPA',
'-DCAPSTONE_HAS_M68K',
'-DCAPSTONE_HAS_LOONGARCH',
'-DCAPSTONE_HAS_M680X',
'-DCAPSTONE_HAS_M68K',
'-DCAPSTONE_HAS_MIPS',
'-DCAPSTONE_HAS_POWERPC',
'-DCAPSTONE_HAS_SPARC',
'-DCAPSTONE_HAS_SYSTEMZ',
'-DCAPSTONE_HAS_X86',
'-DCAPSTONE_HAS_XCORE',
'-DCAPSTONE_HAS_TMS320C64X',
'-DCAPSTONE_HAS_TRICORE',
'-DCAPSTONE_HAS_X86',
'-DCAPSTONE_HAS_XCORE',
'-DCAPSTONE_HAS_XTENSA',
]

134
test/db/analysis/loongarch Normal file
View file

@ -0,0 +1,134 @@
NAME=loongarch32 ensure correct cpu is selected
FILE=bins/elf/hello.loongarch32.o
CMDS=<<EOF
iI~arch
iI~bits
aaa
pdf @ sym.main
EOF
EXPECT=<<EOF
arch loongarch
machine LoongArch
bits 32
/ int sym.main(int argc, char **argv, char **envp);
| 0x0800005c addi.w sp, sp, -0x20
| 0x08000060 st.w ra, sp, 0x1c
| 0x08000064 st.w fp, sp, 0x18
| 0x08000068 addi.w fp, sp, 0x20
| 0x0800006c st.w zero, fp, -0xc
| 0x08000070 ori a0, zero, 1
| 0x08000074 st.w a0, fp, -0x10
| 0x08000078 ori a0, zero, 2
| 0x0800007c st.w a0, fp, -0x14
| 0x08000080 ld.w a0, fp, -0x10
| 0x08000084 ld.w a1, fp, -0x14
| 0x08000088 add.w a0, a0, a1
| 0x0800008c st.w a0, fp, -0x18
| 0x08000090 move a0, zero
| 0x08000094 ld.w fp, sp, 0x18
| 0x08000098 ld.w ra, sp, 0x1c
| 0x0800009c addi.w sp, sp, 0x20
\ 0x080000a0 ret
EOF
RUN
NAME=loongarch64 ensure correct cpu is selected
FILE=bins/elf/ncat.loongarch64
CMDS=<<EOF
iI~arch
iI~bits
aaa
pdf
pdf @ fcn.00010208
EOF
EXPECT=<<EOF
arch loongarch
intrp /lib64/ld-linux-loongarch-lp64d.so.1
machine LoongArch
bits 64
/ entry0();
| 0x00007508 move a5, a0
| 0x0000750c pcalau12i a0, -3
| 0x00007510 addi.d t0, zero, 0x3a0
| 0x00007514 lu32i.d t0, 0
| 0x00007518 lu52i.d t0, t0, 0
| 0x0000751c add.d a0, a0, t0
| 0x00007520 ld.d a1, sp, 0
| 0x00007524 addi.d a2, sp, 8
| 0x00007528 bstrins.d sp, zero, 3, 0
| 0x0000752c move a3, zero
| 0x00007530 move a4, zero
| 0x00007534 move a6, sp
| 0x00007538 pcalau12i ra, -3
| 0x0000753c addi.d t0, zero, 0x340
| 0x00007540 lu32i.d t0, 0
| ; DATA XREF from fcn.00016abc @ 0x16abc
| 0x00007544 lu52i.d t0, t0, 0
| 0x00007548 add.d ra, ra, t0
| 0x0000754c jirl ra, ra, 0
| 0x00007550 pcalau12i ra, -3
| 0x00007554 addi.d t0, zero, 0x330
| 0x00007558 lu32i.d t0, 0
| 0x0000755c lu52i.d t0, t0, 0
| 0x00007560 add.d ra, ra, t0
| 0x00007564 jirl ra, ra, 0
| 0x00007568 nop
| 0x0000756c nop
| 0x00007570 nop
| 0x00007574 nop
| 0x00007578 nop
\ 0x0000757c nop
; XREFS(70)
/ fcn.00010208();
| 0x00010208 addi.d sp, sp, -0x70
| 0x0001020c st.d ra, sp, 0x28
| 0x00010210 st.d s0, sp, 0x20
| 0x00010214 st.d s1, sp, 0x18
| 0x00010218 st.d s2, sp, 0x10
| 0x0001021c pcalau12i s1, 0x24
| 0x00010220 pcalau12i s0, 0x24
| 0x00010224 st.d a4, sp, 0x50
| 0x00010228 st.d a5, sp, 0x58
| 0x0001022c st.d a6, sp, 0x60
| 0x00010230 st.d a7, sp, 0x68
| 0x00010234 st.d a1, sp, 0x38
| 0x00010238 st.d a2, sp, 0x40
| 0x0001023c st.d a3, sp, 0x48
| 0x00010240 ld.d s0, s0, -segment.NOTE
| 0x00010244 ld.d s1, s1, -0x210
| 0x00010248 ldptr.d t1, s1, 0
| 0x0001024c move s2, a0
| 0x00010250 ldptr.d a0, s0, 0
| 0x00010254 pcalau12i t0, 0x24
| 0x00010258 pcaddi a3, str.Ncat ; 0x23048 ; "Ncat"
| 0x0001025c pcaddi a2, str.s: ; 0x251c8 ; "%s: "
| 0x00010260 addi.w a1, zero, 1
| 0x00010264 ld.d t0, t0, -0x560
| 0x00010268 st.d t1, sp, 8
| 0x0001026c jirl ra, t0, 0
| 0x00010270 ldptr.d a0, s0, 0
| 0x00010274 pcalau12i t0, 0x24
| 0x00010278 addi.d a3, sp, 0x38
| 0x0001027c move a2, s2
| 0x00010280 addi.w a1, zero, 1
| 0x00010284 ld.d t0, t0, -0x588
| 0x00010288 stptr.d a3, sp, 0
| 0x0001028c jirl ra, t0, 0
| 0x00010290 ldptr.d a0, s0, 0
| 0x00010294 pcalau12i t0, 0x24
| 0x00010298 ld.d t0, t0, -0x180
| 0x0001029c jirl ra, t0, 0
| 0x000102a0 ld.d t1, sp, 8
| 0x000102a4 ldptr.d t0, s1, 0
| ,=< 0x000102a8 bne t1, t0, 0x1c
| | 0x000102ac ld.d ra, sp, 0x28
| | 0x000102b0 ld.d s0, sp, 0x20
| | 0x000102b4 ld.d s1, sp, 0x18
| | 0x000102b8 ld.d s2, sp, 0x10
| | 0x000102bc addi.d sp, sp, 0x70
| | 0x000102c0 ret
| `-> 0x000102c4 pcalau12i t0, 0x24
| 0x000102c8 ld.d t0, t0, -0x4a8
\ 0x000102cc jirl ra, t0, 0
EOF
RUN

File diff suppressed because one or more lines are too long