Let me start by getting some facts on the table to have them fact-checked, so that there's no confusion:
- An ELF binary with a dynamic section will be compiled with some symbols unresolved. The resolution will be carried out by the linker sometime during execution of the binary.
- There are pros and cons to dynamic linking. But if the target library which is required by your binary is not present on the system (in the required version), the binary won't run.
- Static linking mitigates this problem, but introduces a new one on the lower layer. By linking the binary statically, the libraries' executable code was embedded in your binary, hence there's no problem with the binary-library interface anymore. However, things can now break on the library-OS interface. Is that correct? What problems may arise here?
Now let's discuss this in the context of Go. I've noticed that, if I build a binary with CGO_ENABLED=1 go build ...
, I get a binary with a dynamic section:
david@x1 /tmp (git)-[master] % readelf -d rtloggerd.cgo1
Dynamic section at offset 0x7a6140 contains 19 entries:
Tag Type Name/Value
0x0000000000000004 (HASH) 0x914e40
0x0000000000000006 (SYMTAB) 0x915340
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000005 (STRTAB) 0x915100
0x000000000000000a (STRSZ) 570 (bytes)
0x0000000000000007 (RELA) 0x914a38
0x0000000000000008 (RELASZ) 24 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0xba6000
0x0000000000000015 (DEBUG) 0x0
0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000006ffffffe (VERNEED) 0x914de0
0x000000006fffffff (VERNEEDNUM) 2
0x000000006ffffff0 (VERSYM) 0x914d80
0x0000000000000014 (PLTREL) RELA
0x0000000000000002 (PLTRELSZ) 816 (bytes)
0x0000000000000017 (JMPREL) 0x914a50
0x0000000000000000 (NULL) 0x0
david@x1 /tmp (git)-[master] % ldd rtloggerd.cgo1
linux-vdso.so.1 (0x00007ffd9a972000)
libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fcb2853c000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fcb28378000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fcb2858a000)
If, on the other hand, when I CGO_ENABLED=0 go build ...
, there's no dynamic section:
130 david@x1 /tmp (git)-[master] % readelf -d rtloggerd.cgo0
There is no dynamic section in this file.
- Does this mean the libs were linked statically? I guess so, but the size difference is negligible on my machine (some 72 kB), which surprises me.
- With regard to portability across Linux systems, which one is preferable and why?
- How does Go's standard library carry out its business? Is it calling the C functions provided by the
libc
(glibc
in my case) in fact? I have assumed there's a native system call interface. On the other hand, I imagine that re-implementing the entire stdlib in native Go would be difficult. - Finally, I've heard that there's "no guarantee that different distros, or even different versions of the same distro, are ABI compatible". Is this true? I assumed that ABI is mostly the binary executable format (ELF on Linux for quite some time), so I would assume no problems here. What could this mean?
Thanks!