Thư viện Nhập hoạt động như thế nào? Chi tiết?


88

Tôi biết điều này có vẻ khá cơ bản đối với những người yêu thích máy tính. Nhưng tôi muốn làm cho nó rõ ràng.

Khi tôi muốn sử dụng Win32 DLL, thông thường tôi chỉ gọi các API như LoadLibrary () và GetProcAdderss (). Nhưng gần đây, tôi đang phát triển với DirectX9 và tôi cần thêm các tệp d3d9.lib , d3dx9.lib , v.v.

Tôi đã nghe đủ rằng LIB dành cho liên kết tĩnh và DLL dành cho liên kết động.

Vì vậy, hiểu biết hiện tại của tôi là LIB chứa việc triển khai các phương thức và được liên kết tĩnh tại thời điểm liên kết như một phần của tệp EXE cuối cùng. Trong khi DLL được tải động trong thời gian chạy và không phải là một phần của tệp EXE cuối cùng.

Nhưng đôi khi, có một số tệp LIB đi kèm với tệp DLL, vì vậy:

  • Những tệp LIB này để làm gì?
  • Làm thế nào để họ đạt được những gì họ muốn?
  • Có công cụ nào có thể cho phép tôi kiểm tra nội bộ của các tệp LIB này không?

Cập nhật 1

Sau khi kiểm tra wikipedia, tôi nhớ rằng các tệp LIB này được gọi là thư viện nhập . Nhưng tôi đang tự hỏi làm thế nào nó hoạt động với ứng dụng chính của tôi và các tệp DLL được tải động.

Cập nhật 2

Như RBerteig đã nói, có một số mã sơ khai trong các tệp LIB được sinh ra từ các tệp DLL. Vì vậy, trình tự gọi sẽ như thế này:

Ứng dụng chính của tôi -> sơ khai trong LIB -> DLL đích thực

Vậy những thông tin nào cần có trong các LIB này? Tôi có thể nghĩ đến những điều sau:

  • Tệp LIB phải chứa đường dẫn đầy đủ của DLL tương ứng; Vì vậy, DLL có thể được tải trong thời gian chạy.
  • Địa chỉ tương đối (hoặc độ lệch tệp?) Của mỗi điểm nhập của phương thức xuất DLL phải được mã hóa trong sơ khai; Vì vậy, có thể thực hiện các cuộc gọi nhảy / phương thức chính xác.

Tôi có đúng về điều này không? Còn gì nữa không?

BTW: Có công cụ nào có thể kiểm tra thư viện nhập không? Nếu tôi có thể nhìn thấy nó, sẽ không còn nghi ngờ gì nữa.


4
Tôi thấy rằng không ai giải quyết phần cuối cùng của câu hỏi của bạn, liên quan đến các công cụ có thể kiểm tra thư viện nhập. Với Visual C ++, có ít nhất hai cách để làm điều đó: lib /list xxx.liblink /dump /linkermember xxx.lib. Xem câu hỏi Stack Overflow này .
Alan

Ngoài ra, dumpbin -headers xxx.libcung cấp một số thông tin chi tiết hơn, so với các tiện ích liblinkdịch vụ.
m_katsifarakis

Câu trả lời:


102

Liên kết đến tệp DLL có thể diễn ra ngầm tại thời gian liên kết biên dịch hoặc rõ ràng vào thời gian chạy. Dù bằng cách nào, DLL kết thúc được tải vào không gian bộ nhớ quy trình và tất cả các điểm nhập đã xuất của nó đều có sẵn cho ứng dụng.

Nếu được sử dụng một cách rõ ràng tại thời điểm chạy, bạn sử dụng LoadLibrary()GetProcAddress()để tải DLL theo cách thủ công và lấy các con trỏ đến các hàm bạn cần gọi.

Nếu được liên kết ngầm khi chương trình được xây dựng, thì các bản gốc cho mỗi lần xuất DLL được chương trình sử dụng sẽ được liên kết với chương trình từ một thư viện nhập và các bản gốc đó sẽ được cập nhật khi EXE và DLL được tải khi quá trình khởi chạy. (Vâng, tôi đã đơn giản hóa hơn một chút ở đây ...)

Các sơ khai đó cần đến từ đâu đó và trong chuỗi công cụ của Microsoft, chúng đến từ một dạng tệp .LIB đặc biệt được gọi là thư viện nhập . .LIB bắt buộc thường được xây dựng cùng lúc với DLL và chứa sơ khai cho mỗi hàm được xuất từ ​​DLL.

Thật khó hiểu, một phiên bản tĩnh của cùng một thư viện cũng sẽ được chuyển dưới dạng tệp .LIB. Không có cách nào đơn giản để phân biệt chúng, ngoại trừ LIB là thư viện nhập cho các tệp DLL thường sẽ nhỏ hơn (thường nhỏ hơn nhiều) so với LIB tĩnh phù hợp.

