Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions librz/arch/types/cc-arm-64.sdb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,25 @@ cc.swift.maxargs=8
cc.swift.self=x20
cc.swift.error=x21
cc.swift.ret=x0

golang=cc
cc.golang.arg0=x0
cc.golang.arg1=x1
cc.golang.arg2=x2
cc.golang.arg3=x3
cc.golang.arg4=x4
cc.golang.arg5=x5
cc.golang.arg6=x6
cc.golang.arg7=x7
cc.golang.arg8=x8
cc.golang.arg9=x9
cc.golang.arg10=x10
cc.golang.arg11=x11
cc.golang.arg12=x12
cc.golang.arg13=x13
cc.golang.arg14=x14
cc.golang.arg15=x15
cc.golang.argn=stack
cc.golang.maxargs=16
cc.golang.self=x28
cc.golang.ret=x0
5 changes: 5 additions & 0 deletions librz/arch/types/cc-x86-32.sdb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,8 @@ cc.watcom.maxargs=4
pascal=cc
cc.pascal.argn=stack_rev
cc.pascal.maxargs=0

golang=cc
cc.golang.argn=stack
cc.golang.maxargs=0
cc.golang.ret=eax
20 changes: 20 additions & 0 deletions librz/arch/types/cc-x86-64.sdb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,23 @@ cc.amd64syscall.arg4=r8
cc.amd64syscall.arg5=r9
cc.amd64syscall.maxargs=6
cc.amd64syscall.ret=rax

golang=cc
cc.golang.arg0=rax
cc.golang.arg1=rbx
cc.golang.arg2=rcx
cc.golang.arg3=rdi
cc.golang.arg4=rsi
cc.golang.arg5=r8
cc.golang.arg6=r9
cc.golang.arg7=r10
cc.golang.arg8=r11
cc.golang.argn=stack
cc.golang.maxargs=9
cc.golang.ret=rax
cc.golang.self=r14

golang_abi0=cc
cc.golang_abi0.argn=stack
cc.golang_abi0.maxargs=0
cc.golang_abi0.ret=rax
4 changes: 2 additions & 2 deletions librz/arch/types/types-golang-32.sdb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct.runtime.iface.data=void*,4,0

hmap=struct
struct.hmap=count,flags,B,noverflow,hash0,buckets,oldbuckets,nevacuate,extra
struct.hmap.count=int,0,0
struct.hmap.count=int32_t,0,0
struct.hmap.flags=uint8_t,4,0
struct.hmap.B=uint8_t,5,0
struct.hmap.noverflow=uint16_t,6,0
Expand All @@ -57,7 +57,7 @@ struct.hmap.extra=void*,24,0

runtime.hmap=struct
struct.runtime.hmap=count,flags,B,noverflow,hash0,buckets,oldbuckets,nevacuate,extra
struct.runtime.hmap.count=int,0,0
struct.runtime.hmap.count=int32_t,0,0
struct.runtime.hmap.flags=uint8_t,4,0
struct.runtime.hmap.B=uint8_t,5,0
struct.runtime.hmap.noverflow=uint16_t,6,0
Expand Down
4 changes: 2 additions & 2 deletions librz/arch/types/types-golang-64.sdb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct.runtime.iface.data=void*,8,0

hmap=struct
struct.hmap=count,flags,B,noverflow,hash0,buckets,oldbuckets,nevacuate,extra
struct.hmap.count=int,0,0
struct.hmap.count=int64_t,0,0
struct.hmap.flags=uint8_t,8,0
struct.hmap.B=uint8_t,9,0
struct.hmap.noverflow=uint16_t,10,0
Expand All @@ -57,7 +57,7 @@ struct.hmap.extra=void*,40,0

runtime.hmap=struct
struct.runtime.hmap=count,flags,B,noverflow,hash0,buckets,oldbuckets,nevacuate,extra
struct.runtime.hmap.count=int,0,0
struct.runtime.hmap.count=int64_t,0,0
struct.runtime.hmap.flags=uint8_t,8,0
struct.runtime.hmap.B=uint8_t,9,0
struct.runtime.hmap.noverflow=uint16_t,10,0
Expand Down
130 changes: 127 additions & 3 deletions librz/core/golang.c
Original file line number Diff line number Diff line change
Expand Up @@ -512,12 +512,125 @@ static ut32 core_recover_golang_functions_go_1_2(RzCore *core, GoPcLnTab *pclnta
return num_syms;
}

#define GO_ABI_PROLOGUE_WINDOW 24
#define GO_ABI_MAX_SAMPLES 512

/**
* \brief Checks if a Go symbol is compiler boilerplate (ABI wrappers, closures, inittasks).
*/
static bool rz_golang_is_compiler_wrapper_symbol(const char *name) {
if (RZ_STR_ISEMPTY(name)) {
return true;
}
if (!strncmp(name, "sym.go.", 7)) {
name += 7;
}
return strstr(name, ".abi0") ||
strstr(name, ".abiinternal") ||
strstr(name, ".deferwrap") ||
strstr(name, "..inittask") ||
!strncmp(name, "type:", 5) ||
!strncmp(name, "go:", 3);
}

