【发布时间】:2013-06-26 15:04:12
【问题描述】:
我正在尝试为 Arduino Duemilanove (AVR ATmega328P) 编写一些汇编语言。与编译和反汇编C代码并行学习汇编语言,我得到了这个:
(使用AVR_GCC编译)
int main() {
volatile int a = 0;
while (1) {
++a;
}
return 0;
}
变成了
00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
...
64: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
00000068 <__ctors_end>:
68: 11 24 eor r1, r1
6a: 1f be out 0x3f, r1 ; 63
6c: cf ef ldi r28, 0xFF ; 255
6e: d8 e0 ldi r29, 0x08 ; 8
70: de bf out 0x3e, r29 ; 62
72: cd bf out 0x3d, r28 ; 61
00000074 <__do_copy_data>:
74: 11 e0 ldi r17, 0x01 ; 1
76: a0 e0 ldi r26, 0x00 ; 0
78: b1 e0 ldi r27, 0x01 ; 1
7a: e4 ec ldi r30, 0xC4 ; 196
7c: f0 e0 ldi r31, 0x00 ; 0
7e: 02 c0 rjmp .+4 ; 0x84 <__do_copy_data+0x10>
80: 05 90 lpm r0, Z+
82: 0d 92 st X+, r0
84: a0 30 cpi r26, 0x00 ; 0
86: b1 07 cpc r27, r17
88: d9 f7 brne .-10 ; 0x80 <__do_copy_data+0xc>
0000008a <__do_clear_bss>:
8a: 11 e0 ldi r17, 0x01 ; 1
8c: a0 e0 ldi r26, 0x00 ; 0
8e: b1 e0 ldi r27, 0x01 ; 1
90: 01 c0 rjmp .+2 ; 0x94 <.do_clear_bss_start>
00000092 <.do_clear_bss_loop>:
92: 1d 92 st X+, r1
00000094 <.do_clear_bss_start>:
94: a0 30 cpi r26, 0x00 ; 0
96: b1 07 cpc r27, r17
98: e1 f7 brne .-8 ; 0x92 <.do_clear_bss_loop>
9a: 0e 94 53 00 call 0xa6 ; 0xa6 <main>
9e: 0c 94 60 00 jmp 0xc0 ; 0xc0 <_exit>
000000a2 <__bad_interrupt>:
a2: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
000000a6 <main>:
a6: cf 93 push r28
a8: df 93 push r29
aa: 00 d0 rcall .+0 ; 0xac <main+0x6>
ac: cd b7 in r28, 0x3d ; 61
ae: de b7 in r29, 0x3e ; 62
b0: 1a 82 std Y+2, r1 ; 0x02
b2: 19 82 std Y+1, r1 ; 0x01
b4: 89 81 ldd r24, Y+1 ; 0x01
b6: 9a 81 ldd r25, Y+2 ; 0x02
b8: 01 96 adiw r24, 0x01 ; 1
ba: 9a 83 std Y+2, r25 ; 0x02
bc: 89 83 std Y+1, r24 ; 0x01
be: fa cf rjmp .-12 ; 0xb4 <main+0xe>
000000c0 <_exit>:
c0: f8 94 cli
000000c2 <__stop_program>:
c2: ff cf rjmp .-2 ; 0xc2 <__stop_program>
我试图理解一些事情:
- .-8 或类似的语法是什么? (例如地址 0x98 或 0xAA。)
- 地址为 80 到 88 的行(__do_copy_data 结束)周围有一些有趣的事情。在我看来,这会将所有程序代码从地址 0xC4 加载到RAM。为什么?
- 在 __do_clear_bss_start/loop 中,我们通过将 RAM 中的字节设置为 0(r1 的值)来清除刚刚完成的所有工作。为什么?所有这一切最终都打电话给
main。有什么一般性的解释吗? - 为什么反汇编不显示 .bss、.rodata 或其他部分?
- 第 6a 行,为什么 SREG 被清除?不是在每条指令之后都设置为应有的状态吗?
- 第 6c 和 6e 行:0xFF 和 0x08 对应什么? r28 和 r29 是堆栈指针的低位和高位。
- 我玩了一下,添加了一个静态全局变量。为什么我们存储在 RAM 中是从 0x0100 开始而不是 0x0000?
- 在第 8a 行,为什么是
ldi r17, 1?我们以前做过(只是一句愚蠢的话)。或者其他东西可以改变 r17 吗? - 我们开始将闪存中的程序复制到 RAM,从 0xC4(我猜是 .bss 和其他部分)开始,但是 X 的 cpi/cpc 相对于 1 将使所有闪存复制到所有 RAM 中。当 .bss 部分完成复制时,是否只是因为编译器的懒惰才停止复制?
【问题讨论】:
-
您的链接描述文件是什么样的?或者如果您没有指定一个,您在链接时是否使用了
-Tbss和-Tdata选项? -
我用过
avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -o main.elf main.o和avr-objcopy -j .text -j .data -O ihex main.elf main.hex -
编辑,它显示的内容(.text、.bss、.rodata)可能与您的反汇编方式有关,我使用whatever-objdump -D,它通常会这样做。
-
从你那里的情况来看,0x0000 处似乎有一个向量表,所以你不能只把数据放在那里,我必须重新熟悉 avr 细节才能说更多。基本上我会认为代码必须在那里,并且 .bss 和 .data 或任何放置部分取决于它们在链接器脚本或命令行中的描述位置
-
ldi r17,1 出现两次很可能是因为这两个循环是用 asm 编写的,或者该人根本不关心进行优化,或者可能是由于宏或其他原因。如果我写了我会把 ldi 留在那里,因为这两个循环是独立的代码,你可以删除一个而不破坏另一个。