Tùy chọn GCC -fPIC


Câu trả lời:


525

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.


36
Ví dụ này là rõ ràng, nhưng là một người dùng, điều gì sẽ là sự khác biệt nếu tôi tạo một tệp labrary (.so) được chia sẻ mà không có tùy chọn? Có một số trường hợp mà không có -fPIC thì lib của tôi sẽ không hợp lệ?
Narek

16
Có, xây dựng thư viện dùng chung không phải là PIC có thể là một lỗi.
John Zwinck

92
Để cụ thể hơn, thư viện chia sẻ được cho là được chia sẻ giữa các quy trình, nhưng không phải lúc nào cũng có thể tải thư viện ở cùng một địa chỉ trong cả hai. Nếu mã không độc lập với vị trí, thì mỗi quy trình sẽ yêu cầu bản sao riêng.
Simon Richter

19
@Narek: lỗi xảy ra nếu một quá trình muốn tải nhiều hơn một thư viện chia sẻ tại cùng một địa chỉ ảo. Vì các thư viện không thể dự đoán những gì các thư viện khác có thể được tải, vấn đề này là không thể tránh khỏi với khái niệm thư viện chia sẻ truyền thống. Không gian địa chỉ ảo không giúp được gì ở đây.
Phi

6
Bạn có thể bỏ qua -fPICkhi 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.
Simon Richter

61

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.
cướp

Tại sao không luôn luôn sử dụng -fpic?
Jay

1
@Jay - bởi vì nó sẽ yêu cầu thêm một phép tính (địa chỉ hàm) cho mỗi lệnh gọi hàm. Vì vậy, hiệu suất khôn ngoan, nếu không cần thiết thì tốt hơn là không sử dụng nó.
Roee Gavirel

45

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 -fPICtùy chọn đảm bảo rằng GCC tạo mã như vậy.


Tại sao thư viện dùng chung sẽ không được tải tại bất kỳ địa chỉ nào trong bộ nhớ mà không bật -fPICcờ? 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?
Tony Tannous

1
-fPICcờ đượ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 đó.
Tony Tannous

1
Phân biệt giữa xây dựng thư viện dùng chung (tạo 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 '.
Jonathan Leffler

Vì vậy, trước đây bạn phải thêm -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?
Tony Tannous

1
Có, bởi vì nếu bạn không sử dụng cờ PIC, mã không thể di chuyển một cách đáng tin cậy. Những thứ như ASLR (ngẫu nhiên bố trí không gian địa chỉ) không thể thực hiện được nếu mã không phải là PIC (hoặc, ít nhất, rất khó để đạt được đến mức chúng thực sự không thể).
Jonathan Leffler

21

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.


1
Cảm ơn đã giải thích addr ảo.
Hot.PxL

2
Bất cứ ai cũng có thể giải thích làm thế nào đây không phải là vấn đề với thư viện tĩnh, tại sao bạn không phải sử dụng -fPIC trên thư viện tĩnh? Tôi hiểu rằng việc liên kết được thực hiện trong thời gian biên dịch (hoặc ngay sau khi thực sự), nhưng nếu bạn có 2 thư viện tĩnh với mã phụ thuộc vị trí, chúng sẽ được liên kết như thế nào?
Michael P

3
Tệp đối tượng @MichaelP có một bảng các nhãn phụ thuộc vào vị trí và khi tệp obj cụ thể được liên kết, tất cả các nhãn được cập nhật tương ứng. Điều này không thể được thực hiện để thư viện chia sẻ.
Slava

16

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


9

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.

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.