Trình liên kết và bộ tải là gì? Họ làm việc như thế nào?


8

Tôi đang cố gắng để hiểu những thứ như trình liên kết và bộ tải tốt hơn.

Họ thuộc về lĩnh vực khoa học máy tính nào? Trình biên dịch, hệ điều hành, kiến ​​trúc máy tính?

Trường hợp các trình liên kết và bộ tải phát huy tác dụng trong quá trình phát triển?


Tôi đã chỉnh sửa câu hỏi này để biết thêm về trình liên kết và trình tải là gì về những nơi khác để tìm hiểu về chúng, vì trang web này là nơi thích hợp cho loại điều đó. Nếu có những nguồn lực tốt ngoài kia, hãy để chúng nổi lên một cách tự nhiên thông qua các câu trả lời thay vì chỉ cố gắng xây dựng một danh sách các tài nguyên bên ngoài.
Adam Lear

9
Không xúc phạm, nhưng hãy chắc chắn đọc các bài viết trên wikipedia, vì chúng có thể giúp tập trung câu hỏi của bạn: en.wikipedia.org/wiki/Linker_%28computing%29en.wikipedia.org/wiki/Loader_%28computing%29
Eric Wilson

Không chắc chắn tại sao tôi lại bỏ phiếu - Tôi nghĩ câu hỏi này có thể giúp những người mới khác cũng muốn biết điều này - cùng với tôi nhận được một số hướng dẫn. Đây có phải là một câu hỏi không progrmming?
Nishant

2
Tôi có thể thấy số phiếu giảm cho câu hỏi này khá rộng và không cho thấy nhiều công việc được thực hiện để tự mình tìm ra câu trả lời.
JB King

Câu trả lời:


17

Các chính xác mối quan hệ thay đổi một chút. Để bắt đầu, tôi sẽ xem xét (gần) mô hình đơn giản nhất có thể, được sử dụng bởi một cái gì đó như MS-DOS, nơi một tệp thực thi sẽ luôn được liên kết tĩnh. Vì lợi ích của ví dụ, chúng ta hãy xem xét kinh điển "Xin chào, thế giới!" chương trình, mà chúng tôi sẽ giả sử được viết bằng C.

Trình biên dịch

Trình biên dịch sẽ biên dịch điều này thành một vài phần. Nó sẽ lấy chuỗi ký tự "Xin chào, Thế giới!" Và đặt nó vào một phần được đánh dấu là dữ liệu không đổi và nó sẽ tổng hợp một tên cho chuỗi cụ thể đó (ví dụ: "$ L1"). Nó sẽ biên dịch cuộc gọi printfsang một phần khác được đánh dấu là mã. Trong trường hợp này, nó sẽ nói tên là main(hoặc, thường xuyên, _main). Nó cũng sẽ có một cái gì đó để nói đoạn mã này dài N byte và (quan trọng) có chứa một lệnh gọi đến printfoffset M trong mã đó.

Trình liên kết

Khi trình biên dịch được thực hiện xong, trình liên kết sẽ chạy. Nó thường được coi là một phần của chuỗi công cụ phát triển (mặc dù có trường hợp ngoại lệ - MS-DOS được sử dụng để bao gồm một trình liên kết, mặc dù nó hiếm khi được sử dụng). Mặc dù nó thường không hiển thị bên ngoài, nhưng thông thường nó sẽ được thông qua một số đối số dòng lệnh, một chỉ định một tệp đối tượng chứa một số mã khởi động và một chỉ định khác bất kỳ tệp nào chứa thư viện chuẩn C.

Sau đó, trình liên kết sẽ xem xét tệp đối tượng chứa mã khởi động và thấy rằng nó dài 1112 byte và có một lệnh gọi _mainở offset 784 trong đó.

Dựa vào đó, nó sẽ bắt đầu xây dựng một bảng biểu tượng. Nó sẽ có một mục ghi ".startup" (hoặc bất kỳ tên nào) dài 1112 byte và (cho đến nay) không có gì đề cập đến tên đó. Nó sẽ có một mục khác nói "printf" là một độ dài không xác định hiện tại, nhưng nó được gọi từ ".startup + 784".

Sau đó, nó sẽ quét qua thư viện được chỉ định (hoặc thư viện) để cố gắng tìm các định nghĩa về tên trong bảng ký hiệu hiện chưa được xác định - trong trường hợp này printf. Nó sẽ tìm thấy tệp đối tượng cho printf nói rằng nó dài 4087 byte và có các tham chiếu đến các thói quen khác để thực hiện những việc như chuyển đổi một int thành một chuỗi, cũng như những thứ như putchar(hoặc có thể fputc) để viết chuỗi kết quả vào đầu ra tập tin.

