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) {
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;
}
rz_strbuf_appendf(&sb, ", %s", an);
}
if (a2 && a1) {
return rz_str_newf("%s reg(%s, %s, %s)", r0, a0, a1, a2);
}
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);
}

View file

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

View file

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

View file

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

View file

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

View file

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