2010-10-27 20 views
57

Các tệp nhị phân Linux thường được liên kết động với thư viện hệ thống lõi (libc). Điều này giữ cho bộ nhớ của tệp nhị phân khá nhỏ nhưng các tệp nhị phân phụ thuộc vào các thư viện mới nhất sẽ không chạy trên các hệ thống cũ hơn. Ngược lại, các tệp nhị phân được liên kết với các thư viện cũ sẽ chạy một cách vui vẻ trên các hệ thống mới nhất.Liên kết với phiên bản cũ của libc để cung cấp phạm vi ứng dụng lớn hơn

Do đó, để đảm bảo ứng dụng của chúng tôi có phạm vi phủ sóng tốt trong quá trình phân phối, chúng tôi cần tìm ra libc cũ nhất mà chúng tôi có thể hỗ trợ và liên kết nhị phân của chúng tôi với điều đó.

Làm cách nào để xác định phiên bản libc cũ nhất mà chúng tôi có thể liên kết?

Trả lời

65

Tìm ra biểu tượng nào trong tệp thực thi của bạn đang tạo phụ thuộc vào phiên bản glibc không mong muốn.

$ objdump -p myprog 
... 
Version References: 
    required from libc.so.6: 
    0x09691972 0x00 05 GLIBC_2.3 
    0x09691a75 0x00 03 GLIBC_2.2.5 

$ objdump -T myprog | fgrep GLIBC_2.3 
0000000000000000  DF *UND* 0000000000000000 GLIBC_2.3 realpath 

Look trong phụ thuộc-upon thư viện để xem nếu có bất kỳ biểu tượng trong các phiên bản cũ mà bạn có thể liên kết chống lại:

$ objdump -T /lib/libc.so.6 | grep -w realpath 
0000000000105d90 g DF .text 0000000000000021 (GLIBC_2.2.5) realpath 
000000000003e7b0 g DF .text 00000000000004bf GLIBC_2.3 realpath 

Chúng tôi đang ở may mắn!

Yêu cầu phiên bản từ GLIBC_2.2.5 trong mã của bạn:

#include <limits.h> 
#include <stdlib.h> 

__asm__(".symver realpath,[email protected]_2.2.5"); 

int main() { 
    realpath ("foo", "bar"); 
} 

Quan sát GLIBC_2.3 rằng không còn cần thiết:

$ objdump -p myprog 
... 
Version References: 
    required from libc.so.6: 
    0x09691a75 0x00 02 GLIBC_2.2.5 

$ objdump -T myprog | grep realpath 
0000000000000000  DF *UND* 0000000000000000 GLIBC_2.2.5 realpath 

Để biết thêm thông tin, xem http://www.trevorpounds.com/blog/?p=103.

+6

Tôi cũng sẽ thêm rằng thường chỉ có một hoặc hai biểu tượng gây ra sự phụ thuộc vào phiên bản glibc mới, vì vậy nếu như tôi bạn lo lắng bạn sẽ phải liệt kê hàng trăm biểu tượng để xóa phụ thuộc, bạn sẽ không làm như vậy. – Malvineous

+2

dietlibc cũng đáng xem. –

+0

Hoặc nếu bạn có thể liên kết tĩnh (tức là mã của bạn không phải là plugin và sẽ không sử dụng plugin), bạn cũng có thể xem musl-libc. Tôi đã tìm thấy đôi khi các chương trình liên kết tĩnh với musl-libc nhỏ hơn so với các đối tác glibc được liên kết động của chúng. – 0xC0000022L

3

glibc 2.2 là phiên bản tối thiểu khá phổ biến. Tuy nhiên việc tìm kiếm một nền tảng xây dựng cho phiên bản đó có thể không tầm thường.

Có lẽ một hướng tốt hơn là suy nghĩ về hệ điều hành cũ nhất mà bạn muốn hỗ trợ và xây dựng trên đó.

+2

Làm cho cảm giác, tôi chỉ hy vọng cho một số trái cây treo thấp ngon ngọt mà tôi có thể nhổ từ các chi nhánh của cơ hội :-) –

+0

2.4 là Linux Standard Base 4,0 (từ năm 2006), 2.2 là từ năm 2000 ... –

+0

Eh? http://www.gnu.org/software/libc/ nói glibc * 2.17 *, phát hành 2012-12-25, là phiên bản mới nhất. Làm thế nào 2.2 có thể là mức tối thiểu chung? – musiphil

8

Thật không may, giải pháp của @ Sam không hoạt động tốt trong tình huống của tôi. Nhưng theo cách của anh ta, tôi đã tìm ra cách để giải quyết điều đó.

Đây là tình huống của tôi:

Tôi đang viết chương trình C++ bằng khung công tác tiết kiệm (đó là phần mềm trung gian RPC). Tôi thích liên kết tĩnh đến liên kết động hơn, vì vậy chương trình của tôi được liên kết với libthrift.a tĩnh thay vì libthrift.so. Tuy nhiên, libthrift.a là tự động kết nối với glibc, và kể từ libthrift.a tôi được xây dựng trên hệ thống của tôi với glibc 2.15, tôi libthrift.a sử dụng memcpy của phiên bản 2.14 ([email protected]_2.14) do glibc 2.15 cung cấp.

