我写了下面的代码,你能解释一下汇编在这里讲的是什么。
typedef struct
{
int abcd[5];
} hh;
void main()
{
printf("%d", ((hh*)0)+1);
}
大会:
.file "aa.c"
.section ".rodata"
.align 8
.LLC0:
.asciz "%d\n"
.section ".text"
.align 4
.global main
.type main, #function
.proc 020
main:
save %sp, -112, %sp
sethi %hi(.LLC0), %g1
or %g1, %lo(.LLC0), %o0
mov 20, %o1
call printf, 0
nop
return %i7+8
nop
.size main, .-main
.ident "GCC: (GNU) 4.2.1"
答案 0 :(得分:5)
我想我们一行一行?我将跳过一些无趣的样板。
.section ".rodata"
.align 8
.LLC0:
.asciz "%d\n"
这是你在printf
中使用的字符串常量(很明显,我知道!)需要注意的重要事项是它位于.rodata
部分(部分是最终可执行映像的分区;这个是“只读数据”,实际上在运行时是不可变的,并且它已被赋予标签 .LLC0
。以点开头的标签对于目标文件是私有的。之后,编译器会在想要加载字符串常量的地址时引用该标签。
.section ".text"
.align 4
.global main
.type main, #function
.proc 020
main:
.text
是实际机器代码的部分。这是用于定义名为main
的全局函数的样板标题,它在汇编级别与任何其他函数没有区别(在C中 - 在C ++中不一定如此)。我不记得.proc 020
做了什么。
save %sp, -112, %sp
保存上一个寄存器窗口并向下调整堆栈指针。如果您不知道注册窗口是什么,则需要阅读架构手册:http://www.sparc.org/standards/V8.pdf。 (V8是SPARC的最后一次32位迭代,V9是第一个64位迭代。这似乎是32位代码。)
sethi %hi(.LLC0), %g1
or %g1, %lo(.LLC0), %o0
这个双指令序列的作用是将地址.LLC0
(这是你的字符串常量)加载到寄存器%o0
中,这是第一个传出参数寄存器。 (参数 to 此函数位于传入参数寄存器中。)
mov 100, %o1
将立即数常量100加载到%o1
,即第二个输出参数寄存器。这是((foo *)0)+5
计算的值。这是100,因为你的struct foo
长20个字节(5个4字节int
s),你在地址零之后要求第五个。
call printf, 0
nop
致电printf
。 nop
填入延迟槽(再次阅读架构手册)。我不记得零是什么。
return %i7+8
nop
跳转到寄存器%i7
中的地址加上8。这具有从当前函数返回的效果。 应该在延迟槽中成为restore
指令,我不知道它为什么不存在。
答案 1 :(得分:1)
程序集调用printf
,将文本缓冲区和数字20传递给堆栈(这是你以迂回方式要求的)。