是否可以使用 Inline ASM 计算 __cdecl 中的函数(变量)参数?

Is it possible to count the function (variable) arguments in a __cdecl with Inline ASM?

被调用者是否可以在不知道参数类型或数量的情况下通过使用内联 ASM (x86) 偏移堆栈基指针 (rbp) 来迭代(和计数)函数调用参数?

void foo(char *arg, ...);

我正在使用 Intel 编译器,但其文档指出它支持 GCC 样式的内联汇编。因此基于 GCC 的示例就足够了。

#include <stdio.h>
#include <inttypes.h>

int    main(int argc, char **argv){

    uint64_t n;

    __asm__ __volatile__(
      "movq %%rbp, %0\n\t"
      : "=r"(n)
    );

    printf("rbp = 0x%" PRIx64 "\n", n);
    return 0;
}

the code in this post

不,你不能。一般情况下是不可能的。

在使用调试符号进行编译的特定情况下,您可以尝试编写代码来解释您自己的符号,但这种编译模式不适合发布版本,因此不建议编写这种代码。它也是完全不可移植的,但您不关心该限制。

唯一可行的方法是使用标记最后一个参数的唯一标记值(例如 NULL 指针)。

这通常只适用于所有参数都是指针的情况,例如由 POSIX execl(3) functions 使用,签名如

int execl(const char *pathname, const char *arg, ...
                   /* (char  *) NULL */);

(那么你不需要内联汇编;你可以只使用 C VA_ARG 宏。)


另外,rbp也没用;您不知道该函数是否是在启用优化的情况下编译的,因此它可能根本不是帧指针。如果你想要 GNU C 中的帧地址,请使用 __builtin_frame_address(0)。 (查看编译器生成的 asm 以了解它的作用。IIRC,它为该函数强制 -fno-omit-frame-pointer 并且只为您提供 rbp 的值)

甚至获取堆栈帧地址也无法帮助您获取寄存器参数(x86-64 System V 中的前 6 个 integer/pointer 和前 8 个 FP 参数。或者 [=45 中的前 4 个总参数=] x64.)

将您的函数声明为可变参数并使用 VA_ARG 将所有可变参数读取为 uint64_t 这样您就可以检查它们是否为零,或者您选择作为标记的任何位模式。

显然这需要调用者配合传递一个哨兵


顺便说一句,x86-64 不存在名为 __cdecl 的调用约定。微软称他们的 x64 __fastcall__vectorcall.