【问题标题】:How does this get_sp() function work? [closed]这个 get_sp() 函数是如何工作的? [关闭]
【发布时间】:2016-06-06 00:11:16
【问题描述】:

我目前正在练习C语言。

一切似乎都是合理的,但是当我面对这些初始化和函数时

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

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

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

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

有人可以详细解释一下吗?

【问题讨论】:

  • @TalhaIrfan :这个问题只回答了一半的问题。函数get_sp() 没有解释,恕我直言,不是这个的重复。
  • @MichaelPetch 我认为你是对的迈克尔。我正在编辑问题以使其与 get_sp()
  • 这是 2 个完全独立的问题,一个关于函数指针,一个关于堆栈指针函数(如果它被内联,它将中断,因为它没有将 %eax 声明为输出)。

标签: c assembly


【解决方案1】:

第一,您正在定义一个函数get_sp(),它返回一个无符号长整数。 该函数的内容是一些内联汇编,它获取堆栈指针地址,将其放入寄存器 eax,然后与 0xff000000 进行运算。 IE:在 eax 中获取一个值,该值设置了堆栈指针地址的前 8 位中的任何一个。 eax寄存器用于返回值,所以返回这个被屏蔽的堆栈指针。

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

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

【讨论】:

    【解决方案2】:

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

    第一部分更有趣:

    它将%esp 放入一个 C 变量中,并屏蔽掉低 24 位。即向下舍入到 16MiB 边界。 IDK 这是干什么用的,但非内联函数调用的 4B 偏移量可能会或可能无关紧要(如果 %esp 非常接近 16MiB 边界,或者这就是您要检测的明确内容。)

    如果编译器有机会内联代码,问题中发布的版本将以不明显的方式破坏您的代码(例如,使用跨文件内联)。它没有从asm 语句正确声明输出操作数,而是在没有返回语句的函数内修改%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 在 cmets 中指出的那样,将其设为始终内联函数,甚至宏,可能是保持一致性的好主意。 (尽管优化和未优化的构建无论如何都会消耗不同数量的堆栈空间。)


    请参阅 标签 wiki,了解更多关于如何编写不烂的 GNU C 内联汇编的信息。

    【讨论】:

    • 仅对此发表评论是他在寻找 ESP 的什么价值?例如,如果在没有优化的情况下编译此代码会发生什么。如果存在堆栈帧,函数调用本身现在将产生一个返回地址和可能的 EBP 在堆栈上的先前值(移位 ESP)。我可能会将其声明为static inline,或者在最坏的情况下声明为保证始终内联的 C 宏。您还确定 =g 的输出约束吗?这意味着i 是有效的,但您不能使用i(imm.值)作为目标。也许=rm
    • @MichaelPetch:我考虑了esp 你会得到什么的问题。由于get_sp 屏蔽了除高字节之外的所有内容,我认为它是否内联可能并不重要。对于输出约束,我认为 gcc 总是会选择r,因为它需要屏蔽结果。我可能应该这样做。我在想也许在某些情况下它可以只在某个地方存储%esp 而不需要屏蔽,就像它稍后只加载高字节一样。我不认为i 可能匹配仅输出约束有任何风险,因此我不需要手动排除它。
    • 从技术上讲,与 ESP 对齐在 16MB 的边界上是正确的,在这种情况下,额外的推送会产生不同的值。当然,ESP 接近该边界的可能性很小,但不一定是不可能的。我只是没有在答案或评论中看到任何人说“AND”有效地将堆栈指针向下舍入到最近的 16MB 边界(对齐?)。
    • @MichaelPetch:好点,我更新了我的答案,讨论了它实际在做什么,以及接近边界的影响。谢谢。
    • 我怀疑这段代码是缓冲区利用的一部分。我以前见过。我认为无论谁是代码的来源,都不需要知道内联汇编器实际上是垃圾,而且他们很幸运,它恰好按照他们想要的方式工作。
    猜你喜欢
    • 2021-11-14
    • 1970-01-01
    • 2018-06-12
    • 2020-08-18
    • 2014-12-04
    • 2015-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多