共享库中的重定位项

4

我正在研究共享库的迁移,并遇到一些奇怪的问题。考虑下面这段代码:

int myglob;

int ml_util_func(int p)
{
    return p + 2;
}

int ml_func2(int a, int b)
{
    int c = ml_util_func(a);
    return c + b + myglob;
}

我使用gcc -shared将其编译成非PIC共享库。我是在x86架构的32位Ubuntu上进行的。

生成的.so文件针对ml_func2中对ml_util_func的调用有一个重定位入口。这是objdump -dR -Mintelml_func2上的输出:

0000050d <ml_func2>:
 50d:   55                      push   ebp
 50e:   89 e5                   mov    ebp,esp
 510:   83 ec 14                sub    esp,0x14
 513:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 516:   89 04 24                mov    DWORD PTR [esp],eax
 519:   e8 fc ff ff ff          call   51a <ml_func2+0xd>
                        51a: R_386_PC32 ml_util_func
 51e:   89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 521:   8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 524:   8b 55 fc                mov    edx,DWORD PTR [ebp-0x4]
 527:   01 c2                   add    edx,eax
 529:   a1 00 00 00 00          mov    eax,ds:0x0
                        52a: R_386_32   myglob
 52e:   8d 04 02                lea    eax,[edx+eax*1]
 531:   c9                      leave  
 532:   c3                      ret    
 533:   90                      nop

请注意call指令上的R_386_PC32重定位。

现在,我的问题是e8是“相对调用...”在x86中,由于ml_util_func在同一个对象中定义,因此链接器肯定可以计算出它和调用之间的相对偏移量,而不必将其留给动态加载程序来计算吧?

有趣的是,如果将ml_util_func声明为static,那么重定位就会消失,并且链接器会正确地计算并插入偏移量。是什么让链接器对ml_util_func也被导出变得懒惰呢?
附言:我故意使用非PIC代码,以了解加载时重定位。

为什么要在没有-fPIC的情况下编译.so文件? - osgx
@osgx:因为,正如我的问题在第一句中所述-我特别关注加载时重定位。 我现在添加了一个P.S.来澄清。 - Eli Bendersky
您是否可以使用另一个库(LD_PRELOAD)重载一个库中的全局符号? - osgx
Eli,还有一个attribute((visibility("type"))) ,其中type是hidden、internal、protected、default之一,可以精细控制任何符号的绑定顺序。 - osgx
2个回答

4

我不知道为什么,但这是binutils关于此的评论:

binutils-2.11.90-20010705-src.tar.gz/bfd/elf32-i386.c : 679

      /* If we are creating a shared library, and this is a reloc
         against a global symbol, or a non PC relative reloc
         against a local symbol, then we need to copy the reloc
         into the shared library.  However, if we are linking with
         -Bsymbolic, we do not need to copy a reloc against a
         global symbol which is defined in an object we are

我认为,这个重定位是为了允许用户重载库中的任何全局符号。而且,似乎-Bsymbolic会禁用此功能,并且不会为来自库本身的符号生成重定位。

http://www.rocketaware.com/man/man1/ld.1.htm

-Bsymbolic 此选项会导致输出中的所有符号引用在此链接编辑会话中解析。仅剩下的运行时重定位要求是基于基地址的重定位,即相对于加载地址进行翻译。无法解析任何符号引用会导致报告错误。

有关各种-B模式和限制(C ++)的更长描述在此处:

http://developers.sun.com/sunstudio/documentation/ss12/mr/man1/CC.1.html

-Bbinding

           Specifies whether a library binding for linking is
           symbolic, dynamic (shared), or static (nonshared).

           -Bdynamic is the default.  You can use the -B
           option several times on a command line.

           For more information on the -Bbinding option, see
           the ld(1) man page and the Solaris documentation.


           -Bdynamic directs the link editor to look for
           liblib.so files. Use this option if you want
           shared library bindings for linking.  If the
           liblib.so files are not found, it looks for
           liblib.a files.

           -Bstatic directs the link editor to look only for
           liblib.a files. The .a suffix indicates that the
           file is static, that is, nonshared.  Use this
           option if you want nonshared library bindings for
           linking.

           -Bsymbolic forces symbols to be resolved within a
           shared library if possible, even when a symbol is
           already defined elsewhere. For an explanation of
           -Bsymbolic, see the ld(1) man page.

           This option and its arguments are passed to the
           linker, ld.  If you compile and link in separate
           steps and are using the -Bbinding option, you must
           include the option in the link step.

           Warning:

           Never use -Bsymbolic with programs containing C++
           code, use linker scoping instead. See the C++
           User's Guide for more information on linker scop-
           ing. See also the -xldscope option.

           With -Bsymbolic, references in different modules
           can bind to different copies of what is supposed
           to be one global object.

           The exception mechanism relies on comparing
           addresses. If you have two copies of something,
           their addresses won't compare equal, and the
           exception mechanism can fail because the exception
           mechanism relies on comparing what are supposed to
           be unique addresses.

1
有趣的是,还有这个页面:http://www.technovelty.org/code/c/bsymbolic.html,它解释了Bsymbolic。我会研究一下,感谢你的指引。 - Eli Bendersky
我认为Bsymbolic只是一个相关的标志,真正重要的是符号的搜索顺序 - 首先在可执行文件中,然后在共享库中,并且可以使用此搜索顺序覆盖全局符号。 LD_PRELOAD是另一种方法。 - Eli Bendersky

0
请注意,对象不一定是完全链接的块。有办法将符号放在单独的部分中,这些部分可以根据代码引用放置在最终的 .exe 中。(搜索 -gc-sections 链接器选项和相关的部分生成 gcc 选项)
当没有使用部分时,可能只是简单地不进行微优化。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接