簡體   English   中英

在匯編中跟蹤一個簡單的程序

[英]Tracing a simple program in assembly

我創建了一個簡單的 c 程序來添加兩個數字:

void main(){
     int a = 4;
     int b = 5;
     int c = a+b;
}

並將其命名為 test.c 我使用“arm-linux-gcc -S test.c”來創建 test.s(匯編代碼) 現在我想在每條匯編指令之后查看 16 個寄存器中每個寄存器的值。 我應該怎么辦? 我沒有任何組裝經驗,而且我對 linux 比較陌生,所以我不太了解使用的工具。 請幫忙。 提前致謝。

那么你在談論兩個不同的事情。 如果要查看寄存器的內容,則需要執行程序,因此需要制作二進制文件。 以系統為目標,然后單步執行。 是的,如果您有正確的 gdb 指向正確的系統,則 gdb 將起作用。 您還可以使用 jtag 調試器、單步然后轉儲寄存器。

這與匯編語言幾乎沒有任何關系,您肯定會希望在單步執行時查看匯編語言級別的指令,但是您需要編譯為二進制文件才能運行它。

由於您的程序不執行任何操作,因此您需要注意不要優化,即使是 -O1 優化也會刪除您的代碼。

這里有一些東西可以嘗試。 我有一個拇指指令集模擬器,拇指是 ARM 的 16 位子集(仍在 ARM 系列中,與 ARM 指令一對一關系)。 Go 到 github 並下載。 在 thumbulator.c 更改此循環:

int run ( void )
{
    unsigned int ra;
    reset();
    while(1)
    {
        printf("-- 0x%08X --\n",reg_norm[15]-3);
        if(execute()) break;
        for(ra=0;ra< 8;ra++) printf("r%u 0x%08X ",ra,reg_norm[ra]); printf("\n");
        for(    ;ra<16;ra++) printf("r%u 0x%08X ",ra,reg_norm[ra]); printf("\n");

    }
    dump_counters();
    return(0);
}

添加 printfs 以顯示寄存器。

Go 進入blinker 目錄,更改notmain() 以類似於您的程序:

int notmain ( void )
{
    int a = 4;
    int b = 5;
    int c;
    c = a + b;
    return(0);
}

編輯 Makefile

更改此行以使用您的編譯器(我在構建期間添加了 -gcc)。

ARMGNU = arm-linux

從此行中刪除 -O2 :

COPS = -Wall -mthumb -nostdlib -nostartfiles -ffreestanding

並將其更改為僅構建 gnu/gcc 二進制文件而不是 llvm 二進制文件。

全部:gnotmain.bin

現在構建它。

查看文件 gnotmain.list,您會看到類似的內容,但不一定完全如此,這取決於您的 gcc。