Trình liên kết sẽ quét lại để cố gắng tìm các định nghĩa của các ký hiệu đó, theo cách đệ quy, cho đến khi đạt được một trong hai kết luận: đó là tìm các định nghĩa của tất cả các ký hiệu, hoặc nếu không có ký hiệu mà nó không thể tìm thấy định nghĩa.

Nếu nó tìm thấy một tham chiếu nhưng không có định nghĩa, nó sẽ dừng lại và đưa ra một thông báo lỗi thường nói điều gì đó về "XXX bên ngoài không xác định", và bạn sẽ phải tìm ra thư viện hoặc tệp đối tượng nào khác mà bạn cần liên kết .

Nếu nó tìm thấy định nghĩa của tất cả các biểu tượng, nó sẽ chuyển sang giai đoạn tiếp theo: nó đi qua danh sách các địa điểm đề cập đến từng biểu tượng và nó sẽ điền vào địa chỉ nơi biểu tượng đó được đưa vào bộ nhớ, vì vậy (ví dụ: ) nơi mã khởi động gọi main, nó sẽ điền địa chỉ 1112làm địa chỉ chính. Khi hoàn thành tất cả, nó sẽ ghi tất cả mã và dữ liệu vào một tệp thực thi.

Có một vài chi tiết nhỏ khác có thể được đề cập: thông thường sẽ giữ riêng mã và dữ liệu và sau khi hoàn tất, chúng sẽ đặt tất cả chúng lại với nhau tại (nhiều hoặc ít) địa chỉ liên tiếp (ví dụ: tất cả các phần mã, sau đó tất cả các phần của dữ liệu). Thông thường cũng sẽ có một số quy tắc về cách kết hợp các định nghĩa cho phần / phân đoạn - ví dụ: nếu các tệp đối tượng khác nhau đều có phân đoạn mã, thì nó sẽ chỉ sắp xếp các đoạn mã lần lượt. Nếu hai hoặc nhiều chuỗi ký tự giống hệt nhau (hoặc các hằng số khác) được xác định, thông thường nó sẽ hợp nhất các chuỗi đó lại với nhau để tất cả chúng tham chiếu đến cùng một vị trí. Cũng có một vài quy tắc phải làm gì khi / nếu nó tìm thấy các định nghĩa trùng lặp của cùng một biểu tượng. Trong một trường hợp điển hình, đây đơn giản sẽ là một lỗi. Trong một vài trường hợp, nó sẽ có những thứ như "nếu ai đó cũng định nghĩa nó, đừng coi đó là lỗi - chỉ cần sử dụng định nghĩa đó thay vì định nghĩa này.

Khi nó có các mục cho tất cả các biểu tượng, trình liên kết phải sắp xếp các "mảnh" và gán địa chỉ cho chúng. Thứ tự sắp xếp các mảnh sẽ thay đổi phần nào - thông thường sẽ có một số cờ về các loại mảnh khác nhau, vì vậy (ví dụ) tất cả các dữ liệu không đổi kết thúc cạnh nhau, tất cả các đoạn mã bên cạnh nhau và như vậy. Trong hệ thống đơn giản giống như MS-DOS của chúng tôi, hầu hết điều này sẽ không thành vấn đề.

Máy xúc lật

