这个get_sp()函数如何工作?

时间:2016-06-06 00:11:17

标签: c assembly

我目前正在练习C语言。

一切看似合理,但是当我遇到这些初始化和功能时

unsigned long get_sp(void) {
    __asm__("movl %esp,%eax\n\t"
            "and $0xff000000, %eax"
           );
}

int  (*fp)(char *)=(int(*)(char *))&puts;

我真的不知道这些线是什么意思。

什么是真正的变量?它是什么类型的? ...

有人可以深入解释我吗?

2 个答案:

答案 0 :(得分:3)

1,您正在定义一个返回unsigned long的函数get_sp()。 该函数的内容是一些内联汇编,它获取堆栈指针地址,将其放入寄存器eax,然后将其与0xff000000进行对比。 IE:获取eax中的值,该值设置了堆栈指针地址的前8位中的任何一位。 eax寄存器用于返回值,因此返回此屏蔽堆栈指针。

第二行是为fp分配函数puts的地址。 puts是一个返回int的函数,并期望输入char *。因此类型/名称int (*fp)(char *)

在该行之后,您可以将puts函数调用为fp("hello");

答案 1 :(得分:2)

第二部分只是一个带有初始化器的函数指针(强制&puts)。

第一部分更有趣:

它将%esp变为C变量,并屏蔽低24位。即向下舍入到16MiB边界。 IDK是什么意思,但是非内联函数调用的4B偏移量可能会或可能不重要(如果%esp非常接近16MiB边界,或者明确表示你正在尝试什么检测。)

如果编译器有机会内联(例如使用跨文件内联),问题中发布的版本将以非显而易见的方式破坏您的代码。它不是从asm语句中正确声明输出操作数,而是在没有return语句的函数内修改%eax。与告诉编译器你返回的内容相比,这真的很愚蠢,没有任何优势。

/********* Safe version of the function *************/
// Actually unsigned long was fine, since this asm only works on 32bit anyway
static inline uintptr_t get_sp(void) {
    uintptr_t result;
    __asm__("movl %%esp, %0\n\t"
            : "=g" (result)
           );
    result &= 0xff000000;  // do this outside the inline asm so the compiler knows that the low 24b are always zero.
    return result;
}

compiles to the same asm when not inlined,但可以安全地内联。 (例如,将其放在带有static inline的标题中)。它当然也避免编译器警告有关缺少返回值的函数。

正如Michael Petch在评论中指出的那样,将其作为一个始终内联函数,甚至是一个宏,对于一致性来说可能是一个好主意。 (尽管优化与未优化的构建将消耗不同数量的堆栈空间。)

请参阅标记wiki,了解更多关于如何编写GNU C inline asm的信息。