23mar10

ldd并不总是给出真正的答案。

很奇怪吧?

链接以在 Linux 系统上创建可移植二进制文件

可移植可执行文件意味着生成的二进制文件将在不同的现代Linux发行版上运行。

链接到系统 libc 和系统 libm 对我来说是可以的。您只需要确保您链接到的glibc版本比您针对的所有发行版都要旧。因此,构建机器可以链接到 GLIBC 2.7,然后所有具有 GLIBC >=2.7 的发行版都可以运行输出二进制文件。出于许多神秘的原因,您不想静态链接到glibc。我不会静态地链接到glibc(libc)或libm,我也不包括这些lib。我让exe找到这些库的系统版本。我也懒得包含我自己的pthreads lib。这些是核心库。

我的目标是现代Linux发行版。问题的范围不能太大。

我构建了我想要在运行旧 debian 的虚拟框中可移植的可执行文件。最近我一直在使用 debian lenny 来编译 ffmpeg 和它的支持库。

考虑 RPATH 和LD_LIBRARY_PATH

我一直在使用嵌入在可执行文件中的rpath来设置高优先级的库搜索路径。而不是使用非常脆弱的LD_LIBRARY_PATH,搜索路径被直接放入可执行文件中,因此您可以执行一些事情,例如在搜索系统库之前,在exe所在的同一目录中搜索它所依赖的库。

RPATH 存储在 elf 可执行文件的动态部分中。它可以是相对路径。因此,您可以移动exe及其支持库,它始终会找到它的库并在尝试系统库之前加载它们,从而隔离您的二进制文件。

LD_LIBRARY_PATH得到了很多松弛,其中一些是应得的。我不喜欢它的原因是,你不能只是符号链接可执行文件并在任何地方运行它,可执行文件需要变量集,所以你总是必须运行一个shell脚本来引导具有该变量的环境,然后调用可执行文件。此外,它只采取绝对路径。所以它很脆。

其他人不喜欢它,因为有些人倾向于把它变成一个全球系统设置,以微妙的方式打破其他东西。另一个原因是,现在任何具有LD_LIBRARY_PATH设置的进程都会将相同的变量导出到它启动的任何子进程中,因此当其他进程生成时,您会再次在不知不觉中中断。

以下是 readelf 的一些输出,告诉它只显示动态部分。我修剪了大部分输出。

$ readelf -d ffmpeg Dynamic section at offset 0x12574 contains 27 entries:Tag        Type                         Name/Value0x00000001 (NEEDED)                     Shared library: [libavdevice.so.52]0x00000001 (NEEDED)                     Shared library: [libavformat.so.52]0x00000001 (NEEDED)                     Shared library: [libavcodec.so.52]0x00000001 (NEEDED)                     Shared library: [libavutil.so.49]0x00000001 (NEEDED)                     Shared library: [libm.so.6]0x00000001 (NEEDED)                     Shared library: [libc.so.6]0x0000000f (RPATH)                      Library rpath: [$ORIGIN/../lib]0x0000000c (INIT)                       0x804a438
etc.

断续器我G我N isaspecialvariablethatmeans'thisex ecutabl e′,and itmeanstheactualexecutable f ilename,asread linkwoul dseit,sosy mlinksare folllowed.我诺特赫沃尔ds,ORIGINisaspecialvariablethatmeans' thisexecutable′,anditmeanstheac tualexecutablefilename,asreadlin kwouldseeit,sosymlinksarefol哎呀。 Inotherwords,ORIGIN 是特殊的,可以解析到二进制文件在运行时所在的位置。

您可以在那里看到 RPATH。它是精灵部分常量池中的一个字符串。由于它是字符串常量池中的索引,因此只能在可执行文件生成为相同大小或更小后才能更改它。我使用名为chrpath的伟大程序,这是一个特殊用途的工具,用于更改可执行文件中的单个字符串。

