2013-10-19 29 views
5

Cách tôi hiểu quy trình biên dịch:Liên kết hoạt động chính xác như thế nào?

1) Tiền xử lý trước: Tất cả macro của bạn được thay thế bằng giá trị thực, tất cả nhận xét đều bị xóa, v.v. Thay thế #include statements bằng văn bản chữ của tệp bạn ' đã bao gồm.

2) Biên soạn: Không khoan sâu quá vào đây, nhưng kết quả là tệp lắp ráp cho bất kỳ kiến ​​trúc nào bạn đang sử dụng.

3) Lắp ráp: Đưa tệp lắp ráp và chuyển đổi nó thành các hướng dẫn nhị phân, tức là, mã máy.

4) Liên kết: Đây là nơi tôi đang bối rối. Tại thời điểm này bạn có một tập tin thực thi. Nhưng nếu bạn thực sự chạy mà thực thi những gì sẽ xảy ra? Có phải vấn đề mà bạn có thể đã bao gồm các tệp * .h và những tệp chỉ chứa các nguyên mẫu chức năng không? Vì vậy, nếu bạn thực sự gọi một trong những chức năng từ những tập tin đó, nó sẽ không có một định nghĩa và chương trình của bạn sẽ sụp đổ?

Nếu trường hợp đó xảy ra, chính xác thì liên kết làm, dưới mui xe là gì? Làm thế nào để nó tìm thấy tệp .c được liên kết với .h mà bạn đã bao gồm, và nó chèn nó vào mã máy của bạn như thế nào? Nó không phải trải qua toàn bộ quá trình biên dịch một lần nữa cho tập tin đó?

Bây giờ, tôi đã hiểu rằng có hai loại liên kết, động và tĩnh. Là tĩnh khi bạn thực sự biên dịch lại nguồn gốc của thư viện cho mỗi tập tin thực thi bạn tạo ra? Tôi không hoàn toàn hiểu cách liên kết động sẽ hoạt động. Vì vậy, bạn biên dịch một thư viện thực thi được chia sẻ bởi tất cả các quy trình của bạn sử dụng nó? Làm thế nào là có thể, chính xác? Nó sẽ không được bên ngoài không gian địa chỉ của các quá trình cố gắng truy cập nó? Ngoài ra, để liên kết động, bạn vẫn không cần phải biên dịch thư viện vào một thời điểm nào đó? Có phải chỉ cần ngồi đó liên tục trong bộ nhớ chờ đợi để được sử dụng? Khi nào nó được biên dịch?

Bạn có thể thực hiện các bước trên và xóa tất cả các hiểu lầm, giả định sai và thay thế giải thích đúng của bạn không?

+0

