从 C 库调用符号时忽略 return 值是否安全

Is it safe to ignore return values when calling symbols from a C library

我一直在摆弄 LLVM 并编写了一个简单的编译器。它使用 libc 作为标准库。当然,我必须以某种方式在我的 IR 中声明函数。

我注意到以下似乎有效:

declare void @puts(i8*)

在 C 中,函数定义如下:

int puts(const char *s);

所以它真的应该是

declare i32 @puts(i8*)

这是一个非常简单的案例,但我确信我在过程中的某个地方会在声明这些函数时出错。例如,在阅读联机帮助页之前,我并不知道 puts 返回了一个 int。

这些错误有多严重?它会弄乱堆栈还是 LLVM 会以某种方式处理它?此类错误的安全隐患是什么?

注意:我无法使用 putsvoid 声明产生任何错误。

小 return 值通常放在 return 值寄存器中,因此忽略这些值不会致命崩溃。对于较大的值,某些 ABI 要求调用者分配堆栈 space 并将其作为不可见的第一个参数传递给函数,在这种情况下,您的程序可能会很快崩溃,因为您不会分配或传递它。如果您使用的是不存储前一帧指针的 abi,​​即它必须知道它自己的栈帧有多大并且abi允许被调用者调整栈指针,这也是致命的。

基本上它可能会起作用,直到它不起作用。

这个问题的答案取决于您的 C 编译器的 ABI 使用的调用约定。在 x86 和 x86-64 上大多数 C 编译器使用的约定中,return 值在寄存器中传递。将 int-returning 函数错误声明为 void 将导致 return 寄存器的值被忽略(如果您不使用它)。这不会造成任何伤害,因为调用者无论如何都要负责保存 eax 寄存器。

例如下面的代码:

void callee(int, int, int);

void caller(void)
{
  callee(1, 2, 3);
}

...如果您将 callee 声明为 return int 而不是 void.

,将被编译成完全相同的程序集

这适用于 "small" return 类型,即由整数、双精度浮点数或 64 位整数组成的类型(其中 x86 return s 在两个整数寄存器中)。大型 return 类型的处理方式不同 - 如果您将 callee 的声明更改为:

struct { char x[100]; } callee(int, int, int);

...调用代码将发生巨大变化,尽管传入的类型没有改变。 return 结构现在将分配到调用者的堆栈上,其地址将作为隐藏的第一个参数传递给被调用者(这是在 x86 上,在 x86-64 上情况略有不同),预计将 return 值写入该区域。

换句话说,只要您了解调用约定,并且注意不要错误声明 return 大值类型的函数(AFAIK 在标准 C 和POSIX 个库),错误的声明将起作用。

理查德

到目前为止的答案都很好,但我认为一个重要的含义是,如果您忽略 C 函数 return,作为其功能的一部分,分配内存或 open/create 文件等.等等,然后 return 某种指针。

当然,忽略这些将孤立只有在程序退出时才会释放的内存(如果它能做到这一点),让文件保持打开状态,等等。

基本上,如果您调用的函数 return 是任何东西 但是 寄存器值或堆栈实例值,其含义可能很重要。