chrpath,我使用的版本是0.13
http://ftp.tux.org/pub/X-Windows/ftp.hungry.com/chrpath/

因此,您必须编译可执行文件,以便它将RPATH放在标头中。您可以通过向gcc提供一个特殊标志来执行此操作,该标志将将其提供给ld,链接器。它是这样的:

-Wl,-rpath=$ORIGIN/../lib

将此值放入 gcc 并不容易。由于引用问题,你不能只是把它贴在任何地方,美元符号被shell解释,等等,所以我喜欢做的就是把它设置为这个:

-Wl,-rpath=XORIGIN/../lib

我用字母X替换了美元符号。在编译并制作二进制文件后,我将使用chrpath将字符串设置为我想要的内容,这与美元符号相同。请记住常量池,这就是为什么您需要在exe中保留空间的原因。这是一个技巧,可以避开网络上许多人(包括我自己)所遭受的引用地狱。幸运的是,我看到了一个整齐的旁路。

哄骗 ./配置以将其放入其中:

LDFLAGS="-Wl,-rpath=XORIGIN/../lib" ./configure --prefix=/blabla/place

看到 X 了吗?稍后,当您在生成的二进制文件上运行 chrpath 时,它将被美元符号取代。配置脚本将看到LDFLAGS并将其传递给gcc等,构建系统将包含该标志。看到 -Wl 和 -rpath 之间的逗号了吗?这也是必要的。

完成此操作后,您将获得上面的readelf输出,这将使exe在.中查找。/lib 在查找像 /lib 和 /usr/lib 这样的系统 lib dirs 之前,它是依赖项。

因此,这意味着您可以按如下方式设置二进制文件:

place/bin/ffmpeg
place/lib/libavcodec.so
place/lib/libavdevice.so
place/lib/libavformat.so
place/lib/libavutil.so

你可以移动地方/周围,ffmpeg exe总是会在尝试加载系统库之前找到它的库。这对于您可能实际上在系统lib dirs中拥有libs但不希望它们被加载的系统来说是很好的,也许它们没有使用您想要的选项进行编译。也适用于系统库甚至根本没有库的系统。它有效地隔离了二进制文件。

现在回到ldd有时没有给出正确的答案。我认为这是基于这样一个事实,即ldd是一个shell脚本,而不是可执行文件本身。通过执行"文件'哪个ldd'"并查看它是什么类型的文件来检查它。

如果我们在ffmpeg上做ldd,你会得到正确的答案:

user@debian:~/i/bin$ ldd ffmpeglinux-gate.so.1 =>  (0xb77c1000)libavdevice.so.52 => /home/user/i/bin/./../lib/libavdevice.so.52 (0xb77b9000)libavformat.so.52 => /home/user/i/bin/./../lib/libavformat.so.52 (0xb779e000)libavcodec.so.52 => /home/user/i/bin/./../lib/libavcodec.so.52 (0xb769c000)libavutil.so.49 => /home/user/i/bin/./../lib/libavutil.so.49 (0xb768b000)libm.so.6 => /lib/i686/cmov/libm.so.6 (0xb7657000)libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7510000)/lib/ld-linux.so.2 (0xb77c2000)

但现在让我们创建一个指向此文件的符号链接,并将符号链接放在另一个目录中,然后在符号链接上运行ldd:

user@debian:~$ ldd ./symlinked-ffmpeglinux-gate.so.1 =>  (0xb77d5000)libavdevice.so.52 => /usr/lib/i686/cmov/libavdevice.so.52 (0xb77bb000)libavformat.so.52 => /usr/lib/i686/cmov/libavformat.so.52 (0xb76c2000)libavcodec.so.52 => /usr/lib/i686/cmov/libavcodec.so.52 (0xb6e77000)libavutil.so.49 => /usr/lib/i686/cmov/libavutil.so.49 (0xb6e67000)libm.so.6 => /lib/i686/cmov/libm.so.6 (0xb6e41000)libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb6cfa000)etc output trimmed.

