繁体   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