linux can静态编译,gcc Linux ldd中的“静态链接”和“不是动态可执行文件”有什么区别? - 糯米PHP...

这里有两件事:

是否请求ELF解释器(ld.so)。

就像#!/bin/sh但对于二进制文件一样,在之前运行_start。

这是静态可执行文件与动态可执行文件之间的区别。

可供ld.so加载的动态链接库的列表恰好为空。

显然,这就是ldd所谓的“静态链接”,即您在构建时可能已链接的任何库都是静态库。

其他工具喜欢file和readelf提供更多信息,并使用与您期望的术语相匹配的术语。

您的GCC已配置-pie为默认值,并且对于没有动态库的特殊情况,gcc不会产生静态派。

gcc -nostdlib只是使一个PIE碰巧不链接到任何库,但在其他方面与普通的PIE相同,但指定了ELF解释器。

ldd令人困惑地称其为“静态链接”。

file:ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2 ...

gcc -nostdlib -static覆盖-pie默认值并生成一个真正的静态可执行文件。

file:ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked ...

gcc -nostdlib -no-pie对于根本没有动态库的情况,它也选择使静态可执行文件作为优化。由于无论如何都无法对非PIE可执行文件进行ASLR处理,因此这很有意义。逐字节相同-static。

gcc -nostdlib -static-pie制作不需要ELF解释器的ASLRable可执行文件。默认情况下gcc -pie -nostdlib,GCC不会执行此操作,这与无派情况不同,在这种情况下,ld.so当不涉及动态链接库时,它会选择回避。

file:ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked ...

-static-pie是模糊的,很少使用,并且较旧的版本file不会将其标识为静态链接。

-nostdlib并不暗示-no-pie或-static,-static-pie必须明确指定才能实现。

gcc -static-pie调用ld -static -pie,所以ld必须知道这意味着什么。与非PIE情况不同,在这种情况下,您不必显式地请求动态可执行文件,如果传递ld任何.so库,则只会得到一个。我认为这就是为什么您碰巧从中获取静态可执行文件的原因gcc -nostdlib -no-pie-GCC不必做任何特殊的事情,它只是ld在进行优化。

但是即使指定了没有链接的共享库,ld也不会-static隐式启用when-pie指定的时间。

细节

用gcc --versiongcc(Arch Linux 9.3.0-1)生成的示例9.3.0

ld --versionGNU ld(GNU Binutils)2.34(也readelf是binutils)

ldd --versionldd(GNU libc)2.31

file --versionfile-5.38-注意,在最近的补丁程序中,静态派检测已更改,Ubuntu挑选了一个未发布的补丁。(感谢@Joseph的侦探工作)-这在2019年检测到dynamic =具有PT_INTERP来处理静态派,但已还原为基于PT_DYNAMIC进行检测,因此共享库计为dynamic。Debian错误#948269。static-pie是一个不起眼的很少使用的功能。

GCC最终ld -pie exit.o以指定的动态链接器路径运行,并且没有库。(和其他选项的一大堆,支持LTO可能链接时优化,但这里的关键是-dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie,collect2大约只是一个包装ld。)

$ gcc -nostdlib exit.s -v # output manually line wrapped with \ for readability

...

COLLECT_GCC_OPTIONS='-nostdlib' '-v' '-mtune=generic' '-march=x86-64'

/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/collect2 \

-plugin /usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/liblto_plugin.so \

-plugin-opt=/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/lto-wrapper \

-plugin-opt=-fresolution=/tmp/ccoNx1IR.res \

--build-id --eh-frame-hdr --hash-style=gnu \

-m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie \

-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0 \

-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../lib -L/lib/../lib \

-L/usr/lib/../lib \

-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../.. \

/tmp/cctm2fSS.o

您将获得一个动态PIE,而无需依赖其他库。运行它仍然会在它上调用“ ELF解释器”/lib64/ld-linux-x86-64.so.2,然后在跳转到您的之前运行它_start。(尽管内核已经将可执行文件的ELF段以及ld.so的text / data / bss映射到ASLRed虚拟地址)。

file和readelf更具描述性。

PIE非静态可执行文件来自gcc -nostdlib

$ gcc -nostdlib exit.s -o exit-default

$ ls -l exit-default

-rwxr-xr-x 1 peter peter 13536 May 2 02:15 exit-default

$ ldd exit-default

statically linked

$ file exit-default

exit-default: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=05a4d1bdbc94d6f91cca1c9c26314e1aa227a3a5, not stripped

$ readelf -a exit-default

...

Type: DYN (Shared object file)

Machine: Advanced Micro Devices X86-64

Version: 0x1