现在它找到了系统库,所以即使rpath被放在精灵头中,它看起来也没有效果。但这实际上是错误的,可以通过执行以下操作来验证:

user@debian:~$ LD_TRACE_LOADED_OBJECTS=1 ./symlinked-ffmpeglinux-gate.so.1 =>  (0xb77fc000)libavdevice.so.52 => /home/user/i/bin/../lib/libavdevice.so.52 (0xb77f4000)libavformat.so.52 => /home/user/i/bin/../lib/libavformat.so.52 (0xb77d9000)libavcodec.so.52 => /home/user/i/bin/../lib/libavcodec.so.52 (0xb76d7000)libavutil.so.49 => /home/user/i/bin/../lib/libavutil.so.49 (0xb76c6000)libm.so.6 => /lib/i686/cmov/libm.so.6 (0xb7692000)libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb754b000)/lib/ld-linux.so.2 (0xb77fd000)

所以这个命令实际上是有效的。
此命令的作用是设置一个名为 LD_TRACE_LOADED_OBJECTS 的环境变量,然后运行可执行文件。当 linux 加载程序看到此 env 变量已设置时,它将输出它加载的库并退出,而不是运行 exe。所以你看到的是加载的"真正的"库,而不是一些shell脚本搞砸了,这就是我认为ldd是什么。

确切地告诉我加载了什么库:
LD_TRACE_LOADED_OBJECTS=1 ./ffmpeg
LD_TRACE_LOADED_OBJECTS=1 ./symlinked-ffmpeg
相同的输出

并使用详细标志查看更多信息:
LD_VERBOSE=1 LD_TRACE_LOADED_OBJECTS=1 ./ffmpeg
LD_VERBOSE=1 LD_TRACE_LOADED_OBJECTS=1 ./symlinked-ffmpeg
再次输出相同

另一种方法是拿出大枪,在exe上运行strace:
strace ./ffmpeg

strace输出告诉我,真正的文件(不是符号链接)是通过readlink找到的:
readlink("/proc/self/exe","/home/user/i/bin/ffmpeg",4096)= 30

然后它尝试找到libavdevice并成功,请记住,现在使用真实的文件路径,因为它确实读取了链接:
open("/home/user/i/bin/../lib/libavdevice.so.52", O_RDONLY) = 3

你有它,RPATH实际上有效。无需再设置LD_LIBRARY_PATH。

很难将RPATH放入精灵可执行文件中,因为引用$ORIGIN问题很猖獗,但这是值得的,特别是如果你回避引用并首先保留空间,然后使用chrpath。

我不确定,但您可能还想在编译生成的任何共享库上运行chrpath,以便可以解决后续的依赖关系级别,但这可能只是由您正在运行的可执行文件中的RPATH处理。我不知道。

兔子洞有多深?

