2011-07-15 80 views
10

我有一個Linux二進制文件,沒有來源,即一臺機器上工作,我想提出一個自包含的包,會在不同的機器相同的架構上運行。什麼是實現這個的方法?如何使Unix二進制獨立?

在我的情況下,兩臺機器具有相同的結構,相同的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將向上LD_LIBRARY_PATH並重新啓動本身,它求助於老的解釋。對我而言,解決方案是在文本編輯器中打開二進制文件,找到解釋器(/lib/ld-linux-x86-64.so.2),並用相同長度的路徑替換爲打補丁的解釋器。

+4

聽起來像你想要一個靜態二進制。它建立在它使用的所有庫中。 –

+0

@VLC說什麼,你必須將它標記爲靜態鏈接,儘管這可能有點痛苦。 –

+0

您是否構建了有問題的二進制文件?如果你這樣做了,你或許可以靜態重建它。 – Cascabel

回答

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) 

一旦你有了這個,你可以製作副本,並把它們在目標機器上的正確位置。

+0

其實我已經做了類似的事情(使用'strace'來查找'ldd'沒有報告的其他所需的庫),但我不喜歡我的程序的想法潛在的因爲'〜'以外的事情我不能控制。 'chroot'看起來不錯,但是需要努力找出如何重建文件系統。 –

4

正如其他人所說的,靜態鏈接是一個選項。除了與glibc的靜態鏈接之外,每次發佈都會有更多的破解(抱歉,沒有參考;僅僅是我的經驗)。

chroot想法可能是矯枉過正。

據我所知,大多數商業產品使用的解決方案是將其「應用程序」設置爲設置LD_LIBRARY_PATH然後運行實際可執行文件的shell腳本。沿着這些路線的東西:

#!/bin/sh 
here=`dirname "$0"` 
export LD_LIBRARY_PATH="$here"/lib 
exec "$here"/bin/my_app "[email protected]" 

然後你只轉儲所有相關的.so文件的副本lib/下,把你的可執行bin/下,把腳本.,和船舶整個樹。

(要生產價值,適當預先準備"$here"/libLD_LIBRARY_PATH如果非空,等等)

[編輯,用你的更新去]

我想你可能會感到困惑什麼是硬編碼,什麼不是。 ld-linux-x86-64.so.2是動態鏈接器本身;並且您的路徑是硬編碼到ELF標頭中是正確的。但其他庫不是硬編碼的;他們被動態鏈接器搜索,它將會兌現LD_LIBRARY_PATH

如果你真的需要,而不是修補ELF頭不同ld-linux.so,只需運行動態連接器本身:

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

這將使用而不是在ELF中列出的鏈接器頭。

修補可執行文件本身是邪惡的。請考慮在你繼續前進之後必須維護你的東西的可憐人...... 沒有人會指望你手動破解ELF頭部。 任何人都可以讀取shell腳本正在做什麼。

只是我的0.02美元。

+0

這裏的問題是在'/ lib'中的東西會在'LD_LIBRARY_PATH'中的東西之前加載。我在沒有使用'chroot'的情況下解決了這個問題,更新了問題主體 –

+0

@Yaroslav:在那裏,完成了...請注意,您還可以運行二進制文件「/lib/my-ld-linux.so 」。當你這樣做時,'my-ld-linux.so'會忽略ELF頭部中的版本。從腳本中運行這樣的可執行文件(這樣任何人都可以看到你在做什麼)比在可執行文件中修補ELF頭文件(這是人們無法想象的)要更清潔。 – Nemo

+0

是的,你真的不能完全用glibc完成靜態鏈接,因爲名稱服務切換機制允許東西包含其他類型的主機名解析。 – Spudd86