Entry point address: 0x1000

...

Program Headers:

Type Offset VirtAddr PhysAddr

FileSiz MemSiz Flags Align

PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040

0x00000000000001f8 0x00000000000001f8 R 0x8

INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238

0x000000000000001c 0x000000000000001c R 0x1

[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000

0x00000000000002b1 0x00000000000002b1 R 0x1000

LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000

0x0000000000000009 0x0000000000000009 R E 0x1000

... (the Read+Exec segment to be mapped at virt addr 0x1000 is where your text section was linked.)

如果您跟踪它,还可以看到不同之处:

$ gcc -nostdlib exit.s -o exit-default

$ strace ./exit-default

execve("./exit-default", ["./exit-default"], 0x7ffe1f526040 /* 51 vars */) = 0

brk(NULL) = 0x5617eb1e4000

arch_prctl(0x3001 /* ARCH_??? */, 0x7ffcea703380) = -1 EINVAL (Invalid argument)

access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)

mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9ff5b3e000

arch_prctl(ARCH_SET_FS, 0x7f9ff5b3ea80) = 0

mprotect(0x5617eabac000, 4096, PROT_READ) = 0

exit(0) = ?

+++ exited with 0 +++

vs. -static and -static-pie the first instruction executed in user-space is your _start (which you can also check with GDB using starti).

$ strace ./exit-static-pie

execve("./exit-static-pie", ["./exit-static-pie"], 0x7ffcdac96dd0 /* 51 vars */) = 0

exit(0) = ?

+++ exited with 0 +++

gcc -nostdlib -static-pie

$ gcc -nostdlib -static-pie exit.s -o exit-static-pie

$ ls -l exit-static-pie

-rwxr-xr-x 1 peter peter 13440 May 2 02:18 exit-static-pie

peter@volta:/tmp$ ldd exit-static-pie

statically linked

peter@volta:/tmp$ file exit-static-pie

exit-static-pie: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=daeb4a8f11bec1bb1aaa13cd48d24b5795af638e, not stripped

$ readelf -a exit-static-pie

...

Type: DYN (Shared object file)

Machine: Advanced Micro Devices X86-64

Version: 0x1

Entry point address: 0x1000

...

Program Headers:

Type Offset VirtAddr PhysAddr

FileSiz MemSiz Flags Align

LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000

0x0000000000000229 0x0000000000000229 R 0x1000

LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000

0x0000000000000009 0x0000000000000009 R E 0x1000

... (no Interp header, but still a read+exec text segment)

Notice that the addresses are still relative to the image base, leaving ASLR up to the kernel.

Surprisingly, ldd doesn't say that it's not a dynamic executable. That might be a bug, or a side effect of some implementation detail.

gcc -nostdlib -static traditional non-PIE old-school static executable

$ gcc -nostdlib -static exit.s -o exit-static

$ ls -l exit-static

-rwxr-xr-x 1 peter peter 4744 May 2 02:26 exit-static

peter@volta:/tmp$ ldd exit-static

not a dynamic executable

peter@volta:/tmp$ file exit-static

exit-static: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=1b03e3d05709b7288fe3006b4696fd0c11fb1cb2, not stripped

peter@volta:/tmp$ readelf -a exit-static

ELF Header:

...

Type: EXEC (Executable file)

Machine: Advanced Micro Devices X86-64

Version: 0x1

Entry point address: 0x401000

... (Note the absolute entry-point address nailed down at link time)

(And that the ELF type is EXEC, not DYN)

Program Headers:

Type Offset VirtAddr PhysAddr

FileSiz MemSiz Flags Align

LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000

0x000000000000010c 0x000000000000010c R 0x1000

LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000

0x0000000000000009 0x0000000000000009 R E 0x1000

NOTE 0x00000000000000e8 0x00000000004000e8 0x00000000004000e8

0x0000000000000024 0x0000000000000024 R 0x4

Section to Segment mapping:

Segment Sections...

00 .note.gnu.build-id

01 .text

02 .note.gnu.build-id

...

Those are all the program headers; unlike pie / static-pie I'm not leaving any out, just other whole parts of the readelf -a output.

还要注意程序头中的绝对虚拟地址,它不能使内核选择在虚拟地址空间中映射文件的位置。这是ELF对象的EXEC和DYN类型之间的区别。PIE可执行文件是带有入口点的共享对象,这使我们可以获取主要可执行文件的ASLR。实际的EXEC可执行文件具有链接时选择的内存布局。

ldd显然,在以下两种情况下,仅报告“不是动态可执行文件”:

没有ELF解释器(动态链接器)路径

ELF类型= EXEC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值