Linux ldd 中的“静态链接”和“非动态可执行文件”有什么区别?

2024-10-21 09:14:00
admin
原创
82
摘要:问题描述:考虑这个 AMD64 汇编程序:.globl _start _start: xorl %edi, %edi movl $60, %eax syscall 如果我使用它进行编译gcc -nostdlib并运行ldd a.out,我会得到以下结果: statical...

问题描述:

考虑这个 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 linked和之间有什么区别not a dynamic executable?如果我的二进制文件已经静态链接,为什么添加会-static影响任何事情?


解决方案 1:

这里有两件不同的事情:

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

类似#!/bin/sh,但对于二进制文件,在 之前运行_start。这是静态可执行文件与动态可执行文件

之间的区别。

  • ld.so 要加载的动态链接库列表恰好是空的。

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

其他工具如filereadelf可以提供更多信息并使用符合您期望的术语。


您的 GCC 已配置为-pie默认设置,并且 gcc 不会为没有动态库的特殊情况创建静态派。

  • gcc -nostdlib只是创建一个恰好不链接到任何库但在其他方面与普通 PIE 相同的 PIE,指定一个 ELF 解释器。

ldd令人困惑的是,它称之为“静态链接”。

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

  • gcc -nostdlib -static覆盖-pie默认值并使其成为真正的静态可执行文件。

fileELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked ...

  • gcc -nostdlib -no-pie还选择制作静态可执行文件,以优化完全没有动态库的情况。由于非 PIE 可执行文件无论如何都不可能被 ASLRed,所以这样做是有道理的。逐字节与这种情况相同-static

  • gcc -nostdlib -static-pie生成不需要 ELF 解释器的 ASLRable 可执行文件。默认情况下,GCC 不会对 执行此操作,这与 no-pie 情况不同,在 no-pie 情况下,当不涉及动态链接库时,gcc -pie -nostdlib它会选择回避。 :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 情况不同,在非 PIE 情况下,您不必明确请求动态可执行文件,只要传递ld任何.so库,您就会得到一个。我认为这就是您恰好从中获取静态可执行文件的原因gcc -nostdlib -no-pie- GCC 不必做任何特殊的事情,它只是ld进行优化。

但是当指定时ld不会-static隐式启用-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 年检测到动态 = 具有 PT_INTERP 来处理静态饼图,但它被恢复为基于 PT_DYNAMIC 进行检测,因此共享库算作dynamic。debian 错误#948269。 static-pie是一个鲜为人知的很少使用的功能。

GCC 最终ld -pie exit.o以指定的动态链接器路径运行,但没有库。(还有大量其他选项来支持可能的 LTO 链接时优化,但这里的关键是-dynamic-linker /lib64/ld-linux-x86-64.so.2 -piecollect2只是 的包装器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  \n-plugin /usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/liblto_plugin.so \n-plugin-opt=/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/lto-wrapper \n-plugin-opt=-fresolution=/tmp/ccoNx1IR.res \n--build-id --eh-frame-hdr --hash-style=gnu \n-m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie \n-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0 \n-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../lib -L/lib/../lib \n-L/usr/lib/../lib \n-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../.. \n/tmp/cctm2fSS.o

您将获得一个不依赖其他库的动态 PIE。运行它仍会调用/lib64/ld-linux-x86-64.so.2其上的“ELF 解释器”,该解释器在跳转到您的 之前运行_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.)

如果你进行 strace 操作,你还可以看到差异:

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

-static-static-pie用户空间中执行的第一条指令相比_start,您也可以使用 GDB 进行检查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)

请注意,这些地址仍然相对于图像基址,而 ASLR 则由内核决定。

令人惊讶的是,ldd并没有说它不是一个动态可执行文件。 这可能是一个错误,或者是某些实现细节的副作用。


gcc -nostdlib -static传统的非 PIE 老式静态可执行文件

$ 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 
   ...

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

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


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

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

  • ELF 类型 = EXEC

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用