Initial support for D calling convention ##analysis

* Only for x86-32 for now
* Uses a reverse register list for passing arguments
* See libr/anal/d/README.md for more details
* Support ret2 (to return 64bit values)
* Add fcn->realname for demangled names
This commit is contained in:
pancake 2023-08-25 18:45:02 +02:00
parent e6a30d820c
commit c15ace8508
15 changed files with 167 additions and 35 deletions

View File

@ -97,6 +97,10 @@ R_API void r_anal_cc_get_json(RAnal *anal, PJ *pj, const char *name) {
return;
}
pj_ks (pj, "ret", ret);
const char *ret2 = sdb_const_get (DB, r_strf ("cc.%s.ret2", name), 0);
if (ret2) {
pj_ks (pj, "ret2", ret2);
}
char *sig = r_anal_cc_get (anal, name);
pj_ks (pj, "signature", sig);
free (sig);
@ -121,26 +125,40 @@ R_API void r_anal_cc_get_json(RAnal *anal, PJ *pj, const char *name) {
}
R_API char *r_anal_cc_get(RAnal *anal, const char *name) {
Sdb *db = anal->sdb_cc;
r_return_val_if_fail (anal && name, NULL);
int i;
// get cc by name and print the expr
if (strcmp (sdb_const_get (DB, name, 0), "cc")) {
if (strcmp (sdb_const_get (db, name, 0), "cc")) {
R_LOG_ERROR ("Invalid calling convention name (%s)", name);
return NULL;
}
r_strf_var (ccret, 128, "cc.%s.ret", name);
const char *ret = sdb_const_get (DB, ccret, 0);
const char *ret = sdb_const_get (db, ccret, 0);
if (!ret) {
R_LOG_ERROR ("Cannot find return type for %s", name);
return NULL;
}
r_strf_var (ccret2, 128, "cc.%s.ret2", name);
const char *ret2 = sdb_const_get (db, ccret2, 0);
RStrBuf *sb = r_strbuf_new (NULL);
const char *self = r_anal_cc_self (anal, name);
r_strbuf_appendf (sb, "%s %s%s%s (", ret, r_str_get (self), self? ".": "", name);
if (ret2) {
r_strbuf_appendf (sb, "%s:%s %s%s%s (", ret, ret2, r_str_get (self), self? ".": "", name);
} else {
r_strbuf_appendf (sb, "%s %s%s%s (", ret, r_str_get (self), self? ".": "", name);
}
bool isFirst = true;
bool revarg = false;
{
r_strf_var (k, 128, "cc.%s.revarg", name);
const char *s = sdb_const_get (db, k, 0);
revarg = r_str_is_true (s);
}
for (i = 0; i < R_ANAL_CC_MAXARG; i++) {
r_strf_var (k, 128, "cc.%s.arg%d", name, i);
const char *arg = sdb_const_get (DB, k, 0);
const char *arg = sdb_const_get (db, k, 0);
if (!arg) {
break;
}
@ -148,7 +166,7 @@ R_API char *r_anal_cc_get(RAnal *anal, const char *name) {
isFirst = false;
}
r_strf_var (rename, 128, "cc.%s.argn", name);
const char *argn = sdb_const_get (DB, rename, 0);
const char *argn = sdb_const_get (db, rename, 0);
if (argn) {
r_strbuf_appendf (sb, "%s%s", isFirst? "": ", ", argn);
}
@ -160,27 +178,38 @@ R_API char *r_anal_cc_get(RAnal *anal, const char *name) {
}
r_strbuf_append (sb, ";");
if (revarg) {
r_strbuf_append (sb, " // revarg");
}
return r_strbuf_drain (sb);
}
R_API bool r_anal_cc_exist(RAnal *anal, const char *convention) {
r_return_val_if_fail (anal && convention, false);
const char *x = sdb_const_get (DB, convention, 0);
return x && *x && !strcmp (x, "cc");
R_API bool r_anal_cc_exist(RAnal *anal, const char *cc) {
r_return_val_if_fail (anal && cc, false);
const char *x = sdb_const_get (DB, cc, 0);
return (x != NULL) && !strcmp (x, "cc");
}
R_API const char *r_anal_cc_arg(RAnal *anal, const char *convention, int n) {
R_API const char *r_anal_cc_arg(RAnal *anal, const char *cc, int n, int lastn) {
r_return_val_if_fail (anal && n >= 0, NULL);
if (!convention) {
if (!cc) {
return NULL;
}
Sdb *db = DB;
r_strf_buffer (64);
char *query = r_strf ("cc.%s.arg%d", convention, n);
const char *ret = sdb_const_get (DB, query, 0);
if (lastn >= 0) {
char *revarg = r_strf ("cc.%s.revarg", cc);
if (r_str_is_true (revarg)) {
// check if revarg is set, this is used only for D
R_LOG_INFO ("EXPERIMENTAL: Reversing argument position");
n = lastn - n;
}
}
char *query = r_strf ("cc.%s.arg%d", cc, n);
const char *ret = sdb_const_get (db, query, 0);
if (!ret) {
query = r_strf ("cc.%s.argn", convention);
ret = sdb_const_get (DB, query, 0);
query = r_strf ("cc.%s.argn", cc);
ret = sdb_const_get (db, query, 0);
}
return ret? r_str_constpool_get (&anal->constpool, ret): NULL;
}
@ -207,7 +236,7 @@ R_API const char *r_anal_cc_error(RAnal *anal, const char *convention) {
R_CRITICAL_ENTER (anal);
r_strf_var (query, 64, "cc.%s.error", convention);
const char *error = sdb_const_get (DB, query, 0);
const char * res = error? r_str_constpool_get (&anal->constpool, error): NULL;
const char *res = error? r_str_constpool_get (&anal->constpool, error): NULL;
R_CRITICAL_LEAVE (anal);
return res;
}

View File

@ -8,3 +8,45 @@ structures and this is used for the code analysis and type propagation logic.
* spec.sdb.txt = format modifiers like %p %d %s its used for type propagation
* types.sdb.txt = basic C-like types
* $os-$bits.sdb.txt = os-arch-bits structs and enums
## Types: structs / enums / constants
## Calling Conventions
Those are defined in the `cc-${arch}-${bits}.sdb.txt` files.
### dlang calling convention
* narg = 1 : edi
* narg = 2 : esi, edi
* narg = 3 : edx, esi, edi
* narg = 4 : ecx, edx, esi, edi
* narg = 5 : r8d, ecx, edx, esi, edi
* narg = 6 : r9d, r8d, ecx, edx, esi, edi
* narg = 7 : push, r9d, r8d, ecx, edx, esi, edi
```asm
mov R9D,1
mov R8D,2
mov ECX,3
mov EDX,4
mov ESI,5
mov EDI,6
mov R8D,1
mov ECX,2
mov EDX,3
mov ESI,4
mov EDI,5
push 1
push 2
mov R9D,3
mov R8D,4
mov ECX,5
mov EDX,6
mov ESI,7
mov EDI,8
call int example.square(int, int, int, int, int, int, int, int)@PLT32
add RSP,010h
```

View File

@ -30,3 +30,17 @@ p9=cc
cc.p9.arg0=x0
cc.p9.argn=stack
cc.p9.ret=x0
dlang=cc
cc.dlang.arg0=x0
cc.dlang.arg1=x1
cc.dlang.arg2=x2
cc.dlang.arg3=x3
cc.dlang.arg4=x4
cc.dlang.arg5=x5
cc.dlang.arg6=x6
cc.dlang.arg7=x7
cc.dlang.argn=stack
cc.dlang.self=x20
cc.dlang.error=x21
cc.dlang.ret=x0

View File

@ -44,3 +44,16 @@ cc.watcom.argn=stack
pascal=cc
cc.pascal.ret=eax
cc.pascal.argn=stack_rev
# reverse argument calling convention. only for gcc and dmd, ldc2 uses normal cdecl CC
dlang=cc
cc.dlang.ret=eax
cc.dlang.ret2=edx
cc.dlang.revarg=1
cc.dlang.arg0=edi
cc.dlang.arg1=esi
cc.dlang.arg2=edx
cc.dlang.arg3=ecx
cc.dlang.arg4=r8d
cc.dlang.arg5=r9d
cc.dlang.argn=stack_rev

View File

@ -51,3 +51,14 @@ p9=cc
cc.p9.arg0=rbp
cc.p9.argn=stack
cc.p9.ret=rax
dlang=cc
cc.dlang.ret=rax
cc.dlang.ret2=rdx
cc.dlang.arg0=rdi
cc.dlang.arg1=rsi
cc.dlang.arg2=rdx
cc.dlang.arg3=rcx
cc.dlang.arg4=r8d
cc.dlang.arg5=r9d
cc.dlang.argn=stack

View File

@ -589,9 +589,10 @@ R_API int r_anal_var_get_argnum(RAnalVar *var) {
r_unref (ri);
int i;
char *cc = var->fcn->cc ? strdup (var->fcn->cc): NULL;
int arg_max = cc ? r_anal_cc_max_arg (anal, cc) : 0;
const int arg_max = cc ? r_anal_cc_max_arg (anal, cc) : 0;
int total = 0;
for (i = 0; i < arg_max; i++) {
const char *reg_arg = r_anal_cc_arg (anal, cc, i);
const char *reg_arg = r_anal_cc_arg (anal, cc, i, total);
if (reg_arg && !strcmp (ri_name, reg_arg)) {
free (cc);
free (ri_name);
@ -1001,6 +1002,7 @@ static void extract_arg(RAnal *anal, RAnalFunction *fcn, RAnalOp *op, const char
R_LOG_WARN ("Analysis didn't fill op->src/dst at 0x%" PFMT64x, op->addr);
}
const int maxarg = 32; // TODO: use maxarg ?
int rw = (op->direction == R_ANAL_OP_DIR_WRITE) ? R_ANAL_VAR_ACCESS_TYPE_WRITE : R_ANAL_VAR_ACCESS_TYPE_READ;
if (*sign == '+') {
const bool isarg = type == R_ANAL_VAR_KIND_SPV ? ptr >= fcn->stack : ptr >= fcn->bp_off;
@ -1021,7 +1023,7 @@ static void extract_arg(RAnal *anal, RAnalFunction *fcn, RAnalOp *op, const char
}
char *varname = NULL, *vartype = NULL;
if (isarg) {
const char *place = fcn->cc ? r_anal_cc_arg (anal, fcn->cc, ST32_MAX) : NULL;
const char *place = fcn->cc ? r_anal_cc_arg (anal, fcn->cc, maxarg, -1) : NULL;
bool stack_rev = place ? !strcmp (place, "stack_rev") : false;
char *fname = r_type_func_guess (anal->sdb_types, fcn->name);
if (fname) {
@ -1245,6 +1247,7 @@ R_API void r_anal_extract_rarg(RAnal *anal, RAnalOp *op, RAnalFunction *fcn, int
callee_rargs_l = r_anal_var_list (anal, f, R_ANAL_VAR_KIND_REG);
}
int i;
const int total = callee_rargs;
for (i = 0; i < callee_rargs; i++) {
if (reg_set[i]) {
continue;
@ -1253,7 +1256,7 @@ R_API void r_anal_extract_rarg(RAnal *anal, RAnalOp *op, RAnalFunction *fcn, int
char *type = NULL;
char *name = NULL;
int delta = 0;
const char *regname = r_anal_cc_arg (anal, fcn->cc, i);
const char *regname = r_anal_cc_arg (anal, fcn->cc, i, total);
if (regname) {
RRegItem *ri = r_reg_get (anal->reg, regname, -1);
if (ri) {
@ -1300,8 +1303,9 @@ R_API void r_anal_extract_rarg(RAnal *anal, RAnalOp *op, RAnalFunction *fcn, int
return;
}
const int total = 0; // TODO: pass argn
for (i = 0; i < max_count; i++) {
const char *regname = r_anal_cc_arg (anal, fcn->cc, i);
const char *regname = r_anal_cc_arg (anal, fcn->cc, i, total);
if (!regname) {
// WIP break;
} else {

View File

@ -415,13 +415,14 @@ static void type_match(RCore *core, char *fcn_name, ut64 addr, ut64 baddr, const
return;
}
int i, j, pos = 0, size = 0, max = r_type_func_args_count (TDB, fcn_name);
const char *place = r_anal_cc_arg (anal, cc, ST32_MAX);
int lastarg = ST32_MAX;
const char *place = r_anal_cc_arg (anal, cc, lastarg, -1);
r_cons_break_push (NULL, NULL);
if (place && !strcmp (place, "stack_rev")) {
stack_rev = true;
}
place = r_anal_cc_arg (anal, cc, 0);
place = r_anal_cc_arg (anal, cc, 0, -1);
if (place && r_str_startswith (place, "stack")) {
in_stack = true;
}
@ -468,7 +469,7 @@ static void type_match(RCore *core, char *fcn_name, ut64 addr, ut64 baddr, const
// XXX: param arg_num must be fixed to support floating point register
// before this change place could be null
DD eprintf ("not in stack\n");
const char *p = r_anal_cc_arg (anal, cc, arg_num);
const char *p = r_anal_cc_arg (anal, cc, arg_num, -1);
if (p && r_str_startswith (p, "stack")) {
in_stack = true;
place = p;

View File

@ -25,7 +25,7 @@ static void set_fcn_args_info(RAnalFuncArg *arg, RAnal *anal, const char *fcn_na
arg->fmt = sdb_const_get (TDB, query, 0);
const char *t_query = r_strf ("type.%s.size", arg->c_type);
arg->size = sdb_num_get (TDB, t_query, 0) / 8;
arg->cc_source = r_anal_cc_arg (anal, cc, arg_num);
arg->cc_source = r_anal_cc_arg (anal, cc, arg_num, -1);
}
R_API char *resolve_fcn_name(RAnal *anal, const char *func_name) {
@ -231,7 +231,7 @@ R_API RList *r_core_get_func_args(RCore *core, const char *fcn_name) {
return NULL;
}
char *cc = strdup (r_anal_cc_func (core->anal, key));
const char *src = r_anal_cc_arg (core->anal, cc, 0); // src of first argument
const char *src = r_anal_cc_arg (core->anal, cc, 0, -1); // src of first argument
if (!cc) {
// unsupported calling convention
free (key);

View File

@ -287,8 +287,8 @@ R_API int r_core_pseudo_code(RCore *core, const char *input) {
pj_ka (pj, "annotations");
}
const char *cc = fcn->cc ? fcn->cc: "default";
const char *cc_a0 = r_anal_cc_arg (core->anal, cc, 0);
const char *cc_a1 = r_anal_cc_arg (core->anal, cc, 1);
const char *cc_a0 = r_anal_cc_arg (core->anal, cc, 0, -1);
const char *cc_a1 = r_anal_cc_arg (core->anal, cc, 1, -1);
const char *a0 = cc_a0? cc_a0: r_reg_get_name_by_type (core->anal->reg, "A0");
const char *a1 = cc_a1? cc_a1: r_reg_get_name_by_type (core->anal->reg, "A1");
const char *r0 = r_reg_get_name_by_type (core->anal->reg, "R0");

View File

@ -27,7 +27,7 @@ R_API ut64 r_debug_arg_get(RDebug *dbg, const char *cc, int num) {
return (ut64)n32;
}
}
const char *rn = r_anal_cc_arg (dbg->anal, cc, num);
const char *rn = r_anal_cc_arg (dbg->anal, cc, num, -1);
if (rn) {
return r_debug_reg_get (dbg, rn);
}
@ -43,7 +43,7 @@ R_API bool r_debug_arg_set(RDebug *dbg, const char *cc, int num, ut64 val) {
if (!R_STR_ISEMPTY (cc)) {
cc = r_anal_syscc_default (dbg->anal);
}
const char *rn = r_anal_cc_arg (dbg->anal, cc, num);
const char *rn = r_anal_cc_arg (dbg->anal, cc, num, -1);
if (rn) {
r_debug_reg_set (dbg, rn, val);
return true;

View File

@ -255,7 +255,6 @@ struct r_anal_attr_t {
};
/* Stores useful function metadata */
/* TODO: Think about moving more stuff to this structure? */
typedef struct r_anal_function_meta_t {
// _min and _max are calculated lazily when queried.
// On changes, they will either be updated (if this can be done trivially) or invalidated.
@ -269,7 +268,7 @@ typedef struct r_anal_function_meta_t {
typedef struct r_anal_function_t {
char *name;
// R2_590: add realname for the mangled one
char *realname; // R2_590: add realname for the mangled one
int bits; // ((> bits 0) (set-bits bits))
int type;
const char *cc; // calling convention, should come from RAnal.constpool
@ -1203,7 +1202,7 @@ R_API bool r_anal_cc_set(RAnal *anal, const char *expr);
R_API char *r_anal_cc_get(RAnal *anal, const char *name);
R_API bool r_anal_cc_once(RAnal *anal);
R_API void r_anal_cc_get_json(RAnal *anal, PJ *pj, const char *name);
R_API const char *r_anal_cc_arg(RAnal *anal, const char *convention, int n);
R_API const char *r_anal_cc_arg(RAnal *anal, const char *convention, int n, int lastn);
R_API const char *r_anal_cc_self(RAnal *anal, const char *convention);
R_API void r_anal_cc_set_self(RAnal *anal, const char *convention, const char *self);
R_API const char *r_anal_cc_error(RAnal *anal, const char *convention);

View File

@ -9,6 +9,7 @@ EOF
EXPECT=<<EOF
amd64
amd64syscall
dlang
ms
p9
reg
@ -74,6 +75,7 @@ afcl
EOF
EXPECT=<<EOF
arm64
dlang
p9
reg
swift

View File

@ -92,6 +92,7 @@ EOF
EXPECT=<<EOF
amd64
amd64syscall
dlang
ms
p9
reg
@ -100,6 +101,7 @@ r0 reg(r0, r1, r2, r3)
rax reg(rdi, rsi, rdx, rcx)
amd64
amd64syscall
dlang
ms
p9
reg

View File

@ -3230,6 +3230,16 @@ EXPECT=<<EOF
"cc.cdecl-fastcall-ms.ret=eax",
"cc.cdecl.argn=stack",
"cc.cdecl.ret=eax",
"cc.dlang.arg0=edi",
"cc.dlang.arg1=esi",
"cc.dlang.arg2=edx",
"cc.dlang.arg3=ecx",
"cc.dlang.arg4=r8d",
"cc.dlang.arg5=r9d",
"cc.dlang.argn=stack_rev",
"cc.dlang.ret=eax",
"cc.dlang.ret2=edx",
"cc.dlang.revarg=1",
"cc.fastcall.arg0=ecx",
"cc.fastcall.arg1=edx",
"cc.fastcall.argn=stack",
@ -3257,6 +3267,7 @@ EXPECT=<<EOF
"cdecl=cc",
"cdecl-fastcall-ms=cc",
"default.cc=cdecl",
"dlang=cc",
"fastcall=cc",
"optlink=cc",
"pascal=cc",

View File

@ -8,6 +8,7 @@ EOF
EXPECT=<<EOF
amd64
amd64syscall
dlang
ms
p9
reg
@ -21,6 +22,7 @@ CMDS=tcc
EXPECT=<<EOF
amd64
amd64syscall
dlang
ms
p9
reg
@ -36,15 +38,17 @@ tccl
tcc*
EOF
EXPECT=<<EOF
{"amd64":{"ret":"rax","signature":"rax amd64 (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4);","args":["rdi","rsi","rdx","rcx","r8","r9","xmm0","xmm1","xmm2","xmm3","xmm4"]},"amd64syscall":{"ret":"rax","signature":"rax amd64syscall (rdi, rsi, rdx, r10, r8, r9);","args":["rdi","rsi","rdx","r10","r8","r9"]},"ms":{"ret":"rax","signature":"rax ms (rcx, rdx, r8, r9, stack);","args":["rcx","rdx","r8","r9"],"argn":"stack"},"p9":{"ret":"rax","signature":"rax p9 (rbp, stack);","args":["rbp"],"argn":"stack"},"reg":{"ret":"rax","signature":"rax reg (rdi, rsi, rdx, rcx);","args":["rdi","rsi","rdx","rcx"]},"swift":{"ret":"rax","signature":"rax r13.swift (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4) r12;","args":["rdi","rsi","rdx","rcx","r8","r9","xmm0","xmm1","xmm2","xmm3","xmm4"],"error":"r12"}}
{"amd64":{"ret":"rax","signature":"rax amd64 (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4);","args":["rdi","rsi","rdx","rcx","r8","r9","xmm0","xmm1","xmm2","xmm3","xmm4"]},"amd64syscall":{"ret":"rax","signature":"rax amd64syscall (rdi, rsi, rdx, r10, r8, r9);","args":["rdi","rsi","rdx","r10","r8","r9"]},"dlang":{"ret":"rax","ret2":"rdx","signature":"rax:rdx dlang (rdi, rsi, rdx, rcx, r8d, r9d, stack);","args":["rdi","rsi","rdx","rcx","r8d","r9d"],"argn":"stack"},"ms":{"ret":"rax","signature":"rax ms (rcx, rdx, r8, r9, stack);","args":["rcx","rdx","r8","r9"],"argn":"stack"},"p9":{"ret":"rax","signature":"rax p9 (rbp, stack);","args":["rbp"],"argn":"stack"},"reg":{"ret":"rax","signature":"rax reg (rdi, rsi, rdx, rcx);","args":["rdi","rsi","rdx","rcx"]},"swift":{"ret":"rax","signature":"rax r13.swift (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4) r12;","args":["rdi","rsi","rdx","rcx","r8","r9","xmm0","xmm1","xmm2","xmm3","xmm4"],"error":"r12"}}
rax amd64 (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4);
rax amd64syscall (rdi, rsi, rdx, r10, r8, r9);
rax:rdx dlang (rdi, rsi, rdx, rcx, r8d, r9d, stack);
rax ms (rcx, rdx, r8, r9, stack);
rax p9 (rbp, stack);
rax reg (rdi, rsi, rdx, rcx);
rax r13.swift (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4) r12;
tcc rax amd64 (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4);
tcc rax amd64syscall (rdi, rsi, rdx, r10, r8, r9);
tcc rax:rdx dlang (rdi, rsi, rdx, rcx, r8d, r9d, stack);
tcc rax ms (rcx, rdx, r8, r9, stack);
tcc rax p9 (rbp, stack);
tcc rax reg (rdi, rsi, rdx, rcx);