/**
* \brief Sample a function's prologue to vote on register ABI (R14 holds g) vs stack ABI (g from TLS).
*/
static void rz_golang_vote_abi_prologue(RzIO *io, ut64 func_addr, ut32 *reg_votes, ut32 *stack_votes) {
ut8 code[GO_ABI_PROLOGUE_WINDOW + 8] = { 0 };
if (0 > rz_io_nread_at(io, func_addr, code, sizeof(code))) {
return;
}
for (ut32 k = 0; k <= GO_ABI_PROLOGUE_WINDOW; k++) {
// 49 3B 66 10 / 4D 3B 66 10 (CMPQ SP|R12, 16(R14)) -> Register ABI (R14 holds g)
if ((code[k] == 0x49 || code[k] == 0x4d) && code[k + 1] == 0x3b && code[k + 2] == 0x66 && code[k + 3] == 0x10) {
(*reg_votes)++;
return;
}
// 64|65 48|4C 8B /r with SIB 0x25 (MOVQ FS|GS:[disp32], reg) -> Stack ABI (g loaded from TLS)
if ((code[k] == 0x64 || code[k] == 0x65) && (code[k + 1] == 0x48 || code[k + 1] == 0x4c) && code[k + 2] == 0x8b &&
(code[k + 3] & 0xc7) == 0x04 && code[k + 4] == 0x25) {
(*stack_votes)++;
return;
}
}
}

typedef struct {
RzCore *core;
ut32 samples_count;
ut32 reg_votes;
ut32 stack_votes;
} GolangVoteCtx;

static bool rz_golang_sample_flag_for_abi(RzFlagItem *fi, void *user) {
GolangVoteCtx *ctx = (GolangVoteCtx *)user;
if (rz_golang_is_compiler_wrapper_symbol(fi->name)) {
return true;
}
if (ctx->samples_count >= GO_ABI_MAX_SAMPLES) {
return false;
}
ctx->samples_count++;
rz_golang_vote_abi_prologue(ctx->core->io, fi->offset, &ctx->reg_votes, &ctx->stack_votes);
return true;
}

typedef struct {
RzCore *core;
const char *cc;
} GolangAnalyseCtx;

static bool analyse_golang_symgo_function(RzFlagItem *fi, void *user) {
RzCore *core = (RzCore *)user;
GolangAnalyseCtx *ctx = (GolangAnalyseCtx *)user;
RzCore *core = ctx->core;

if (rz_golang_is_compiler_wrapper_symbol(fi->name)) {
return true;
}

rz_core_analysis_fcn(core, fi->offset, UT64_MAX, RZ_ANALYSIS_XREF_TYPE_NULL, 1);

if (ctx->cc) {
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, fi->offset);
if (fcn) {
fcn->cc = ctx->cc;
}
}
return true;
}

static const char *golang_detect_cc(RzCore *core, const RzSpace *symbols) {
const char *asm_arch = rz_config_get(core->config, "asm.arch");
ut32 asm_bits = rz_config_get_i(core->config, "asm.bits");

if (RZ_STR_ISEMPTY(asm_arch) || asm_bits == 32 || strcmp(asm_arch, "x86") != 0) {
return "golang";
}

// Run statistical prologue vote across sampled functions to determine active ABI
GolangVoteCtx vote_ctx = {
.core = core,
.samples_count = 0,
.reg_votes = 0,
.stack_votes = 0
};
rz_flag_foreach_space_glob(core->flags, "sym.go.*", symbols, rz_golang_sample_flag_for_abi, &vote_ctx);

ut32 total_votes = vote_ctx.reg_votes + vote_ctx.stack_votes;
if (total_votes >= 8 && vote_ctx.stack_votes * 10 >= total_votes * 9) {
RZ_LOG_INFO("Golang ABI prologue vote: Stack ABI (votes: %u reg, %u stack)\n",
vote_ctx.reg_votes, vote_ctx.stack_votes);
return "golang_abi0";
}

if (total_votes >= 8 && vote_ctx.reg_votes * 10 >= total_votes * 9) {
RZ_LOG_INFO("Golang ABI prologue vote: Register ABI (votes: %u reg, %u stack)\n",
vote_ctx.reg_votes, vote_ctx.stack_votes);
}
return "golang";
}

/**
* \brief Analyse Golang symbols matching "sym.go.*"
* \param core RzCore Pointer
Expand All @@ -527,7 +640,18 @@ static void analyse_golang_symbols(RzCore *core) {
if (!symbols) {
return;
}
rz_flag_foreach_space_glob(core->flags, "sym.go.*", symbols, analyse_golang_symgo_function, core);

const char *detected_cc = golang_detect_cc(core, symbols);
if (detected_cc && !rz_analysis_cc_exist(core->analysis, detected_cc)) {
detected_cc = NULL;
}
RzStrConstPool *cpool = rz_analysis_get_const_pool(core->analysis);
GolangAnalyseCtx actx = {
.core = core,
.cc = (detected_cc && cpool) ? rz_str_constpool_get(cpool, detected_cc) : NULL
};

rz_flag_foreach_space_glob(core->flags, "sym.go.*", symbols, analyse_golang_symgo_function, &actx);
}

/**
Expand Down Expand Up @@ -656,10 +780,10 @@ RZ_API bool rz_core_analysis_recover_golang_functions(RzCore *core) {
ut32 num_libs = sort_recovered_library(core);
rz_core_notify_done(core, "Recovered %u symbols and saved them at sym.go.*", num_syms);
rz_core_notify_done(core, "Recovered %u go packages", num_libs);
rz_golang_load_types(core);
rz_core_notify_begin(core, "Analyze all flags starting with sym.go. (aF @@f:sym.go.*)");
analyse_golang_symbols(core);
rz_core_notify_done(core, "Analyze all flags starting with sym.go. (aF @@f:sym.go.*)");
rz_golang_load_types(core);
return true;
}

Expand Down
Loading
Loading