ELF entry point和裝載位址

2021-07-10 03:42:54 字數 4915 閱讀 3006

為了研究elf檔案裝載到記憶體的**,以及從**開始執行程式,環境:ubuntu12.04 64位,gcc4.6.3。

使用的源**是:

#include void hello(void)

int main(void)

程式並不是從main函式開始執行的,gcc -o main main.c時,缺省會連線libc.so(可以指定-nodefaultlib, -nostdlib取消連線),並且會新增乙個啟動**_start函式(可以指定-nodefaultlib, -nostdlib不新增啟動**),用於初始化,並提供main函式的argc, argv等引數,_start函式中會呼叫main函式。

$ readelf -d main

dynamic section at offset 0xe50 contains 20 entries:

tag type name/value

0x0000000000000001 (needed) shared library: [libc.so.6]

0x000000000000000c (init) 0x400390

0x000000000000000d (fini) 0x4005c8

$ objdump -d main | grep _start

400394: e8 63 00 00 00 callq 4003fc 00000000004003b0 <__libc_start_main@plt-0x10>:

00000000004003c0 <__libc_start_main@plt>:

00000000004003d0 <_start>:

4003f4: e8 c7 ff ff ff callq 4003c0 <__libc_start_main@plt>

readelf -h main | grep entry可以看到:

entry point address:               0x4003d0
程式的入口位址是0x4003d0,也就是_start函式的位址,程式裝載到記憶體後,從0x4003d0(_start)開始執行。

那麼,我不想執行_start函式呢,可以通過ld的引數-e指定入口函式,使用gcc -o main mian.c -wl,-ehello編譯,-wl用於指定後面的引數是給ld的,-e指定入口函式是hello。

$ ./main

$ echo $?

42

$ objdump -d main | grep hello

00000000004004f4 :

$ readelf -h main | grep entry

entry point address: 0x4004f4

證明了-wl,-ehello可以指定程式的入口函式為hello。

那麼程式的入口位址0x4004f4是怎麼來的呢?

0x4f4其實是hello函式在elf檔案中的偏移量,可以通過hexdump -c main | grep -a5 -b5 4f0檢視到

$ hexdump -c main | grep -a5 -b5 4f0

000004a0 0b 20 00 ff 14 c5 38 0e 60 00 48 8b 05 77 0b 20 |. ....8.`.h..w. |

000004b0 00 48 39 d8 72 e2 c6 05 63 0b 20 00 01 48 83 c4 |.h9.r...c. ..h..|

000004c0 08 5b 5d c3 66 66 66 2e 0f 1f 84 00 00 00 00 00 |..fff.........|

000004d0 48 83 3d 70 09 20 00 00 55 48 89 e5 74 12 b8 00 |h.=p. ..uh..t...|

000004e0 00 00 00 48 85 c0 74 08 5d bf 48 0e 60 00 ff e0 |...h..t.].h.`...|

000004f0 5d c3 90 90 55 48 89 e5 bf 2a 00 00 00 e8 fe fe |]...uh...*......|

00000500 ff ff 55 48 89 e5 b8 18 00 00 00 5d c3 90 90 90 |..uh.......]....|

00000510 48 89 6c 24 d8 4c 89 64 24 e0 48 8d 2d 03 09 20 |h.l$.l.d$.h.-.. |

00000520 00 4c 8d 25 fc 08 20 00 4c 89 6c 24 e8 4c 89 74 |.l.%.. .l.l$.l.t|

00000530 24 f0 4c 89 7c 24 f8 48 89 5c 24 d0 48 83 ec 38 |$.l.|$.h.\$.h..8|

00000540 4c 29 e5 41 89 fd 49 89 f6 48 c1 fd 03 49 89 d7 |l).a..i..h...i..|

然後objdump -d main | grep -a5 -b5 hello檢視hello對應的機器碼:

$ objdump -d main | grep -a5 -b5 hello

4004f0: 5d pop %rbp

4004f1: c3 retq

4004f2: 90 nop

4004f3: 90 nop

00000000004004f4 :

4004f4: 55 push %rbp

4004f5: 48 89 e5 mov %rsp,%rbp

4004f8: bf 2a 00 00 00 mov $0x2a,%edi

4004fd: e8 fe fe ff ff callq 400400

證明了這一點。

然後0x400000是什麼呢?

這個是elf裝載到記憶體時的起始位置,可以通過ld的引數-wl,-ttext-segment,0x400000指定,比如我們gcc -o main main.c -wl,-ehello -wl,-ttext-segment,0x4200000編譯程式,給程式換乙個裝載位址,然後readelf -h main | grep entry得到:

entry point address:               0x42004f4

$ objdump -d main | grep hello -a 10

00000000042004f4 :

42004f4: 55 push %rbp

42004f5: 48 89 e5 mov %rsp,%rbp

42004f8: bf 2a 00 00 00 mov $0x2a,%edi

42004fd: e8 fe fe ff ff callq 4200400 0000000004200502 :

4200502: 55 push %rbp

4200503: 48 89 e5 mov %rsp,%rbp

4200506: b8 18 00 00 00 mov $0x18,%eax

420050b: 5d pop %rbp

注意1、-ttext-segment指定的必需是乙個頁對齊的位址。

2、動態庫的裝載位置不是固定的,一般可以認為動態庫的-ttext-segment=0,沒有使用;如果想直接執行.so的話(glibc裡面很多.so都可以直接執行),需要為.so指定ld-linux.so(.so預設沒有指定):

gcc -shared -fpic -o libx.so x.c -wl,--dynamic-linker=some_ld_linux.so.x

或者**裡面寫:const char my_interp __attribute__((section(".interp"))) = "/lib/ld-linux.so.3"; //需要保證這個ld-linux.so存在且能用

可以使用readelf -l檢視.inter段,也就是指定的ld-linux.so

3、-ttext-segment並不是指定.text段的載入位置,而是指定整個elf的載入位置。

如果想指定.text段的載入位置,可以:

-tbss=org

-tdata=org

-ttext=org

same as --section-start, with ".bss", ".data" or ".text" as the

sectionname.

4、gcc -wl,-ttext-segment=0x400000 -wl,-ttext=0x800000  -o x x.c這樣是可以的,但是如果-ttext指定的比較小,要麼程式無法執行,要麼可能和其他段衝突。加完-ttext之後,.text段以及後面的段,在檔案中的偏移會變大不少,中間填充的0是為了載入到記憶體後的頁對齊,在記憶體中的載入位置都會從-ttext指定的位置開始。

軟裝和硬裝又有哪些不同?

所謂軟裝,是相對硬裝而言的,硬裝一般指的室內裝修中固定的 不能移動的裝飾物如吊頂 牆面造型 地面裝飾 門窗工程等,軟裝是除掉硬裝,進行家居的二度裝飾,它是可以移動的 易於更換的飾物,如頂部燈具 牆面窗簾 桌布 掛畫 地面可移動家具 沙發 抱枕 地毯 臥室床上用品等以及裝飾工藝品 居室植物等。我今天發...

集合和JSON裝換

使用json lib將pagebean物件轉為json,通過輸出流寫回頁面中 jsonobject 將單一物件轉為json jsonarray 將陣列或者集合物件轉為json 注 需要將引用型別的去掉,否則會報錯,迴圈引用 public string findall throws ioexcepti...

裝tomcat和nginx心得

開機啟動tomcat 1 在 etc rc.d init.d目錄下生成乙個檔案tomcat8080 2 在檔案裡新增如下內 bin bash 2345linux執行級別 10開機啟動優先順序,數值越大越排在前面,最大值100 90關機優先順序 chkconfig 2345 10 90 descrip...