15

我的问题:

  1. C标准是否保证函数指针相等?
  2. 如果(1)的答案是肯定的。无论在不同的最终编译单元(例如主可执行文件和共享库)中获得指针是否都是这种情况?
  3. 动态加载器如何处理这个问题?(我可以考虑一些这可能很棘手的原因,所有这些都与 PIC 代码有关(例如,elf 中的 GOT 表和任何等效的 COFF 用于此的))。不管 (1) 和 (2),linux 加载程序似乎都可以保证这一点。

这是一个例子。上面的问题归结为 C 是否保证main.c打印内容: "Function equality: 1"或者"Function equality: 0",在第一种情况下,动态加载程序是如何实现的。

common.h:

extern void * getc_main;
extern void * getc_shared;
void assign_getc_shared(); 

main.c:

#include <stdio.h>
#include "common.h"

int main()
{
  getc_main = (void*) getc;
  assign_getc_shared();
  printf("Function equality: %d\n", getc_main == getc_shared);
  return 0;
}

shared.c:

#include <stdio.h>
#include "common.h"

void assign_getc_shared()
{
   getc_shared = (void*) getc;
}

在 Unix 中,这将使用以下命令编译:

cc -shared -fPIC -o libshared.so shared.c
cc -o main main.c -L. -lshared

并执行:

LD_LIBRARY_PATH=. ./main
4

1 回答 1

14

C 2011(N1570 委员会草案)6.5.9 6:“两个指针比较相等当且仅当……两者都是指向同一个……函数……的指针。所以,是的,指向同一个函数的两个指针比较相等。

当一个函数的地址被两个不同的目标模块获取时,编译器会在目标代码中放置一个占位符。当对象模块链接到可执行文件或在运行时与动态库链接时,将填充该占位符。

对于动态库,要么动态加载器根据需要填充可执行文件中的所有占位符,要么每个函数的地址实际上是一些跳转到实际函数的存根代码的位置,并填充该存根代码中或由该存根代码使用的占位符由动态加载器进入。

此外,请注意可执行文件可以包含多个函数实例。编译器可能会在几个地方内联插入函数,或者由于其自身的原因,可能包括函数的专门化以及通用版本。但是,在取函数地址时,编译器必须提供单个通用版本的地址。(或者编译器必须确保程序的行为就像已经完成一样。例如,如果编译器可以检测到程序没有比较指针,那么理论上它可能能够为地址的某些实例使用不同的地址的功能。)

于 2013-02-20T17:05:28.097 回答