Tại sao và làm thế nào một số thư viện chia sẻ có thể chạy được, như thể chúng là các tệp thực thi?


55

Trên các hệ thống Linux 32 bit, gọi điều này

$ /lib/libc.so.6

và trên các hệ thống 64 bit này

$ /lib/x86_64-linux-gnu/libc.so.6

trong một vỏ, cung cấp một đầu ra như thế này:

GNU C Library stable release version 2.10.1, by Roland McGrath et al.
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.0 20090506 (Red Hat 4.4.0-4).
Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19.
Available extensions:
    The C stubs add-on version 2.1.2.
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
    RT using linux kernel aio
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

Tại sao và làm thế nào điều này xảy ra, và làm thế nào có thể làm điều tương tự trong các thư viện chia sẻ khác?

Tôi nhìn vào /usr/libđể tìm thực thi, và tôi tìm thấy /usr/lib/libvlc.so.5.5.0. Chạy nó dẫn đến một lỗi phân khúc . : - /


Ngoài ra tất cả các câu trả lời sau đây, tôi nhớ là nếu bạn đặt bit x trên thư viện dùng chung, có thể (có thể vẫn) có thể tải nó từ một tệp thực thi ngay cả với bit r rõ ràng. Nó đã từng được coi là một thực tiễn bảo mật tốt để trục xuất r bit khỏi thế giới trên các thư viện và thư viện thực thi hệ thống. Do nguồn mở rộng rãi, điều này chỉ thực sự áp dụng cho thư mục ftp ẩn danh / bin / ls nữa. Đối với tôi, việc để lại tập x bit trông giống như một hình ảnh của thực tiễn cũ đó.
Joshua

Câu trả lời:


52

Thư viện đó có một main()chức năng hoặc điểm nhập tương đương, và được biên dịch theo cách nó hữu ích cả khi thực thi và như một đối tượng chia sẻ.

Đây là một gợi ý về cách thực hiện việc này, mặc dù nó không hiệu quả với tôi.

Đây là một câu trả lời khác cho một câu hỏi tương tự trên SO , mà tôi sẽ biết xấu hổ đạo văn, chỉnh sửa và thêm một chút giải thích.

Đầu tiên, nguồn cho thư viện mẫu của chúng tôi , test.c:

#include <stdio.h>                  

void sayHello (char *tag) {         
    printf("%s: Hello!\n", tag);    
}                                   

int main (int argc, char *argv[]) { 
    sayHello(argv[0]);              
    return 0;                       
}                   

Biên dịch rằng:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E

Ở đây, chúng tôi đang biên dịch một thư viện dùng chung ( -fPIC), nhưng nói với trình liên kết rằng đó là một tệp thực thi ( -pie) thông thường và để làm cho bảng biểu tượng của nó có thể xuất được ( -Wl,-E), sao cho nó có thể được liên kết hữu ích với nó.

Và, mặc dù filesẽ nói đó là một đối tượng được chia sẻ, nhưng nó hoạt động như một tệp thực thi:

> ./libtest.so 
./libtest.so: Hello!

Bây giờ chúng ta cần phải xem nếu nó thực sự có thể được liên kết động. Một chương trình ví dụ , program.c:

#include <stdio.h>

extern void sayHello (char*);

int main (int argc, char *argv[]) {
    puts("Test program.");
    sayHello(argv[0]);
    return 0;
}

Sử dụng externtiết kiệm chúng ta phải tạo một tiêu đề. Bây giờ biên dịch rằng:

gcc program.c -L. -ltest

Trước khi chúng ta có thể thực thi nó, chúng ta cần thêm đường dẫn của libtest.sotrình tải động:

export LD_LIBRARY_PATH=./

Hiện nay:

> ./a.out
Test program.
./a.out: Hello!

ldd a.outsẽ hiển thị các liên kết đến libtest.so.

Lưu ý rằng tôi nghi ngờ đây là cách glibc thực sự được biên dịch, vì nó có thể không di động như chính glibc (xem man gccliên quan đến -fPICvà các -piecông tắc), nhưng nó thể hiện cơ chế cơ bản. Để biết chi tiết thực sự, bạn phải xem tệp tạo tệp nguồn.


1
Câu trả lời tuyệt vời, cảm ơn! :-) Tôi đã thử sử dụng nmtrên thư viện dùng chung, nhưng nó không phải là phiên bản gỡ lỗi. Vì vậy, tại sao libvlcvà những người khác sụp đổ?
Ho1

