librz/reg: derive CC with more than four argument registers (#6600)
rz_reg_profile_to_cc() only emitted the first four argument registers (A0-A3), so architectures that pass more arguments in registers -- the C6000 EABI uses ten, and x86-64/riscv/ppc all declare more than four -- got a truncated convention. Walk the whole A0-A9 role range, stopping at the first role the profile leaves undefined, and build the cc string with RzStrBuf. Covered by a new test_reg unit test. Co-authored-by agent: Claude/claude-opus-4-8 Co-authored-by: Anton Kochkov <anton.kochkov@gmail.com>
This commit is contained in:
parent
4897885c5c
commit
155ead6822
6 changed files with 93 additions and 18 deletions
|
|
@ -663,9 +663,6 @@ RZ_API char *rz_reg_parse_gdb_profile(const char *profile_file) {
|
|||
RZ_API char *rz_reg_profile_to_cc(RzReg *reg) {
|
||||
const char *r0 = rz_reg_get_name_by_type(reg, "R0");
|
||||
const char *a0 = rz_reg_get_name_by_type(reg, "A0");
|
||||
const char *a1 = rz_reg_get_name_by_type(reg, "A1");
|
||||
const char *a2 = rz_reg_get_name_by_type(reg, "A2");
|
||||
const char *a3 = rz_reg_get_name_by_type(reg, "A3");
|
||||
|
||||
if (!a0) {
|
||||
RZ_LOG_WARN("It is mandatory to have at least one argument register defined in the register profile.\n");
|
||||
|
|
@ -674,14 +671,21 @@ RZ_API char *rz_reg_profile_to_cc(RzReg *reg) {
|
|||
if (!r0) {
|
||||
r0 = a0;
|
||||
}
|
||||
if (a3 && a2 && a1) {
|
||||
return rz_str_newf("%s reg(%s, %s, %s, %s)", r0, a0, a1, a2, a3);
|
||||
// Gather every consecutive argument register the profile declares (A0..A9),
|
||||
// stopping at the first undefined role, so an arch that passes more than four
|
||||
// arguments in registers gets a complete convention rather than a truncated
|
||||
// one.
|
||||
static const char *arg_roles[] = { "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9" };
|
||||
RzStrBuf sb;
|
||||
rz_strbuf_init(&sb);
|
||||
rz_strbuf_appendf(&sb, "%s reg(%s", r0, a0);
|
||||
for (size_t i = 0; i < RZ_ARRAY_SIZE(arg_roles); i++) {
|
||||
const char *an = rz_reg_get_name_by_type(reg, arg_roles[i]);
|
||||
if (!an) {
|
||||
break;
|
||||
}
|
||||
if (a2 && a1) {
|
||||
return rz_str_newf("%s reg(%s, %s, %s)", r0, a0, a1, a2);
|
||||
rz_strbuf_appendf(&sb, ", %s", an);
|
||||
}
|
||||
if (a1) {
|
||||
return rz_str_newf("%s reg(%s, %s)", r0, a0, a1);
|
||||
}
|
||||
return rz_str_newf("%s reg(%s)", r0, a0);
|
||||
rz_strbuf_append(&sb, ")");
|
||||
return rz_strbuf_drain_nofree(&sb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -675,7 +675,7 @@ A5 r9 0x0 0
|
|||
A6 r10 0x0 0
|
||||
A7 r11 0x0 0
|
||||
[{"role":"A0","reg":"rdi","value":"0x804866b","refstr":"134514283 .rodata str.Password_OK,rdi R X 'push rax' Password OK!"},{"role":"A1","reg":"rsi","value":"0x0","refstr":"0"},{"role":"A2","reg":"rdx","value":"0x8048679","refstr":"134514297 .rodata str.Password_Incorrect,rdx R X 'push rax' Password Incorrect!"},{"role":"A3","reg":"rcx","value":"0x0","refstr":"0"},{"role":"A4","reg":"r8","value":"0x0","refstr":"0"},{"role":"A5","reg":"r9","value":"0x0","refstr":"0"},{"role":"A6","reg":"r10","value":"0x0","refstr":"0"},{"role":"A7","reg":"r11","value":"0x0","refstr":"0"}]
|
||||
rdi reg(rdi, rsi, rdx, rcx)
|
||||
rdi reg(rdi, rsi, rdx, rcx, r8, r9, r10, r11)
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ ms
|
|||
reg
|
||||
swift
|
||||
r0 reg(r0, r1, r2, r3)
|
||||
rdi reg(rdi, rsi, rdx, rcx)
|
||||
rdi reg(rdi, rsi, rdx, rcx, r8, r9, r10, r11)
|
||||
amd64
|
||||
amd64syscall
|
||||
ms
|
||||
|
|
|
|||
|
|
@ -55,7 +55,11 @@ cc.reg.arg0=rdi
|
|||
cc.reg.arg1=rsi
|
||||
cc.reg.arg2=rdx
|
||||
cc.reg.arg3=rcx
|
||||
cc.reg.maxargs=4
|
||||
cc.reg.arg4=r8
|
||||
cc.reg.arg5=r9
|
||||
cc.reg.arg6=r10
|
||||
cc.reg.arg7=r11
|
||||
cc.reg.maxargs=8
|
||||
cc.reg.ret=rdi
|
||||
cc.swift.arg0=rdi
|
||||
cc.swift.arg1=rsi
|
||||
|
|
@ -183,7 +187,9 @@ EXPECT=<<EOF
|
|||
"cc.reg.arg1=ebx",
|
||||
"cc.reg.arg2=ecx",
|
||||
"cc.reg.arg3=edx",
|
||||
"cc.reg.maxargs=4",
|
||||
"cc.reg.arg4=esi",
|
||||
"cc.reg.arg5=edi",
|
||||
"cc.reg.maxargs=6",
|
||||
"cc.reg.ret=eax",
|
||||
"cc.stdcall.argn=stack",
|
||||
"cc.stdcall.maxargs=0",
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ tccj
|
|||
tccl
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[{"name":"amd64","ret":"rax","args":["rdi","rsi","rdx","rcx","r8","r9","xmm0","xmm1","xmm2","xmm3","xmm4"]},{"name":"amd64syscall","ret":"rax","args":["rdi","rsi","rdx","r10","r8","r9"]},{"name":"ms","ret":"rax","args":["rcx","rdx","r8","r9"]},{"name":"reg","ret":"rdi","args":["rdi","rsi","rdx","rcx"]},{"name":"swift","ret":"rax","args":["rdi","rsi","rdx","rcx","r8","r9","xmm0","xmm1","xmm2","xmm3","xmm4"],"self":"r13","error":"r12"}]
|
||||
[{"name":"amd64","ret":"rax","args":["rdi","rsi","rdx","rcx","r8","r9","xmm0","xmm1","xmm2","xmm3","xmm4"]},{"name":"amd64syscall","ret":"rax","args":["rdi","rsi","rdx","r10","r8","r9"]},{"name":"ms","ret":"rax","args":["rcx","rdx","r8","r9"]},{"name":"reg","ret":"rdi","args":["rdi","rsi","rdx","rcx","r8","r9","r10","r11"]},{"name":"swift","ret":"rax","args":["rdi","rsi","rdx","rcx","r8","r9","xmm0","xmm1","xmm2","xmm3","xmm4"],"self":"r13","error":"r12"}]
|
||||
rax amd64 (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4);
|
||||
rax amd64syscall (rdi, rsi, rdx, r10, r8, r9);
|
||||
rax ms (rcx, rdx, r8, r9, stack);
|
||||
rdi reg (rdi, rsi, rdx, rcx);
|
||||
rdi reg (rdi, rsi, rdx, rcx, r8, r9, r10, r11);
|
||||
rax r13.swift (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4) r12;
|
||||
EOF
|
||||
RUN
|
||||
|
|
|
|||
|
|
@ -386,6 +386,70 @@ bool test_rz_reg_set_bv(void) {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_reg_profile_to_cc(void) {
|
||||
RzReg *reg = rz_reg_new();
|
||||
mu_assert_notnull(reg, "rz_reg_new () failed");
|
||||
|
||||
// More than four argument registers must all appear in the derived cc.
|
||||
rz_reg_set_profile_string(reg,
|
||||
"=R0 r0\n"
|
||||
"=A0 r0\n"
|
||||
"=A1 r1\n"
|
||||
"=A2 r2\n"
|
||||
"=A3 r3\n"
|
||||
"=A4 r4\n"
|
||||
"=A5 r5\n"
|
||||
"gpr r0 .32 0 0\n"
|
||||
"gpr r1 .32 4 0\n"
|
||||
"gpr r2 .32 8 0\n"
|
||||
"gpr r3 .32 12 0\n"
|
||||
"gpr r4 .32 16 0\n"
|
||||
"gpr r5 .32 20 0\n");
|
||||
char *cc = rz_reg_profile_to_cc(reg);
|
||||
mu_assert_streq_free(cc, "r0 reg(r0, r1, r2, r3, r4, r5)", "six argument registers");
|
||||
|
||||
// Four argument registers, the classic case, is unchanged.
|
||||
rz_reg_set_profile_string(reg,
|
||||
"=R0 a\n"
|
||||
"=A0 a\n"
|
||||
"=A1 b\n"
|
||||
"=A2 c\n"
|
||||
"=A3 d\n"
|
||||
"gpr a .32 0 0\n"
|
||||
"gpr b .32 4 0\n"
|
||||
"gpr c .32 8 0\n"
|
||||
"gpr d .32 12 0\n");
|
||||
cc = rz_reg_profile_to_cc(reg);
|
||||
mu_assert_streq_free(cc, "a reg(a, b, c, d)", "four argument registers");
|
||||
|
||||
// The argument list stops at the first undefined role and the return
|
||||
// register falls back to A0 when R0 is absent.
|
||||
rz_reg_set_profile_string(reg,
|
||||
"=A0 a\n"
|
||||
"=A1 b\n"
|
||||
"gpr a .32 0 0\n"
|
||||
"gpr b .32 4 0\n");
|
||||
cc = rz_reg_profile_to_cc(reg);
|
||||
mu_assert_streq_free(cc, "a reg(a, b)", "two args, return falls back to A0");
|
||||
|
||||
// A0 alone is a valid single-argument convention.
|
||||
rz_reg_set_profile_string(reg,
|
||||
"=A0 a\n"
|
||||
"gpr a .32 0 0\n");
|
||||
cc = rz_reg_profile_to_cc(reg);
|
||||
mu_assert_streq_free(cc, "a reg(a)", "single argument register");
|
||||
|
||||
// Without any argument register there is no convention to derive.
|
||||
rz_reg_set_profile_string(reg,
|
||||
"=PC pc\n"
|
||||
"gpr pc .32 0 0\n");
|
||||
cc = rz_reg_profile_to_cc(reg);
|
||||
mu_assert_null(cc, "no argument register yields no cc");
|
||||
|
||||
rz_reg_free(reg);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
int all_tests() {
|
||||
mu_run_test(test_rz_reg_set_profile_string);
|
||||
mu_run_test(test_rz_reg_get_value_gpr);
|
||||
|
|
@ -395,6 +459,7 @@ int all_tests() {
|
|||
mu_run_test(test_rz_reg_get_list);
|
||||
mu_run_test(test_rz_reg_get_bv);
|
||||
mu_run_test(test_rz_reg_set_bv);
|
||||
mu_run_test(test_rz_reg_profile_to_cc);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue