点击上方蓝字 江湖评谈设为关注/星标
前言
NET9在Linux入口是Glibc,偶然看到Go的main入口不是Glibc,这难道是GO的性能和体积都具有优势的地方,本篇看下Glibc。
Go操作
#apt install golang-go 安装
#go verison 查看版本号
#which gdb查看调试器
#go run godemo.go 运行
#go build 生成可执行文件
例子:
# filename gofirstproj.go 可执行文件名 gofirstproj
package main
import "fmt"
func main() {
fmt.Printf("Welcome to golang World\n")
}
GDB
#readelf gofirstproj 查看下go可执行文件头
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x45bfa0
Start of program headers: 64 (bytes into file)
Start of section headers: 456 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 7
Size of section headers: 64 (bytes)
Number of section headers: 23
Section header string table index: 3
#gdb gofirstproj
(gdb)b main.main 两个main注意了
(gdb)r
(gdb)n 下一步
(gdb)bt
#0 runtime.sigtramp () at /usr/lib/go-1.18/src/runtime/sys_linux_amd64.s:353
#1 0x000000000045c5e0 in ?? ()
#2 0x0000000000000007 in ?? ()
#3 0x0000000000000000 in ?? ()
看到GO的这个main入口是在文件sys_linux_amd64.s里,具体代码如下(实际GO main调用是在353行PUSH_REGS_HOST_TO_ABI0):
337 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
338 MOVQ fn+0(FP), AX
339 MOVL sig+8(FP), DI
340 MOVQ info+16(FP), SI
341 MOVQ ctx+24(FP), DX
342 PUSHQ BP
343 MOVQ SP, BP
344 ANDQ $~15, SP // alignment for x86_64 ABI
345 CALL AX
346 MOVQ BP, SP
347 POPQ BP
348 RET
349
350 // Called using C ABI.
351 TEXT runtime·sigtramp(SB),NOSPLIT,$0
352 // Transition from C ABI to Go ABI.
353 PUSH_REGS_HOST_TO_ABI0()
354
355 // Call into the Go signal handler
356 NOP SP // disable vet stack checking
357 ADJSP $24
358 MOVQ DI, 0(SP) // sig
359 MOVQ SI, 8(SP) // info
360 MOVQ DX, 16(SP) // ctx
361 CALL ·sigtrampgo(SB)
362 ADJSP $-24
可以看到main确实不是glibc里面被调用的,而是go1.18版本自己用汇编代码实现的main调用。把CGO_ENABLED关闭和开启分别试下:
#go env 查看go环境变量,CGO_ENABLED默认为1
#CGO_ENABLED=0 go build -o no_cgo gofirstproj.go
#CGO_ENABLED=1 go build -o yes_cgo gofirstproj.go
#ldd no_cgo yes_cgo
no_cgo:
not a dynamic executable
yes_cgo:
not a dynamic executable
看到无论CGO_ENABLED开启或者是关闭都没有动态依赖,难道go1.18都是静态链接了?
.NET9
用lldb看下.NET9的corerun-Main的Linux下面是被谁调用的
#lldb corerun ConsoleApp1.dll
(lldb) b main
(lldb)r
(lldb)bt
* thread #1, name = 'corerun', stop reason = breakpoint 1.1
* frame #0: 0x000055555556c420 corerun`main(argc=<unavailable>, argv=<unavailable>) at corerun.cpp:616
frame #1: 0x00007ffff7829d90 libc.so.6`__libc_start_call_main(main=(corerun`main at corerun.cpp:616), argc=2, argv=0x00007fffffffdef8) at libc_start_call_main.h:58:16
frame #2: 0x00007ffff7829e40 libc.so.6`__libc_start_main_impl(main=(corerun`main at corerun.cpp:616), argc=2, argv=0x00007fffffffdef8, init=0x00007ffff7ffd040, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fffffffdee8) at libc-start.c:392:3
frame #3: 0x000055555556bf45 corerun`_start + 37
很清晰的看到了libc-start.c,它即 是 GNU C 库(Glibc)的一部分。
结尾
Go能直接避开Glibc用自己的汇编代码调用Main入口,确实属于非常独立的可执行文件,可以避免很多的依赖纠缠,各种发行版的特性阻碍。.NET目前似乎尚未实现此功能,.NET9 PreView2里面的AOT自举来看,以后不排除也会这样搞。至于性能和体积方面的优势,需要更多深入了解GO才能下结论。
往期精彩回顾