C 或 C++ 链接器中是不是有任何类型检查?

Posted

技术标签:

【中文标题】C 或 C++ 链接器中是不是有任何类型检查?【英文标题】:Is there any type checking in C or C++ linkers?C 或 C++ 链接器中是否有任何类型检查? 【发布时间】:2015-03-21 08:49:23 【问题描述】:

我是否正确地说链接器不进行函数参数检查。他们不检查函数调用的数量或类型,也不检查全局数据引用的类型。所有链接器都这样吗?

我在 x86-64 上使用面向 Linux 的 Clang。链接器是否检查引用是否在正确的段中?或者就链接器而言,外部引用实际上只是一个 void *?

我来自高级语言背景 C# 和 Scala,因此对于那些沉浸在低级世界中的人来说,这似乎很明显。我在汇编器中编写了几个函数(系统调用),我注意到汇编器中没有外部函数的参数原型。

上下文:我实际上正在编写一个编译器。目前,我的目标是预处理 C .i 文件,其中包含用于系统调用的汇编程序函数,但替代方案是 C++、汇编程序甚至机器代码,因此我试图权衡成本和收益,尤其是类型检查的成本和收益。汇编器/编译器/链接器我可以用来检查我自己的程序的正确性及其函数原型的生成。

【问题讨论】:

可能你永远不会遇到这些类型的错误,编译器会在链接开始之前抛出错误。 Cyber​​ 是正确的 - 原则上,链接器只处理地址(当然还有符号名称)。 在 C++ 中,名称(通常?)与类型混淆,因此类型不匹配将导致无法找到符号:这主要是因为重载函数的能力。我不知道非函数变量,我从来没有遇到过问题。 C++ 的链接阶段未指定,并且修饰不一致(在实际实践中!不同的编译器以不同的方式修饰符号),所以我不能说比通常更多。在 C 中,它们(通常?)不使用类型进行管理,因为不可能重载。 C的link-stage规范我不是很了解,但是比较一致。 相关:***.com/questions/27777071/… 通常链接器所做的唯一检查是检查指针类型是否相同大小(例如,您不会将 32 位模块链接到 64 位程序,反之亦然)指出,原型修改是为了允许在 C++ 中重载,因此名称不会出现,因为它们是用源代码编写的。 【参考方案1】:

在 c++ 中,所有编译器都实现了某种形式的名称修饰来分离重载函数;但是,由于返回类型不包含在 mangle 中(通常),无论如何这里可能存在相同的问题。

在 C 中你是对的 - 链接器无法检查,但这确实没有你想象的那么严重。请记住,编译器已经检查了对函数的调用是否与提供的头文件匹配,因此导致问题的唯一方法是将头文件的不同重复版本编译成两个不同的 c 文件,然后再链接。

这很难意外地做到(尽管如果你做到了,你最终可能会遇到一些非常微妙的错误)。

【讨论】:

【参考方案2】:

正如@Yakk 所解释的,函数可以根据其参数进行重载,因此编译器会生成包含参数及其类型信息的错位函数名称。链接器主要只检查符号名称和大小,但由于重整确保函数名称不同,不匹配的参数将不会链接。

函数返回类型 不是修改的一部分(因为返回类型的重载是不合法的),所以如果您在一个翻译单元中声明 int test() 并在另一个翻译单元中调用 float test() ,链接器不会捕捉到它,你会得到不好的结果。

同样,链接器不会检查全局变量的类型(以及类的静态成员等),因此如果您在一个翻译单元中声明 extern int test; 并在另一个翻译单元中定义 float test;,您将得到不好的结果。

在某些情况下,链接器可以比较两个不同翻译单元中符号的大小,并可以通过这种方式发现一些问题。

在实践中,这在普通 C++ 开发中很少出现,因为每当 >1 个翻译单元需要一个函数、变量或类时,您将在一个包含在两个翻译单元中的头文件中声明它,并且编译器将在链接器运行之前捕获任何错误。 (一个可能出现问题的实例是,如果您使用的是外部二进制库,并且您拥有的头文件与该库不匹配。)

【讨论】:

实际上,当您使用的头文件与外部库的内容不匹配时,这可能会出现问题。 @JoshKelley 谢谢,关于检查不同翻译单元的 sizeof 的更多信息吗? @RichOliver - 抱歉,关于链接器如何工作的详细信息,我不知道它何时检查符号大小。我知道不会检测到一个简单的案例(一个文件中的extern int x;,另一个文件中的char x;);我认为当内联函数和模板实例化等 C++ 特性导致在 >1 个翻译单元中发出弱符号时,可以检测到错误。 遇到返回类型不匹配问题的另一种方法:具有不包含在标头中的内部函数,然后在测试源文件中向它们添加声明。我的函数调用被编译为重用来自相邻帧的堆栈,没有编译器、链接器或地址清理程序错误,以及无关代码中的神秘 GPF。糟糕的交易 IMO,除非你想练习调试内存布局。【参考方案3】:

许多链接器包含提供某种级别的类型检查的功能,但细节各不相同。一些编译器会在使用一种调用约定的函数名称前加上下划线,但在使用不同调用约定的函数名称中省略下划线;如果一个翻译单元使用一种约定声明一个函数,但实际函数是使用另一种约定定义的,则程序将在链接时被拒绝。

某些平台(例如 PIC 的 HiTech C)允许编译器或汇编语言程序在声明或引用符号时指定 16 位值,如果引用点提供的值不“ t 符合定义。 C 编译器根据参数类型和返回类型的组合为每个函数生成一个散列值,如果尝试调用一个函数,其签名散列值在定义和调用位置不同,则链接器将发出警告。

【讨论】:

以上是关于C 或 C++ 链接器中是不是有任何类型检查?的主要内容,如果未能解决你的问题,请参考以下文章

C 和 C++ 编码标准

以编程方式在 C 中检查 NIC 在 linux 中关闭时是不是有链接

如何检查 String 对象是不是是链接? [复制]

链接 vs 2012 c++ GDAL

检查文件是不是存在并提供指向它的链接 php

C++ 优化 - 堆栈分配的数组类型与外部链接维度?