“静态链接”和“不是动态可执行文件”有什么区别?

问题描述 投票:1回答:1

考虑此AMD64汇编程序:

.globl _start
_start:
    xorl %edi, %edi
    movl $60, %eax
    syscall

如果我使用gcc -nostdlib进行编译并运行ldd a.out,则会得到以下信息:

        statically linked

如果我改为使用gcc -static -nostdlib进行编译并运行ldd a.out,则会得到以下信息:

        not a dynamic executable

statically linkednot a dynamic executable有什么区别?如果我的二进制文件已经静态链接,为什么添加-static会产生任何影响?

gcc linker x86-64 static-linking ldd
1个回答
0
投票

这里有两件事:

  • 是否请求ELF解释器(ld.so)。与#!/bin/sh类似,但对于二进制文件,请在_start之前运行。这是静态与动态executable
  • 之间的区别
  • 供ld.so加载的动态链接库列表恰好为空。这显然是ldd所称的“静态链接”,即您在构建时可能链接的任何库都是静态库。

[其他工具,例如filereadelf提供了更多信息,并使用了与您期望的术语相匹配的术语。


您的GCC为configured so -pie is the default,对于没有动态库的特殊情况,gcc不会产生静态派。

  • -pie只是使一个PIE碰巧不链接到任何库,但在其他方面与普通的PIE相同,指定了ELF解释器。[gcc -nostdlib]令人困惑地称其为“静态链接”。lddfile exit-default
  • ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2 ...会覆盖默认的gcc -nostdlib -static并生成一个真正的静态可执行文件。-piefile exit-static
  • ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked ...还选择创建静态可执行文件,以作为根本没有动态库的情况的优化。由于无论如何都无法对非PIE可执行文件进行ASLR处理,因此这很有意义。 gcc -nostdlib -no-pie情况下逐字节相同。
  • [-static使ASLRable可执行文件不需要ELF解释器。默认情况下,对于gcc -nostdlib -static-pie,GCC不会执行此操作,这与无饼情况不同,在这种情况下,当不涉及动态链接库时,它会选择回避gcc -pie -nostdlibld.sofile exit-static-pie

[ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked ...并不表示-nostdlib-no-pie,必须明确指定-static才能实现。


[GCC最终在运行-static-pie时指定了动态链接器路径],并且没有库。 (还有很多其他选项可以支持可能的LTO链接时间优化,但是这里的键是ld -pie exit.o-dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie只是collect2的包装。)

ld

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

_start和readelf更具描述性。

[C0中的PIE非静态可执行文件

file

如果您发现它也可以看到不同之处:

gcc -nostdlib

vs。 $ 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 +++

注意,地址仍然是相对于映像库的,将ASLR保留给内核。

令人惊讶的是,-static-pie并不表示它不是动态可执行文件。这可能是一个错误,或者是某些实现细节的副作用。


vs。 $ 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) 传统的非PIE老式静态可执行文件

ldd

这些都是程序头文件;与pie / static-pie不同,我没有遗漏任何内容,只是readelf -static输出的其他整个部分。

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


[$ 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 ... 显然仅在以下两种情况下报告“不是动态可执行文件”:

  • 没有ELF解释器(动态链接器)路径
  • ELF类型= EXEC
© www.soinside.com 2019 - 2024. All rights reserved.