我正在玩 gcc 和 g ++ 编译器并尝试在这些编译器中编译一些C代码,我的目的是看看编译器/链接器在执行时如何强制执行将模型与某个函数声明链接到具有该函数实现的模型,正确的函数被链接(根据传递的参数和返回的值)
例如,让我们来看看这段代码
#include <stdio.h>
extern int foo(int b, int c);
int main()
{
int f = foo(5, 8);
printf("%d",f);
}
在我的符号表中编译后,我有一个foo的符号,但在elf文件格式中没有描述所采用的参数和函数签名的地方(int(int,int)
),所以基本上如果我写一些其他代码,如:
char foo(int a, int b, int c)
{
return (char) ( a + b + c );
}
编译该模型它还会有一些名为foo的符号,如果我将这些模型链接在一起,会发生什么?我从来没有想过这个,编译器如何克服这个弱点......我知道在g ++中,编译器为每个符号生成一些关于它的命名空间的前缀,但是它是否也考虑到了签名?如果有人遇到过这种情况,那么如果能够解决这个问题就会很好。
答案 0 :(得分:2)
问题已解决with name mangling。
在编译器构造中,名称修改(也称为名称修饰) 是一种用于解决由需要引起的各种问题的技术 为许多现代的编程实体解析唯一的名称 编程语言。
它提供了一种以a的名称编码附加信息的方法 函数,结构,类或其他数据类型,以传递更多 从编译器到连接器的语义信息。
需要语言允许不同的实体 只要它们占用不同的名称,就用相同的标识符命名 namespace(命名空间通常由模块,类定义, 或显式命名空间指令)或具有不同的签名(例如 函数重载)。
在C ++程序中考虑以下两个f()定义:
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()也会被破坏;名称 修剪适用于所有符号。
答案 1 :(得分:0)
所以我写了下面的代码并在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;
}
<强> foo.c的强>
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
的条目,我将这两个链接在一起,令人惊讶的是它有效,我跑了{{ 1}}并且显然遇到了一些分段错误,这是有道理的,因为在main.o
中实现的foo
的实际实现可能需要三个参数(第一个应该是结构加法器),这个参数不是&#39 ; t将foo.o
下的main.o
传递给foo
,然后实际实现从main
的堆栈帧访问一些不属于它的内存,然后尝试访问它认为有的地址,并最终出现分段错误,这很好,
现在我用 g ++ 而不是 gcc 再次编译了两个模型,结果是惊人的......我发现foo.o
下的符号条目是_Z3fooii
,main.o
下_Z3fooic
ii
,我的猜测是int int
后缀表示ic
,int char
后缀表示foo
这可能是指应该传递给函数的参数,因此允许编译器知道一些函数减速得到实际的实现。所以我将main.c
中的extern int foo(int a, int b);
声明更改为
_Z3fooii
重新编译,这次得到了符号pip install xmlsec
,我再次链接了两个模型并且令人惊讶地这次它起作用,我尝试运行它并再次遇到分段错误,这也是有意义的,因为编译器不会总是甚至授权正确的返回值..无论如何我最初的想法 - g ++ 包括符号名称中的函数签名,从而强制链接器给函数实现获取正确的参数来纠正函数声明