Nếu bạn sử dụng chuỗi công cụ GCC, ngẫu nhiên, bạn không thực sự cần các thư viện nhập để khớp với các tệp DLL của mình. Phiên bản của trình liên kết Gnu được chuyển sang Windows hiểu trực tiếp các tệp DLL và có thể tổng hợp nhanh chóng hầu hết các sơ khai cần thiết.

Cập nhật

Nếu bạn không thể cưỡng lại việc biết tất cả các đai ốc và bu lông thực sự ở đâu và điều gì đang thực sự xảy ra, thì MSDN luôn có điều gì đó để giúp bạn. Bài viết của Matt Pietrek Tìm hiểu sâu về Định dạng tệp thực thi di động của Win32 là một tổng quan rất đầy đủ về định dạng của tệp EXE và cách nó được tải và chạy. Nó thậm chí còn được cập nhật để bao gồm .NET và hơn thế nữa kể từ khi nó xuất hiện lần đầu trên Tạp chí MSDN ca. Năm 2002.

Ngoài ra, có thể hữu ích nếu biết cách tìm hiểu chính xác những gì các tệp DLL được một chương trình sử dụng. Công cụ cho điều đó là Dependency Walker, hay còn gọi là depend.exe. Một phiên bản của nó được bao gồm trong Visual Studio, nhưng phiên bản mới nhất có sẵn từ tác giả của nó tại http://www.dependencywalker.com/ . Nó có thể xác định tất cả các DLL đã được chỉ định tại thời điểm liên kết (cả tải sớm và tải chậm) và nó cũng có thể chạy chương trình và theo dõi bất kỳ DLL bổ sung nào mà nó tải tại thời điểm chạy.

Cập nhật 2

Tôi đã sửa lại một số văn bản trước đó để làm rõ khi đọc lại, và sử dụng các thuật ngữ nghệ thuật liên kết ngầmrõ ràng để nhất quán với MSDN.

Vì vậy, chúng ta có ba cách mà các hàm thư viện có thể được tạo sẵn để một chương trình sử dụng. Câu hỏi tiếp theo rõ ràng là: "Làm thế nào để tôi chọn cách nào?"

Liên kết tĩnh là cách phần lớn chương trình được liên kết. Tất cả các tệp đối tượng của bạn đều được liệt kê và được trình liên kết thu thập vào tệp EXE. Trong quá trình thực hiện, trình liên kết sẽ thực hiện các công việc nhỏ như sửa các tham chiếu đến các ký hiệu toàn cục để các mô-đun của bạn có thể gọi các chức năng của nhau. Các thư viện cũng có thể được liên kết tĩnh. Các tệp đối tượng tạo nên thư viện được thủ thư thu thập cùng nhau trong tệp .LIB mà trình liên kết tìm kiếm các mô-đun chứa các ký hiệu cần thiết. Một tác dụng của liên kết tĩnh là chỉ những mô-đun từ thư viện được chương trình sử dụng mới được liên kết với nó; các mô-đun khác bị bỏ qua. Ví dụ, thư viện toán học C truyền thống bao gồm nhiều hàm lượng giác. Nhưng nếu bạn liên kết chống lại nó và sử dụngcos(), bạn sẽ không nhận được bản sao mã cho sin()hoặc tan()trừ khi bạn cũng gọi các hàm đó. Đối với các thư viện lớn với nhiều tính năng phong phú, việc đưa vào các mô-đun một cách chọn lọc là rất quan trọng. Trên nhiều nền tảng như hệ thống nhúng, tổng kích thước của mã có sẵn để sử dụng trong thư viện có thể lớn so với không gian có sẵn để lưu trữ tệp thực thi trong thiết bị. Nếu không có sự bao gồm có chọn lọc, sẽ khó quản lý các chi tiết của việc xây dựng các chương trình cho các nền tảng đó.

Tuy nhiên, việc có một bản sao của cùng một thư viện trong mọi chương trình đang chạy sẽ tạo ra gánh nặng cho một hệ thống thường chạy nhiều quy trình. Với loại hệ thống bộ nhớ ảo phù hợp, các trang bộ nhớ có nội dung giống hệt nhau chỉ cần tồn tại một lần trong hệ thống, nhưng có thể được sử dụng bởi nhiều quá trình. Điều này tạo ra một lợi ích để tăng khả năng các trang chứa mã có khả năng giống với một số trang trong càng nhiều quy trình đang chạy khác càng tốt. Tuy nhiên, nếu các chương trình liên kết tĩnh với thư viện thời gian chạy, thì mỗi chương trình có một tổ hợp các chức năng khác nhau, mỗi chức năng được bố trí trong bản đồ bộ nhớ xử lý ở các vị trí khác nhau và không có nhiều trang mã có thể chia sẻ trừ khi nó là một chương trình tự nó là chạy trong nhiều hơn quá trình. Vì vậy, ý tưởng về một DLL đã đạt được một lợi thế khác.

