Tôi đã đọc về Tùy chọn của GCC cho các Quy ước tạo mã , nhưng không thể hiểu "Tạo mã độc lập với vị trí (PIC)" là gì. Xin cho một ví dụ để giải thích cho tôi ý nghĩa của nó.
Tôi đã đọc về Tùy chọn của GCC cho các Quy ước tạo mã , nhưng không thể hiểu "Tạo mã độc lập với vị trí (PIC)" là gì. Xin cho một ví dụ để giải thích cho tôi ý nghĩa của nó.
Câu trả lời:
Mã độc lập vị trí có nghĩa là mã máy được tạo không phụ thuộc vào vị trí tại một địa chỉ cụ thể để hoạt động.
Ví dụ, bước nhảy sẽ được tạo ra tương đối chứ không phải là tuyệt đối.
Lắp ráp giả:
PIC: Điều này sẽ hoạt động cho dù mã ở địa chỉ 100 hoặc 1000
100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP
Non-PIC: Điều này sẽ chỉ hoạt động nếu mã ở địa chỉ 100
100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP
EDIT: Đáp lại bình luận.
Nếu mã của bạn được biên dịch bằng -fPIC, nó phù hợp để đưa vào thư viện - thư viện phải có thể được di chuyển từ vị trí ưa thích trong bộ nhớ sang địa chỉ khác, có thể có một thư viện đã được tải tại địa chỉ mà thư viện của bạn thích.
-fPIC
khi biên dịch chương trình hoặc thư viện tĩnh, bởi vì chỉ có một chương trình chính sẽ tồn tại trong một quy trình, do đó không cần phải di chuyển thời gian chạy. Trên một số hệ thống, các chương trình vẫn được đặt độc lập để tăng cường bảo mật.
Tôi sẽ cố gắng giải thích những gì đã được nói một cách đơn giản hơn.
Bất cứ khi nào một lib chia sẻ được tải, trình tải (mã trên HĐH tải bất kỳ chương trình nào bạn chạy) sẽ thay đổi một số địa chỉ trong mã tùy thuộc vào nơi đối tượng được tải.
Trong ví dụ trên, "111" trong mã không phải PIC được trình tải ghi vào lần đầu tiên được tải.
Đối với các đối tượng không được chia sẻ, bạn có thể muốn nó giống như vậy bởi vì trình biên dịch có thể thực hiện một số tối ưu hóa trên mã đó.
Đối với đối tượng được chia sẻ, nếu một quá trình khác muốn "liên kết" đến mã đó, anh ta phải đọc nó đến cùng địa chỉ ảo hoặc "111" sẽ không có ý nghĩa. nhưng không gian ảo đó có thể đã được sử dụng trong quy trình thứ hai.
Whenever a shared lib is loaded, the loader changes some addresses in the code depending on where the object was loaded to.
Tôi nghĩ điều này không đúng nếu được biên dịch bằng -fpic và lý do tại sao -fpic tồn tại tức là vì lý do hiệu suất hoặc vì bạn có trình tải không thể di dời hoặc vì bạn cần nhiều bản sao ở các vị trí khác nhau hoặc vì nhiều lý do khác.
Mã được tích hợp vào các thư viện dùng chung thường là mã độc lập với vị trí, để thư viện dùng chung có thể dễ dàng được tải tại (nhiều hơn hoặc ít hơn) bất kỳ địa chỉ nào trong bộ nhớ. Các -fPIC
tùy chọn đảm bảo rằng GCC tạo mã như vậy.
-fPIC
cờ? nó không được liên kết với chương trình? khi chương trình đang chạy, hệ điều hành sẽ tải nó vào bộ nhớ. Tui bỏ lỡ điều gì vậy?
-fPIC
cờ được sử dụng, để đảm bảo lib này có thể được tải đến bất kỳ địa chỉ ảo nào trong quá trình liên kết nó? xin lỗi vì nhận xét kép 5 phút trôi qua không thể chỉnh sửa ý kiến trước đó.
libwotnot.so
) và liên kết với nó ( -lwotnot
). Trong khi liên kết, bạn không cần phải bận tâm -fPIC
. Nó từng là trường hợp khi xây dựng thư viện dùng chung, bạn cần đảm bảo -fPIC
được sử dụng cho tất cả các tệp đối tượng được tích hợp vào thư viện dùng chung. Các quy tắc có thể đã thay đổi vì trình biên dịch xây dựng với mã PIC theo mặc định, những ngày này. Vì vậy, những gì quan trọng 20 năm trước, và có thể đã quan trọng 7 năm trước, ngày nay ít quan trọng hơn, tôi tin. Địa chỉ bên ngoài nhân o / s là 'luôn luôn' địa chỉ ảo '.
-fPIC
. Không vượt qua cờ này, mã được tạo khi xây dựng .so cần được tải đến các địa chỉ ảo cụ thể có thể được sử dụng?
Thêm nữa ...
Mọi quy trình đều có cùng một không gian địa chỉ ảo (Nếu ngẫu nhiên hóa địa chỉ ảo bị dừng bằng cách sử dụng cờ trong hệ điều hành linux) (Để biết thêm chi tiết Tắt và chỉ bật lại ngẫu nhiên bố cục không gian địa chỉ địa chỉ )
Vì vậy, nếu một exe của nó không có liên kết chia sẻ (kịch bản giả thuyết), thì chúng ta luôn có thể cung cấp cùng một địa chỉ ảo cho cùng một lệnh asm mà không gây hại.
Nhưng khi chúng tôi muốn liên kết đối tượng chia sẻ với exe, thì chúng tôi không chắc địa chỉ bắt đầu được gán cho đối tượng chia sẻ vì nó sẽ phụ thuộc vào thứ tự các đối tượng được chia sẻ được liên kết. Điều đó được nói, lệnh asm bên trong .so sẽ luôn có địa chỉ ảo khác nhau tùy thuộc vào quá trình liên kết của nó với.
Vì vậy, một quy trình có thể cung cấp địa chỉ bắt đầu cho .so là 0x45678910 trong không gian ảo của chính nó và quy trình khác đồng thời có thể cung cấp địa chỉ bắt đầu là 0x12131415 và nếu chúng không sử dụng địa chỉ tương đối, thì cũng không hoạt động.
Vì vậy, họ luôn phải sử dụng chế độ địa chỉ tương đối và do đó tùy chọn fpic.
Liên kết đến một chức năng trong thư viện động được giải quyết khi thư viện được tải hoặc trong thời gian chạy. Do đó, cả tệp thực thi và thư viện động đều được tải vào bộ nhớ khi chương trình được chạy. Địa chỉ bộ nhớ mà thư viện động được tải không thể được xác định trước, bởi vì một địa chỉ cố định có thể xung đột với một thư viện động khác yêu cầu cùng một địa chỉ.
Có hai phương pháp thường được sử dụng để xử lý vấn đề này:
1. Định vị. Tất cả các con trỏ và địa chỉ trong mã được sửa đổi, nếu cần, để phù hợp với địa chỉ tải thực tế. Việc di dời được thực hiện bởi trình liên kết và trình tải.
2. Mã độc lập vị trí. Tất cả các địa chỉ trong mã đều liên quan đến vị trí hiện tại. Các đối tượng được chia sẻ trong các hệ thống giống như Unix sử dụng mã độc lập theo vị trí theo mặc định. Điều này kém hiệu quả hơn so với việc di chuyển nếu chương trình chạy trong một thời gian dài, đặc biệt là ở chế độ 32 bit.
Tên " mã độc lập vị trí " thực sự ngụ ý sau:
Phần mã không chứa địa chỉ tuyệt đối cần di chuyển, mà chỉ có địa chỉ tự tương đối. Do đó, phần mã có thể được tải tại một địa chỉ bộ nhớ tùy ý và được chia sẻ giữa nhiều quy trình.
Phần dữ liệu không được chia sẻ giữa nhiều quy trình vì nó thường chứa dữ liệu có thể ghi. Do đó, phần dữ liệu có thể chứa con trỏ hoặc địa chỉ cần di chuyển.
Tất cả các chức năng công cộng và dữ liệu công khai có thể bị ghi đè trong Linux. Nếu một hàm trong tệp thực thi chính có cùng tên với một hàm trong đối tượng dùng chung, thì phiên bản chính sẽ được ưu tiên, không chỉ khi được gọi từ chính, mà cả khi được gọi từ đối tượng chia sẻ. Tương tự, khi một biến toàn cục trong main có cùng tên với một biến toàn cục trong đối tượng dùng chung, thì thể hiện trong main sẽ được sử dụng, ngay cả khi được truy cập từ đối tượng chia sẻ.
Cái gọi là sự xen kẽ biểu tượng này nhằm bắt chước hành vi của các thư viện tĩnh.
Một đối tượng được chia sẻ có một bảng các con trỏ tới các chức năng của nó, được gọi là bảng liên kết thủ tục (PLT) và một bảng các con trỏ tới các biến của nó được gọi là bảng bù toàn cục (GOT) để thực hiện tính năng "ghi đè" này. Tất cả các truy cập vào các hàm và các biến công khai đều đi qua bảng này.
ps Trong trường hợp không thể tránh liên kết động, có nhiều cách khác nhau để tránh các tính năng định thời gian của mã độc lập vị trí.
Bạn có thể đọc thêm từ bài viết này: http://www.agner.org/optizes/optimizing_cpp.pdf
Một bổ sung nhỏ cho các câu trả lời đã được đăng: các tệp đối tượng không được biên dịch thành vị trí độc lập có thể định vị lại được; chúng chứa các mục nhập bảng di dời.
Các mục này cho phép trình tải (bit mã đó tải chương trình vào bộ nhớ) để ghi lại các địa chỉ tuyệt đối để điều chỉnh cho địa chỉ tải thực tế trong không gian địa chỉ ảo.
Một hệ điều hành sẽ cố gắng chia sẻ một bản sao của "thư viện đối tượng chia sẻ" được tải vào bộ nhớ với tất cả các chương trình được liên kết với cùng thư viện đối tượng được chia sẻ đó.
Do không gian địa chỉ mã (không giống như các phần của không gian dữ liệu) không cần phải liền kề nhau và vì hầu hết các chương trình liên kết đến một thư viện cụ thể đều có cây phụ thuộc thư viện khá cố định, nên điều này thành công hầu hết thời gian. Trong những trường hợp hiếm hoi có sự khác biệt, vâng, có thể cần phải có hai hoặc nhiều bản sao của thư viện đối tượng dùng chung trong bộ nhớ.
Rõ ràng, bất kỳ nỗ lực nào để ngẫu nhiên hóa địa chỉ tải của thư viện giữa các chương trình và / hoặc phiên bản chương trình (để giảm khả năng tạo mẫu có thể khai thác) sẽ khiến các trường hợp đó trở nên phổ biến, không phải là hiếm, do đó, một hệ thống đã kích hoạt khả năng này, người ta phải cố gắng biên dịch tất cả các thư viện đối tượng dùng chung thành độc lập với vị trí.
Vì các cuộc gọi vào các thư viện này từ phần thân của chương trình chính cũng sẽ được thực hiện có thể di chuyển được, điều này làm cho ít có khả năng một thư viện chia sẻ sẽ phải được sao chép.