Gcc 签名不匹配的链接函数

Gcc 签名不匹配的链接函数,gcc,linker,g++,elf,Gcc,Linker,G++,Elf,我正在使用gcc和g++编译器,并试图在这些编译器中编译一些C代码,我的目的是看看编译器/链接器是如何强制执行的,当将具有某个函数声明的模型链接到具有该函数实现的模型时,会链接正确的函数(根据传递的参数和返回的值) 例如,让我们来看一下这个代码 #include <stdio.h> extern int foo(int b, int c); int main() { int f = foo(5, 8); printf("%d",f); } 编译这个模型,它也会有一个叫做f

我正在使用gccg++编译器,并试图在这些编译器中编译一些C代码,我的目的是看看编译器/链接器是如何强制执行的,当将具有某个函数声明的模型链接到具有该函数实现的模型时,会链接正确的函数(根据传递的参数和返回的值)

例如,让我们来看一下这个代码

#include <stdio.h>

extern int foo(int b, int c);

int main()
{
 int f = foo(5, 8);

 printf("%d",f);
}
编译这个模型,它也会有一个叫做foo的符号,如果我把这些模型链接在一起,会发生什么?我从来没有想过这一点,编译器如何克服这个弱点…我知道在g++中,编译器会为每个符号生成一些前缀,关于它的名称空间,但它也会考虑签名吗?如果有人遇到过这个问题,如果他能解释一下这个问题,那就太好了

问题解决了

在编译器构造中,名称mangling(也称为修饰) 是一种用于解决因需要而引起的各种问题的技术 在许多现代应用程序中解析编程实体的唯一名称 编程语言

它提供了一种以文件名对附加信息进行编码的方法 函数、结构、类或其他数据类型,以便传递更多 从编译器到链接器的语义信息

当语言允许使用不同的实体时,就会产生这种需求 使用相同的标识符命名,只要它们占用不同的 名称空间(其中名称空间通常由模块、类、, 或显式命名空间指令)或具有不同的签名(例如 函数重载)

注:

在C++程序中考虑F2()的以下两个定义:

int  f (void) { return 1; }
int  f (int)  { return 0; }
void g (void) { int i = f(), j = f(0); }
这些是不同的功能,彼此之间没有区别 如果它们在本机翻译成C时没有 更改后,结果将是一个错误-C不允许两个 同名函数。因此,C++编译器将编码。 符号名称中的类型信息,结果是 类似于:

int  __f_v (void) { return 1; }
int  __f_i (int)  { return 0; }
void __g_v (void) { int i = __f_v(), j = __f_i(0); }
请注意,即使没有冲突,g()也已损坏;name 损坏适用于所有符号

问题解决了

在编译器构造中,名称mangling(也称为修饰) 是一种用于解决因需要而引起的各种问题的技术 在许多现代应用程序中解析编程实体的唯一名称 编程语言

它提供了一种以文件名对附加信息进行编码的方法 函数、结构、类或其他数据类型,以便传递更多 从编译器到链接器的语义信息

当语言允许使用不同的实体时,就会产生这种需求 使用相同的标识符命名,只要它们占用不同的 名称空间(其中名称空间通常由模块、类、, 或显式命名空间指令)或具有不同的签名(例如 函数重载)

注:

在C++程序中考虑F2()的以下两个定义:

int  f (void) { return 1; }
int  f (int)  { return 0; }
void g (void) { int i = f(), j = f(0); }
这些是不同的功能,彼此之间没有区别 如果它们在本机翻译成C时没有 更改后,结果将是一个错误-C不允许两个 同名函数。因此,C++编译器将编码。 符号名称中的类型信息,结果是 类似于:

int  __f_v (void) { return 1; }
int  __f_i (int)  { return 0; }
void __g_v (void) { int i = __f_v(), j = __f_i(0); }
请注意,即使没有冲突,g()也已损坏;name 损坏适用于所有符号


哇,我一直在自己探索和测试它,我想出了一个解决方案,悄悄地让我惊讶不已

因此,我编写了以下代码并在gcc编译器上编译

main.c

#include <stdio.h>

extern int foo(int a, char b);

int main()
{
 int g = foo(5, 6);
 printf("%d", g);
 return 0;
}
typedef struct{
 int a;
 int b;
 char c;
 char d;

} mystruct;

mystruct foo(int a, int b)
{
 mystruct myl;

 my.a = a;
 my.b = a + 1;
 my.c = (char) b;
 my.d = (char b + 1;

 return my1;
}
现在,我首先使用gcc
foo.c
编译为
foo.o
,并使用
readelf
我有一个条目叫做
foo

同样,在我编译了
main.c
main.o
之后,我检查了符号表,它还有一个名为
foo
的条目,我将这两个条目链接在一起,令人惊讶的是它工作了,我运行了
main.o
,显然遇到了一些分段错误,这对于
foo
的实际实现是有意义的正如在
foo.o
中实现的那样,可能需要三个参数(第一个参数应该是结构加法器),一个未在其定义下传递到
foo
main.o
中的参数,然后实际实现从
main
的堆栈框架中访问不属于它的内存,然后尝试访问它认为得到的地址,最后出现分段错误,这很好

现在我用g++和notgcc再次编译了这两个模型,得到的结果令人惊讶。我发现
foo.o
下的符号条目是
\uz3fooii
,而
main.o
下的符号条目是
\uz3fooic
,现在我猜
ii
后缀的意思是
int
de>ic后缀意味着
int char
,它可能是指应该传递给函数的参数,因此允许编译器知道一些函数的实际实现。因此我将
main.c
中的
foo
声明更改为

extern int foo(int a, int b);
重新编译,这一次得到了符号
\u Z3fooii
,我再次链接了这两个模型,令人惊讶的是,这一次它成功了,我尝试运行它,再次遇到了分段错误,这也是有意义的,因为编译器甚至不总是授权正确的返回值。无论如何,我最初的想法是什么-那g++在符号名中包含函数签名,从而强制链接器为函数实现提供正确的参数,以更正函数声明