2009-04-30 10 views
25

Chỉ sử dụng thực tế tùy chọn liên kết --whole-archive mà tôi đã thấy trong việc tạo thư viện được chia sẻ từ các thư mục tĩnh. Gần đây tôi đã đi qua Makefile (s) mà luôn luôn sử dụng tùy chọn này khi liên kết với các thư viện trong nhà tĩnh. Điều này tất nhiên làm cho các tập tin thực thi để kéo không cần thiết trong mã đối tượng không được chấp nhận. Phản ứng của tôi với điều này là điều này là sai, tôi đang thiếu một cái gì đó ở đây?câu hỏi liên kết ld: tùy chọn --whole-archive

Câu hỏi thứ hai tôi phải thực hiện với nội dung tôi đã đọc về tùy chọn lưu trữ toàn bộ nhưng không thể phân tích cú pháp. Một cái gì đó để có hiệu lực là tùy chọn --whole-archive nên được sử dụng trong khi liên kết với một thư viện tĩnh nếu thực thi cũng liên kết với một thư viện được chia sẻ mà lần lượt có (một phần) mã đối tượng giống như thư viện tĩnh. Đó là thư viện được chia sẻ và thư viện tĩnh có chồng chéo về mặt mã đối tượng. Sử dụng tùy chọn này sẽ buộc tất cả các ký hiệu (bất kể việc sử dụng) được giải quyết trong tệp thực thi. Điều này được cho là để tránh trùng lặp mã đối tượng. Điều này là khó hiểu, nếu một biểu tượng được đề cập trong chương trình, nó phải được giải quyết duy nhất tại thời điểm liên kết, doanh nghiệp này về sao chép là gì? (Hãy tha thứ cho tôi nếu đoạn này không hẳn là hình ảnh thu nhỏ của sự rõ ràng)

Cảm ơn

Trả lời

3

Tôi đồng ý rằng việc sử dụng -whole-archive để xây dựng thực thi có lẽ không phải những gì bạn muốn (do liên kết trong mã không cần thiết và tạo phần mềm cồng kềnh). Nếu họ có lý do chính đáng để làm như vậy họ cần phải ghi lại nó trong hệ thống xây dựng, như bây giờ bạn còn lại để đoán.

Đối với phần thứ hai của câu hỏi. Nếu tệp thực thi liên kết cả thư viện tĩnh và thư viện động có (một phần) mã đối tượng giống như thư viện tĩnh thì —khi lưu trữ sẽ đảm bảo rằng tại thời điểm liên kết, mã từ thư viện tĩnh được ưu tiên . Đây thường là những gì bạn muốn khi bạn làm liên kết tĩnh.

50

Có sử dụng hợp pháp --whole-archive khi liên kết tệp thực thi với thư viện tĩnh. Một ví dụ được xây dựng C++, nơi trường toàn cầu "đăng ký" mình trong nhà thầu của họ (cảnh báo: Mã chưa được kiểm tra):

main.cc

typedef void (*handler)(const char *protocol); 
typedef map<const char *, handler> M; 
M m; 

void register_handler(const char *protocol, handler) { 
    m[protocol] = handler; 
} 
int main(int argc, char *argv[]) 
{ 
    for (int i = 1; i < argc-1; i+= 2) { 
     M::iterator it = m.find(argv[i]); 
     if (it != m.end()) it.second(argv[i+1]); 
    } 
} 

http.cc (một phần của libhttp.a)

class HttpHandler { 
    HttpHandler() { register_handler("http", &handle_http); } 
    static void handle_http(const char *) { /* whatever */ } 
}; 
HttpHandler h; // registers itself with main! 

Lưu ý rằng không có biểu tượng nào trong số http.cc nhu cầu main.cc. Nếu bạn liên kết này như

g++ main.cc -lhttp 

bạn sẽ không nhận được một handler http liên kết vào thực thi chính, và sẽ không thể gọi handle_http(). Ngược lại điều này với những gì sẽ xảy ra khi bạn liên kết dưới dạng:

g++ main.cc -Wl,--whole-archive -lhttp -Wl,--no-whole-archive 

Cùng một kiểu "tự đăng ký" cũng có thể ở đồng bằng C, ví dụ: với tiện ích mở rộng __attribute__((constructor)) GNU.

+1

Russion Nếu libhttp.a có thể được xây dựng thì nó chứng minh rằng hàm register_handler tồn tại trong libhttp.a đó. Vì vậy, làm thế nào có thể chức năng này tham khảo register_handler trong main.cc? Vì vậy, trường hợp này chúng ta phải sử dụng một số cách khác để thực hiện ý tưởng của bạn. – longbkit

9

Sử dụng hợp pháp khác cho --whole-archive là dành cho các nhà phát triển bộ công cụ phân phối các thư viện chứa nhiều tính năng trong một thư viện tĩnh đơn. Trong trường hợp này, nhà cung cấp không biết phần nào của thư viện sẽ được người tiêu dùng sử dụng và do đó phải bao gồm mọi thứ.

+4

s/đã chia sẻ/tĩnh / – Igor

1

Truy vấn cũ, nhưng trên câu hỏi đầu tiên của bạn ("Tại sao"), tôi đã thấy - lưu trữ-lưu trữ được sử dụng cho thư viện nội bộ, chủ yếu để tránh tham chiếu vòng tròn giữa các thư viện đó. Nó có xu hướng ẩn kiến ​​trúc nghèo nàn của các thư viện, vì vậy tôi không khuyên bạn nên nó. Tuy nhiên đó là một cách nhanh chóng để có được một thử nghiệm nhanh chóng làm việc.

Đối với truy vấn thứ hai của bạn, nếu cùng một biểu tượng có trong một đối tượng được chia sẻ và thư viện tĩnh, trình liên kết sẽ thỏa mãn tham chiếu với thư viện nào nó gặp trước.
Nếu thư viện được chia sẻ và thư viện tĩnh có chia sẻ mã chính xác, điều này có thể chỉ hoạt động. Nhưng khi thư viện chia sẻ và thư viện tĩnh có các triển khai khác nhau của cùng một biểu tượng, chương trình của bạn sẽ vẫn biên dịch nhưng sẽ hoạt động khác nhau dựa trên thứ tự của các thư viện.

Buộc tất cả các biểu tượng được tải từ thư viện tĩnh là một cách để loại bỏ nhầm lẫn về những gì được tải từ đâu. Nhưng nói chung điều này nghe có vẻ như giải quyết vấn đề sai; bạn hầu như không muốn các ký hiệu giống nhau trong các thư viện khác nhau.

0

Một kịch bản bổ sung tốt trong đó --whole-archive được sử dụng tốt là khi xử lý các thư viện tĩnh liên kết gia tăng.

Chúng ta hãy giả sử rằng:

  1. libA thực hiện các chức năng a()b().
  2. Một số phần của chương trình phải được liên kết với chỉ libA, ví dụ: do một số chức năng gói sử dụng --wrap (một ví dụ cổ điển là malloc)
  3. libC thực hiện các c() chức năng và sử dụng a()
  4. chương trình cuối cùng sử dụng a()c()

Incremental bước liên kết có thể là:

ld -r -o step1.o module1.o --wrap malloc --whole-archive -lA 
ld -r -o step2.o step1.o module2.o --whole-archive -lC 
cc step3.o module3.o -o program 

Không chèn - lưu trữ -những gì sẽ bỏ chức năng c() w hich được sử dụng bởi program, ngăn chặn quá trình biên dịch chính xác.

Tất nhiên, đây là trường hợp góc cụ thể trong đó liên kết gia tăng phải được thực hiện để tránh gói tất cả các cuộc gọi đến malloc trong tất cả các mô-đun, nhưng là trường hợp được hỗ trợ thành công bởi --whole-archive.