我一直在摆弄 LLVM 并编写了一个简单的编译器。它使用 libc 作为标准库。当然,我必须以某种方式在我的 IR 中声明函数。
我注意到以下似乎有效:
declare void @puts(i8*)
在 C 中,函数定义如下:
int puts(const char *s);
应该是这样
declare i32 @puts(i8*)
这是一个非常简单的案例,但我确信我在过程中的某个地方会在声明这些函数时出错。例如,在阅读联机帮助页之前,我并不知道 puts
返回了一个 int。
这些错误有多严重?它会弄乱堆栈还是 LLVM 会以某种方式处理它?此类错误的安全隐患是什么?
注意:我无法用 puts
的 void
声明产生任何错误。
最佳答案
这个问题的答案取决于您的 C 编译器的 ABI 使用的调用约定。在 x86 和 x86-64 上大多数 C 编译器使用的约定中,返回值在寄存器中传递。将 int
返回函数错误声明为 void
将导致返回寄存器的值被忽略(如果您不使用它,无论如何都会被忽略)。这不会造成任何伤害,因为调用者无论如何都要负责保存 eax
寄存器。
例如下面的代码:
void callee(int, int, int);
void caller(void)
{
callee(1, 2, 3);
}
...如果您声明 callee
返回 int
而不是 void
,将被编译成完全相同的程序集。
这适用于“小”返回类型,即由整数、 double float 或 64 位整数(x86 在两个整数寄存器中返回)组成的类型。大型返回类型的处理方式不同 - 如果您将 callee
的声明更改为类似以下内容:
struct { char x[100]; } callee(int, int, int);
...调用代码将发生巨大变化,尽管传入的类型没有改变。返回结构现在将分配在调用者的堆栈上,其地址将作为隐藏的第一个参数传递给被调用者(这是在 x86 上,x86-64 上的情况略有不同),预计将写入返回值到那个区域。
换句话说,只要您了解调用约定,并且注意不要错误声明按值返回大类型的函数(据我所知,标准 C 和 POSIX 库中不存在),错误的声明将起作用。
关于c - 从 C 库调用符号时忽略返回值是否安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35995509/