gcc編譯引數-fPIC的一些問題
ppc_85xx-gcc -shared
-fPIC liberr.c -o liberr.so
-fPIC 作用於編譯階段,告訴編譯器產生與位置無關程式碼(Position-Independent Code),
則產生的程式碼中,沒有絕對地址,全部使用相對地址,故而程式碼可以被載入器載入到記憶體的任意
位置,都可以正確的執行。這正是共享庫所要求的,共享庫被載入時,在記憶體的位置不是固定的。
不加fPIC編譯出來的so,是要再載入時根據載入到的位置再次重定位的.(因為它裡面的程式碼並不是位置無關程式碼)
如果被多個應用程式共同使用,那麼它們必須每個程式維護一份so的程式碼副本了.(因為so被每個程式載入的位置都不同,顯然這些重定位後的程式碼也不同,當然不能共享)
我們總是用fPIC來生成so,也從來不用fPIC來生成a.
fPIC與動態連結可以說基本沒有關係,libc.so一樣可以不用fPIC編譯,只是這樣的so必須要在載入到使用者程式的地址空間時重定向所有表目.
因此,不用fPIC編譯so並不總是不好.
如果你滿足以下4個需求/條件:
1.該庫可能需要經常更新
2.該庫需要非常高的效率(尤其是有很多全域性量的使用時)
3.該庫並不很大.
4.該庫基本不需要被多個應用程式共享
如果用沒有加這個引數的編譯後的共享庫,也可以使用的話,可能是兩個原因:
1:gcc預設開啟-fPIC選項
2:loader使你的程式碼位置無關
從GCC來看,shared應該是包含fPIC選項的,但似乎不是所以系統都支援,所以最好顯式加上fPIC選項。參見如下
`-shared'
Produce a shared object which can then be linked with other
objects to form an executable. Not all systems support this
option. For predictable results, you must also specify the same
set of options that were used to generate code (`-fpic', `-fPIC',
or model suboptions) when you specify this option.(1)
-fPIC 的使用,會生成 PIC 程式碼,.so 要求為 PIC,以達到動態連結的目的,否則,無法實現動態連結。
non-PIC 與 PIC 程式碼的區別主要在於 access global data, jump label 的不同。
比如一條 access global data 的指令,
non-PIC 的形勢是:ld r3, var1
PIC 的形式則是:ld r3, var1-offset@GOT,意思是從 GOT 表的 index 為 var1-offset 的地方處
指示的地址處裝載一個值,即var1-offset@GOT處的4個 byte 其實就是 var1 的地址。這個地址只有在執行的時候才知道,是由 dynamic-loader(ld-linux.so) 填進去的。
再比如 jump label 指令
non-PIC 的形勢是:jump printf ,意思是呼叫 printf。
PIC 的形式則是:jump printf-offset@GOT,
意思是跳到 GOT 表的 index 為 printf-offset 的地方處指示的地址去執行,
這個地址處的程式碼擺放在 .plt section,
每個外部函式對應一段這樣的程式碼,其功能是呼叫dynamic-loader(ld-linux.so) 來查詢函式的地址(本例中是 printf),然後將其地址寫到 GOT 表的 index 為 printf-offset 的地方,
同時執行這個函式。這樣,第2次呼叫 printf 的時候,就會直接跳到 printf 的地址,而不必再查詢了。
GOT 是 data section, 是一個 table, 除專用的幾個 entry,每個 entry 的內容可以再執行的時候修改;
PLT 是 text section, 是一段一段的 code,執行中不需要修改。
每個 target 實現 PIC 的機制不同,但大同小異。比如 MIPS 沒有 .plt, 而是叫 .stub,功能和 .plt 一樣。
可見,動態連結執行很複雜,比靜態連結執行時間長;但是,極大的節省了 size,PIC 和動態連結技術是計算機發展史上非常重要的一個里程碑。
gcc manul上面有說
-fpic If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC and 32k on the m68k and RS/6000. The 386 has no such limit.)
-fPIC If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on the m68k, PowerPC and SPARC. Position-independent code requires special support, and therefore works only on certain machines.
關鍵在於GOT全域性偏移量表裡面的跳轉項大小。
intel處理器應該是統一4位元組,沒有問題。
powerpc上由於彙編碼或者機器碼的特殊要求,所以跳轉項分為短、長兩種。
-fpic為了節約記憶體,在GOT裡面預留了“短”長度。
而-fPIC則採用了更大的跳轉項。
-fPIC 作用於編譯階段,告訴編譯器產生與位置無關程式碼(Position-Independent Code),
則產生的程式碼中,沒有絕對地址,全部使用相對地址,故而程式碼可以被載入器載入到記憶體的任意
位置,都可以正確的執行。這正是共享庫所要求的,共享庫被載入時,在記憶體的位置不是固定的。
gcc -shared -fPIC -o 1.so 1.c
這裡有一個-fPIC引數
PIC就是position independent code
PIC使.so檔案的程式碼段變為真正意義上的共享
如果不加-fPIC,則載入.so檔案的程式碼段時,程式碼段引用的資料物件需要重定位, 重定位會修改程式碼段的內容,這就造成每個使用這個.so檔案程式碼段的程式在核心裡都會生成這個.so檔案程式碼段的copy.每個copy都不一樣,取決於 這個.so檔案程式碼段和資料段記憶體對映的位置.
不加fPIC編譯出來的so,是要再載入時根據載入到的位置再次重定位的.(因為它裡面的程式碼並不是位置無關程式碼)
如果被多個應用程式共同使用,那麼它們必須每個程式維護一份so的程式碼副本了.(因為so被每個程式載入的位置都不同,顯然這些重定位後的程式碼也不同,當然不能共享)
我們總是用fPIC來生成so,也從來不用fPIC來生成a.
fPIC與動態連結可以說基本沒有關係,libc.so一樣可以不用fPIC編譯,只是這樣的so必須要在載入到使用者程式的地址空間時重定向所有表目.
因此,不用fPIC編譯so並不總是不好.
如果你滿足以下4個需求/條件:
1.該庫可能需要經常更新
2.該庫需要非常高的效率(尤其是有很多全域性量的使用時)
3.該庫並不很大.
4.該庫基本不需要被多個應用程式共享
如果用沒有加這個引數的編譯後的共享庫,也可以使用的話,可能是兩個原因:
1:gcc預設開啟-fPIC選項
2:loader使你的程式碼位置無關
從GCC來看,shared應該是包含fPIC選項的,但似乎不是所以系統都支援,所以最好顯式加上fPIC選項。參見如下
`-shared'
Produce a shared object which can then be linked with other
objects to form an executable. Not all systems support this
option. For predictable results, you must also specify the same
set of options that were used to generate code (`-fpic', `-fPIC',
or model suboptions) when you specify this option.(1)
-fPIC 的使用,會生成 PIC 程式碼,.so 要求為 PIC,以達到動態連結的目的,否則,無法實現動態連結。
non-PIC 與 PIC 程式碼的區別主要在於 access global data, jump label 的不同。
比如一條 access global data 的指令,
non-PIC 的形勢是:ld r3, var1
PIC 的形式則是:ld r3, var1-offset@GOT,意思是從 GOT 表的 index 為 var1-offset 的地方處
指示的地址處裝載一個值,即var1-offset@GOT處的4個 byte 其實就是 var1 的地址。這個地址只有在執行的時候才知道,是由 dynamic-loader(ld-linux.so) 填進去的。
再比如 jump label 指令
non-PIC 的形勢是:jump printf ,意思是呼叫 printf。
PIC 的形式則是:jump printf-offset@GOT,
意思是跳到 GOT 表的 index 為 printf-offset 的地方處指示的地址去執行,
這個地址處的程式碼擺放在 .plt section,
每個外部函式對應一段這樣的程式碼,其功能是呼叫dynamic-loader(ld-linux.so) 來查詢函式的地址(本例中是 printf),然後將其地址寫到 GOT 表的 index 為 printf-offset 的地方,
同時執行這個函式。這樣,第2次呼叫 printf 的時候,就會直接跳到 printf 的地址,而不必再查詢了。
GOT 是 data section, 是一個 table, 除專用的幾個 entry,每個 entry 的內容可以再執行的時候修改;
PLT 是 text section, 是一段一段的 code,執行中不需要修改。
每個 target 實現 PIC 的機制不同,但大同小異。比如 MIPS 沒有 .plt, 而是叫 .stub,功能和 .plt 一樣。
可見,動態連結執行很複雜,比靜態連結執行時間長;但是,極大的節省了 size,PIC 和動態連結技術是計算機發展史上非常重要的一個里程碑。
gcc manul上面有說
-fpic If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC and 32k on the m68k and RS/6000. The 386 has no such limit.)
-fPIC If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on the m68k, PowerPC and SPARC. Position-independent code requires special support, and therefore works only on certain machines.
關鍵在於GOT全域性偏移量表裡面的跳轉項大小。
intel處理器應該是統一4位元組,沒有問題。
powerpc上由於彙編碼或者機器碼的特殊要求,所以跳轉項分為短、長兩種。
-fpic為了節約記憶體,在GOT裡面預留了“短”長度。
而-fPIC則採用了更大的跳轉項。
相關文章
- gcc或g++的編譯選項 -shared -fPIC 與 -g -rdynamic 部分轉載GC編譯
- GCC編譯遇到“a label can only be part of a statement and a declaration is not a statement”問題GC編譯
- 記錄一些工程編譯問題編譯
- php編譯引數PHP編譯
- 配置pycharm 編譯器時遇到的一些問題PyCharm編譯
- Linux中gcc編譯工具LinuxGC編譯
- GCC編譯器背後的故事GC編譯
- 關於Jmeter引數化的編碼問題JMeter
- 編譯Spring原始碼的步驟及一些問題編譯Spring原始碼
- Dev 日誌 | 一次 Segmentation Fault 和 GCC Illegal Instruction 編譯問題排查devSegmentationGCStruct編譯
- 記錄一次gcc的編譯GC編譯
- 探索gcc編譯最佳化細節 編譯器最佳化gcc -o3GC編譯
- GCC編譯過程(預處理->編譯->彙編->連結)GC編譯
- PHP 編譯引數儲存PHP編譯
- libmemcached編譯問題IBM編譯
- SPI編譯問題編譯
- Nginx編譯引數大全 configure引數中文詳解Nginx編譯
- Mybatis PageHelper編譯SQL引發的一次效能問題.18286262MyBatis編譯SQL
- 記一次編譯GCC的經歷編譯GC
- GCC編譯和連結過程GC編譯
- 【問題記錄】—.NetCore 編譯問題NetCore編譯
- 模板引數,模板分離編譯編譯
- gcc 和 g++ 的聯絡和區別,使用 gcc 編譯 c++GC編譯C++
- go的編譯優化問題Go編譯優化
- nginx 編譯出現的問題Nginx編譯
- LOG巨集的引數問題
- PHP編譯安裝之Configure引數PHP編譯
- Linux下nginx編譯安裝教程和編譯引數詳解LinuxNginx編譯
- 一個nvcc編譯的小問題編譯
- CMake編譯Qt工程時的問題編譯QT
- Flutter 混合開發實戰問題記錄(四)編譯執行時問題的一些總結Flutter編譯
- gcc編譯階段列印巨集定義的內容GC編譯
- 32位支援:使用 GCC 交叉編譯GC編譯
- CentOS下檢視nginx和php的編譯引數CentOSNginxPHP編譯
- Linux下C語言編譯的問題LinuxC語言編譯
- 關於c#的HttpUtility.UrlDecode/使用 Request.QueryString 接受引數時,跟編碼有關的一些問題C#HTTP
- linux 改變GCC編譯器的位元組對齊方式LinuxGC編譯
- c++模板類的使用,編譯的問題C++編譯
- Gradle 編譯警告亂碼問題Gradle編譯