本文为看雪论坛精华文章
看雪论坛作者ID:SYJ-Re
一
Ready
这里简单提一下main_main的查找:
*有符号,直接ctrl+f,IDA中搜索main_main即可
runtime_mainIDA反编译代码参考
void runtime_main()
{
PVOID ArbitraryUserPointer; // rax
__int64 v1; // rdx
__int64 i; // rax
__int64 v3; // [rsp+0h] [rbp-50h]
__int64 v4; // [rsp+0h] [rbp-50h]
__int64 v5; // [rsp+0h] [rbp-50h]
__int64 v6; // [rsp+10h] [rbp-40h]
__int64 v7; // [rsp+20h] [rbp-30h] BYREF
__int64 v8; // [rsp+28h] [rbp-28h]
__int64 v9; // [rsp+30h] [rbp-20h]
__int128 v10; // [rsp+38h] [rbp-18h]
void *retaddr; // [rsp+50h] [rbp+0h] BYREF
while ( (unsigned __int64)&retaddr <= *(_QWORD *)(*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer + 16LL) )
runtime_morestack_noctxt();
v10 = 0LL;
HIBYTE(v7) = 0;
v9 = *(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer;
*(_QWORD *)(**(_QWORD **)(v9 + 48) + 304LL) = 0LL;
runtime_maxstacksize = 1000000000LL;
runtime_maxstackceiling = 2000000000LL;
runtime_mainStarted = 1;
_InterlockedExchange((volatile __int32 *)&unk_5683A0, 1);
runtime_systemstack((__int64)&off_4D6020);
ArbitraryUserPointer = NtCurrentTeb()->NtTib.ArbitraryUserPointer;
++*(_DWORD *)(*(_QWORD *)(*(_QWORD *)ArbitraryUserPointer + 48LL) + 580LL);
v1 = *(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer;
*(_QWORD *)(*(_QWORD *)(v1 + 48) + 312LL) = v1;
*(_QWORD *)(v1 + 216) = *(_QWORD *)(v1 + 48);
if ( *(_UNKNOWN **)(v9 + 48) != &runtime_m0 )
goto LABEL_28;
byte_568678 = 1;
runtime_nanotime1(v3);
runtime_runtimeInitTime = v4;
if ( !v4 )
{
LABEL_27:
runtime_throw((__int64)"nanotime returning zero", 23LL);
LABEL_28:
runtime_throw((__int64)"runtime.main not on m0", 22LL);
runtime_deferreturn(v5);
return;
}
if ( dword_5AB048 )
{
qword_5AAE88 = *(_QWORD *)(*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer + 152LL);
runtime_inittrace = 1;
}
runtime_doInit((__int64)&runtime__inittask);
HIWORD(v7) = 257;
*((_QWORD *)&v10 + 1) = &off_4D6028;
*(_QWORD *)&v10 = (char *)&v7 + 6;
runtime_gcenable();
v6 = runtime_makechan((__int64)&unk_4B2580, 0LL);
if ( runtime_writeBarrier )
runtime_gcWriteBarrier();
else
runtime_main_init_done = v6;
if ( !runtime_iscgo )
goto LABEL_12;
if ( !cgo_thread_start )
{
LABEL_26:
runtime_throw((__int64)"_cgo_thread_start missing", 25LL);
goto LABEL_27;
}
if ( !cgo_notify_runtime_init_done )
{
runtime_throw((__int64)"_cgo_notify_runtime_init_done missing", 37LL);
goto LABEL_26;
}
runtime_startTemplateThread();
runtime_cgocall(cgo_notify_runtime_init_done, 0LL, v6);
LABEL_12:
runtime_doInit((__int64)&main__inittask);
runtime_inittrace = 0;
runtime_closechan(runtime_main_init_done);
BYTE6(v7) = 0;
runtime_unlockOSThread();
if ( !runtime_isarchive && !runtime_islibrary )
{
main_main();
if ( runtime_runningPanicDefers )
{
for ( i = 0LL; i < 1000 && runtime_runningPanicDefers; i = v8 + 1 )
{
v8 = i;
runtime_mcall();
}
}
if ( runtime_panicking )
v7 = runtime_gopark(0LL, 0LL, 4104, 1LL);
runtime_exit(0);
while ( 1 )
MEMORY[0] = 0;
}
HIBYTE(v7) = 0;
runtime_main_func2(v10);
}
-gcflags "-N -l"
go build -o hello.exe -gcflags "-N -l" hello.go
二
Go数据结构解析
struct String{
char * strPtr; //指向目标字符串的开始地址
int64 size; //字符串大小
}
func ArrDemo() *[3]int {
a := [...]int{1, 2, 3}
b := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7}
c := [...]int{1, 2, 3}
if len(a) < len(b) {return &c}
return nil
}
type arrayHeader struct {
Data uintptr
Len int
}
x := []int{1, 2, 3, 4, 5}
y := []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
{
指向slice中第一个元素的指针
slice的长度
slice的容量
}
slice := make([]int, len)
slice1 := make([]int, 5)
slice1[3] = 66
// runtime_makeslice
func makeslice(et *_type, len, cap int) unsafe.Pointer {
mem, overflow := math.MulUintptr(et.size, uintptr(cap))
if overflow || mem > maxAlloc || len < 0 || len > cap {
// NOTE: Produce a 'len out of range' error instead of a
// 'cap out of range' error when someone does make([]T, bignumber).
// 'cap out of range' is true too, but since the cap is only being
// supplied implicitly, saying len is clearer.
// See golang.org/issue/4085.
mem, overflow := math.MulUintptr(et.size, uintptr(len))
if overflow || mem > maxAlloc || len < 0 {
panicmakeslicelen()
}
panicmakeslicecap()
}
return mallocgc(mem, et, true)
}
slice1 = append(slice1, 123)
如果新的大小是当前大小2倍以上,则大小增长为新大小
否则循环以下操作:如果当前大小小于1024,按每次2倍增长,否则每次按当前大小1/4增长。直到增长的大小超过或等于新大小。
myvar := slice1[1:3]
type hmap struct {
count int //map中键值对数量
flags uint8 //map当前是否处于写入状态登
B uint8 //2的B次幂表示当前map中桶的数量
noverflow uint16 //map中溢出桶的数量,当溢出桶太多时,map会进行等量扩容
hash0 uint32 //生成hash的随机数种子
buckets unsafe.Pointer //当前map对应的桶的指针
oldbuckets unsafe.Pointer //map扩容时指向旧桶的指针,当所有旧桶中的数据转移到新桶时,清空
nevacuate uintptr //扩容时,用于标记当前旧桶中小于nevacute的数据都已经转移到了新桶
extra *mapextra //存储map的溢出桶
}
type mapextra struct {
overflow *[]*bmap
oldoverflow *[]*bmap
nextOverflow *bmap
}
type bmap struct {
tophash [8]uint8 //存储Hash值的高8位
data []byte //key value数据:key/key/key.../value/value/value...
overflow *bmap //溢出bucket的地址
}
BUCKETSIZE是用宏定义的8,每个bucket中存放最多8个key/value对, 如果多于8个,那么会申请一个新的bucket,并将它与之前的bucket链起来。
按key的类型采用相应的hash算法得到key的hash值。将hash值的低位当作Hmap结构体中buckets数组的index,找到key所在的bucket。将hash的高8位存储在了bucket的tophash中。注意,这里高8位不是用来当作key/value在bucket内部的offset的,而是作为一个主键,在查找时对tophash数组的每一项进行顺序匹配的。先比较hash值高位与bucket的tophash[i]是否相等,如果相等则再比较bucket的第i个的key与所给的key是否相等。如果相等,则返回其对应的value,反之,在overflow buckets中按照上述方法继续寻找。
data区存放的是key—value数据,其中keys放在一起,values放在一起,如此存储是为了节省字节对齐带来的空间浪费。例如map[int64]int8。
func fastrand() uint32
func makemap(mapType *byte, hint int, mapbuf *any) (hmap map[any]any)
func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any)
func mapaccess2(mapType *byte, hmap map[any]any, key *any) (val *any, pres bool)
func mapassign(mapType *byte, hmap map[any]any, key *any) (val *any)
func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
func mapiternext(hiter *any)
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer{}
func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {}
package main
import "fmt"
func main() {
countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
fmt.Println("France首都是", countryCapitalMap ["France"])
}
package main
import "fmt"
func main() {
var countryCapitalMap map[string]string /*创建集合, 默认map是nil*/
//如果不初始化 map,那么就会创建一个 nil map, nil map 不能用来存放键值对
countryCapitalMap = make(map[string]string)
countryCapitalMap [ "France" ] = "巴黎"
countryCapitalMap [ "Italy" ] = "罗马"
countryCapitalMap [ "Japan" ] = "东京"
countryCapitalMap [ "India " ] = "新德里"
//或者:countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
/*使用键输出地图值 */
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [country])
}
}
void __cdecl main_main()
{
int v0; // [rsp+10h] [rbp-230h]
__int64 v1; // [rsp+20h] [rbp-220h]
__int64 v2; // [rsp+20h] [rbp-220h]
__int64 v3; // [rsp+20h] [rbp-220h]
__int64 v4; // [rsp+20h] [rbp-220h]
_QWORD *v5; // [rsp+30h] [rbp-210h]
__int64 *v6; // [rsp+30h] [rbp-210h]
__int64 v7; // [rsp+38h] [rbp-208h]
__int64 v8; // [rsp+40h] [rbp-200h]
__int64 v9; // [rsp+48h] [rbp-1F8h]
__int64 v10; // [rsp+50h] [rbp-1F0h]
__int64 v11; // [rsp+58h] [rbp-1E8h]
__int64 v12; // [rsp+60h] [rbp-1E0h]
_QWORD v13[5]; // [rsp+68h] [rbp-1D8h] BYREF
__int64 v14; // [rsp+90h] [rbp-1B0h] BYREF
__int128 v15; // [rsp+98h] [rbp-1A8h] BYREF
__int128 v16; // [rsp+A8h] [rbp-198h]
__int128 v17; // [rsp+B8h] [rbp-188h]
__int64 v18[12]; // [rsp+C8h] [rbp-178h] BYREF
char v19; // [rsp+128h] [rbp-118h] BYREF
while ( (unsigned __int64)&v14 <= *(_QWORD *)(*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer + 16LL) )
runtime_morestack_noctxt();
v15 = 0LL;
v16 = 0LL;
v17 = 0LL;
((void (*)(void))loc_46651C)();
*(_QWORD *)&v16 = &v19;
runtime_fastrand();
HIDWORD(v15) = v0;
runtime_mapassign_faststr((__int64)&unk_4B7040, (__int64)&v15, (__int64)"France", 6LL);
v5[1] = 6LL;
if ( runtime_writeBarrier )
runtime_gcWriteBarrier();
else
*v5 = "巴黎";
runtime_mapassign_faststr((__int64)&unk_4B7040, (__int64)&v15, (__int64)"Italy", 5LL);
v5[1] = 6LL;
if ( runtime_writeBarrier )
runtime_gcWriteBarrier();
else
*v5 = "罗马";
runtime_mapassign_faststr((__int64)&unk_4B7040, (__int64)&v15, (__int64)"Japan", 5LL);
v5[1] = 6LL;
if ( runtime_writeBarrier )
runtime_gcWriteBarrier();
else
*v5 = "东京";
v8 = runtime_mapassign_faststr((__int64)&unk_4B7040, (__int64)&v15, (__int64)"India ", 6LL);
v5[1] = 9LL;
if ( runtime_writeBarrier )
runtime_gcWriteBarrier();
else
*v5 = "新德里";
((void (*)(void))loc_466551)();
runtime_mapiterinit((__int64)&unk_4B7040, &v15, (__int64)v18);
while ( v18[0] )
{
v11 = *(_QWORD *)v18[0];
v10 = *(_QWORD *)(v18[0] + 8);
runtime_convTstring(*(_QWORD *)v18[0], v10, v1);
v12 = v2;
v9 = runtime_mapaccess1_faststr((__int64)&unk_4B7040, (__int64)&v15, v11, v10, (__int64)v5);
v7 = runtime_convTstring(*v6, v6[1], v3);
v13[0] = &unk_4B3260;
v13[1] = v12;
v13[2] = &unk_4B3260;
v13[3] = &off_4EF518;
v13[4] = &unk_4B3260;
v14 = v4;
fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v13, 3LL, 3LL, v7, v8, v9);
runtime_mapiternext((__int64)v18);
}
}
根据key计算出hash值。
如果存在old table, 首先在old table中查找,如果找到的bucket已经evacuated,转到步骤3。反之,返回其对应的value。
在new table中查找对应的value。
do { //对每个桶b
//依次比较桶内的每一项存放的tophash与所求的hash值高位是否相等
for(i = 0, k = b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
if(b->tophash[i] == top) {
k2 = IK(h, k);
t->key->alg->equal(&eq, t->key->size, key, k2);
if(eq) { //相等的情况下再去做key比较...
*keyp = k2;
return IV(h, v);
}
}
}
b = b->overflow; //b设置为它的下一下溢出链
} while(b != nil);
三
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
type g struct {
// Stack parameters.
// stack describes the actual stack memory: [stack.lo, stack.hi).
// stackguard0 is the stack pointer compared in the Go stack growth prologue.
// It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption.
// stackguard1 is the stack pointer compared in the C stack growth prologue.
// It is stack.lo+StackGuard on g0 and gsignal stacks.
// It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash).
// 记录该goroutine使用的栈
stack stack // offset known to runtime/cgo
//下面两个成员用于栈溢出检查,实现栈的自动伸缩,抢占调度也会用到stackguard0
stackguard0 uintptr // offset known to liblink
stackguard1 uintptr // offset known to liblink
_panic *_panic // innermost panic - offset known to liblink
_defer *_defer // innermost defer
// 此goroutine正在被哪个工作线程执行
m *m // current m; offset known to arm liblink
//这个字段跟调度切换有关,G切换时用来保存上下文,保存什么,看下面gobuf结构体
sched gobuf
syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc
syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc
stktopsp uintptr // expected sp at top of stack, to check in traceback
param unsafe.Pointer // passed parameter on wakeup,wakeup唤醒时传递的参数
// 状态Gidle,Grunnable,Grunning,Gsyscall,Gwaiting,Gdead
atomicstatus uint32
stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
goid int64
//schedlink字段指向全局运行队列中的下一个g,
//所有位于全局运行队列中的g形成一个链表
schedlink guintptr
waitsince int64 // approx time when the g become blocked
waitreason waitReason // if status==Gwaiting,g被阻塞的原因
//抢占信号,stackguard0 = stackpreempt,如果需要抢占调度,设置preempt为true
preempt bool // preemption signal, duplicates stackguard0 = stackpreempt
paniconfault bool // panic (instead of crash) on unexpected fault address
preemptscan bool // preempted g does scan for gc
gcscandone bool // g has scanned stack; protected by _Gscan bit in status
gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; TODO: remove?
throwsplit bool // must not split stack
raceignore int8 // ignore race detection events
sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine
sysexitticks int64 // cputicks when syscall has returned (for tracing)
traceseq uint64 // trace event sequencer
tracelastp puintptr // last P emitted an event for this goroutine
// 如果调用了 LockOsThread,那么这个 g 会绑定到某个 m 上
lockedm muintptr
sig uint32
writebuf []byte
sigcode0 uintptr
sigcode1 uintptr
sigpc uintptr
// 创建这个goroutine的go表达式的pc
gopc uintptr // pc of go statement that created this goroutine
ancestors *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors)
startpc uintptr // pc of goroutine function
racectx uintptr
waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order
cgoCtxt []uintptr // cgo traceback context
labels unsafe.Pointer // profiler labels
timer *timer // cached timer for time.Sleep,为 time.Sleep 缓存的计时器
selectDone uint32 // are we participating in a select and did someone win the race?
// Per-G GC state
// gcAssistBytes is this G's GC assist credit in terms of
// bytes allocated. If this is positive, then the G has credit
// to allocate gcAssistBytes bytes without assisting. If this
// is negative, then the G must correct this by performing
// scan work. We track this in bytes to make it fast to update
// and check for debt in the malloc hot path. The assist ratio
// determines how this corresponds to scan work debt.
gcAssistBytes int64
}
// Stack describes a Go execution stack.
// The bounds of the stack are exactly [lo, hi),
// with no implicit data structures on either side.
// 描述 goroutine 执行栈
// 栈边界为[lo, hi),左包含右不包含,即 lo≤stack<hi
// 两边都没有隐含的数据结构。
type stack struct {
lo uintptr //栈顶,指向内存低地址
hi uintptr //栈底,指向内存搞地址
}
type m struct {
/*
1. 所有调用栈的Goroutine,这是一个比较特殊的Goroutine。
2. 普通的Goroutine栈是在Heap分配的可增长的stack,而g0的stack是M对应的线程栈。
3. 所有调度相关代码,会先切换到该Goroutine的栈再执行。
*/
g0 *g
curg *g // M当前绑定的结构体G
// SP、PC寄存器用于现场保护和现场恢复
vdsoSP uintptr
vdsoPC uintptr
// 省略…}
四
五
void runtime.morestack() {
if(g == g0) {
panic();
} else {
m->morebuf.gobuf_pc = getCallerCallerPC();
void *SP = getCallerSP();
m->morebuf.gobuf_sp = SP;
m->moreargp = SP;
m->morebuf.gobuf_g = g;
m->morepc = getCallerPC();
void *g0 = m->g0;
g = g0;
setSP(g0->g_sched.gobuf_sp);
runtime.newstack();
}
}
六
第一个返回值:rsp + 0x10
第二个返回值:rsp + 0x18
以此类推
package main
import "fmt"
func main() {
var a, b int
a, b = sayHello(123)
a = a + 1
b = b + 2
fmt.Println(a, b)
}
func sayHello(a int)(i,j int){
i = a + 1
fmt.Println("execute half")
j = a + 2
return
}
package main
import (
"fmt"
)
func main() {
var a, b int
a, b = sayHello(1234, 5678)
a = a + 1
b = b + 2
fmt.Println(a, b)
}
func sayHello(a int, b int)(i,j int){
i = a + 1
fmt.Println("execute half")
j = b + 2
return
}
七
写屏障
看雪ID:SYJ-Re
https://bbs.pediy.com/user-home-921830.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!