có thể trùng lặp của [Cách liên kết C++ hoạt động trong thực tế?] (Http://stackoverflow.com/questions/12122446/how-does-c-linking-work-in-practice) –

Trả lời

11

At this point you have an executable.

Không. Tại thời điểm này, bạn có tệp đối tượng không tự thực thi được.

But if you actually run that executable what happens?

Something như thế này:

h2co3-macbook:~ h2co3$ clang -Wall -o quirk.o quirk.c -c 
h2co3-macbook:~ h2co3$ chmod +x quirk.o 
h2co3-macbook:~ h2co3$ ./quirk.o 
-bash: ./quirk.o: Malformed Mach-o file 

tôi nói bạn nó không phải là một thực thi.

Is the problem that you may have included *.h files, and those only contain function prototypes?

Khá gần, thực sự. Một đơn vị dịch (tập tin .c) (thường) được chuyển thành mã lắp ráp/máy đại diện cho những gì nó làm. Nếu nó gọi một hàm, thì sẽ có một tham chiếu đến hàm đó trong tệp, nhưng không có định nghĩa.

So if you actually call one of the functions from those files, it won't have a definition and your program will crash?

Như tôi đã nói, nó thậm chí sẽ không chạy. Hãy để tôi lặp lại: một tệp đối tượng là không thể thực thi được.

what exactly does linking do, under the hood? How does it find the .c file associated with the .h that you included [...]

Không. Nó tìm kiếm các tệp đối tượng khác được tạo từ các tệp .c và cuối cùng là các thư viện (về cơ bản chỉ là các tập hợp các tệp đối tượng khác).

Và nó tìm thấy chúng bởi vì bạn nói với nó những gì cần tìm. Giả sử bạn có một dự án bao gồm hai file .c mà gọi chức năng của nhau, điều này sẽ không làm việc:

gcc -c file1.c -o file1.o 
gcc -c file2.c -o file2.o 
gcc -o my_prog file1.o 

Nó sẽ thất bại với một lỗi mối liên kết: các mối liên kết sẽ không tìm thấy định nghĩa của các hàm được triển khai trong file2.c (và file2.o). Nhưng điều này sẽ làm việc: file

gcc -c file1.c -o file1.o 
gcc -c file2.c -o file2.o 
gcc -o my_prog file1.o file2.o 

[...] and how does it inject that into your machine code?

Object chứa tài liệu tham khảo còn sơ khai (thường là dưới hình thức địa chỉ điểm vào chức năng hoặc rõ ràng, tên con người có thể đọc được) đến các chức năng mà họ gọi. Sau đó, trình liên kết xem xét từng thư viện và tệp đối tượng, tìm các tham chiếu (, phát ra lỗi nếu không tìm thấy định nghĩa hàm), sau đó thay thế các tham chiếu sơ khai bằng các hướng dẫn mã máy "thực sự gọi hàm này". (Vâng, đây phần lớn là đơn giản, nhưng nếu không có bạn hỏi về một kiến ​​trúc cụ thể và một trình biên dịch cụ thể/mối liên kết, thật khó để nói chính xác hơn ...)

Is static when you actually recompile the source of the library for every executable you create?

số liên kết tĩnh có nghĩa là mã máy của các tệp đối tượng của thư viện thực sự được sao chép/hợp nhất vào tệp thực thi cuối cùng của bạn. Liên kết động có nghĩa là một thư viện được nạp vào bộ nhớ một lần, sau đó các tham chiếu hàm stub nói trên được giải quyết bởi hệ điều hành khi tệp thực thi của bạn được khởi chạy. Không có mã máy nào từ thư viện sẽ được sao chép vào tệp thực thi cuối cùng của bạn. (Vì vậy, ở đây, mối liên kết trong chuỗi công cụ chỉ thực hiện một phần công việc.)

Sau đây có thể giúp bạn đạt được giác ngộ: nếu bạn liên kết tĩnh một tệp thực thi, nó sẽ được khép kín. Nó sẽ chạy bất cứ nơi nào (trên một kiến ​​trúc tương thích anyway). Nếu bạn liên kết nó động, nó sẽ chỉ chạy trên một máy nếu máy đó có tất cả các thư viện được cài đặt mà chương trình tham chiếu.

So you compile one executable library that is shared by all of your processes that use it? How is that possible, exactly? Wouldn't it be outside of the address space of the processes trying to access it?

Thành phần liên kết/bộ tải động của HĐH sẽ xử lý tất cả.

Also, for dynamic linking, don't you still need to compile the library at some juncture in time?

Như tôi đã đề cập: có, nó đã được biên soạn rồi. Sau đó, nó được tải tại một số điểm (thường là khi nó lần đầu tiên được sử dụng) vào bộ nhớ.

When is it compiled?

Một thời gian trước khi có thể sử dụng. Thông thường, một thư viện được biên dịch, sau đó cài đặt vào một vị trí trên hệ thống của bạn để hệ điều hành và trình biên dịch/liên kết biết về sự tồn tại của nó, sau đó bạn có thể bắt đầu biên dịch (um, liên kết) các chương trình sử dụng thư viện đó. Không sớm hơn.

+0

Câu trả lời sáng chói! Cảm ơn. Tất nhiên bây giờ tôi có theo dõi câu hỏi nếu bạn sẽ bắt buộc: - Bạn nói nếu nó gọi một chức năng, sẽ có một tham chiếu đến một chức năng. Vì vậy, các tập tin đối tượng theo nghĩa đen chỉ chứa một số loại biểu tượng có thể được giải quyết khi liên kết? Nó không phải là một hướng dẫn máy thực tế? Giống như nó chỉ nói "printf" hoặc một cái gì đó như thế? Bởi vì nó không thể biết trước khi tay trong bộ nhớ mà chức năng sẽ là ... – ordinary

+0

- Trong mô hình liên kết động, từ lời giải thích của bạn có vẻ như thực thi cuối cùng sẽ có các biểu tượng chưa được giải quyết.Vì vậy, khi bạn tải chương trình vào bộ nhớ, điều đó có nghĩa là có một bước trung gian của hệ điều hành trước khi chương trình thực sự được nạp, trong đó nó xem xét tất cả các ký hiệu chưa được giải quyết và cung cấp cho chúng địa chỉ chữ cho các chức năng đó? Điều gì sẽ xảy ra nếu bạn gọi một hàm không tồn tại? Đâu là điểm thất bại? – ordinary

+1

@ordinary Có, nó chứa các ký hiệu sơ khai ("biểu tượng" - đó chính xác là những gì chúng được gọi). Đây là một phần lý do tại sao các tệp này không thực sự thực thi được. –

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