如何使Unix二进制自包含?

时间:2011-07-15 00:54:17

标签: linux unix ubuntu linker

我有一个Linux二进制文件,没有源代码,可以在一台机器上运行,我想制作一个可以在同一架构的不同机器上运行的独立软件包。有什么方法可以达到这个目的?

在我的情况下,两台机器具有相同的体系结构,相同的Ubuntu内核,但目标机器没有make并且/lib/usr下的文件版本错误

我的一个想法是使用chroot并重新创建二进制使用的文件系统的子集,可能使用strace来确定它需要什么。是否有工具可以做到这一点?

对于后代,这是我如何确定流程打开的文件

#!/usr/bin/python
# source of trace_fileopen.py
# Runs command and prints all files that have been successfully opened with mode O_RDONLY
# example: trace_fileopen.py ls -l
import re, sys, subprocess, os

if __name__=='__main__':
  strace_fn = '/tmp/strace.out'
  strace_re = re.compile(r'([^(]+?)\((.*)\)\s*=\s*(\S+?)\s+(.*)$')

  cmd = sys.argv[1]
  nowhere = open('/dev/null','w')#
  p = subprocess.Popen(['strace','-o', strace_fn]+sys.argv[1:], stdout=nowhere, stderr=nowhere)
  sts = os.waitpid(p.pid, 0)[1]

  output = []
  for line in open(strace_fn):
    # ignore lines like --- SIGCHLD (Child exited) @ 0 (0) ---
    if not strace_re.match(line):
      continue
    (function,args,returnval,msg) = strace_re.findall(line)[0]
    if function=='open' and returnval!='-1':
      (fname,mode)=args.split(',',1)
      if mode.strip()=='O_RDONLY':
        if fname.startswith('"') and fname.endswith('"') and len(fname)>=2:
          fname = fname[1:-1]
        output.append(fname)
  prev_line = ""
  for line in sorted(output):
    if line==prev_line:
      continue
    print line
    prev_line = line

更新LD_LIBRARY_PATH解决方案的问题在于/lib被硬编码到解释器中并优先于LD_LIBRARY_PATH,因此将首先加载本机版本。解释器被硬编码到二进制文件中。一种方法可能是修补解释器并将二进制文件运行为patched_interpreter mycommandline问题是当mycommandlinejava开头时,这不起作用,因为Java设置{{1}并重新启动自身,它转向旧的解释器。对我有用的解决方案是在文本编辑器中打开二进制文件,找到解释器(LD_LIBRARY_PATH),并将其替换为修补解释器的相同长度路径

3 个答案:

答案 0 :(得分:4)

正如其他人所提到的,静态链接是一种选择。除了与glibc的静态链接在每次发布时都会受到更多破坏(抱歉,没有参考;只是我的经验)。

您的chroot想法可能有点过分。

据我所知,大多数商业产品使用的解决方案是使他们的“应用程序”成为设置LD_LIBRARY_PATH的shell脚本,然后运行实际的可执行文件。这些方面的东西:

#!/bin/sh
here=`dirname "$0"`
export LD_LIBRARY_PATH="$here"/lib
exec "$here"/bin/my_app "$@"

然后,您只需转储lib/下所有相关.so文件的副本,将您的可执行文件放在bin/下,将脚本放在.中,然后运送整棵树。< / p>

(要有生产价值,如果它是非空的话,请"$here"/lib正确地添加LD_LIBRARY_PATH等。)

[编辑,继续更新]

我认为你可能会对硬编码和不编码的内容感到困惑。 ld-linux-x86-64.so.2是动态链接器本身;并且你的路径被硬编码到ELF头中是正确的。但其他库不是硬编码的;它们由动态链接器搜索,它将尊重LD_LIBRARY_PATH

如果你真的需要一个不同的ld-linux.so,而不是修补ELF头,只需运行动态链接器本身:

/path/to/my-ld-linux.so my_program <args>

这将使用您的链接器而不是ELF标头中列出的链接器。

修补可执行文件本身是邪恶的。请考虑一下在你搬家之后必须维护你的东西的穷人...... 没人会指望你手工砍掉ELF标题。 任何人都可以阅读shell脚本正在做什么。

只需我0.02美元。

答案 1 :(得分:2)

几乎肯定有更好的答案,但您可以使用ldd命令(ls二进制文件的示例)找出二进制文件需要哪些库:

$ ldd /bin/ls
linux-vdso.so.1 =>  (0x00007ffffff18000)
librt.so.1 => /lib/librt.so.1 (0x00007f5ae565c000)
libselinux.so.1 => /lib/libselinux.so.1 (0x00007f5ae543e000)
libacl.so.1 => /lib/libacl.so.1 (0x00007f5ae5235000)
libc.so.6 => /lib/libc.so.6 (0x00007f5ae4eb2000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00007f5ae4c95000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5ae588b000)
libdl.so.2 => /lib/libdl.so.2 (0x00007f5ae4a90000)
libattr.so.1 => /lib/libattr.so.1 (0x00007f5ae488b000)

完成此操作后,您可以复制并将它们放在目标计算机上的适当位置。

答案 2 :(得分:2)

CDE个软件可以完全按照您的要求进行操作。这是谷歌技术谈论它 http://www.youtube.com/watch?v=6XdwHo1BWwY