RPATH $ORIGIN LD_LIBRARY_PATH和可移植 linux 二进制文件的描述相关推荐

  1. Linux下文件描述符

    Linux下文件描述符 标签: linuxfilelinux内核apacheunixsocket 2012-08-17 15:45 5798人阅读 评论(0) 收藏 举报 分类: 调优和安全(5) 版 ...

  2. linux下文件描述符的介绍

    linux下文件描述符的介绍 (2012-10-02 16:01:56) 转载▼ 标签: 描述符 调用 返回 进程 限制 it 分类:linux 当某个程序打开文件时,操作系统返回相应的文件描述符,程 ...

  3. Linux 内核如何描述一个进程?

    哈喽,我是吴同学,继续记录我的学习心得. 一.关于写文章 许多知识,书上或者网络上都有,就算这两个地方都没有,代码里也会有答案.但有时恰恰是 资料太多,反而让人难以检索出有用的信息. 面对同样的资料, ...

  4. linux 文件指针,Linux中文件描述符fd与文件指针FILE*互相转换实例解析

    本文研究的主要是Linux中文件描述符fd与文件指针FILE*互相转换的相关内容,具体介绍如下. 1.文件描述符fd的定义:文件描述符在形式上是一个非负整数.实际上,它是一个索引值,指向内核为每一个进 ...

  5. 深入理解Linux/Unix文件描述符和epoll

    Linux/Unix 文件描述符(File Describer)的本质 Linux/Unix(以下简称Linux)系统中,每个进程都有一个专用的数组,数组的元素是一个结构体,称为文件描述符File D ...

  6. Linux磁盘术语描述

    文章目录 Linux磁盘术语描述 什么是磁盘 软盘(Floppy Disk) 硬盘 硬盘的接口类型 IDE SATA SCSI 光纤通道 SAS 非DOS分区 主分区 扩展分区 分区格式 FAT16 ...

  7. linux用户文件描述符2表示,Linux下文件描述符

    Linux下文件描述符 文件描述符是一个简单的整数,用以标明每一个被进程所打开的文件和socket.第一个打开的文件是0,第二个是1,依此类推.Unix操作系 统通常给每个进程能打开的文件数量强加一个 ...

  8. gitbash执行linux二进制文件,Git大文件存储将帮助Git处理大型二进制文件

    GitHub宣布 ,作为 开源的Git扩展 ,Git大文件存储(Large File Storage,简称LFS)的目标是更好地把"大型二进制文件,比如音频文件.数据集.图像和视频" ...

  9. linux ram 权限,我如何将Linux二进制文件限制为有限的RAM数量?

    我希望能够限制已安装的二进制文件只能使用一定数量的RAM.如果它超过它,我不希望它被杀死,只有那将是它可以使用的最大数量.一旦达到一定量的RAM,我希望这个过程能够死掉,最好是在服务器开始大量交换之前 ...

最新文章

  1. Python学习札记(二十八) 模块1
  2. (王道408考研操作系统)第四章文件管理-第二节2:磁盘调度算法
  3. xlwings复制sheet_Python操作Excel的Xlwings教程(六)
  4. Could not connect to Redis at IP No route to host
  5. 2019百度之星程序设计大赛 1005 Seq
  6. memcached客户端_Memcached Java客户端示例
  7. OpenStack云计算快速入门之三:OpenStack镜像管理
  8. Java 中override、overload、overwrite区别,以及与多态的关系
  9. 发那科机器人编程软件fanuc roboguide授权补丁_工业机器人离线编程与应用:ROBOGUIDE V8.3版本的工程文件创建...
  10. ADB工具连接Android手机
  11. 兔玩游戏微博html,和小兔玩游戏小班教案.doc
  12. 对LNode*与LinkLinst等价却不等用的理解
  13. 蓝屏总结(二)——系统蓝屏及转储方法
  14. 正点原子stm32视频教程第7~10节知识总结
  15. Error serializing object. Cause: java.io.NotSerializableException: com.qi
  16. 集成声卡和独立声卡哪个的性能好些
  17. 在线学习系统源代码_学习系统设计和软件体系结构必看的5门在线免费课程
  18. C++定义全局变量的两种方式
  19. 关于自定义标签当中的unable to find setter method for attribute:xxx错误 小记
  20. JSF Faces API中的FacesContext和ExternalContext

热门文章

  1. XSS漏洞原理及防范措施
  2. 智能家居发展中存在的问题
  3. 工作三年转行学Python:比起掉头发,我更害怕掉队
  4. 敏捷教练----Scrum-每日站会和敏捷回顾
  5. 会考flash中文字变形为三角形_信息技术会考 Flash操作题(样例3)
  6. 美图秀秀DBA谈MySQL运维及优化
  7. 使用MATLAB完成一个双轮差速驱动的移动机器人“走8字”的仿真,并生成视频
  8. Samba实现文件共享
  9. 求字符串长度(指针)
  10. php 扩展库curl下载,PHP添加CURL扩展库的二种方法