Một DLL cho một thư viện chứa tất cả các chức năng của nó, sẵn sàng để sử dụng bởi bất kỳ chương trình khách hàng nào. Nếu nhiều chương trình tải DLL đó, tất cả chúng đều có thể chia sẻ các trang mã của nó. Mọi người đều thắng. (Chà, cho đến khi bạn cập nhật DLL với phiên bản mới, nhưng đó không phải là một phần của câu chuyện này. Địa ngục DLL của Google cho phần đó của câu chuyện.)

Vì vậy, lựa chọn lớn đầu tiên cần thực hiện khi lập kế hoạch cho một dự án mới là giữa liên kết động và liên kết tĩnh. Với liên kết tĩnh, bạn có ít tệp hơn để cài đặt và bạn không bị các bên thứ ba cập nhật DLL mà bạn sử dụng. Tuy nhiên, chương trình của bạn lớn hơn và nó không phải là công dân tốt của hệ sinh thái Windows. Với liên kết động, bạn có nhiều tệp hơn để cài đặt, bạn có thể gặp sự cố với bên thứ ba khi cập nhật DLL mà bạn sử dụng, nhưng nhìn chung bạn thân thiện hơn với các quy trình khác trên hệ thống.

Một lợi thế lớn của DLL là nó có thể được tải và sử dụng mà không cần biên dịch lại hoặc thậm chí khởi động lại chương trình chính. Điều này có thể cho phép nhà cung cấp thư viện bên thứ ba (chẳng hạn như Microsoft và C runtime) sửa lỗi trong thư viện của họ và phân phối nó. Sau khi người dùng cuối cài đặt DLL cập nhật, họ ngay lập tức nhận được lợi ích của việc sửa lỗi đó trong tất cả các chương trình sử dụng DLL đó. (Trừ khi nó phá vỡ mọi thứ. Xem DLL Hell.)

Lợi thế khác đến từ sự phân biệt giữa tải ngầm và tải rõ ràng. Nếu bạn cố gắng nhiều hơn để tải rõ ràng, thì DLL thậm chí có thể không tồn tại khi chương trình được viết và xuất bản. Điều này cho phép các cơ chế mở rộng có thể khám phá và tải các plugin chẳng hạn.


3
Xóa bài đăng của tôi và tán thành điều này, bởi vì bạn giải thích mọi thứ theo cách tốt hơn tôi làm;) Câu trả lời tuyệt vời.
vào

2
@RBerteig: Cảm ơn câu trả lời tuyệt vời của bạn. Chỉ cần sửa một chút, theo đây ( msdn.microsoft.com/en-us/library/9yd93633.aspx ), có 2 loại liên kết động với DLL, liên kết ngầm trong thời gian tảiliên kết ngầm trong thời gian chạy . Không có liên kết thời gian biên dịch . Bây giờ tôi đang tự hỏi sự khác biệt giữa Liên kết tĩnh truyền thống (liên kết đến tệp * .lib có chứa triển khai đầy đủ) và liên kết động Load-Time với một DLL (thông qua thư viện nhập) là gì?
smwikipedia

1
Tiếp tục: Ưu và nhược điểm của Liên kết tĩnhLiên kết động trong thời gian tải là gì? Có vẻ như cả 2 cách tiếp cận này đều tải tất cả các tệp cần thiết vào không gian địa chỉ khi bắt đầu quá trình. Tại sao chúng ta cần 2 trong số chúng? Cảm ơn.
smwikipedia

1
bạn có thể sử dụng một công cụ như "objdump" để xem bên trong tệp .lib và tìm xem đó là thư viện nhập hay thư viện tĩnh thực sự. trên Linux khi biên dịch chéo sang mục tiêu Windows, có thể chạy 'ar' hoặc 'nm' trên các tệp .a (phiên bản mingw của tệp .lib) và lưu ý rằng các lib nhập có tên tệp .o chung và không có mã (chỉ là một lệnh 'jmp'), trong khi các lệnh lib tĩnh có rất nhiều chức năng và mã bên trong.
don sáng

1
Chỉnh sửa nhỏ: Bạn cũng có thể liên kết ngầm trong thời gian chạy. Hỗ trợ trình liên kết cho DLL tải chậm giải thích điều này chi tiết. Điều này rất hữu ích nếu bạn muốn tự động thay đổi đường dẫn tìm kiếm DLL hoặc xử lý một cách linh hoạt lỗi độ phân giải nhập (ví dụ: để hỗ trợ các tính năng hệ điều hành mới nhưng vẫn chạy trên các phiên bản cũ hơn).
IInspectable

5

