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) {
|
RZ_API char *rz_reg_profile_to_cc(RzReg *reg) {
|
||||||
const char *r0 = rz_reg_get_name_by_type(reg, "R0");
|
const char *r0 = rz_reg_get_name_by_type(reg, "R0");
|
||||||
const char *a0 = rz_reg_get_name_by_type(reg, "A0");
|
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) {
|
if (!a0) {
|
||||||
RZ_LOG_WARN("It is mandatory to have at least one argument register defined in the register profile.\n");
|
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) {
|
if (!r0) {
|
||||||
r0 = a0;
|
r0 = a0;
|
||||||
}
|
}
|
||||||
if (a3 && a2 && a1) {
|
// Gather every consecutive argument register the profile declares (A0..A9),
|
||||||
return rz_str_newf("%s reg(%s, %s, %s, %s)", r0, a0, a1, a2, a3);
|
// 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;
|
||||||
|
}
|
||||||
|
rz_strbuf_appendf(&sb, ", %s", an);
|
||||||
}
|
}
|
||||||
if (a2 && a1) {
|
rz_strbuf_append(&sb, ")");
|
||||||
return rz_str_newf("%s reg(%s, %s, %s)", r0, a0, a1, a2);
|
return rz_strbuf_drain_nofree(&sb);
|
||||||
}
|
|
||||||
if (a1) {
|
|
||||||
return rz_str_newf("%s reg(%s, %s)", r0, a0, a1);
|
|
||||||
}
|
|
||||||
return rz_str_newf("%s reg(%s)", r0, a0);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -675,7 +675,7 @@ A5 r9 0x0 0
|
||||||
A6 r10 0x0 0
|
A6 r10 0x0 0
|
||||||
A7 r11 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"}]
|
[{"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
|
EOF
|
||||||
RUN
|
RUN
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ ms
|
||||||
reg
|
reg
|
||||||
swift
|
swift
|
||||||
r0 reg(r0, r1, r2, r3)
|
r0 reg(r0, r1, r2, r3)
|
||||||
rdi reg(rdi, rsi, rdx, rcx)
|
rdi reg(rdi, rsi, rdx, rcx, r8, r9, r10, r11)
|
||||||
amd64
|
amd64
|
||||||
amd64syscall
|
amd64syscall
|
||||||
ms
|
ms
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,11 @@ cc.reg.arg0=rdi
|
||||||
cc.reg.arg1=rsi
|
cc.reg.arg1=rsi
|
||||||
cc.reg.arg2=rdx
|
cc.reg.arg2=rdx
|
||||||
cc.reg.arg3=rcx
|
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.reg.ret=rdi
|
||||||
cc.swift.arg0=rdi
|
cc.swift.arg0=rdi
|
||||||
cc.swift.arg1=rsi
|
cc.swift.arg1=rsi
|
||||||
|
|
@ -183,7 +187,9 @@ EXPECT=<<EOF
|
||||||
"cc.reg.arg1=ebx",
|
"cc.reg.arg1=ebx",
|
||||||
"cc.reg.arg2=ecx",
|
"cc.reg.arg2=ecx",
|
||||||
"cc.reg.arg3=edx",
|
"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.reg.ret=eax",
|
||||||
"cc.stdcall.argn=stack",
|
"cc.stdcall.argn=stack",
|
||||||
"cc.stdcall.maxargs=0",
|
"cc.stdcall.maxargs=0",
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,11 @@ tccj
|
||||||
tccl
|
tccl
|
||||||
EOF
|
EOF
|
||||||
EXPECT=<<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 amd64 (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4);
|
||||||
rax amd64syscall (rdi, rsi, rdx, r10, r8, r9);
|
rax amd64syscall (rdi, rsi, rdx, r10, r8, r9);
|
||||||
rax ms (rcx, rdx, r8, r9, stack);
|
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;
|
rax r13.swift (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4) r12;
|
||||||
EOF
|
EOF
|
||||||
RUN
|
RUN
|
||||||
|
|
|
||||||
|
|
@ -386,6 +386,70 @@ bool test_rz_reg_set_bv(void) {
|
||||||
mu_end;
|
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() {
|
int all_tests() {
|
||||||
mu_run_test(test_rz_reg_set_profile_string);
|
mu_run_test(test_rz_reg_set_profile_string);
|
||||||
mu_run_test(test_rz_reg_get_value_gpr);
|
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_list);
|
||||||
mu_run_test(test_rz_reg_get_bv);
|
mu_run_test(test_rz_reg_get_bv);
|
||||||
mu_run_test(test_rz_reg_set_bv);
|
mu_run_test(test_rz_reg_set_bv);
|
||||||
|
mu_run_test(test_rz_reg_profile_to_cc);
|
||||||
return tests_passed != tests_run;
|
return tests_passed != tests_run;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue