我正在做一个关于计算机基础知识的高中课程,我试图理解汇编代码并在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");
}
答案 0 :(得分:3)
使用GNU调试器&#34; gdb&#34;!
gdb --args ./store01
启动GNU调试器
它应该像控制台一样
您可以输入quit
退出
但是当然你想逐步运行程序并检查寄存器上下文。所以输入start
,跳转到main()并跳过初始化的东西。
然后输入disassemble
或disas
以显示您正在运行的汇编程序
现在按键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
作为降序堆栈指针,并将寄存器fp
和lr
以此顺序放置在堆栈上。 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
操作。