从gcc输出中了解程序集

时间:2017-09-17 08:03:57

标签: c gcc assembly raspberry-pi arm

我正在做一个关于计算机基础知识的高中课程,我试图理解汇编代码并在C中创建一个hello world并用汇编代码编译它。我理解" mov r0,r3"将数据从寄存器3移到寄存器0.但是,我如何计算出r3的值是什么?

以下是我用于理解的汇编代码:

.arch armv6
.eabi_attribute 27, 3
.eabi_attribute 28, 1
.fpu vfp
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 6
.eabi_attribute 34, 1
.eabi_attribute 18, 4
.file   "hello.c"
.section    .rodata
.align  2
.LC0:
    .ascii  "Hello World\000"
    .text
    .align  2
    .global main
    .type   main, %function
main:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 1, uses_anonymous_args = 0
    stmfd   sp!, {fp, lr}
    add     fp, sp, #4
    ldr     r0, .L2
    bl      printf
    mov     r0, r3
    ldmfd   sp!, {fp, pc}
.L3:
    .align  2
.L2:
    .word   .LC0
    .size   main, .-main
    .ident  "GCC: (Raspbian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",%progbits

以下是我的C代码:

// Hello World program in C

#include<stdio.h>

main()
{
    printf("Hello World");
}

2 个答案:

答案 0 :(得分:3)

使用GNU调试器&#34; gdb&#34;!
gdb --args ./store01启动GNU调试器 它应该像控制台一样 您可以输入quit退出 但是当然你想逐步运行程序并检查寄存器上下文。所以输入start,跳转到main()并跳过初始化的东西。
然后输入disassembledisas以显示您正在运行的汇编程序 现在按键stepi按指令运行程序指令 现在有趣的部分:键入info registers r3并观察输出!
还有一个很棒的事情:你可以改变运行时的值:试试p $r0 = 2 这不是全部 这是官方文档:https://www.gnu.org/software/gdb/documentation/
一个好的,小而有用的教程:http://thinkingeek.com/2013/01/12/arm-assembler-raspberry-pi-chapter-4

答案 1 :(得分:1)

好吧,让我们分解一下。

stmfd sp!, {fp, lr}

STMFD的意思是“存储多个完全下降”。本质上,它使用寄存器sp作为降序堆栈指针,并将寄存器fplr以此顺序放置在堆栈上。 fp是帧指针,lr是链接寄存器(保存返回地址,稍后再说)。

add fp, sp, #4

将帧指针寄存器设置为等于sp + 4(指向fp刚刚存储的位置)。这是未经优化的编译产物。

ldr r0, .L2

这是加载存储在地址.L2中的32位值的简写形式(在本例中为.LC0的地址,即字符串(井,字符数组)"Hello World!\000")进入寄存器r0

bl printf

通过对以空字符结尾的字符串printf调用"Hello World"函数进行组装。回想一下,指向该数组的指针刚刚加载到r0中。通常,GCC将寄存器r0-r3分别用作函数的第一,第二,第三和第四自变量。其他参数根据需要存储在堆栈中。

mov r0, r3

这表明您的代码中存在错误,此版本的GCC未能捕获该错误。基本上,您的函数返回一个类型(假定为int,因为您从未指定其他类型),但是从未指定返回值。因此,返回值就是r3中未定义的任何值。

ldmfd sp!, {fp, pc}

这与在函数开始处编译的stmfd指令相反。但是,我们没有加载到fp, lr中,而是加载到了fp, pc中。这导致lr的原始值直接移到程序计数器pc中。 pc是一个特殊的寄存器,它指向下一条执行的指令。每当pc设置为一个值时,CPU便立即开始执行该值指向的代码。此属性用于return到调用例程,因为ARM指令集没有显式的ret操作。