[英]Is it possible to transform a no-pie executable to pie executable without source code?
[英]Why GCC -mcmodel=large adds an offset to the function call even with -no-pie flag, without -fPIC?
我嘗試使用 gcc 編譯非 PIC 代碼,我注意到 GCC 生成的匯編代碼不使用純 function 地址來調用,而是添加了一個奇怪的偏移量。
我使用 GCC 9.3.0 作為gcc test.c -o test-nopic -mcmodel=large -no-pie -O0
使用以下代碼。 我遺漏了-fPIC
。
#include <stdio.h>
int var1 = 1;
int var2 = 2;
void putstr(int* ptr) {
printf("val: %d\n", *ptr);
}
int main() {
putstr(&var1);
putstr(&var2);
}
這是來自objdump -wdrC -M intel test-nopic
的main()
代碼清單。
000000000040117e <main>:
40117e: 55 push rbp
40117f: 48 89 e5 mov rbp,rsp
401182: 53 push rbx
401183: 48 83 ec 08 sub rsp,0x8
401187: 48 8d 1d f9 ff ff ff lea rbx,[rip+0xfffffffffffffff9] # 401187 <main+0x9>
40118e: 49 bb 79 2e 00 00 00 00 00 00 movabs r11,0x2e79
401198: 4c 01 db add rbx,r11
40119b: 48 b8 30 00 00 00 00 00 00 00 movabs rax,0x30
4011a5: 48 8d 3c 03 lea rdi,[rbx+rax*1]
4011a9: 48 b8 26 d1 ff ff ff ff ff ff movabs rax,0xffffffffffffd126
4011b3: 48 8d 04 03 lea rax,[rbx+rax*1]
4011b7: ff d0 call rax
4011b9: 48 b8 34 00 00 00 00 00 00 00 movabs rax,0x34
4011c3: 48 8d 3c 03 lea rdi,[rbx+rax*1]
4011c7: 48 b8 26 d1 ff ff ff ff ff ff movabs rax,0xffffffffffffd126
4011d1: 48 8d 04 03 lea rax,[rbx+rax*1]
4011d5: ff d0 call rax
4011d7: b8 00 00 00 00 mov eax,0x0
4011dc: 48 83 c4 08 add rsp,0x8
4011e0: 5b pop rbx
4011e1: 5d pop rbp
4011e2: c3 ret
pusr(int*) 的地址是0x401126
。 readelf -l test-nopic
顯示文件類型為 EXEC 和以下標題:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x0000000000000268 0x0000000000000268 R 0x8
INTERP 0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000004d8 0x00000000000004d8 R 0x1000
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000
0x0000000000000275 0x0000000000000275 R E 0x1000
LOAD 0x0000000000002000 0x0000000000402000 0x0000000000402000
0x0000000000000168 0x0000000000000168 R 0x1000
LOAD 0x0000000000002e00 0x0000000000403e00 0x0000000000403e00
0x0000000000000238 0x0000000000000240 RW 0x1000
DYNAMIC 0x0000000000002e10 0x0000000000403e10 0x0000000000403e10
0x00000000000001d0 0x00000000000001d0 RW 0x8
NOTE 0x00000000000002c4 0x00000000004002c4 0x00000000004002c4
0x0000000000000044 0x0000000000000044 R 0x4
GNU_EH_FRAME 0x0000000000002010 0x0000000000402010 0x0000000000402010
0x0000000000000044 0x0000000000000044 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000002e00 0x0000000000403e00 0x0000000000403e00
0x0000000000000200 0x0000000000000200 R 0x1
movabs rax, 0x401126
? 通過@Jester 的評論,我解開了這個謎團。 我還必須使用-fno-pic
標志進行編譯,以禁用在大多數現代 GNU/Linux 發行版中默認啟用的 PIE 代碼生成。 -no-pie
只是 linker 選項, -fno-pic
或-fno-pie
是代碼生成選項。 請參閱x86-64 Linux 中不再允許使用 32 位絕對地址?
問題中的代碼(使用-mcmodel=large -no-pie -O0
編譯)使用對從rax
寄存器獲取的絕對地址的調用。 該地址是使用以下代碼從rip
寄存器計算的。
401187: 48 8d 1d f9 ff ff ff lea rbx,[rip+0xfffffffffffffff9] # 401187 <main+0x9>
40118e: 49 bb 79 2e 00 00 00 00 00 00 movabs r11,0x2e79
401198: 4c 01 db add rbx,r11
40119b: 48 b8 30 00 00 00 00 00 00 00 movabs rax,0x30
4011a5: 48 8d 3c 03 lea rdi,[rbx+rax*1]
4011a9: 48 b8 26 d1 ff ff ff ff ff ff movabs rax,0xffffffffffffd126
4011b3: 48 8d 04 03 lea rax,[rbx+rax*1]
4011b7: ff d0 call rax
我計算了存儲在rip
中的地址,它看起來指向 0x40118e。 它用於計算 function 及其參數的地址(var1 的地址存儲在rdi
寄存器中,它指向 RW LOAD 段)。 使用-fno-pic
標志,function 調用看起來如我所願。
40115c: 48 bf 30 40 40 00 00 00 00 00 movabs rdi,0x404030
401166: 48 b8 26 11 40 00 00 00 00 00 movabs rax,0x401126
401170: ff d0 call rax
large
)中: 如果沒有-mcmodel=large
標志( -no-pie -fno-pic -O0
),它看起來會有所不同。 Static 數據和代碼可通過 32 位相對位移,甚至非 PIE 代碼中的 32 位絕對位移來訪問。 這效率更高,尤其是對於代碼; 盡可能避免-mcmodel=large
。 如果您只需要一些巨大的 static arrays,請使用-mcmodel=medium
。
這是相對版本中的一個調用:對於與位置相關的代碼,它可以將 static 地址放入具有高效mov r32, imm32
的寄存器中( 如何在 GNU ABAC850 EEE34Z 中加載 function 或 ZD304BA20E96D8774Z 的地址到寄存器中)
401150: bf 30 40 40 00 mov edi,0x404030
401155: e8 cc ff ff ff call 401126 <putstr>
這是一個只有-fpie
的代碼(在我的配置中默認啟用)。
1165: 48 8d 3d c4 2e 00 00 lea rdi,[rip+0x2ec4] # 4030 <var1>
116c: e8 c8 ff ff ff call 1139 <putstr>
在添加-fpic
標志以啟用全局函數(如共享庫)的符號插入之后:鏈接后沒有真正的區別,只是一個額外的不必要的mov
而不是首先將 arg 放入rdi
。 (這是-O0
的產物:編譯快,不好)
1165: 48 8d 05 c4 2e 00 00 lea rax,[rip+0x2ec4] # 4030 <var1>
116c: 48 89 c7 mov rdi,rax
116f: e8 c5 ff ff ff call 1139 <putstr>
如果我們將var1
聲明為static
(C 中的“靜態”是什么意思?), gcc -O0
恰好避免了額外的mov
。 或者更簡單地說,至少-Og
啟用優化,更常見的是-O2
或-O3
。 未優化的代碼充滿了浪費的指令。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.