Nhưng vấn đề là máy chủ của chúng tôi chỉ có phiên bản glibc 2.5 chỉ có [email protected]_2.2.5. Giá thầu này thấp hơn nhiều so với [email protected]_2.14. Vì vậy, tất nhiên, chương trình máy chủ của tôi không thể chạy trên các máy đó.

Và tôi thấy solusion này:

  1. Sử dụng .symver để có được những ref để [email protected]_2.2.5.

  2. Viết __wrap_memcpy riêng chức năng của tôi mà chỉ gọi [email protected]_2.2.5 trực tiếp.

  3. Khi liên kết chương trình của tôi, hãy thêm tùy chọn -Wl, - wrap = memcpy vào gcc/g ++.

Mã tham gia vào bước 1 và 2 là ở đây: https://gist.github.com/nicky-zs/7541169

+1

Sự cố có vẻ như với '_FORTIFY_SOURCE', mặc định là 1 trên các phiên bản GCC mới hơn ở các mức tối ưu nhất định. Khi được bật, một số chức năng sẽ được đeo mặt nạ. Đối với tôi không xác định và sau đó xác định lại nó thành 0 làm cho giải pháp trên hoạt động (GCC 4.8.4). – 0xC0000022L

7

Để làm điều này một cách tự động, bạn có thể sử dụng kịch bản sau đây để tạo ra một danh sách tất cả những biểu tượng mà mới trong GLIBC của bạn hơn trong một phiên bản nhất định (được đặt trên dòng 2). Nó tạo ra một tệp glibc.h (tên tệp được đặt bởi đối số tập lệnh) chứa tất cả các khai báo .symver cần thiết. Sau đó, bạn có thể thêm -include glibc.h vào CFLAGS của mình để đảm bảo rằng nó được chọn ở mọi nơi trong quá trình biên dịch của bạn.

Điều này là đủ nếu bạn không sử dụng bất kỳ thư viện tĩnh nào được biên dịch mà không có các điều kiện trên bao gồm. Nếu bạn làm thế, và bạn không muốn biên dịch lại, bạn có thể sử dụng objcopy để tạo một bản sao của thư viện với các biểu tượng được đổi tên thành các phiên bản cũ. Dòng thứ hai đến cuối của tập lệnh tạo ra phiên bản hệ thống của bạn libstdc++.a sẽ liên kết với các biểu tượng glibc cũ. Thêm -L. (hoặc -Lpath/to/libstdc++.a/) sẽ làm cho chương trình của bạn liên kết tĩnh libstdC++ mà không cần liên kết trong một loạt các ký hiệu mới. Nếu bạn không cần điều này, hãy xóa hai dòng cuối cùng và dòng printf ... redeff.

#!/bin/bash 
maxver=2.9 
headerf=${1:-glibc.h} 
set -e 
for lib in libc.so.6 libm.so.6 libpthread.so.0 libdl.so.2 libresolv.so.2 librt.so.1; do 
objdump -T /usr/lib/$lib 
done | awk -v maxver=${maxver} -vheaderf=${headerf} -vredeff=${headerf}.redef -f <(cat <<'EOF' 
BEGIN { 
split(maxver, ver, /\./) 
limit_ver = ver[1] * 10000 + ver[2]*100 + ver[3] 
} 
/GLIBC_/ { 
gsub(/\(|\)/, "",$(NF-1)) 
split($(NF-1), ver, /GLIBC_|\./) 
vers = ver[2] * 10000 + ver[3]*100 + ver[4] 
if (vers > 0) { 
    if (symvertext[$(NF)] != $(NF-1)) 
     count[$(NF)]++ 
    if (vers <= limit_ver && vers > symvers[$(NF)]) { 
     symvers[$(NF)] = vers 
     symvertext[$(NF)] = $(NF-1) 
    } 
} 
} 
END { 
for (s in symvers) { 
    if (count[s] > 1) { 
     printf("__asm__(\".symver %s,%[email protected]%s\");\n", s, s, symvertext[s]) > headerf 
     printf("%s %[email protected]%s\n", s, s, symvertext[s]) > redeff 
    } 
} 
} 
EOF 
) 
sort ${headerf} -o ${headerf} 
objcopy --redefine-syms=${headerf}.redef /usr/lib/libstdc++.a libstdc++.a 
rm ${headerf}.redef 
+0

Tại sao 'sắp xếp'? – Otheus

+0

Nó không thực sự cần thiết, nó chỉ làm cho các tập tin có thể tái sản xuất hơn nếu bạn muốn kiểm tra nó trong kiểm soát nguồn hoặc ngăn chặn hệ thống xây dựng của bạn xây dựng lại mọi thứ – patstew

+0

Giải pháp 'objcopy --redefine-syms' này không thực sự hoạt động. tôi giờ để tìm lý do tại sao. objcopy cố gắng thực hiện một chuỗi match-replace cho các ký hiệu đã cho, nhưng các phiên bản của các ký hiệu là _not_ được biểu diễn dưới dạng chuỗi.objcopy chỉ đơn giản là không làm việc trên chúng - các phiên bản được thể hiện trong một bảng tra cứu, mà objcopy 2,20.50 không cố gắng sửa đổi. Không bao giờ. Có _is_ một biểu tượng "mặc định" có thể thay đổi theo cách này - 'symbol @@ 1.2.3' có thể được thay đổi. – Otheus

Các vấn đề liên quan