1
Bởi vì hầu hết các thư viện chia sẻ không có ý định thực thi, GNU libclà một ngoại lệ.
goldilocks

Tôi tìm thấy hai người khác: ldlibpthread.
Ho1

@ Ho1 ld.sođặc biệt theo những cách khác. Ở một mức độ nào đó, nó giống như một thực thi thực sự hơn là một thực thi được liên kết động thông thường là.
Random832

1
Các tùy chọn trên mặc dù tạo thư viện chia sẻ thực thi, nhưng không đầy đủ theo nghĩa, nó đánh dấu lỗi, khi một số thực thi cố gắng liên kết đến điều này. Tham khảo mẫu chi tiết được thêm vào đây: unix.stackexchange.com/a/479334/152034
ký sinh

21

Chúng ta hãy đi tìm câu trả lời trong repo glibc ngẫu nhiên trong github. Phiên bản này cung cấp một „banner banner tại tập tin version.c.

Trong cùng một tệp có một vài điểm thú vị: __libc_print_versionchức năng cung cấp in cho stdin cùng văn bản và ký hiệu __libc_main (void)được ghi lại dưới dạng điểm nhập cảnh. Vì vậy, biểu tượng này được gọi khi chạy thư viện.

Vậy làm thế nào để linker / trình biên dịch biết rằng đây chính xác là chức năng nhập điểm?

Hãy đi sâu vào makefile . Trong các cờ liên kết có cờ thú vị:

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main

Vì vậy, đây là cờ liên kết để thiết lập điểm vào trong thư viện. Khi xây dựng thư viện, bạn có thể cung cấp -e function_namecho trình liên kết tạo hành vi thực thi. Nó thực sự làm gì? Hãy xem hướng dẫn sử dụng (hơi cũ nhưng vẫn hợp lệ) :

Ngôn ngữ lệnh của trình liên kết bao gồm một lệnh đặc biệt để xác định lệnh thực thi đầu tiên trong tệp đầu ra (điểm vào của nó). Đối số của nó là một tên biểu tượng:

ENTRY (ký hiệu)

Giống như các bài tập ký hiệu, lệnh ENTRY có thể được đặt dưới dạng một lệnh độc lập trong tệp lệnh hoặc trong số các định nghĩa phần trong lệnh SECTION - bất cứ điều gì có ý nghĩa nhất đối với bố cục của bạn.

ENTRY chỉ là một trong một số cách chọn điểm vào. Bạn có thể chỉ ra nó theo bất kỳ cách nào sau đây (được hiển thị theo thứ tự ưu tiên giảm dần: các phương thức cao hơn trong danh sách các phương thức ghi đè xuống thấp hơn).

the `-e' entry command-line option;
the ENTRY(symbol) command in a linker control script;
the value of the symbol start, if present;
the address of the first byte of the .text section, if present;
The address 0. 

Ví dụ: bạn có thể sử dụng các quy tắc này để tạo điểm nhập cảnh bằng câu lệnh gán: nếu không có biểu tượng bắt đầu nào được xác định trong các tệp đầu vào của bạn, bạn có thể chỉ cần xác định nó, gán cho nó một giá trị phù hợp ---

bắt đầu = 0x2020;

Ví dụ hiển thị một địa chỉ tuyệt đối, nhưng bạn có thể sử dụng bất kỳ biểu thức nào. Ví dụ: nếu các tệp đối tượng đầu vào của bạn sử dụng một số quy ước tên biểu tượng khác cho điểm vào, bạn có thể chỉ định giá trị của bất kỳ ký hiệu nào chứa địa chỉ bắt đầu để bắt đầu:

bắt đầu = other_symbol;

(tài liệu hiện tại có thể được tìm thấy ở đây )

Trình ldliên kết thực sự tạo ra một tệp thực thi với chức năng điểm vào nếu bạn cung cấp cho nó tùy chọn dòng lệnh -e(giải pháp thực tế nhất), cung cấp ký hiệu hàm starthoặc nhập địa chỉ ký hiệu trong trình biên dịch chương trình.

Tuy nhiên, xin lưu ý rằng rõ ràng không được đảm bảo để làm việc với các trình liên kết khác (tôi không biết nếu llvm lld có cùng cờ). Tại sao điều này sẽ hữu ích cho các mục đích khác ngoài việc cung cấp thông tin trên tập tin như vậy, tôi không biết.


1
Nếu nó là python, nó sẽ cung cấp các bài kiểm tra đơn vị.
Erik Aronesty
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.