Các tệp thư viện nhập .LIB này được sử dụng trong thuộc tính dự án sau Linker->Input->Additional Dependencies, khi xây dựng một loạt các tệp dll cần thông tin bổ sung tại thời điểm liên kết được cung cấp bởi tệp .LIB thư viện nhập. Trong ví dụ dưới đây để không gặp lỗi trình liên kết, tôi cần tham chiếu đến A, B, C và D của dll thông qua các tệp lib của chúng. (lưu ý để trình liên kết tìm thấy các tệp này, bạn có thể cần đưa đường dẫn triển khai của chúng vào Linker->General->Additional Library Directoriesnếu không, bạn sẽ gặp lỗi xây dựng về việc không thể tìm thấy bất kỳ tệp lib nào được cung cấp.)

Trình liên kết-> Đầu vào-> Phụ thuộc bổ sung

Nếu giải pháp của bạn đang xây dựng tất cả các thư viện động, bạn có thể tránh được đặc tả phụ thuộc rõ ràng này bằng cách thay vào đó dựa vào các cờ tham chiếu hiển thị trong Common Properties->Framework and Referenceshộp thoại. Các cờ này dường như tự động thực hiện liên kết thay mặt bạn bằng cách sử dụng các tệp * .lib. Khung và tài liệu tham khảo

Tuy nhiên, điều này giống như nó nói Thuộc tính chung , không phải là cấu hình hoặc nền tảng cụ thể. Nếu bạn cần hỗ trợ một kịch bản xây dựng hỗn hợp như trong ứng dụng của chúng tôi, chúng tôi đã có một cấu hình xây dựng để hiển thị một bản dựng tĩnh và một cấu hình đặc biệt để xây dựng một bản dựng có giới hạn của một tập hợp con được triển khai dưới dạng thư viện động. Tôi đã sử dụng Use Library Dependency Inputs và các Link Library Dependenciescờ được đặt thành true trong nhiều trường hợp khác nhau để xây dựng mọi thứ và sau đó nhận ra để đơn giản hóa mọi thứ nhưng khi giới thiệu mã của mình cho các bản dựng tĩnh, tôi đã đưa ra rất nhiều cảnh báo về trình liên kết và quá trình xây dựng cực kỳ chậm đối với các bản dựng tĩnh. Tôi kết thúc việc giới thiệu một loạt các loại cảnh báo này ...

warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored  D.lib(JSource.obj)

Và tôi kết thúc bằng cách sử dụng đặc tả thủ công của Additional Dependenciesđể đáp ứng trình liên kết cho các bản dựng động trong khi giữ cho các trình tạo tĩnh hài lòng bằng cách không sử dụng một thuộc tính chung làm chậm chúng. Khi tôi triển khai bản dựng tập hợp con động, tôi chỉ triển khai các tệp dll vì các tệp lib này chỉ được sử dụng tại thời điểm liên kết, không phải trong thời gian chạy.


3

Có ba loại thư viện: thư viện tĩnh, thư viện chia sẻ và thư viện được tải động.

Các thư viện tĩnh được liên kết với mã ở giai đoạn liên kết, vì vậy chúng thực sự nằm trong tệp thực thi, không giống như thư viện được chia sẻ, chỉ có các phần gốc (ký hiệu) để tìm kiếm trong tệp thư viện được chia sẻ, được tải vào thời gian chạy trước chức năng chính được gọi.

Những cái được tải động giống như các thư viện được chia sẻ, ngoại trừ chúng được tải khi và nếu nhu cầu phát sinh bởi mã bạn đã viết.


@Cảm ơn zacsek. Nhưng tôi không chắc về tuyên bố của bạn về thư viện được chia sẻ.
smwikipedia

@smwikipedia: Linux có chúng, tôi sử dụng chúng, vì vậy chúng chắc chắn tồn tại. Cũng đọc: en.wikipedia.org/wiki/Library_(computing)
Zoltán Szőcs

3
Đó là một sự khác biệt tinh tế. Thư viện động và chia sẻ đều là tệp DLL. Sự khác biệt là khi chúng được tải. Thư viện chia sẻ được tải bởi hệ điều hành cùng với EXE. Thư viện động được tải bằng cách gọi mã LoadLibrary()và các API liên quan.
RBerteig

Tôi đọc từ [1] rằng DLL là cách triển khai khái niệm Thư viện được chia sẻ của Microsoft. [1]: en.wikipedia.org/wiki/Dynamic-link_library#Import_libraries
smwikipedia

Tôi không đồng ý rằng đó là một sự khác biệt nhỏ, từ chế độ xem lập trình, nó tạo ra sự khác biệt rất lớn cho dù thư viện được chia sẻ có được tải động hay không (nếu nó được tải động, thì bạn phải thêm mã boilerplate để truy cập các hàm).
Zoltán Szőcs

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.