1. 程式人生 > >linux反彙編除錯

linux反彙編除錯

反彙編有有以下幾種方法: 1.使用gcc -S test.c 或者gcc -S test.c>out.txt 2.使用gdb除錯,在除錯中輸入disass 函式名 就可以 3.objdump -D test 一般常用1,2兩種, ~~~~~C語言程式碼example.c
int triangle( int width, int height)
{
int arr{0,1,2,3,4};
int area;
area = width * height /2;
return (area);
}
void main()
{
triangle(5,4);

}

~~~~~gdb反彙編程式碼
$ gdb example
(gdb) disass main
Dump of assembler code for function main:
0x080483f6 <+0>:    push   %ebp
0x080483f7 <+1>:    mov    %esp,%ebp
0x080483f9 <+3>:    sub    $0x8,%esp

目標碼格式列表
--demangle
-C 將底層的符號名解碼成使用者級名字,除了去掉所有開頭的下劃線之外,還使得C++函式名以可理解的方式顯示出來。

—disassemble或者-d 反彙編可執行段

.—dissassemble-all或者-D 反彙編所有段

--debugging顯示除錯資訊。企圖解析儲存在檔案中的除錯資訊並以C語言的語法顯示出來。僅僅支援某些型別的除錯資訊。

--prefix-addresses反彙編的時候,顯示每一行的完整地址。這是一種比較老的反彙編格式。顯示效果並不理想,但可能會用到其中的某些顯示,自己可以對比。

--disassemble-zeroes  一般反彙編

輸出將省略大塊的零,該選項使得這些零塊也被反彙編

-EB -EL  指定位元組序  --endian={big|little}這個選項將影響反彙編出來的指令。little-endian就是我們當年在dos下玩彙編的時候常說的高位在高地址,x86都是這種。

--file-headers或者  -f 顯示objfile中每個檔案的整體頭部摘要資訊。

--section-headers;--headers 或者-h 顯示目標檔案各個section的頭部摘要資訊。

--info或者-i 顯示對於 -b 或者 -m 選項可用的架構和目標格式列表。顯示支援的目標檔案格式和CPU架構


--section=name或者-j name 僅僅顯示指定section的資訊

--line-numbers或者-l 用檔案名和行號標註相應的目的碼,僅僅和-d、-D或者-r一起使用使用-ld和使用-d的區別不是很大,在原始碼級除錯的時候有用,要求編譯時使用了-g之  類的除錯編譯選項。

--architecture=machine 或者-m machine指定反彙編目標檔案時使用的架構,當待反彙編檔案本身沒有描述架構資訊的時候(比如S-records),這個選項很有用。可以用-i選項列出這裡能夠指定的架構
--reloc或者-r 顯示檔案的重定位入口。如果和-d或者-D一起使用,重定位部分以反彙編後的格式顯示出來。

--dynamic-reloc    -R 顯示檔案的動態重定位入口,僅僅對於動態目標檔案有意義,比如某些共享庫。

--full-contents  -s 顯示指定section的完整內容。

objdump --section=.text -s inet.o | more
--source    -S 儘可能反彙編出原始碼,尤其當編譯的時候指定了-g這種除錯引數時,效果比較明顯。隱含了-d引數。
--show-raw-insn反彙編的時候,顯示每條彙編指令對應的機器碼,除非指定了--prefix-addresses,這將是預設選項。

--no-show-raw-insn

-S 儘可能反彙編出原始碼,尤其當編譯的時候指定了-g這種除錯引數時,

   效果比較明顯。隱含了-d引數。

-l 用檔名和行號標註相應的目的碼,僅僅和-d、-D或者-r一起使用

  使用-ld和使用-d的區別不是很大,在原始碼級除錯的時候有用,要求

  編譯時使用了-g之類的除錯編譯選項。

-j name 僅僅顯示指定section的資訊


如何使用linux下objdump命令對任意一個二進位制檔案進行反彙編?

可以使用如下命令:

objdump -D -b binary -m i386 a.bin

-D表示對全部檔案進行反彙編,-b表示二進位制,-m表示指令集架構,a.bin就是我們要反彙編的二進位制檔案

objdump -m可以檢視更多支援的指令集架構,如i386:x86-64,i8086等

另外上面的所有objdump命令的引數同樣適用於arm-linux-objdump。

同時我們也可以指定big-endian或little-endian(-EB或-EL),我們可以指定從某一個位置開始反彙編等。

objdump命令是Linux下的反彙編目標檔案或者可執行檔案的命令,它還有其他作用,下面以ELF格式可執行檔案test為例詳細介紹:

objdump -f test  顯示test的檔案頭資訊

objdump -d test  反彙編test中的需要執行指令的那些section

objdump -D test  與-d類似,但反彙編test中的所有section

objdump -h test  顯示test的Section Header資訊

objdump -x test  顯示test的全部Header資訊

objdump -s test  除了顯示test的全部Header資訊,還顯示他們對應的十六進位制檔案程式碼


使用arm-linux 工具鏈裡面的arm-linux-objdump 就能反彙編

cd到bin檔案所在的目錄, 在命令列下輸入:

arm-linux-objdump -D -b binary -m arm xxx.bin > xxx.asm

引數:

-D 反編譯所有程式碼

-m 主機型別, arm

-b 檔案格式, binary

對於ELF格式的檔案只要一個-D引數即可

就可以把xxx.bin反彙編到xxx.asm檔案


Arm-linux-objdump –D elf_file > dis_file 或者

Arm-linux-objdump –D –b binary –m arm bin_file > dis_file


記憶體地址反向查找出問題的程式:

<1>.通過彙編去查詢.
Linux 平臺:
  1. 在程式訊號處理部分, 加入程式碼捕捉引起錯誤點的地址,簡單來說,方法就是在註冊自己的訊號處理函式,在這個函式中加入獲取記憶體錯誤地址的程式碼,並把結果寫到一個日誌檔案中。
  2. 編譯 DEBUG 版本 程式 (compile 時用 -g , 生成可執行檔案後不用 strip 去掉symbol 資訊)
  3. 在程式出問題時, 檢視日誌記錄, 得到錯誤點的地址.
  4. 用objdump -S 匯出Debug 版本的彙編程式碼, 查詢錯誤地址, 則得出那條語句出錯.
windows 下c 語言除錯
  1. release 版編譯/連線選項, 把"generate debug info/" 打鉤選擇
  2.dumpbin /DISASM /OUT:dump.out.txt.1 prep.exe 可反編譯exe檔案
  3.得到程式非法地址(可從管理工具-》事件檢視器裡得到),與彙編比較。


對目標檔案:***.o:

arm-none-Linux-gnueabi-objdump -D  ./kernel/sched.o > sched.S

對可執行檔案***.bin:

arm-linux-objdump -D -b binary -m arm xxx.bin > xxx.asm


為了執行ARM彙編程式碼,需要使用交叉編譯器arm-linux-gcc對ARM彙編程式碼進行編譯。下載交叉編譯器安裝完畢後,對ARM彙編程式碼進行編譯。

arm-linux-gcc main.s -o main -nostdlib

編譯選項“-nostdlib”表示不使用任何執行時庫檔案,編譯生成的可執行檔案main只能在ARM體系結構的系統上執行。

通常認為,產生異常的地址是lr暫存器的值,從上面的異常資訊可以看到[lr]的值是c01a4e30。

接下來,我們可以通過核心映象檔案反彙編來找到這個地址。核心編譯完成後,會在核心程式碼根目錄下生成vmlinux檔案,我們可以通過以下命令來反彙編:

arm-none-eabi-objdump -Dz -S vmlinux >linux.dump

值得注意的是,arm-none-eabi-objdump的引數-S表示儘可能的把原來的程式碼和反彙編出來的程式碼一起呈現出來,-S引數需要結合 arm-linux-gcc編譯引數-g,才能達到反彙編時同時輸出原來的程式碼。所以,我在linux核心程式碼根目錄的Makefile中增加-g編譯參 數:

KBUILD_CFLAGS   := -g -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
     -fno-strict-aliasing -fno-common \
     -Werror-implicit-function-declaration \
     -Wno-format-security \
     -fno-delete-null-pointer-checks

修改Makefile後,重新編譯核心,在根目錄中生成的vmlinux檔案就會包含了原來的程式碼資訊,因此,該檔案的大小也比原來大一倍!

最後執行“arm-none-eabi-objdump -Dz-S vmlinux >linux.dump”,由於加入了-g編譯引數,執行這個反彙編命令需要很長時間(本人在虛擬機器上執行,花了近6個小時!),反彙編出來的linux.dump檔案也比原來的44MB增大到驚人的503MB。

(博主加:這裡有一點要注意,如果是ko模組檔案,反彙編時如果想看到ko檔案的某函式反彙編程式碼,該函式不能加static關鍵字修飾,而且module_init修飾的入口函式,其名字即為module_init)

接下來可以用UltraEdit開啟linux.dump檔案,查詢“c01a4e30”字串。

最後定位到的資訊是:

==================================================================================================================

/*
 * tasklet handling tty stuff outside the interrupt handler.
 */
static void atmel_tasklet_func(unsigned long data)
{
c01a4e20: e92d45f0  push {r4, r5, r6, r7, r8, sl, lr}
c01a4e24: e24dd01c  sub sp, sp, #28 ; 0x1c
c01a4e28: e1a04000  mov r4, r0
 /* The interrupt handler does not take the lock */
 spin_lock(&port->lock);

 if (atmel_use_pdc_tx(port))
  atmel_tx_pdc(port);
 else if (atmel_use_dma_tx(port))
c01a4e2c: ebfffda1  bl c01a44b8 <atmel_use_dma_tx>
c01a4e30: e3500000  cmp r0, #0 ; 0x0
c01a4e34: e5943034  ldr r3, [r4, #52]
c01a4e38: 0a00007b  beq c01a502c <atmel_tasklet_func+0x20c>

==================================================================================================================

可以看出來,異常的產生位於atmel_tasklet_func函式的 else if (atmel_use_dma_tx(port))一行

估計atmel_use_dma_tx(port)的“port”引數為空指標所致!