4

我写了下面的代码,你能解释一下程序集在这里告诉我什么吗?

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"
4

2 回答 2

7

哇哦,SPARC 汇编语言,我好几年没见过

我想我们一行一行地走?我将跳过一些无趣的样板。

        .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 ://sparc.org/wp-content/uploads/2014/01/v8.pdf.gz 。(V8 是 SPARC 的最后一个 32 位迭代,V9 是第一个 64 位迭代。这似乎是 32 位代码。)

        sethi   %hi(.LLC0), %g1
        or      %g1, %lo(.LLC0), %o0

.LLC0这两个指令序列具有将地址(即您的字符串常量)加载到 register 的净效果%o0,这是第一个传出参数寄存器。(这个函数的参数在传入参数寄存器中。)

        mov     20, %o1

将立即数 100 加载到%o1第二个传出参数寄存器中。这是由 计算的值((foo *)0)+1。它是 20,因为您的struct foo长度为 20 个字节(五个 4 字节int),并且您要求数组中从地址 0 开始的第二个字节。

顺便说一句,只有当基指针的地址处实际上有一个足够大的数组时,才在 C 中明确定义了从指针计算偏移量。((foo *)0)是一个空指针,所以那里没有数组,所以表达式在((foo *)0)+1技术上具有未定义的行为。针对托管 SPARC 的 GCC 4.2.1 恰好将其解释为“假设foo在地址 0 处有一个任意大的 s 数组并计算数组成员 1 的预期偏移量”,但其他(尤其是较新的)编译器可能会完全执行某些操作不同的。

        call    printf, 0
        nop

打电话printf。我不记得零是干什么用的。该call指令有一个延迟槽(再次阅读架构手册),其中填充了一个无操作指令,nop.

        return  %i7+8
        nop

跳转到寄存器中的地址%i7加八。这具有从当前函数返回的效果。

return还有一个延迟槽,里面是另一个nop. restore在这个延迟槽中应该有一条指令,匹配save函数顶部的 ,以便main调用者取回它的寄存器窗口。我不知道为什么它不在那里。评论中的讨论讨论了main可能不需要弹出注册窗口,和/或您已声明mainvoid main()(不能保证与任何 C 实现一起使用,除非其文档明确说明,并且总是不好的风格)......但是在 SPARC 上推送而不弹出注册窗口是一件很麻烦的事情,我认为这两种解释都不能令人信服。我什至可以称它为编译器错误。

于 2012-02-15T05:09:41.403 回答
1

程序集调用printf,传递您的文本缓冲区和堆栈上的数字 20(这是您以迂回的方式要求的)。

于 2012-02-15T04:59:17.313 回答