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:
NOT XVilka 2026-07-06 03:35:41 +08:00 committed by GitHub
parent 4897885c5c
commit 155ead6822
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 93 additions and 18 deletions

View file

@ -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);
} }

View file

@ -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

View file

@ -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

View file

@ -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",

View file

@ -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

View file

@ -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;
} }