00000074 <notmain>:
  74:   b580        push    {r7, lr}
  76:   b084        sub sp, #16
  78:   af00        add r7, sp, #0
  7a:   2304        movs    r3, #4
  7c:   60fb        str r3, [r7, #12]
  7e:   2305        movs    r3, #5
  80:   60bb        str r3, [r7, #8]
  82:   68fa        ldr r2, [r7, #12]
  84:   68bb        ldr r3, [r7, #8]
  86:   18d3        adds    r3, r2, r3
  88:   607b        str r3, [r7, #4]
  8a:   2300        movs    r3, #0
  8c:   1c18        adds    r0, r3, #0
  8e:   46bd        mov sp, r7
  90:   b004        add sp, #16
  92:   bd80        pop {r7, pc}

您還將看到一些啟動處理器的代碼:

00000000 <hang-0x50>:
   0:   40080000    andmi   r0, r8, r0
   4:   00000053    andeq   r0, r0, r3, asr r0
   8:   00000051    andeq   r0, r0, r1, asr r0
...

00000052 <_start>:
  52:   f000 f80f   bl  74 <notmain>
  56:   df01        svc 1
  58:   e7fe        b.n 58 <_start+0x6>

Which is different than what you will see on an ARM, thumbulator boots like an ARM cortex-m3 not like a traditional ARM instruction set based ARM. 所以在這種情況下,地址 4 處的數字就是要重置的地址,(lsbit 設置為表示拇指模式,所以地址實際上是 0x52)。 然后 _start 代碼調用 notmain ,你就可以得到你的代碼。

我提到這一點的原因是因為當你運行thumbulator (./thumbulator blinker/gnotmain.bin) 並且你添加的那些printfs 轉儲出所有寄存器時,你會看到它在notmain 之前和之后做了一些事情。

-- 0x00000052 -- r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x40080000 r14 0xFFFFFFFF r15 0x00000057 -- 0x00000054 -- r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x40080000 r14 0x00000057 r15 0x00000077 -- 0x00000074 -- r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFF8 r14 0x00000057 r15 0x00000079 -- 0x00000076 -- r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFE8 r14 0x00000057 r15 0x0000007B -- 0x0000007 8 -- r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x4007FFE8 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFE8 r14 0x00000057 r15 0x0000007D -- 0x0000007A -- r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000004 r4 0x00000000 R5 0x00000000 r6 0x000000000000000000007FFE8 R8 R8 0x00000000 R9 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000來

-- 0x00000052 -- 是執行的第一條指令,是_start之后的第一條指令,它是一條兩條指令,所以0x52和0x54,分支到0x74,這是notmain的開始。 就我的編譯而言,它以 r7 的推送開始,因此 r13 應該更改以反映推送的內容。 下一條指令 sub sp,#16,再次 r13 將改變(sp 是 r13,堆棧指針)。

在 0x7A,我們到達 C 代碼的第一位。 mov r3,#4 然后將其存儲到 0x7C 的堆棧中(這不是優化代碼)。 然后 b = 5 行代碼 c = a + b 涉及大量堆棧的東西(未優化)。 並且它蜿蜒而下。

優化與它有什么關系? 那么你的程序實際上什么都不做,所以如果你要優化它(把 -O2 放回 COPS 環境變量中)你會得到這個:

00000074 <notmain>:
  74:   2000        movs    r0, #0
  76:   4770        bx  lr

基本上是這樣的:

int notmain ( void )
{
    return(0);
}

節目的肉。

如果您想查看您正在嘗試執行的優化代碼,請在 SEPARATE.C 文件中輸入以下代碼:

int xfun ( int a, int b )
{
    return(a+b);
}

將其添加到項目中(單獨編譯。到它自己的.o 文件中)。

將 notmain 更改為此

int xfun ( int, int );
int notmain ( void )
{
    return(xfun(4,5));
}

現在你看到了你可能感興趣的東西。

00000080 <xfun>:
  80:   1808        adds    r0, r1, r0
  82:   4770        bx  lr

用 thumbulator 模擬,看看指令 0x80 之前和之后

-- 0x0000007C -- r0 0x00000004 r1 0x00000005 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFF8 r14 0x0000007F r15 0x00000083 -- 0x00000080 -- r0 0x00000009 r1 0x00000005 r2 0x00000000 r3 0x00000000 R4 0x00000000 R5 0x00000000 r6 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000個R9 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000來

在 r0 為 4 之前,r1 為 5,添加 r0 = r0 + r1 發生並且 r0 現在為 9。

現在您真的不需要查看 static 寄存器來跟蹤代碼,這是一種相當痛苦的方法。 回到只是反匯編,而不是使用 thumbulator 或任何東西來執行和轉儲指令:

00000074 <notmain>:
  74:   b508        push    {r3, lr}
  76:   2004        movs    r0, #4
  78:   2105        movs    r1, #5
  7a:   f000 f801   bl  80 <xfun>
  7e:   bd08        pop {r3, pc}

00000080 <xfun>:
  80:   1808        adds    r0, r1, r0
  82:   4770        bx  lr

movs 是更新標志的動作,將在幾秒鍾內到達 ARM 代碼。 將數字 4 移動到 r0,然后將數字 5 移動到 r1,bl 分支鏈接基本上是一個分支,其返回值設置在 r14 中,因此您可以返回(一個 function 調用而不是分支)。 bl 到 xfun 我們看到了 r0, r1, r0 的加法,這意味着 r0 = r1 + r0 所以我們知道 r0 被破壞了,原來是 4 現在是 9。基本上你的程序的內容就完成了。

所以現在,回到 ARM 指令,這對我正在使用的編譯器很有趣,使用你的編譯器變體進行演示:

arm-linux-gcc -S -O2 notmain.c

mov r0, #4
mov r1, #5
b   xfun

當你深入研究它的實質時,尾部優化的事情xfun 的返回值與notmain 的返回值相同,因此它使r14 保持不變並讓xfun 返回給名為notmain 的任何人。

另一個文件:

arm-linux-gcc -S -O2 xbox.c

add r0, r1, r0
bx  lr

由於 ARM 指令可以選擇修改標志或不修改標志,因此您看不到 movs 並添加您看到 mov 和 add 因為代碼沒有執行與這些指令相關的任何條件,因此它們沒有生成 s 版本。

當我輸入這個時,我實際上正處於幾個 gidhub 項目的中間。 mbed_samples 是基於 ARM cortex-m3(僅限thumb/thumb2 指令)微控制器板。 我寫了關於啟動和使用工具等類似的長篇文章,以便構建和執行二進制文件(你很快就會想做的事情)。 前幾天我也剛剛發布了 lsasim,不是 ARM,與 ARM 無關,但其中有 LEARNASM.txt,這可能對第一次學習 ASM 有用。 C 編譯器后端非常簡陋,我不會真正搞砸它,請瀏覽 LEARNASM.txt 教程。 Then go to ARMs website http://infocenter.arm.com on the left under contents click on to expand ARM architecture, then click on Reference Manual, to see the reference manuals available. 在右側,它將向您展示 ARMv5 ARM(架構參考手冊),它曾經被稱為 ARM ARM,之前它們有很多不同的內核。 這將列出您正在構建的傳統 ARM 32 位指令集。 它還具有 thumb 指令集,可在大多數當前的 ARM 內核上運行。 Thumb2 僅在某些內核上,您可能需要類似 ARMv7-M ARM 的 cortex-m3,它正在取代嵌入式微控制器的 ARM7(ARM7 是 ARMv4T,我知道數字可能會令人困惑)。 QEMU 可以運行 ARM、thumb 和 thumb2 指令,但是讓一些程序運行在你可以看到正在發生的事情的地方需要很長時間。 gdb has the ARMulator in it, which is what ARM used for a long time for various reasons, I have no use for gdb so dont know anything more than there is an ARM simulator in there (it does thumb and maybe thumb2 as well if that很重要)。 這可能是您運行某些東西和轉儲寄存器的最快途徑,基於 gdb 的 arm。 也許 codesourcery 附帶一個,如果不是尋找 emdebian 或者只是構建你自己的。 thumbulator 也是一個選項,但我嚴格限制它使用拇指指令,它像 cortex-m3 一樣啟動,而不是 arm(如果你願意,很容易改變)。 mame 有一個 arm 它,但是讓它在 mame 之外的任何地方編譯並為其提供程序可能比 gdb 中的 armulator 源更多的工作。

一條完全不同的路徑是從 sparkfun.com 購買 olimex sam7-h64 板,他們以每塊 16 美元左右的價格出售,過去是兩倍或三倍。 它是一個 ARM7,將運行 ARM 指令。 找一個 olimex wiggler 或者我更喜歡 amontek jtag-tiny。 然后,您可以使用 jtag 將程序加載到 ram 中,並在需要時單步轉儲任何或所有寄存器或 memory。 我無法想到任何我知道的板子是否運行 ARM 指令,但是板子中內置了一個 jtag,今天與 ftdi 芯片無關,而是基於 cortex-m 的微控制器出現了一種東西,要么是提供串行和位敲擊 jtag 的 ftdi 芯片,要么是更常見的電路板實際上有兩個來自該供應商的微控制器,一個是 usb 接口,你不能重新編程另一個是你購買電路板的芯片玩,前面的微控制器處理jtag的東西。 其中,恆星讓你使用 jtag

我知道,回復很長,需要消化很多。 如果您或其他閱讀本文的人花時間學習組裝並保持知識的活力,那么值得付出努力。 有人必須繼續開發處理器和編譯器的能力,因為在 C 語言出現之前就長大了,退休了。 只是你似乎所追求的知識,用幾行 C 代碼並查看程序集,即使你每天從不使用程序集,也會教你編寫更好、更快、更可靠的程序。

祝你好運。

objdump 應該能夠反匯編您的 ARM 匯編代碼。 另一個選項可能是 gdb,但我不確定它是否支持 ARM。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM