简体   繁体   English

在汇编中跟踪一个简单的程序

[英]Tracing a simple program in assembly

I have created a simple c program to add two numbers:我创建了一个简单的 c 程序来添加两个数字:

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

and named it test.c I used "arm-linux-gcc -S test.c" to create test.s (Assembly code) Now I want to see the value of each of 16 registers after each assembly instruction.并将其命名为 test.c 我使用“arm-linux-gcc -S test.c”来创建 test.s(汇编代码) 现在我想在每条汇编指令之后查看 16 个寄存器中每个寄存器的值。 What should I do?我应该怎么办? I dont have any experience in assembly and I am relatively new to linux so I am not much aware of the tools used.我没有任何组装经验,而且我对 linux 比较陌生,所以我不太了解使用的工具。 Please help.请帮忙。 Thanks in advance.提前致谢。

Well you are talking about two different things.那么你在谈论两个不同的事情。 If you want to see the contents of registers you need to execute the program so you need to make a binary.如果要查看寄存器的内容,则需要执行程序,因此需要制作二进制文件。 Targeted at a system, and then single step through it.以系统为目标,然后单步执行。 yes gdb will work if you have the right gdb pointing at the right system.是的,如果您有正确的 gdb 指向正确的系统,则 gdb 将起作用。 You can also use a jtag debugger, single step and then dump registers.您还可以使用 jtag 调试器、单步然后转储寄存器。

Little of this has anything to do with assembly language, you will want to see the instructions at an assembly language level when single stepping, sure, but you need to compile to a binary to run it.这与汇编语言几乎没有任何关系,您肯定会希望在单步执行时查看汇编语言级别的指令,但是您需要编译为二进制文件才能运行它。

Since your program does not do anything you need to be careful not to optimize, even a -O1 optimization will remove your code.由于您的程序不执行任何操作,因此您需要注意不要优化,即使是 -O1 优化也会删除您的代码。

Here is something to try.这里有一些东西可以尝试。 I have a thumb instruction set simulator, thumb is the 16 bit subset to ARM (still in the ARM family, one to one relationship to ARM instructions).我有一个拇指指令集模拟器,拇指是 ARM 的 16 位子集(仍在 ARM 系列中,与 ARM 指令一对一关系)。 Go to github and download it. Go 到 github 并下载。 In thumbulator.c change this loop:在 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);
}

To add the printfs to show the registers.添加 printfs 以显示寄存器。

Go into the blinker directory, change notmain() to resemble your program: Go 进入blinker 目录,更改notmain() 以类似于您的程序:

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

Edit the Makefile编辑 Makefile

Change this line to use your compiler (I add the -gcc during the build).更改此行以使用您的编译器(我在构建期间添加了 -gcc)。

ARMGNU = arm-linux ARMGNU = arm-linux

remove the -O2 from this line:从此行中删除 -O2 :

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

And change this to just build the gnu/gcc binary not the llvm binary.并将其更改为仅构建 gnu/gcc 二进制文件而不是 llvm 二进制文件。

all: gnotmain.bin全部:gnotmain.bin

Now build that.现在构建它。

Look at the file gnotmain.list, you will see something like this but not necessarily exactly this, depends on your gcc.查看文件 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}

You will also see some code that boots up the processor:您还将看到一些启动处理器的代码:

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. 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. So the number at address 4 in this case is the address to reset to, (lsbit is set to indicate thumb mode, so the address is really 0x52).所以在这种情况下,地址 4 处的数字就是要重置的地址,(lsbit 设置为表示拇指模式,所以地址实际上是 0x52)。 Then the _start code calls notmain and you get to your code.然后 _start 代码调用 notmain ,你就可以得到你的代码。

The reason I mention this is because when you run thumbulator (./thumbulator blinker/gnotmain.bin) and those printfs you added dump out all the registers you will see it doing a few things before and after 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 -- 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 -- 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 -- is the first instruction executed, which is the first instruction after _start, it is a two instruction instruction so 0x52 and 0x54, that branches to 0x74 which is the start of notmain. -- 0x00000052 -- 是执行的第一条指令,是_start之后的第一条指令,它是一条两条指令,所以0x52和0x54,分支到0x74,这是notmain的开始。 Which starts with a push of r7 in the case of my compile, so r13 should change to reflect something was pushed.就我的编译而言,它以 r7 的推送开始,因此 r13 应该更改以反映推送的内容。 Next instruction sub sp, #16, again r13 will change (sp is r13, the stack pointer).下一条指令 sub sp,#16,再次 r13 将改变(sp 是 r13,堆栈指针)。

At 0x7A we get to the first bit of your C code.在 0x7A,我们到达 C 代码的第一位。 mov r3,#4 then it stores that to the stack in 0x7C (this is not optimized code). mov r3,#4 然后将其存储到 0x7C 的堆栈中(这不是优化代码)。 Then the b = 5 line of code the c = a + b stuff with lots of stack involved (not optimized).然后 b = 5 行代码 c = a + b 涉及大量堆栈的东西(未优化)。 and it winds its way down.并且它蜿蜒而下。

What does optimization have to do with it?优化与它有什么关系? Well your program actually does nothing, so if you were to optimize it (put the -O2 back into the COPS environment variable) you would get this:那么你的程序实际上什么都不做,所以如果你要优化它(把 -O2 放回 COPS 环境变量中)你会得到这个:

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

Basically it is this:基本上是这样的:

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

The meat of the program.节目的肉。

If you want to see optimize code of what you are trying to do, IN A SEPARATE.C FILE put this code:如果您想查看您正在尝试执行的优化代码,请在 SEPARATE.C 文件中输入以下代码:

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

add that to the project (compile it SEPARATELY. to its own.o file).将其添加到项目中(单独编译。到它自己的.o 文件中)。

change notmain to this将 notmain 更改为此

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

And now you see the kind of thing you are probably interested in.现在你看到了你可能感兴趣的东西。

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

Simulate that with thumbulator and look at the before and after instruction 0x80用 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 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFF8 r14 0x0000007F r15 0x00000085 -- 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来

Before r0 is 4, r1 is 5 the add r0 = r0 + r1 happens and r0 is now 9.在 r0 为 4 之前,r1 为 5,添加 r0 = r0 + r1 发生并且 r0 现在为 9。

Now you dont really need to look at the static registers to follow the code, it is a fairly painful way to do it.现在您真的不需要查看 static 寄存器来跟踪代码,这是一种相当痛苦的方法。 Back to just disassembling, and not using thumbulator or anything to execute and dump instructions:回到只是反汇编,而不是使用 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

the movs is a move with the flags updated, will get to the ARM code in a sec. movs 是更新标志的动作,将在几秒钟内到达 ARM 代码。 mov the number 4 into r0, then move the number 5 into r1, the bl branch link basically it is a branch with a return value set in r14 so you can get back (a function call instead of a branch).将数字 4 移动到 r0,然后将数字 5 移动到 r1,bl 分支链接基本上是一个分支,其返回值设置在 r14 中,因此您可以返回(一个 function 调用而不是分支)。 bl to xfun we see the add of r0, r1, r0 which means r0 = r1 + r0 so we know that r0 is destroyed, was a 4 now it is a 9. And basically the meat of your program is finished. bl 到 xfun 我们看到了 r0, r1, r0 的加法,这意味着 r0 = r1 + r0 所以我们知道 r0 被破坏了,原来是 4 现在是 9。基本上你的程序的内容就完成了。

So now, back to ARM instructions, and this was interesting with the compiler I am using, using your compiler variation for demonstration:所以现在,回到 ARM 指令,这对我正在使用的编译器很有趣,使用你的编译器变体进行演示:

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

gives

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

when you dig into the meat of it, does the tail optimization thing the return value from xfun is the same as the return value from notmain so it leaves r14 unmodified and lets xfun return to whomever called notmain.当你深入研究它的实质时,尾部优化的事情xfun 的返回值与notmain 的返回值相同,因此它使r14 保持不变并让xfun 返回给名为notmain 的任何人。

And the other file:另一个文件:

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

Gives

add r0, r1, r0
bx  lr

Since ARM instructions can choose to modify the flags or not you dont see movs and adds you see mov and add because the code is not doing any conditionals related to those instructions so they did not generate the s version.由于 ARM 指令可以选择修改标志或不修改标志,因此您看不到 movs 并添加您看到 mov 和 add 因为代码没有执行与这些指令相关的任何条件,因此它们没有生成 s 版本。

I am literally in the middle of a few gidhub projects as I type this.当我输入这个时,我实际上正处于几个 gidhub 项目的中间。 mbed_samples which is an ARM cortex-m3 based (thumb/thumb2 instructions only) microcontroller board. mbed_samples 是基于 ARM cortex-m3(仅限thumb/thumb2 指令)微控制器板。 I wrote similarly long ramblings about the thing booting and using the tools and such in order to build and execute binaries (something you will be wanting to do before long).我写了关于启动和使用工具等类似的长篇文章,以便构建和执行二进制文件(你很快就会想做的事情)。 I also just posted lsasim the other day, not an ARM, not related to an ARM, but has LEARNASM.txt in there which might be useful for learning ASM for the first time.前几天我也刚刚发布了 lsasim,不是 ARM,与 ARM 无关,但其中有 LEARNASM.txt,这可能对第一次学习 ASM 有用。 The C compiler backend is very crude, and I wouldnt really mess with it much, walk through the LEARNASM.txt tutorial. 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. 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. On the right side it will show you that the ARMv5 ARM (Architectural Reference Manual) which used to be just called the ARM ARM before they had so many different cores.在右侧,它将向您展示 ARMv5 ARM(架构参考手册),它曾经被称为 ARM ARM,之前它们有很多不同的内核。 That will have a listing of the traditional ARM 32 bit instruction set which is what you were building.这将列出您正在构建的传统 ARM 32 位指令集。 it also has the thumb instruction set which runs on most of the current ARM able cores.它还具有 thumb 指令集,可在大多数当前的 ARM 内核上运行。 Thumb2 is only on some cores and you would want something like the ARMv7-M ARM for the cortex-m3, which is replacing the ARM7 (the ARM7 is an ARMv4T, I know the numbers can be confusing) for embedded microcontrollers. Thumb2 仅在某些内核上,您可能需要类似 ARMv7-M ARM 的 cortex-m3,它正在取代嵌入式微控制器的 ARM7(ARM7 是 ARMv4T,我知道数字可能会令人困惑)。 QEMU can run ARM, thumb and thumb2 instructions, but getting some programs running where you have visibility into what is going on will take you quite a while. 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 matters at all). 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很重要)。 that might be your fastest path to running something and dumping registers, an arm based gdb.这可能是您运行某些东西和转储寄存器的最快途径,基于 gdb 的 arm。 Perhaps codesourcery comes with one, if not look for emdebian or just build your own.也许 codesourcery 附带一个,如果不是寻找 emdebian 或者只是构建你自己的。 thumbulator is an option as well but I have strictly limited it to thumb instructions and it boots like a cortex-m3 not an arm (pretty easy to change if you wanted to). thumbulator 也是一个选项,但我严格限制它使用拇指指令,它像 cortex-m3 一样启动,而不是 arm(如果你愿意,很容易改变)。 mame has an arm it it but getting that to compile anywhere outside of mame and feeding it programs is probably more work than the armulator source in gdb. mame 有一个 arm 它,但是让它在 mame 之外的任何地方编译并为其提供程序可能比 gdb 中的 armulator 源更多的工作。

A completely different path would be to get something like say an olimex sam7-h64 board from sparkfun.com they are selling them off at about $16 or so each, used to be double or triple that.一条完全不同的路径是从 sparkfun.com 购买 olimex sam7-h64 板,他们以每块 16 美元左右的价格出售,过去是两倍或三倍。 it is an ARM7 and will run ARM instructions.它是一个 ARM7,将运行 ARM 指令。 get an olimex wiggler or I prefer the amontek jtag-tiny.找一个 olimex wiggler 或者我更喜欢 amontek jtag-tiny。 you can then use jtag to load programs into ram, and single step through them dumping any or all of the registers or memory whenever you want.然后,您可以使用 jtag 将程序加载到 ram 中,并在需要时单步转储任何或所有寄存器或 memory。 I cannot think off hand of any of the boards I know if that run ARM instructions, but have a jtag built into the board, would be trivial today to do with the ftdi chips, instead the cortex-m based microcontrollers are showing up with that kind of thing either an ftdi chip up front that provides serial and bit banging jtag or more common the board actually has two microcontrollers from that vendor on it, one is the usb interface, that you cannot reprogram the other is the chip you bought the board to play with, the up front microcontroller handles the jtag stuff.我无法想到任何我知道的板子是否运行 ARM 指令,但是板子中内置了一个 jtag,今天与 ftdi 芯片无关,而是基于 cortex-m 的微控制器出现了一种东西,要么是提供串行和位敲击 jtag 的 ftdi 芯片,要么是更常见的电路板实际上有两个来自该供应商的微控制器,一个是 usb 接口,你不能重新编程另一个是你购买电路板的芯片玩,前面的微控制器处理jtag的东西。 Of those, the stellaris get you in with jtag其中,恒星让你使用 jtag

Long response and lots to digest, I know.我知道,回复很长,需要消化很多。 Worth the effort if you or someone else reading this takes the time to learn assembly and keeps that knowledge alive.如果您或其他阅读本文的人花时间学习组装并保持知识的活力,那么值得付出努力。 someone has to carry on the ability to develop processors and compilers as the old timers who grew up before there was even a C language, retire.有人必须继续开发处理器和编译器的能力,因为在 C 语言出现之前就长大了,退休了。 just the knowledge you appeared to be after, taking a few lines of C code and looking at the assembly, even if you never use assembly day to day is teaches you to write better, faster, more reliable programs.只是你似乎所追求的知识,用几行 C 代码并查看程序集,即使你每天从不使用程序集,也会教你编写更好、更快、更可靠的程序。

Good luck.祝你好运。

objdump should be able to disassemble your ARM assembly code. objdump 应该能够反汇编您的 ARM 汇编代码。 Another option might be gdb, but I am not sure if that supports ARM or not.另一个选项可能是 gdb,但我不确定它是否支持 ARM。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM