问题描述
我正在尝试自学组装.我在 C、Java 和 Python 方面拥有多年的经验 - 但我无法在这方面取得任何进展,我即将放弃.
I'm trying to teach myself assembly. I've got years and year of experience with C, Java and Python- but I cant make ANY headway here and I'm about to give up.
所以,我下载了 uVision4,并假设我可以编写一个基本的汇编程序:
So, I downloaded uVision4, and assumed I could just write a basic assembly program:
MOV R1, #0x7F0E0C2D
MOV R3, #0x1048B3C5
ADCS R1, R3, ROR #0x18
END
那么,建立两个变量,做一个运算,大功告成.检查寄存器的输出和调试器的条件标志,当然.
So, establish two variables, do an operation, done. Check the Registers for output and debugger for condition flags, surely.
显然,这是不可能的.
我创建文本文件,编写代码,保存为 .asm 文件,然后尝试构建 -
I create the text file, write my code, save as a .asm file, then try to build-
它讨厌那个.
好的,所以我新建一个项目,添加.asm文件,
Okay, so I create a new project, add the .asm file,
它拒绝了,显然要求我编写一个完整的设备驱动程序来做一个该死的你好世界.
And it refuses, demanding I apparently write an entire device driver to do a god damn hello world.
如何运行几行简单的代码来开始学习?
How can I run a simple couple lines of code to start learning?
推荐答案
我一直在我的 x86 桌面上做这样的事情,使用 gdb 单步代码.通常使用 x86 指令,但也适用于 ARM 交叉开发.使用 gcc -nostdlib foo.S
构建,它应该将默认入口点设置为 .text 部分的开头.不过,您确实会收到来自链接器的警告:
I do stuff like this all the time on my x86 desktop, using gdb to single-step code. Usually with x86 instructions, but it's doable for ARM cross-development, too. Build with gcc -nostdlib foo.S
, and it should set the default entry point to the beginning of your .text section. You do get a warning from the linker, though:
$ arm-linux-gnueabi-gcc -nostdlib arm-simple.S
/usr/lib/gcc-cross/arm-linux-gnueabi/5/../../../../arm-linux-gnueabi/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000010098
我必须修改你的源代码才能组装.这是我的 arm-simple.S:
I had to modify your source for it to assemble. Here's my arm-simple.S:
.globl _start
_start: @ make debugging easier to have a symbol name
ldr R1, =#0x7F0E0C2D @ ARM immediate constants can't be arbitrary 32-bit values. Use the ldr reg, =value pseudo-op, which in this case assembles to a PC-relative load from a nearby literal pool. Often it can use mov reg, #imm or movn reg, #imm
ldr R3, =#0x1048B3C5
ADCS R1, R3, ROR #0x18
@END This isn't an instruction.
然后你可以使用gdb并在第一条指令处设置断点,运行它,单步执行.
Then you can use gdb and set a breakpoint at the first instruction, run it, and single step.
您甚至可以在交叉开发环境中执行此操作,但有一些小问题.
You can even do this in a cross-development environment, with a few wrinkles.
在一个终端中,在您的二进制文件上运行 QEMU,等待调试器连接:
$ arm-linux-gnueabi-gcc -g -nostdlib arm-simple.S
$ qemu-arm -g 12345 ./a.out # user-mode emulation, waiting for gdb to connect
如果您想更具体,请使用 -mcpu=something
用于 gcc,使用 -cpu model
用于 qemu.
Use -mcpu=something
for gcc, and -cpu model
for qemu if you want to be specific.
在另一个终端中,运行 ARM gdb(在我的例子中,来自 Ubuntu 的 gdb-arm-none-eabi 包,因为它们 Ubuntu 不分发 arm-linux-gnueabi-gdb cross-x86 的 ARM-gdb 包).
In another terminal, run ARM gdb (in my case, from Ubuntu's gdb-arm-none-eabi package, since they Ubuntu doesn't distribute a arm-linux-gnueabi-gdb cross-ARM-gdb package for x86).
TODO:试试 gdb-multiarch.x86 桌面上的常规 gdb 只能调试 x86 二进制文件,所以你绝对不能使用它.
TODO: try gdb-multiarch. Regular gdb on an x86 desktop can only debug x86 binaries, so you definitely can't use that.
$ arm-none-eabi-gdb ./a.out # give the gdb client the same binary to read symbols / debug info
(gdb) target remote localhost:12345
(gdb) layout asm
(gdb) layout reg
(gdb) si # single step by instruction, not source line
(gdb) si
然后gdb显示:
+--Register group: general-----------------------------------------------------------------------------------------------------------------------------------------+
|r0 0x0 0 r1 0x7f0e0c2d 2131627053 r2 0x0 0 |
|r3 0x1048b3c5 273200069 r4 0x0 0 r5 0x0 0 |
|r6 0x0 0 r7 0x0 0 r8 0x0 0 |
|r9 0x0 0 r10 0x100ac 65708 r11 0x0 0 |
|r12 0x0 0 sp 0xf6ffea40 0xf6ffea40 lr 0x0 0 |
|pc 0x100a0 0x100a0 <_start+8> cpsr 0x10 16 |
| |
| |
| |
| |
| |
| |
| |
| |
| |
----------------------------------------------------------------------------------------------------------------------------------------------------------------+
|0x10098 <_start> ldr r1, [pc, #4] ; 0x100a4 <_start+12> |
|0x1009c <_start+4> ldr r3, [pc, #4] ; 0x100a8 <_start+16> |
>|0x100a0 <_start+8> adcs r1, r1, r3, ror #24 |
|0x100a4 <_start+12> svcvc 0x000e0c2d |
|0x100a8 <_start+16> subne r11, r8, r5, asr #7 |
|0x100ac andeq r1, r0, r1, asr #18 |
|0x100b0 cmnvs r5, r0, lsl #2 |
|0x100b4 tsteq r0, r2, ror #18 |
|0x100b8 andeq r0, r0, pc |
|0x100bc subseq r3, r4, r5, lsl #10 |
|0x100c0 tsteq r8, r6, lsl #6 |
|0x100c4 andeq r0, r0, r9, lsl #2 |
|0x100c8 andeq r0, r0, r12, lsl r0 |
|0x100cc andeq r0, r0, r2 |
|0x100d0 andeq r0, r4, r0 |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
remote Remote target In: _start Line: 6 PC: 0x100a0
(gdb) si
它突出显示最后修改的寄存器,这非常棒.
It highlights the last register(s) modified, which is pretty great.
不过,象征性地解码标志似乎太旧了.现代 x86 gdb 可以做到这一点.
It seems to be too old to decode flags symbolically, though. modern x86 gdb does that.
这篇关于如何运行单行汇编,然后查看 [R1] 和条件标志的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,WP2