Điều đó đưa chúng ta đến giai đoạn tiếp theo: bộ nạp. bộ nạp thường là một phần của hệ điều hành, tải bộ thực thi. Trong các phiên bản cổ (ví dụ: tệp CP / M, MS_DOS .com, trình tải chỉ đọc dữ liệu từ tệp thực thi vào bộ nhớ, sau đó bắt đầu thực thi tại một số địa chỉ. Các trình tải gần đây hơn (ví dụ: đối với tệp MS-DOS .exe) sẽ bắt đầu (nhiều hơn hoặc ít hơn) theo cùng một cách: đọc một tệp vào bộ nhớ. Tuy nhiên, trong trường hợp này, dựa trên các mục được đặt bởi trình liên kết, nó sẽ "sửa" mọi tham chiếu tuyệt đối trong tệp thực thi để tham chiếu đến đúng địa chỉ. Trong ví dụ trên, mã khởi động của chúng tôi được đề cập đếnmain tại địa chỉ 1112, nhưng tệp thực thi đang được tải tại địa chỉ cơ sở của (giả sử) 4000. Trong trường hợp này, trình tải sẽ sửa địa chỉ đó để tham khảo 5112. Tuy nhiên, trong hệ thống đơn giản này, trình tải vẫn là một đoạn mã khá đơn giản - về cơ bản chỉ cần duyệt qua danh sách các địa điểm và thêm địa chỉ cơ sở vào từng địa chỉ.

Bây giờ, hãy xem xét một hệ điều hành hiện đại hơn một chút hỗ trợ một cái gì đó như các tệp đối tượng được chia sẻ hoặc DLL. Điều này về cơ bản chuyển một số công việc từ trình liên kết sang trình tải. Cụ thể, đối với một biểu tượng được xác định trong .so / DLL, trình liên kết sẽ không cố tự gán địa chỉ.

Thay vào đó, nó sẽ tạo ra một mục nhập bảng biểu tượng về cơ bản là "được xác định trong tệp .so / DLL XXX". Khi trình liên kết ghi tệp thực thi, hầu hết các mục trong bảng ký hiệu này về cơ bản sẽ chỉ được sao chép vào tệp thực thi, nói rằng "ký hiệu XXX được xác định trong tệp YYY". Sau đó, đến bộ tải để tìm tệp YYY và địa chỉ ký hiệu XXX trong tệp đó và điền địa chỉ chính xác vào bất cứ nơi nào nó được sử dụng trong tệp thực thi. Giống như trong trình liên kết, điều này sẽ được đệ quy, vì vậy DLL A có thể đề cập đến các ký hiệu trong DLL B, có thể đề cập đến DLL C, v.v. Mặc dù chuỗi từ thực thi đến tất cả các định nghĩa có thể dài, ý tưởng cơ bản của quy trình này khá đơn giản - quét qua danh sách các tham chiếu bên ngoài và tìm định nghĩa cho từng định nghĩa. Cũng lưu ý rằng trong hầu hết các trường hợp,

Một lần nữa, có một số bit và mảnh linh tinh để xem xét. Ví dụ, việc chia sẻ thường sẽ chỉ xảy ra trên cơ sở từng phần, chứ không phải theo từng tệp. Ví dụ, nếu một tệp có một số mã và một số dữ liệu (không cố định), tất cả các quy trình sẽ chia sẻ cùng một phần mã, nhưng mỗi quy trình sẽ có bản sao dữ liệu riêng.


Các trình tải đơn giản nhất thậm chí không có bất kỳ khả năng sửa lỗi nào. Tôi đang nghĩ về TRS-80 cũ. Các tệp thực thi chỉ đơn giản là địa chỉ / kích thước / dữ liệu, được lặp lại khi cần thiết (kích thước là 1 byte để bạn thường cần nhiều khối.) Bạn thậm chí có thể tạo màn hình giật gân bằng cách chỉ định địa chỉ tải trong bộ nhớ video.
Loren Pechtel

1

Để tìm hiểu thêm về các trình liên kết, tôi nghĩ rằng chúng thường sẽ được thảo luận kết hợp với trình biên dịch. Chúng là để đan các mô-đun khác nhau của bạn lại với nhau thành một đơn vị gắn kết, hoàn thiện các địa chỉ trong mã đó. Một số thậm chí có thể cố gắng thực hiện tối ưu hóa.

Để tìm hiểu thêm về trình tải, tôi nghĩ rằng chúng thường sẽ được thảo luận kết hợp với trình biên dịch viết cho các kiến ​​trúc cụ thể trừ khi bạn có nghĩa là trình tải như một từ đồng nghĩa với trình liên kết. Tôi đang nghĩ về trình tải như là một phần của tiêu đề tệp thực thi cho hệ điều hành biết cách mở và thực thi phần mềm đã biên dịch của bạn.

Tôi đồng ý rằng việc đọc các bài viết trên Wikipedia có thể sẽ truyền đạt nhiều thông tin hơn bạn đang tìm kiếm. Về nơi họ phát triển ... nói chung, họ nằm ngoài tầm kiểm soát của dự án và là một phần của việc lựa chọn hệ điều hành và gói phát triển bạn chọn sử dụng. Rất hiếm khi bạn sử dụng (ví dụ) MSVC nhưng muốn chạy trình liên kết dựa trên GCC ... thậm chí có thể không khả thi. Vị trí DUY NHẤT tôi từng sử dụng một trình liên kết không chuẩn là tại IBM khi chúng tôi đang sử dụng các bản sao phát triển.

Nếu bạn có câu hỏi cụ thể hơn, cụ thể hơn về các chủ đề này, tôi nghĩ bạn sẽ tìm thấy câu trả lời tốt hơn nhiều.


1

Trình liên kết và bộ tải là hai khái niệm liên quan nhưng riêng biệt.

Trình liên kết là một phần của lý thuyết trình biên dịch. Khi bạn biên dịch một dự án được tạo thành từ nhiều hơn một mô-đun (tệp mã nguồn), trình biên dịch sẽ xuất ra một tệp trung gian duy nhất cho mỗi mô-đun nguồn. Điều này có một số lợi ích, một trong số đó là nếu bạn chỉ thay đổi một tệp và sau đó phải biên dịch lại, bạn không phải xây dựng lại toàn bộ dự án khi bạn chỉ thực hiện một thay đổi cục bộ.

Nhưng điều này có nghĩa là nếu bạn có mã trong một mô-đun gọi một hàm trong một mô-đun khác, trình biên dịch không thể tạo một CALLlệnh cho nó, bởi vì nó không có vị trí của hàm kia. Nó nằm trong một tệp trung gian khác và vị trí chính xác của hàm có thể thay đổi nếu bạn thực hiện thay đổi cục bộ thành tệp nguồn của trung gian đó và biên dịch lại nó. Vì vậy, thay vào đó, nó chèn một "mã thông báo tham chiếu bên ngoài" (chính xác đó là gì hoặc trông như thế nào không quan trọng, chỉ nghĩ về nó như một khái niệm trừu tượng) có nội dung "Tôi cần chức năng này có địa chỉ chính xác mà tôi không biết tại thời điểm này."

Khi mọi thứ đã được biên dịch thành các tệp trung gian, trình liên kết là thứ hoàn thành công việc. Nó đi qua tất cả các tệp trung gian và liên kết chúng lại với nhau thành một tệp nhị phân cuối cùng. Vì nó đặt mọi thứ lại với nhau, nó biết địa chỉ thực tế của tất cả các hàm và do đó, nó có thể thay thế các mã thông báo tham chiếu bên ngoài bằng các CALLhướng dẫn thực tế đến các vị trí chính xác trong nhị phân.

Trình tải, mặt khác, thuộc về hệ điều hành, không phải trình biên dịch. Công việc của nó là tải nhị phân vào bộ nhớ để nó có thể thực thi và kết thúc quá trình liên kết, vì trình liên kết chỉ có thể giải quyết mã mà nó biết. Nếu chương trình của bạn đang sử dụng bất kỳ DLL nào, chúng nằm ngoài cả nhị phân được biên dịch, vì vậy trình liên kết không biết địa chỉ của chúng. Nó để các mã thông báo tham chiếu bên ngoài ở dạng nhị phân cuối cùng ở định dạng mà trình tải của hệ điều hành biết và sau đó trình tải đi qua và khớp các mã thông báo này với các địa chỉ chức năng thực tế trong DLL khi mọi thứ đã được tải vào bộ nhớ.


-1

Máy tính làm việc cơ bản với số nhị phân.
Mọi người nói ngôn ngữ mẹ đẻ của họ.

Làm, ngôn ngữ lập trình là để giao tiếp giữa người và máy tính.
Nếu bạn nói: Thêm 2 và 3 rồi trừ 1 từ nó, tôi nghi ngờ rằng máy tính sẽ hiểu bất cứ điều gì (có thể trong một số ngôn ngữ lập trình, nó sẽ như vậy).

Vì vậy, bạn cần dịch mã nguồn của mình sang định dạng mà máy tính hiểu, vì vậy bạn cần một trình biên dịch, dịch một ngôn ngữ lập trình để gọi là mã đối tượng. Nhưng mã đối tượng chưa phải là ngôn ngữ mà máy tính hiểu và thực thi trực tiếp. Vì vậy, nó cần một trình liên kết sẽ tạo ra một tệp thực thi chứa các hướng dẫn bằng ngôn ngữ máy; một ngôn ngữ máy là một tập hợp các hoạt động được mã hóa thành các số nhị phân mà bộ xử lý hiểu được. Tất cả các hướng dẫn nhị phân có cấu trúc của nó và được xuất bản bởi một nhà sản xuất bộ xử lý. Bạn có thể tìm nó trên trang web của Intel và xem chúng trông như thế nào.


Dưới đây là hướng dẫn của nhà phát triển từ AMD: developer.amd.com/documentation/guides/Pages/default.aspx
spc
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.