在程序集中跟踪一个简单的程序

1 assembly arm

我创建了一个简单的c程序来添加两个数字:

void main(){
     int a = 4;
     int b = 5;
     int c = a+b;
}
Run Code Online (Sandbox Code Playgroud)

并命名为test.c我使用"arm-linux-gcc -S test.c"来创建test.s(汇编代码)现在我想在每个汇编指令后看到16个寄存器中每个寄存器的值.我该怎么办?我没有任何组装经验,我对linux比较新,所以我不太了解所用的工具.请帮忙!提前致谢.

old*_*mer 5

那么你说的是两件不同的事情.如果要查看寄存器的内容,则需要执行程序,因此需要创建二进制文件.针对一个系统,然后单步执行它.是的,如果你有正确的gdb指向正确的系统,gdb将工作.您还可以使用jtag调试器,单步执行,然后转储寄存器.

这很少与汇编语言有关,你可能希望在单步执行时看到汇编语言级别的指令,当然,你需要编译成二进制文件才能运行它.

由于您的程序没有做任何事情,您需要注意不要优化,即使-O1优化也会删除您的代码.

这是值得一试的.我有一个拇指指令集模拟器,拇指是ARM的16位子集(仍然在ARM系列中,与ARM指令一对一的关系).转到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);
}
Run Code Online (Sandbox Code Playgroud)

添加printfs以显示寄存器.

进入blinker目录,将notmain()更改为类似于您的程序:

int notmain ( void )
{
    int a = 4;
    int b = 5;
    int c;
    c = a + b;
    return(0);
}
Run Code Online (Sandbox Code Playgroud)

编辑Makefile

更改此行以使用您的编译器(我在构建期间添加-gcc).

ARMGNU = arm-linux

从此行中删除-O2:

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

并将其更改为仅构建gnu/gcc二进制文件而不是llvm二进制文件.

all: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}
Run Code Online (Sandbox Code Playgroud)

您还将看到一些启动处理器的代码:

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>
Run Code Online (Sandbox Code Playgroud)

这与你在ARM上看到的不一样,缩略图引导像ARM cortex-m3,而不像传统的基于ARM的ARM指令集.因此,在这种情况下,地址4处的数字是要重置的地址,(lsbit设置为指示拇指模式,因此地址实际上是0x52).然后_start代码调用notmain,你得到你的代码.

我提到这个的原因是因为当你运行缩略图(./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 0x00000000 r7 0x4007FFE8 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFE8 r14 0x00000057 r15 0x0000007F - 0x0000007C -

- 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
Run Code Online (Sandbox Code Playgroud)

基本上是这样的:

int notmain ( void )
{
    return(0);
}
Run Code Online (Sandbox Code Playgroud)

该计划的肉.

如果你想看看你想要做什么的优化代码,IN A SEPARATE .C FILE把这段代码:

int xfun ( int a, int b )
{
    return(a+b);
}
Run Code Online (Sandbox Code Playgroud)

将其添加到项目中(将其单独编译为自己的.o文件).

改变不归于此

int xfun ( int, int );
int notmain ( void )
{
    return(xfun(4,5));
}
Run Code Online (Sandbox Code Playgroud)

现在你看到了你可能感兴趣的那种东西.

00000080 <xfun>:
  80:   1808        adds    r0, r1, r0
  82:   4770        bx  lr
Run Code Online (Sandbox Code Playgroud)

用缩略图模拟它并查看前后指令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 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFF8 r14 0x0000007F r15 0x00000085

在r0为4之前,r1为5,发生add r0 = r0 + r1,r0现为9.

现在你真的不需要看静态寄存器来遵循代码,这是一个相当痛苦的方法.回到刚刚拆解,而不是使用缩略图或任何执行和转储指令:

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
Run Code Online (Sandbox Code Playgroud)

movs是一个更新了标志的移动,将在一秒内到达ARM代码.将数字4移动到r0,然后将数字5移动到r1,bl分支链接基本上它是一个在r14中设置了返回值的分支,因此你可以返回(函数调用而不是分支).bl到xfun我们看到r0,r1,r0的加法,这意味着r0 = r1 + r0所以我们知道r0被破坏了,现在是4是9.基本上你程序的内容已经完成了.

现在,回到ARM指令,这对我正在使用的编译器很有意思,使用您的编译器变体进行演示:

arm-linux-gcc -S -O2 notmain.c
Run Code Online (Sandbox Code Playgroud)

mov r0, #4
mov r1, #5
b   xfun
Run Code Online (Sandbox Code Playgroud)

当你挖掘它的肉时,尾部优化的事情是xfun的返回值与notmain的返回值相同所以它保持r14未修改并让xfun返回到名为notmain的任何人.

另一个档案:

arm-linux-gcc -S -O2 xbox.c
Run Code Online (Sandbox Code Playgroud)

add r0, r1, r0
bx  lr
Run Code Online (Sandbox Code Playgroud)

由于ARM指令可以选择修改标志,你不会看到movs并添加你看到mov和add,因为代码没有做任何与这些指令相关的条件因此它们没有生成s版本.

当我打字时,我确实处于几个gidhub项目的中间.mbed_samples是一个基于ARM cortex-m3(仅限拇指/拇指指令)微控制器板.我写了类似的关于引导和使用工具等的长篇小说,以便构建和执行二进制文件(不久之后你会想做的事情).我前几天刚刚发布了lsasim,而不是ARM,与ARM无关,但在那里有LEARNASM.txt,这对于第一次学习ASM 可能很有用.C编译器后端是非常粗糙的,我不会真的搞乱它,请浏览LEARNASM.txt教程.然后转到ARMs网站http://infocenter.arm.com,在左侧内容下单击以展开ARM体系结构,然后单击参考手册,查看可用的参考手册.在右侧,它将向您显示ARMv5 ARM(架构参考手册),它曾经被称为ARM ARM,之前它们有许多不同的内核.这将列出您正在构建的传统ARM 32位指令集.它还有拇指指令集,可在大多数当前的ARM内核上运行.Thumb2仅适用于某些核心,您可能需要类似ARMv7-M ARM的cortex-m3,它取代了嵌入式微控制器的ARM7(ARM7是ARMv4T,我知道数字可能令人困惑).QEMU可以运行ARM,thumb和thumb2指令,但是让一些程序在你能够看到正在发生的事情的情况下运行会花费你很长时间.gdb里面有ARMulator,这是ARM长期以来出于各种原因使用的,我没有使用gdb所以不知道什么比那里有一个ARM模拟器(它有拇指,也许thumb2以及如果那个重要的是).这可能是你运行某些东西和转储寄存器的最快路径,一个基于arm的gdb.也许代码源附带一个,如果不是寻找emdebian或只是建立自己的.thumbulator也是一个选项,但我严格限制它用拇指指示,它像一个cortex-m3而不是一个手臂(如果你想的话很容易改变).mame有一个手臂它但它可以在mame之外的任何地方进行编译并为它提供程序可能比gdb中的armulator源更多的工作.

一个完全不同的道路是从sparkfun.com获得类似于olimex sam7-h64板的东西,他们以大约16美元左右的价格出售它们,曾经是两倍或三倍.它是一个ARM7,将运行ARM指令.得到一个olimex wiggler或者我更喜欢amontek jtag-tiny.然后你可以使用jtag将程序加载到ram中,然后单步执行它们随时转储任何或所有寄存器或内存.如果运行ARM指令但是在板上内置了一个jtag,那么我无法想到我所知道的任何电路板,今天对于ftdi芯片来说是微不足道的,而基于cortex-m的微控制器正在出现这种情况.有点像ftdi芯片前面提供串行和bit banging jtag或者更常见的板子实际上有两个微控制器来自那个供应商,一个是usb接口,你不能重新编程另一个是你买的板子的芯片要使用,前置微控制器处理jtag的东西.其中,stellaris让你进入jtag

我知道,反应很长,消化很多.值得努力的是,如果您或其他人阅读此内容需要花时间学习装配并保持知识的存在.有人必须继续开发处理器和编译器的能力,就像那些在有C语言之前长大的老计时器一样,退休.只是你看起来想要的知识,拿几行C代码并查看程序集,即使你从不使用日常装配,也教会你写出更好,更快,更可靠的程序.

祝好运.