14

我制作了一段代码,它包含在一个动态库 ( lib.c) 和一个主可执行文件 ( main.c) 中。在这两个文件中,我定义了一个名为: 的全局变量int global。不是很聪明,但这不是问题。

当我编译动态库时,该-fPIC选项似乎是强制性的:

gcc lib.c -fPIC -shared -o lib.so

否则我得到:

/usr/bin/ld: /tmp/ccpUvIPj.o: relocation R_X86_64_32 against '.rodata' can not be used when making a shared object; recompile with -fPIC

当我编译可执行文件时,它不是。

gcc main.c -fPIC -ldl
gcc main.c -ldl

两者都有效,但有不同的行为我无法解释,你可以吗?:

使用 -fPIC,main.c 中的 global 和 lib.c 中的 global 是相同的变量:

global main: 23 (0x601050)
global lib: 23 (0x601050)

如果没有 -fPIC,lib.c 中的全局与 main.c 中的全局无关:

global main: 23 (0x601048)
global lib: 0 (0x7f7742e64028)

这是来源:

库文件

#include <stdio.h>
#include <stdlib.h>

int global;

int f_one() {

    printf("global lib: %d (%p)\n", global, &global);

    return EXIT_SUCCESS;
}

主程序

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void * handle;
int global;

int main() {

    int q = 7;

    int (* f_one_p)(int a) = NULL;

    global = 23;

    handle = dlopen("./lib.so", RTLD_NOW);

    if (handle == 0) {
        return EXIT_FAILURE;
    }

    f_one_p = dlsym(handle, "f_one");

    printf("global main: %d (%p)\n", global, &global);

    f_one_p(q);

    return EXIT_SUCCESS;

}

gcc --version: gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2

uname -a: Linux xxx 2.6.38-11-generic #48-Ubuntu SMP Fri Jul 29 19:02:55 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux

编辑:在 SUN/sparc 和 x86/Linux 架构下测试的代码具有相同类型的意外共享全局变量(使用 -fPIC)。

4

2 回答 2

8

当您使用-fPIC相关对象进行编译时,将使用Global Offset Table确定全局符号的地址。但是,当部分代码是-fPIC而部分不是时会发生什么,您int global的其中一个将使用此表来确定地址,而另一部分则不是。

如果您有两个与 链接的共享对象-fPIC,但您的主程序没有,那么您仍然有两个地址int global,一个使用全局偏移表,一个仅位于非 PIC 代码的本地。

如果您想进一步阅读,关于 PIC 与 PIC 与非 PIC的讨论非常棒。

于 2011-09-04T09:26:18.323 回答
1

默认情况下,在构建可执行文件时,对变量的引用是在内部完成的,具有固定的偏移量并且没有重定位。

但是,您正在传递-fPIC并且对全局数据的访问被转换为通过 GOT 访问,并且添加了 GOT 重定位。

于 2011-09-04T09:27:11.207 回答