源代码
package main
func main() {
s := "hello world"
s += " go\n"
print(s)
}
Go版本
jagitch@34c4dd4d4a3e:str-demo$ go version
go version go1.22.2 linux/amd64
运行
jagitch@34c4dd4d4a3e:str-demo$ go run main.go
hello world go
编译
jagitch@34c4dd4d4a3e:str-demo$ go build main.go
反汇编
jagitch@34c4dd4d4a3e:str-demo$ go tool objdump -S -s '^main.main' main
TEXT main.main(SB) /home/coder/workspace/own/jagitch-code/gitee/go-study/go-asm/str-demo/main.go
func main() {
0x45d120 493b6610 CMPQ SP, 0x10(R14)
0x45d124 7650 JBE 0x45d176
0x45d126 55 PUSHQ BP
0x45d127 4889e5 MOVQ SP, BP
0x45d12a 4883ec38 SUBQ $0x38, SP
s += " go\n"
0x45d12e 31c0 XORL AX, AX
0x45d130 488d1d15140100 LEAQ 0x11415(IP), BX
0x45d137 b90b000000 MOVL $0xb, CX
0x45d13c 488d3da70a0100 LEAQ 0x10aa7(IP), DI
0x45d143 be04000000 MOVL $0x4, SI
0x45d148 e813c7feff CALL runtime.concatstring2(SB)
0x45d14d 4889442430 MOVQ AX, 0x30(SP)
0x45d152 48895c2428 MOVQ BX, 0x28(SP)
print(s)
0x45d157 e8e436fdff CALL runtime.printlock(SB)
0x45d15c 488b442430 MOVQ 0x30(SP), AX
0x45d161 488b5c2428 MOVQ 0x28(SP), BX
0x45d166 e8553ffdff CALL runtime.printstring(SB)
0x45d16b e83037fdff CALL runtime.printunlock(SB)
}
0x45d170 4883c438 ADDQ $0x38, SP
0x45d174 5d POPQ BP
0x45d175 c3 RET
func main() {
0x45d176 e885cdffff CALL runtime.morestack_noctxt.abi0(SB)
0x45d17b eba3 JMP main.main(SB)
解析
CMPQ SP, 0x10(R14)
SP减去0x10(R14)
的值JBE 0x45d176
如果上一步的结果小于等于,则跳转到0x45d176处PUSHQ BP
压入上一个函数的栈帧基址MOVQ SP, BP
将当前栈顶保存到BP寄存器SUBQ $0x38, SP
SP减去0x38,分配0x38个字节的栈空间XORL AX, AX
清空AXLEAQ 0x11415(IP), BX
将"hello world"字符串常量的地址加载到BXMOVL $0xb, CX
将"hello world"字符串的长度加载到CXLEAQ 0x10aa7(IP), DI
将" go\n"字符串的地址加载到DIMOVL $0x4, SI
将" go\n"的长度加载到SICALL runtime.concatstring2(SB)
调用concatstring2函数拼接字符串MOVQ AX, 0x30(SP)
AX是concatstring2的第一个返回值,是拼接后的字符串的地址,将其保存到0x30(SP)MOVQ BX, 0x28(SP)
BX是第二个返回值,是拼接后的字符串的长度,保存到0x28(SP)CALL runtime.printlock(SB)
打印加锁MOVQ 0x30(SP), AX
将字符串的地址加载到AXMOVQ 0x28(SP), BX
将字符串的长度加载到BXCALL runtime.printstring(SB)
打印字符串CALL runtime.printunlock(SB)
解锁打印ADDQ $0x38, SP
SP加上0x38,回收之前分配的栈空间POPQ BP
弹出上个函数的BPRET
返回CALL runtime.morestack_noctxt.abi0(SB)
增加栈空间JMP main.main(SB)
跳转到main包的main函数
结论
-
字符串常量会保存到一块内存中,使用0x11415(IP)等方式可以找到它的地址
-
字符串拼接会调用runtime.concatstring2函数进行拼接
网友评论