前幾天公眾號(hào)里有位兄弟看了幾篇文章之后,也準(zhǔn)備用windbg試試看,結(jié)果這一配就花了好幾天,(づ╥﹏╥)づ,我想也有很多躍躍欲試的朋友在配置的時(shí)候肯定會(huì)遇到這樣和那樣的問題,所以我覺得有必要整理一下,讓大家少走彎路。
現(xiàn)在安裝windbg越來(lái)越麻煩,還要安裝Windows 10 SDK,很多人就栽在這里,其實(shí)大家可以直接在網(wǎng)上找一鍵打包的windbg 6.0版本即可,才30多M,調(diào)生產(chǎn)調(diào)本地都很方便,順帶還可以練練SOS命令。
云盤:https://pan.baidu.com/s/1VqXVIGVHxAZVPNds1525Jg 提取碼:mahg
外網(wǎng):http://www.33lc.com/soft/96743.html
解壓打開會(huì)有一個(gè)x64和x86文件夾,很顯然,32位的程序用x86下的windbg調(diào)試,64位的程序用x64的windbg調(diào)試,如下圖:
我比較喜歡用64bit程序,所以這里使用64位的windbg。
符號(hào)其實(shí)就是pdb文件,我們?cè)赿ebug模式下編譯項(xiàng)目都會(huì)看到這個(gè),它的作用會(huì)對(duì)dll進(jìn)行打標(biāo),這樣在調(diào)試時(shí)通過(guò)pdb就能看到局部變量,全局變量,行號(hào)等等其他信息,在FCL類庫(kù)中的pdb文件就放在微軟的公有服務(wù)器上,SRV*C:\mysymbols*http://msdl.microsoft.com/download/symbols。
很多時(shí)候大家都是事后調(diào)試,所以需要在生產(chǎn)上抓一個(gè)dump文件,為了將dump文件逆向到clr上的運(yùn)行時(shí)狀態(tài),你必須要尋找到當(dāng)時(shí)運(yùn)行程序clr版本,同時(shí)也要找到對(duì)應(yīng)clr版本的sos.dll,他們通常是在一起的,sos 就是 你 和 clr交互的渠道,很多人都卡在尋找正確版本的sos和clr版本上。。。如果不清楚,我可以畫張圖。
有了這個(gè)前置基礎(chǔ),接下來(lái)就可以在windows和centos上進(jìn)行配置實(shí)踐了。。。
為了演示,我先上一段簡(jiǎn)單的代碼:
static void Main(string[] args)
{
var info = "hello world!";
Console.WriteLine(info);
Console.ReadLine();
}
在netcore中,clr的名字變成了 coreclr.dll,路徑: C:\Program Files\dotnet\shared\Microsoft.NETCore.App.1.3
netcore3.0開始,sos就沒有放在版本號(hào)文件下了,詳見 SOS_README.md 內(nèi)容。
SOS and other diagnostic tools now ship of band and work with any version of the .NET Core runtime.
SOS has moved to the diagnostics repo here: https://github.com/dotnet/diagnostics.git.
Instructions to install SOS: https://github.com/dotnet/diagnostics#installing-sos.
看了上面文檔,大概意思就是說(shuō)老版本的windbg,需要通過(guò)小工具dotnet-sos 自己生成一個(gè)sos.dll,那就按照文檔來(lái)吧
PS C:\WINDOWS\system32> dotnet tool install -g dotnet-sos
You can invoke the tool using the following command: dotnet-sos
Tool 'dotnet-sos' (version '3.1.122203') was successfully installed.
PS C:\WINDOWS\system32> dotnet-sos install
Installing SOS to C:\Users\hxc\.dotnet\sos from C:\Users\hxc\.dotnet\tools\.store\dotnet-sos\3.1.122203\dotnet-sos\3.1.122203\tools\netcoreapp2.1\any\win-x64
Installing over existing installation...
Creating installation directory...
Copying files...
Execute '.load C:\Users\hxc\.dotnet\sos\sos.dll' to load SOS in your Windows debugger.
Cleaning up...
SOS install succeeded
PS C:\WINDOWS\system32>
仔細(xì)看輸出,sos.dll 已經(jīng)生成好了,接下來(lái)在任務(wù)管理器中生成一個(gè)dump文件,然后使用 .load 命令把 coreclr 和 sos 加載進(jìn)去即可。
.load C:\Users\hxc\.dotnet\sos\sos.dll
.load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.3\coreclr.dll
最后我們抓一下 info 變量在堆上的分布。
0:000> ~0s
ntdll!ZwReadFile+0x14:
00007ff8`3228aa64 c3 ret
0:000> !clrstack -l
OS Thread Id: 0x41d4 (0)
000000246097EA40 00007FFF89C50F97 Error: Fail to initialize CoreCLR 80131022
ConsoleApp5.Program.Main(System.String[])
LOCALS:
0x000000246097EA68 = 0x0000021d8141aba8
0:000> !do 0x0000021d8141aba8
Name: System.String
MethodTable: 00007fff89cd1e18
EEClass: 00007fff89cc2128
Size: 46(0x2e) bytes
File: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.3\System.Private.CoreLib.dll
String: hello world!
Fields:
MT Field Offset Type VT Attr Value Name
00007fff89c1b1e8 4000242 8 System.Int32 1 instance 12 _stringLength
00007fff89c18000 4000243 c System.Char 1 instance 68 _firstChar
00007fff89cd1e18 4000244 110 System.String 0 static 0000021d81411360 Empty
好了,windows上的netcore調(diào)試就這么簡(jiǎn)單,希望這些配置能節(jié)省您的時(shí)間。
framework程序比netcore配置要方便的多,不需要自己去生成sos了,如下代碼所示:
64位程序加載路徑
.load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.dll
.load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
32位程序加載路徑
.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll
.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
首先要明白,對(duì)于linux內(nèi)核windbg就失效了,那怎么調(diào)試呢? 有兩種方式。
這個(gè)工具的地方在于,sos和clr都不需要你配置,直接使用它生成dump,然后直接調(diào)試,方便至極,下面看看怎么安裝,開兩個(gè)terminal,如下代碼:
terminal 1:
[root@10-25-198-96 data]# dotnet build
[root@10-25-198-96 netcoreapp3.1]# dotnet data.dll
hello world
terminal 2:
[root@10-25-198-96 cs2]# ps -ef | grep dotnet
root 31555 31247 0 22:28 pts/0 00:00:00 dotnet cs2.dll
root 32112 31995 0 22:29 pts/2 00:00:00 grep --color=auto dotnet
[root@10-25-198-96 cs2]# dotnet tool install -g dotnet-dump
You can invoke the tool using the following command: dotnet-dump
Tool 'dotnet-dump' (version '3.1.122203') was successfully installed.
[root@10-25-198-96 cs2]# export PATH=$PATH:$HOME/.dotnet/tools
[root@10-25-198-96 cs2]# dotnet-dump collect --process-id 31555
Writing full to /cs2/core_20200508_223204
Complete
可以看到dump文件已經(jīng)好了 /cs2/core_20200508_223204 ,接下來(lái)用 dotnet-dump 對(duì)dump文件調(diào)試。
[root@10-25-198-96 cs2]# dotnet-dump analyze /cs2/core_20200508_223204
Loading core dump: /cs2/core_20200508_223204 ...
Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command.
Type 'quit' or 'exit' to exit the session.
> clrstack -l
OS Thread Id: 0x7b43 (0)
Child SP IP Call Site
00007FFDFCABF2D0 00007fb0397af7fd [InlinedCallFrame: 00007ffdfcabf2d0] Interop+Sys.ReadStdin(Byte*, Int32)
00007FFDFCABF2D0 00007fafbebbb4db [InlinedCallFrame: 00007ffdfcabf2d0] Interop+Sys.ReadStdin(Byte*, Int32)
00007FFDFCABF2C0 00007FAFBEBBB4DB ILStubClass.IL_STUB_PInvoke(Byte*, Int32)
00007FFDFCABF9D0 00007FAFBECF844D System.Console.ReadLine()
00007FFDFCABF9E0 00007FAFBEBB037D cs2.Program.Main(System.String[]) [/cs2/Program.cs @ 13]
LOCALS:
0x00007FFDFCABF9F0 = 0x00007faf980081d8
00007FFDFCABFD08 00007fb037fc0f7f [GCFrame: 00007ffdfcabfd08]
00007FFDFCAC01F0 00007fb037fc0f7f [GCFrame: 00007ffdfcac01f0]
> dumpobj 0x00007faf980081d8
Name: System.String
MethodTable: 00007fafbec30f90
EEClass: 00007fafbeb9e1b0
Size: 44(0x2c) bytes
File: /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.3/System.Private.CoreLib.dll
String: hello world
Fields:
MT Field Offset Type VT Attr Value Name
00007fafbec2a0e8 400022a 8 System.Int32 1 instance 11 _stringLength
00007fafbec26f00 400022b c System.Char 1 instance 68 _firstChar
00007fafbec30f90 400022c 108 System.String 0 static 00007faf97fff360 Empty
>
就這么簡(jiǎn)單,不過(guò)這個(gè)工具雖好,但是不能調(diào)試非托管堆,而且命令也不是太多,當(dāng)然夠我們平時(shí)用了。
要想實(shí)現(xiàn)windbg級(jí)別的調(diào)試,可以使用lldb調(diào)試器,這個(gè)非常強(qiáng)大,這里我也來(lái)介紹一下吧。
lldb是使用C++寫的,也可以在 https://github.com/dotnet/diagnostics/blob/master/documentation/building/linux-instructions.md 尋找安裝辦法。
sudo yum install centos-release-SCL epel-release
sudo yum install cmake cmake3 gcc gcc-c++ gdb git libicu libunwind make python27 tar wget which zip
cd $HOME
git clone https://github.com/dotnet/diagnostics.git
$HOME/diagnostics/documentation/lldb/centos7/build-install-lldb.sh
一陣抽搐后就安裝好了,從下面可以看到目前版本是3.9.1。
[root@10-25-198-96 cs2]# lldb -v
lldb version 3.9.1 ( revision )
跟windbg一樣,你需要生成一個(gè)sos.dll 。。。 同樣也是使用 dotnet-sos 生成。
[root@10-25-198-96 cs2]# dotnet tool install -g dotnet-sos
You can invoke the tool using the following command: dotnet-sos
Tool 'dotnet-sos' (version '3.1.122203') was successfully installed.
[root@10-25-198-96 cs2]# dotnet-sos install
Installing SOS to /root/.dotnet/sos from /root/.dotnet/tools/.store/dotnet-sos/3.1.122203/dotnet-sos/3.1.122203/tools/netcoreapp2.1/any/linux-x64
Installing over existing installation...
Creating installation directory...
Copying files...
Updating existing /root/.lldbinit file - LLDB will load SOS automatically at startup
Cleaning up...
SOS install succeeded
從上面信息看,sos 是安裝在 /root/.dotnet/sos 目錄下,同時(shí)也看到在lldb啟動(dòng)的時(shí)候會(huì)自動(dòng)加載sos.dll 。。。
每個(gè)dotnet版本下都有一個(gè)createdump程序,可以用它生成dump文件,具體配置文檔可以參見:
https://github.com/dotnet/diagnostics/blob/master/documentation/debugging-coredump.md
https://github.com/dotnet/runtime/blob/master/docs/design/coreclr/botr/xplat-minidump-generation.md#configurationpolicy
[root@10-25-198-96 cs2]# ps -ef | grep dotnet
root 31555 31247 0 22:28 pts/0 00:00:00 dotnet cs2.dll
root 32112 31995 0 22:29 pts/2 00:00:00 grep --color=auto dotnet
[root@10-25-198-96 cs2]# find / -name createdump
/usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.3/createdump
[root@10-25-198-96 3.1.3]# ./createdump 31555 -f /lldb/test.dump
Writing minidump with heap to file /lldb/test.dump
Written 84692992 bytes (20677 pages) to core file
[root@10-25-198-96 3.1.3]# lldb --core /lldb/test.dump
(lldb) target create --core "/lldb/test.dump"
Core file '/lldb/test.dump' (x86_64) was loaded.
(lldb) clrstack -l
OS Thread Id: 0x7b43 (1)
00007FFDFCABF9E0 00007FAFBEBB037D cs2.Program.Main(System.String[]) [/cs2/Program.cs @ 13]
LOCALS:
0x00007FFDFCABF9F0 = 0x00007faf980081d8
00007FFDFCABFD08 00007fb037fc0f7f [GCFrame: 00007ffdfcabfd08]
00007FFDFCAC01F0 00007fb037fc0f7f [GCFrame: 00007ffdfcac01f0]
(lldb) dumpobj 0x00007faf980081d8
Name: System.String
MethodTable: 00007fafbec30f90
EEClass: 00007fafbeb9e1b0
Size: 44(0x2c) bytes
File: /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.3/System.Private.CoreLib.dll
String: hello world
Fields:
MT Field Offset Type VT Attr Value Name
00007fafbec2a0e8 400022a 8 System.Int32 1 instance 11 _stringLength
00007fafbec26f00 400022b c System.Char 1 instance 68 _firstChar
00007fafbec30f90 400022c 108 System.String 0 static 00007faf97fff360 Empty
(lldb)
可以看到,通過(guò)lldb也可以直接打入clr內(nèi)部啦。。。
我覺得這篇文章肯定能給很多朋友節(jié)省不少的時(shí)間,想起朱一旦的那句話:有錢人的快樂,就是這么樸實(shí)無(wú)華且枯燥, 哈哈~
本文簡(jiǎn)要介紹C/C++符號(hào)重定位的原理,并簡(jiǎn)要分析了libc對(duì)main等函數(shù)的調(diào)用順序。
測(cè)試代碼如下:
/*main.c*/
int add(int a_, int b_);
extern int global_extern_int;
int global_int = 3;
int main()
{
static int a = 19;
global_int = 5;
int rtv = 0;
rtv = add(global_int, global_extern_int);
return rtv;
}
/*add.c*/
int global_extern_int = 2;
int add(int a_, int b_)
{
return a_+ b_;
}
編譯、鏈接(32位),
gcc -g -fno-pie -no-pie -m32 -c add.c
gcc -g -fno-pie -no-pie -m32 -c main.c
gcc -g -fno-pie -no-pie -m32 -o main main.o add.o
生成的應(yīng)用程序main的大小為16K。
lyf@lyf:~/test$ ls -hl
total 32K
-rw-r--r-- 1 lyf lyf 74 Dec 2 10:22 add.c
-rw-r--r-- 1 lyf lyf 2.0K Dec 2 10:42 add.o
-rwxr-xr-x 1 lyf lyf 16K Dec 2 13:54 main
-rw-r--r-- 1 lyf lyf 212 Dec 2 11:33 main.c
-rw-r--r-- 1 lyf lyf 2.3K Dec 2 11:34 main.o
大部分的現(xiàn)代操作系統(tǒng)都使用ASCII標(biāo)準(zhǔn)來(lái)表示文本字符,使用單字節(jié)的整數(shù)值來(lái)表示每個(gè)字符,如下所示,程序的源文件為ASCII文件,這種只由ASCII字符構(gòu)成的文件稱為文本文件,除了文本文件,其他的都是二進(jìn)制文件。
lyf@lyf:~/test$ file main.c
main.c: C source, ASCII text
lyf@lyf:~/test$ hexdump -C ./main.c
00000000 69 6e 74 20 61 64 64 28 69 6e 74 20 61 5f 2c 20 |int add(int a_, |
00000010 69 6e 74 20 62 5f 29 3b 0a 65 78 74 65 72 6e 20 |int b_);.extern |
00000020 69 6e 74 20 67 6c 6f 62 61 6c 5f 65 78 74 65 72 |int global_exter|
00000030 6e 5f 69 6e 74 3b 0a 69 6e 74 20 67 6c 6f 62 61 |n_int;.int globa|
00000040 6c 5f 69 6e 74 20 3d 20 33 3b 0a 0a 69 6e 74 20 |l_int = 3;..int |
00000050 6d 61 69 6e 28 29 0a 7b 0a 20 20 20 20 73 74 61 |main().{. sta|
00000060 74 69 63 20 69 6e 74 20 61 20 3d 20 31 39 3b 0a |tic int a = 19;.|
00000070 20 20 20 20 67 6c 6f 62 61 6c 5f 69 6e 74 20 3d | global_int =|
00000080 20 35 3b 0a 20 20 20 20 69 6e 74 20 72 74 76 20 | 5;. int rtv |
00000090 3d 20 30 3b 0a 20 20 20 20 72 74 76 20 3d 20 61 |= 0;. rtv = a|
000000a0 64 64 28 67 6c 6f 62 61 6c 5f 69 6e 74 2c 20 67 |dd(global_int, g|
000000b0 6c 6f 62 61 6c 5f 65 78 74 65 72 6e 5f 69 6e 74 |lobal_extern_int|
000000c0 29 3b 0a 20 20 20 20 72 65 74 75 72 6e 20 72 74 |);. return rt|
000000d0 76 3b 0a 7d |v;.}|
000000d4
lyf@lyf:~/test$ file main.o
main.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), with debug_info, not stripped
lyf@lyf:~/test$ file add.o
add.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), with debug_info, not stripped
lyf@lyf:~/test$ file main
main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=5b8fb49e162dbf05605114331a1fdfb524aefbbf, for GNU/Linux 3.2.0, with debug_info, not stripped
默認(rèn)是動(dòng)態(tài)鏈接的,那么鏈接了什么動(dòng)態(tài)庫(kù)?發(fā)現(xiàn)是libc。
lyf@lyf:~/test$ readelf -d ./main
Dynamic section at offset 0x2f14 contains 24 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
......
執(zhí)行應(yīng)用程序,
lyf@lyf:~/test$ ./main
lyf@lyf:~/test$ echo $?
7
改main為靜態(tài)鏈接,
lyf@lyf:~/test$ gcc -g -fno-pie -no-pie -m32 -static -o main main.o add.o
靜態(tài)鏈接后,main的大小為731K,大了很多。
lyf@lyf:~/test$ ls -hl
total 748K
-rw-r--r-- 1 lyf lyf 74 Dec 2 10:22 add.c
-rw-r--r-- 1 lyf lyf 2.0K Dec 2 10:42 add.o
-rwxr-xr-x 1 lyf lyf 731K Dec 2 13:51 main
-rw-r--r-- 1 lyf lyf 212 Dec 2 11:33 main.c
-rw-r--r-- 1 lyf lyf 2.3K Dec 2 11:34 main.o
如下圖所示,
?
如前面所介紹的,編譯鏈接后的硬盤中的main應(yīng)用程序是ELF格式的文件,通過(guò)shell執(zhí)行main后,會(huì)加載到內(nèi)存中。
?
?
符號(hào)對(duì)應(yīng)的就是地址。變量的符號(hào)是變量的地址,函數(shù)的符號(hào)是函數(shù)的地址。鏈接過(guò)程的本質(zhì)就是把多個(gè)不同的目標(biāo)(.o)文件之間相互“粘”在一起,是解決目標(biāo)文件之間對(duì)函數(shù)和變量地址的引用。
關(guān)于ELF文件格式的細(xì)節(jié),這里不做介紹,網(wǎng)上很多資料。應(yīng)用程序main通過(guò)編譯先有add.o和main.o,然后再鏈接生成main。因此接下來(lái)先分析.o中相關(guān)的符號(hào)(Symbol),然后再分析應(yīng)用程序main中對(duì)相關(guān)符號(hào)是如何處理的。
對(duì)于add.o,其中定義了一個(gè)全局變量global_extern_int和add函數(shù),
lyf@lyf:~/test$ readelf -s add.o
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS add.c
2: 00000000 0 SECTION LOCAL DEFAULT 1 .text
3: 00000000 0 SECTION LOCAL DEFAULT 4 .debug_info
4: 00000000 0 SECTION LOCAL DEFAULT 6 .debug_abbrev
5: 00000000 0 SECTION LOCAL DEFAULT 9 .debug_line
6: 00000000 0 SECTION LOCAL DEFAULT 11 .debug_str
7: 00000000 0 SECTION LOCAL DEFAULT 12 .debug_line_str
8: 00000000 4 OBJECT GLOBAL DEFAULT 2 global_extern_int
9: 00000000 13 FUNC GLOBAL DEFAULT 1 add
從上述輸出我們可以看出來(lái),全局變量global_extern_int和add函數(shù)都是符號(hào)(Symbol),且都是全局的(GLOBAL)。其中全局變量global_extern_int的Ndx為2(.data),add函數(shù)的Ndx為1(.text),對(duì)應(yīng)代碼段和數(shù)據(jù)段。函數(shù)的TYPE為FUNC,變量的TYPE為OBJECT。
下面是add.o中包含的所有的段(Section)的信息,符號(hào)表中的Ndx對(duì)應(yīng)段表中的Nr,表示該符號(hào)屬于哪個(gè)段(Section),
lyf@lyf:~/test$ readelf -S add.o
There are 20 section headers, starting at offset 0x4a4:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 00000d 00 AX 0 0 1
[ 2] .data PROGBITS 00000000 000044 000004 00 WA 0 0 4
[ 3] .bss NOBITS 00000000 000048 000000 00 WA 0 0 1
[ 4] .debug_info PROGBITS 00000000 000048 00006f 00 0 0 1
[ 5] .rel.debug_info REL 00000000 000374 000048 08 I 17 4 4
[ 6] .debug_abbrev PROGBITS 00000000 0000b7 000060 00 0 0 1
[ 7] .debug_aranges PROGBITS 00000000 000117 000020 00 0 0 1
[ 8] .rel.debug_a[...] REL 00000000 0003bc 000010 08 I 17 7 4
[ 9] .debug_line PROGBITS 00000000 000137 00004b 00 0 0 1
[10] .rel.debug_line REL 00000000 0003cc 000020 08 I 17 9 4
[11] .debug_str PROGBITS 00000000 000182 00009c 01 MS 0 0 1
[12] .debug_line_str PROGBITS 00000000 00021e 000030 01 MS 0 0 1
[13] .comment PROGBITS 00000000 00024e 00002c 01 MS 0 0 1
[14] .note.GNU-stack PROGBITS 00000000 00027a 000000 00 0 0 1
[15] .eh_frame PROGBITS 00000000 00027c 000038 00 A 0 0 4
[16] .rel.eh_frame REL 00000000 0003ec 000008 08 I 17 15 4
[17] .symtab SYMTAB 00000000 0002b4 0000a0 10 18 8 4
[18] .strtab STRTAB 00000000 000354 00001d 00 0 0 1
[19] .shstrtab STRTAB 00000000 0003f4 0000af 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), p (processor specific)
對(duì)于main.o而言,引用了add.o中的函數(shù)和全局變量,同時(shí)自己定義了全局變量global_int及局部變量rtv,那么main.o的符號(hào)表,
lyf@lyf:~/test$ readelf -s main.o
Symbol table '.symtab' contains 13 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS main.c
2: 00000000 0 SECTION LOCAL DEFAULT 1 .text
3: 00000004 4 OBJECT LOCAL DEFAULT 3 a.0
4: 00000000 0 SECTION LOCAL DEFAULT 5 .debug_info
5: 00000000 0 SECTION LOCAL DEFAULT 7 .debug_abbrev
6: 00000000 0 SECTION LOCAL DEFAULT 10 .debug_line
7: 00000000 0 SECTION LOCAL DEFAULT 12 .debug_str
8: 00000000 0 SECTION LOCAL DEFAULT 13 .debug_line_str
9: 00000000 4 OBJECT GLOBAL DEFAULT 3 global_int
10: 00000000 72 FUNC GLOBAL DEFAULT 1 main
11: 00000000 0 NOTYPE GLOBAL DEFAULT UND global_extern_int
12: 00000000 0 NOTYPE GLOBAL DEFAULT UND add
如上所示,
main.o中對(duì)符號(hào)的引用是如何進(jìn)行的?也就是如何獲取到所需要的符號(hào)的真實(shí)地址呢?
首先,因?yàn)閙ain.o要使用外部的符號(hào)(add.o中的),因此在main.o中有一個(gè)重定位段(.rel.text),告訴鏈接器該如何去做符號(hào)重定位。下面重定位段中的Offset指的是相對(duì)于.text段起始位置的偏移量。
lyf@lyf:~/test$ readelf -r ./main.o
Relocation section '.rel.text' at offset 0x484 contains 4 entries:
Offset Info Type Sym.Value Sym. Name
00000013 00000901 R_386_32 00000000 global_int
00000024 00000b01 R_386_32 00000000 global_extern_int
00000029 00000901 R_386_32 00000000 global_int
00000033 00000c02 R_386_PC32 00000000 add
......
Name | Value | Field | Calculation |
R_386_NONE | 0 | None | None |
R_386_32 | 2 | dword | S + A |
R_386_PC32 | 1 | dword | S + A – P |
R_386_GOT32 | 3 | dword | G + A |
R_386_PLT32 | 4 | dword | L + A – P |
R_386_COPY | 5 | None | Value is copied directly from shared object |
R_386_GLOB_DAT | 6 | dword | S |
R_386_JMP_SLOT | 7 | dword | S |
R_386_RELATIVE | 8 | dword | B + A |
R_386_GOTOFF | 9 | dword | S + A – GOT |
R_386_GOTPC | 10 | dword | GOT + A – P |
R_386_32PLT | 11 | dword | L + A |
R_386_16 | 20 | word | S + A |
R_386_PC16 | 21 | word | S + A – P |
R_386_8 | 22 | byte | S + A |
R_386_PC8 | 23 | byte | S + A – P |
R_386_SIZE32 | 38 | dword | z + A |
其中,R_386_32和R_386_PC32的重定位計(jì)算方式中,
下面是main.o中對(duì)應(yīng)代碼段的反匯編,
lyf@lyf:~/test$ objdump -drS -Mintel ./main.o
./main.o: file format elf32-i386
Disassembly of section .text:
00000000 <main>:
int add(int a_, int b_);
extern int global_extern_int;
int global_int = 3;
int main()
{
0: 8d 4c 24 04 lea ecx,[esp+0x4]
4: 83 e4 f0 and esp,0xfffffff0
7: ff 71 fc push DWORD PTR [ecx-0x4]
a: 55 push ebp
b: 89 e5 mov ebp,esp
d: 51 push ecx
e: 83 ec 14 sub esp,0x14
static int a = 19;
global_int = 5;
11: c7 05 00 00 00 00 05 mov DWORD PTR ds:0x0,0x5
18: 00 00 00
13: R_386_32 global_int
int rtv = 0;
1b: c7 45 f4 00 00 00 00 mov DWORD PTR [ebp-0xc],0x0
rtv = add(global_int, global_extern_int);
22: 8b 15 00 00 00 00 mov edx,DWORD PTR ds:0x0
24: R_386_32 global_extern_int
28: a1 00 00 00 00 mov eax,ds:0x0
29: R_386_32 global_int
2d: 83 ec 08 sub esp,0x8
30: 52 push edx
31: 50 push eax
32: e8 fc ff ff ff call 33 <main+0x33>
33: R_386_PC32 add
37: 83 c4 10 add esp,0x10
3a: 89 45 f4 mov DWORD PTR [ebp-0xc],eax
return rtv;
3d: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
40: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4]
43: c9 leave
44: 8d 61 fc lea esp,[ecx-0x4]
47: c3 ret
接下來(lái),看看粘和后的main應(yīng)用程序中對(duì)應(yīng)符號(hào)的地址,對(duì)應(yīng)公式中對(duì)應(yīng)的S,
lyf@lyf:~/test$ readelf -s ./main
Symbol table '.dynsym' contains 4 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND _[...]@GLIBC_2.34 (2)
2: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
3: 0804a004 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
Symbol table '.symtab' contains 40 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS crt1.o
2: 080481cc 32 OBJECT LOCAL DEFAULT 3 __abi_tag
3: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
4: 080490b0 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
5: 080490f0 0 FUNC LOCAL DEFAULT 13 register_tm_clones
6: 08049130 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
7: 0804c024 1 OBJECT LOCAL DEFAULT 24 completed.0
8: 0804bf10 0 OBJECT LOCAL DEFAULT 19 __do_global_dtor[...]
9: 08049160 0 FUNC LOCAL DEFAULT 13 frame_dummy
10: 0804bf0c 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_in[...]
11: 00000000 0 FILE LOCAL DEFAULT ABS main.c
12: 0804c01c 4 OBJECT LOCAL DEFAULT 23 a.0
13: 00000000 0 FILE LOCAL DEFAULT ABS add.c
14: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
15: 0804a0ec 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
16: 00000000 0 FILE LOCAL DEFAULT ABS
17: 0804bf14 0 OBJECT LOCAL DEFAULT 20 _DYNAMIC
18: 0804a008 0 NOTYPE LOCAL DEFAULT 16 __GNU_EH_FRAME_HDR
19: 0804c000 0 OBJECT LOCAL DEFAULT 22 _GLOBAL_OFFSET_TABLE_
20: 0804c020 4 OBJECT GLOBAL DEFAULT 23 global_extern_int
21: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_mai[...]
22: 080490a0 4 FUNC GLOBAL HIDDEN 13 __x86.get_pc_thunk.bx
23: 0804c010 0 NOTYPE WEAK DEFAULT 23 data_start
24: 080491ae 13 FUNC GLOBAL DEFAULT 13 add
25: 0804c018 4 OBJECT GLOBAL DEFAULT 23 global_int
26: 0804c024 0 NOTYPE GLOBAL DEFAULT 23 _edata
27: 080491bc 0 FUNC GLOBAL HIDDEN 14 _fini
28: 0804c010 0 NOTYPE GLOBAL DEFAULT 23 __data_start
29: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
30: 0804c014 0 OBJECT GLOBAL HIDDEN 23 __dso_handle
31: 0804a004 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
32: 0804c028 0 NOTYPE GLOBAL DEFAULT 24 _end
33: 08049090 5 FUNC GLOBAL HIDDEN 13 _dl_relocate_sta[...]
34: 08049050 49 FUNC GLOBAL DEFAULT 13 _start
35: 0804a000 4 OBJECT GLOBAL DEFAULT 15 _fp_hw
36: 0804c024 0 NOTYPE GLOBAL DEFAULT 24 __bss_start
37: 08049166 72 FUNC GLOBAL DEFAULT 13 main
38: 0804c024 0 OBJECT GLOBAL HIDDEN 23 __TMC_END__
39: 08049000 0 FUNC GLOBAL HIDDEN 11 _init
下面來(lái)確認(rèn)下,最終重定位的結(jié)果是否正確,查看應(yīng)用程序main的反匯編代碼,
lyf@lyf:~/test$ objdump -d -Mintel ./main
./main: file format elf32-i386
Disassembly of section .init:
08049000 <_init>:
8049000: f3 0f 1e fb endbr32
8049004: 53 push ebx
8049005: 83 ec 08 sub esp,0x8
8049008: e8 93 00 00 00 call 80490a0 <__x86.get_pc_thunk.bx>
804900d: 81 c3 f3 2f 00 00 add ebx,0x2ff3
8049013: 8b 83 fc ff ff ff mov eax,DWORD PTR [ebx-0x4]
8049019: 85 c0 test eax,eax
804901b: 74 02 je 804901f <_init+0x1f>
804901d: ff d0 call eax
804901f: 83 c4 08 add esp,0x8
8049022: 5b pop ebx
8049023: c3 ret
Disassembly of section .plt:
08049030 <__libc_start_main@plt-0x10>:
8049030: ff 35 04 c0 04 08 push DWORD PTR ds:0x804c004
8049036: ff 25 08 c0 04 08 jmp DWORD PTR ds:0x804c008
804903c: 00 00 add BYTE PTR [eax],al
...
08049040 <__libc_start_main@plt>:
8049040: ff 25 0c c0 04 08 jmp DWORD PTR ds:0x804c00c
8049046: 68 00 00 00 00 push 0x0
804904b: e9 e0 ff ff ff jmp 8049030 <_init+0x30>
Disassembly of section .text:
08049050 <_start>:
8049050: f3 0f 1e fb endbr32
8049054: 31 ed xor ebp,ebp
8049056: 5e pop esi
8049057: 89 e1 mov ecx,esp
8049059: 83 e4 f0 and esp,0xfffffff0
804905c: 50 push eax
804905d: 54 push esp
804905e: 52 push edx
804905f: e8 19 00 00 00 call 804907d <_start+0x2d>
8049064: 81 c3 9c 2f 00 00 add ebx,0x2f9c
804906a: 6a 00 push 0x0
804906c: 6a 00 push 0x0
804906e: 51 push ecx
804906f: 56 push esi
8049070: c7 c0 66 91 04 08 mov eax,0x8049166
8049076: 50 push eax
8049077: e8 c4 ff ff ff call 8049040 <__libc_start_main@plt>
804907c: f4 hlt
804907d: 8b 1c 24 mov ebx,DWORD PTR [esp]
8049080: c3 ret
8049081: 66 90 xchg ax,ax
8049083: 66 90 xchg ax,ax
8049085: 66 90 xchg ax,ax
8049087: 66 90 xchg ax,ax
8049089: 66 90 xchg ax,ax
804908b: 66 90 xchg ax,ax
804908d: 66 90 xchg ax,ax
804908f: 90 nop
08049166 <main>:
8049166: 8d 4c 24 04 lea ecx,[esp+0x4]
804916a: 83 e4 f0 and esp,0xfffffff0
804916d: ff 71 fc push DWORD PTR [ecx-0x4]
8049170: 55 push ebp
8049171: 89 e5 mov ebp,esp
8049173: 51 push ecx
8049174: 83 ec 14 sub esp,0x14
8049177: c7 05 18 c0 04 08 05 mov DWORD PTR ds:0x804c018,0x5
804917e: 00 00 00
8049181: c7 45 f4 00 00 00 00 mov DWORD PTR [ebp-0xc],0x0
8049188: 8b 15 20 c0 04 08 mov edx,DWORD PTR ds:0x804c020
804918e: a1 18 c0 04 08 mov eax,ds:0x804c018
8049193: 83 ec 08 sub esp,0x8
8049196: 52 push edx
8049197: 50 push eax
8049198: e8 11 00 00 00 call 80491ae <add>
804919d: 83 c4 10 add esp,0x10
80491a0: 89 45 f4 mov DWORD PTR [ebp-0xc],eax
80491a3: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
80491a6: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4]
80491a9: c9 leave
80491aa: 8d 61 fc lea esp,[ecx-0x4]
80491ad: c3 ret
080491ae <add>:
80491ae: 55 push ebp
80491af: 89 e5 mov ebp,esp
80491b1: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
80491b4: 8b 45 0c mov eax,DWORD PTR [ebp+0xc]
80491b7: 01 d0 add eax,edx
80491b9: 5d pop ebp
80491ba: c3 ret
Disassembly of section .fini:
080491bc <_fini>:
80491bc: f3 0f 1e fb endbr32
80491c0: 53 push ebx
80491c1: 83 ec 08 sub esp,0x8
80491c4: e8 d7 fe ff ff call 80490a0 <__x86.get_pc_thunk.bx>
80491c9: 81 c3 37 2e 00 00 add ebx,0x2e37
80491cf: 83 c4 08 add esp,0x8
80491d2: 5b pop ebx
80491d3: c3 ret
結(jié)合下圖和main的反匯編代碼,其中__libc_start_main的函數(shù)簽名如下所示,main函數(shù)是被其調(diào)用的,
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end));