汇编语言使用C库函数和Linux动态链接
使用printf
代碼
#cpuid2.s -- Using C labrary calls
.section .data
output: .asciz "The processor Vender is '%s'\n"
.section .bss .lcomm buffer, 12
.section .text
.globl _start
_start: movl $0, %eax cpuid movl $buffer, %edi movl %ebx, (%edi) //含義同cpuid.s,向%edi所指向的buffer內存 movl %edx, 4(%edi) //位置寫入3個寄存器的內容,這3個寄存器存儲了 movl %ecx, 8(%edi) //CPU廠商信息的字符串(12字節) pushl $buffer //將buffer和output入棧,為printf提供參數 pushl $output call printf //調用C的printf函數 addl $8, %esp //將堆棧指針回滾8個字節,達到清除printf參數的目的 pushl $0 call exit
?
.asciz是在定義字符串的時侯在字符串結尾加上空字符(即C語言的\0),這樣做的目的是為了讓printf能讀懂字符串。
.lcomm是在本地內存區域中聲明固定長度的未初始化數據,這里初始化了12個字節的空間。
程序里buffer和output內存位置的內容是要向printf傳遞的參數值:
一個是"The processor Vender is '%s'\n"字符串;
另外一個是由cpuid返回結果(在ebx,edx,ecx三個寄存器中)填充的buffer。
需要通過堆棧來傳遞參數,所以在程序中使用
pushl $buffer
pushl $output將參數入棧,printf獲取參數是自右向左,即先buffer后output,所以要把buffer后入棧。
參數入棧之后,用call指令調用printf。
exit的情況同上,使用了一個參數--常數0。
匯編
#as -o cpuid2.o cpuid2.s
采用了動態連接的方式,所以C函數沒有包含在可執行程序中,需要由另外的程序在運行時加載,ld不知道這個程序在哪里,所以我們還得手動指定
動態鏈接
#ld -dynamic-linker /lib/ld-linux.so.2 -lc -o cpuid2 cpuid2.o
#./cpuid2
輸出
The processor Vendor ID is 'GenuineIntel'
GDB調試
# gdb cpuid2
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-56.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/zhms/AS/chap04/cpuid2...done.
(gdb) break _start
Breakpoint 1 at 0x80481b8: file cpuid2.s, line 10.
(gdb) r
Starting program: /usr/zhms/AS/chap04/cpuid2 Breakpoint 1, _start () at cpuid2.s:10
10 movl $0, %eax
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.80.el6_3.6.i686
(gdb) i r
eax 0x0 0
ecx 0x0 0
edx 0x758470 7701616
ebx 0x767fc4 7765956
esp 0xbffff6c0 0xbffff6c0
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80481b8 134513080
eip 0x80481b8 0x80481b8 <_start>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
11 cpuid
(gdb) i r
eax 0x0 0
ecx 0x0 0
edx 0x758470 7701616
ebx 0x767fc4 7765956
esp 0xbffff6c0 0xbffff6c0
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80481b8 134513080
eip 0x80481bd 0x80481bd <_start+5>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
13 movl %ebx, (%edi)
(gdb) i r
eax 0x5 5
ecx 0x6c65746e 1818588270
edx 0x49656e69 1231384169
ebx 0x756e6547 1970169159
esp 0xbffff6c0 0xbffff6c0
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x80481c4 0x80481c4 <_start+12>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
14 movl %edx, 4(%edi)
(gdb) i r
eax 0x5 5
ecx 0x6c65746e 1818588270
edx 0x49656e69 1231384169
ebx 0x756e6547 1970169159
esp 0xbffff6c0 0xbffff6c0
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x80481c6 0x80481c6 <_start+14>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
15 movl %ecx, 8(%edi)
(gdb) i r
eax 0x5 5
ecx 0x6c65746e 1818588270
edx 0x49656e69 1231384169
ebx 0x756e6547 1970169159
esp 0xbffff6c0 0xbffff6c0
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x80481c9 0x80481c9 <_start+17>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
16 pushl $buffer
(gdb) i r
eax 0x5 5
ecx 0x6c65746e 1818588270
edx 0x49656e69 1231384169
ebx 0x756e6547 1970169159
esp 0xbffff6c0 0xbffff6c0
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x80481cc 0x80481cc <_start+20>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
17 pushl $output
(gdb) i r
eax 0x5 5
ecx 0x6c65746e 1818588270
edx 0x49656e69 1231384169
ebx 0x756e6547 1970169159
esp 0xbffff6bc 0xbffff6bc
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x80481d1 0x80481d1 <_start+25>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
18 call printf
(gdb) i r
eax 0x5 5
ecx 0x6c65746e 1818588270
edx 0x49656e69 1231384169
ebx 0x756e6547 1970169159
esp 0xbffff6b8 0xbffff6b8
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x80481d6 0x80481d6 <_start+30>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
0x007b63e0 in printf () from /lib/libc.so.6
(gdb) i r
eax 0x5 5
ecx 0x6c65746e 1818588270
edx 0x49656e69 1231384169
ebx 0x756e6547 1970169159
esp 0xbffff6b4 0xbffff6b4
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x7b63e0 0x7b63e0 <printf>
eflags 0x246 [ PF ZF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
Single stepping until exit from function printf,
which has no line number information.
The processor Vendor ID is 'GenuineIntel'
_start () at cpuid2.s:19
19 addl $8, %esp
(gdb) i r
eax 0x2a 42
ecx 0xbffff6a0 -1073744224
edx 0x8fe364 9429860
ebx 0x756e6547 1970169159
esp 0xbffff6b8 0xbffff6b8
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x80481db 0x80481db <_start+35>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
20 pushl $0
(gdb) i r
eax 0x2a 42
ecx 0xbffff6a0 -1073744224
edx 0x8fe364 9429860
ebx 0x756e6547 1970169159
esp 0xbffff6c0 0xbffff6c0
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x80481de 0x80481de <_start+38>
eflags 0x296 [ PF AF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
21 call exit
(gdb) i r
eax 0x2a 42
ecx 0xbffff6a0 -1073744224
edx 0x8fe364 9429860
ebx 0x756e6547 1970169159
esp 0xbffff6bc 0xbffff6bc
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x80481e0 0x80481e0 <_start+40>
eflags 0x296 [ PF AF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
0x00799060 in exit () from /lib/libc.so.6
(gdb) i r
eax 0x2a 42
ecx 0xbffff6a0 -1073744224
edx 0x8fe364 9429860
ebx 0x756e6547 1970169159
esp 0xbffff6b8 0xbffff6b8
ebp 0x0 0x0
esi 0xbffff6cc -1073744180
edi 0x80492c0 134517440
eip 0x799060 0x799060 <exit>
eflags 0x246 [ PF ZF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) s
Single stepping until exit from function exit,
which has no line number information.Program exited normally.
(gdb) i r
The program has no registers now.
(gdb)
?
ld-linux.so查找共享庫的順序
這里引出一個問題,怎么知道ld-linux.so.2是什么以及定位
Glibc安裝的庫中有一個為ld-linux.so.X,其中X為一個數字,在不同的平臺上名字也會不同。可以用ldd查看:
#ldd /bin/cat
最后一個沒有=>的就是。其中第一個不是實際的庫文件,你是找不到的,它是一個虛擬庫文件用于和kernel交互。
ld-linux.so是專門負責尋找庫文件的庫。以cat為例,cat首先告訴ld-linux.so它需要libc.so.6這個庫文件,ld-linux.so將按一定順序找到libc.so.6庫再給cat調用。
那ld-linux.so又是怎么找到的呢?其實不用找,ld-linux.so的位置是寫死在程序中的,gcc在編譯程序時就寫死在里面了。Gcc寫到程序中ld-linux.so的位置是可以改變的,通過修改gcc的spec文件。
運行時,ld-linux.so查找共享庫的順序
(1)ld-linux.so.6在可執行的目標文件中被指定,可用readelf命令查看
(2)ld-linux.so.6缺省在/usr/lib和lib中搜索;當glibc安裝到/usr/local下時,它查找/usr/local/lib
(3)LD_LIBRARY_PATH環境變量中所設定的路徑
(4)/etc/ld.so.conf(或/usr/local/etc/ld.so.conf)中所指定的路徑,由ldconfig生成二進制的ld.so.cache中
編譯時,ld-linux.so查找共享庫的順序
(1)ld-linux.so.6由gcc的spec文件中所設定
(2)gcc --print-search-dirs所打印出的路徑,主要是libgcc_s.so等庫。可以通過GCC_EXEC_PREFIX來設定
(3)LIBRARY_PATH環境變量中所設定的路徑,或編譯的命令行中指定的-L/usr/local/lib
(4)binutils中的ld所設定的缺省搜索路徑順序,編譯binutils時指定。(可以通過“ld --verbose | grep SEARCH”來查看)
(5)二進制程序的搜索路徑順序為PATH環境變量中所設定。一般/usr/local/bin高于/usr/bin
(6)編譯時的頭文件的搜索路徑順序,與library的查找順序類似。一般/usr/local/include高于/usr/include
用 Linux 進行動態鏈接
ELF映像
Linux 中的動態鏈接的共享庫的過程。當用戶啟動一個應用程序時,它們正在調用一個可執行和鏈接格式(Executable and Linking Format,ELF)映像。內核首先將 ELF 映像加載到用戶空間虛擬內存中。然后內核會注意到一個稱為 .interp 的 ELF 部分,它指明了將要被使用的動態鏈接器(例如:/lib/ld-linux.so)。
一個ELF頭在文件的開始,保存了路線圖(road map),描述了該文件的組織情況。sections保存著object 文件的信息,從連接角度看:包括指令,數據,符號表,重定位信息等等。
?
使用 readelf 來顯示程序標題
#readelf -l cpuid2
注意,ld-linux.so 本身就是一個 ELF 共享庫,但它是靜態編譯的并且不具備共享庫依賴項。當需要動態鏈接時,內核會引導動態鏈接(ELF 解釋器),該鏈接首先會初始化自身,然后加載指定的共享對象(已加載則不必)。接著它會執行必要的再定位,包括目標共享對象所使用的共享對象。
#readelf -l cpuid2.o
There are no program headers in this file.
ldd命令
Linux 提供了很多種查看和解析 ELF 對象(包括共享庫)的工具。其中最有用的一個當屬 ldd命令,可以使用它來發現共享庫依賴項。
#ldd cpuid2
ldd所告訴您的是:該 ELF 映像依賴于 linux-gate.so(一個特殊的共享對象,它處理系統調用,它在文件系統中無關聯文件),GNU C庫(libc.so)以及 Linux 動態加載器(因為它里面有共享庫依賴項)。
#ldd cpuid2.o
ldd: 警告: 你沒有執行權限 `./cpuid2.o'
不是動態可執行文件
readelf識別對象內可再定位的C庫
readelf 命令是一個有很多特性的實用程序,它讓您能夠解析和讀取 ELF 對象。readelf 有一個有趣的用途,就是用來識別對象內可再定位的項。對于我們這個簡單的程序來說,可以看到需要再定位的符號
#readelf -r cpuid2
從這個列表中,您可以看到各種各樣的需要再定位(到 libc.so)的 C庫調用。
#readelf -r cpuid2.o
readelf查看共享庫的依賴庫(NEEDED)和搜索名(SONAME)
#readelf -d cpuid2
#readelf -d cpuid2.o
無輸出
readelf查看ELF頭信息
#readelf -h cpuid2
#readelf -h cpuid2.o
附錄
#man readelf
?
objdump
objdump是用查看目標文件或者可執行的目標文件的構成的GCC工具
反匯編
#objdump -d cpuid2
對于其中的反匯編代碼
左邊是機器指令的字節,右邊是反匯編結果。顯然,所有的符號都被替換成地址了, 注意沒有加$的數表示內存地址,而不表示立即數。
objdump -x obj 以某種分類信息的形式把目標文件的數據組織(被分為幾大塊)輸出 <可查到該文件的所有動態庫>
objdump -t obj 輸出目標文件的符號表()
objdump -h obj 輸出目標文件的所有段概括()
objdump -j .text/.data -S obj 輸出指定段的信息,大概就是反匯編源代碼把
objdump -S obj C語言與匯編語言同時顯示
更多參考
#man objdump
?
?
參考:Linux 動態庫剖析
http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/
總結
以上是生活随笔為你收集整理的汇编语言使用C库函数和Linux动态链接的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用cat /proc/进程id/map
- 下一篇: gcc使用总结