Gcc 签名不匹配的链接函数
我正在使用gcc和g++编译器,并试图在这些编译器中编译一些C代码,我的目的是看看编译器/链接器是如何强制执行的,当将具有某个函数声明的模型链接到具有该函数实现的模型时,会链接正确的函数(根据传递的参数和返回的值) 例如,让我们来看一下这个代码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
#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
和int char
,它可能是指应该传递给函数的参数,因此允许编译器知道一些函数的实际实现。因此我将main.c
中的foo
声明更改为
extern int foo(int a, int b);
重新编译,这一次得到了符号\u Z3fooii
,我再次链接了这两个模型,令人惊讶的是,这一次它成功了,我尝试运行它,再次遇到了分段错误,这也是有意义的,因为编译器甚至不总是授权正确的返回值。无论如何,我最初的想法是什么-那g++在符号名中包含函数签名,从而强制链接器为函数实现提供正确的参数,以更正函数声明