Shared Libraries

Table of Contents

1 Shared Libraries

本文主要参考:
"The Linux Programming Interface" 41 Fundamentals of Shared Libraries
"The Linux Programming Interface" 42 Advanced Features of Shared Libraries

2 Static Libraries

2.1 制作静态库

要制作静态库,先把各个模块编译为对应的.o文件,再用工具 ar 打包为静态库即可。

$ cc -c mod1.c mod2.c mod3.c
$ ar r libdemo.a mod1.o mod2.o mod3.o
$ rm mod1.o mod2.o mod3.o

2.2 使用静态库

要把程序链接到静态库,有两种方法。

方法1:直接指定静态库的文件名。

$ cc -c prog.c
$ cc -o prog prog.o libdemo.a

方法2:通过-l指定静态库的库名(库名为文件名去掉lib前缀和.a后缀)。

$ cc -c prog.c
$ cc -o prog prog.o -ldemo

如果静态库不在标准的搜索路径中,可以通过-L指定静态库的路径。

注: 如果libdemo.so和libdemo.a在库探索路径中同时存在,-ldemo会优先使用共享库,通过指定gcc的 -static 选项可强制使用静态库。

3 Shared Libraries基本用法

静态库有很多缺点,比如更新和维护比较麻烦,如果想使用库的最新版本,必须显示地重新链接。静态库对存储器资源造成极大地浪费,如果100个程序都引用某个静态库,当它们运行时都会复制库中函数代码到每个进程的text段中。
共享库能解决上面提到的静态库缺点。
共享库以两种方法来实现“共享”:首先,在文件系统中,对于一个库只有一个.so文件,所有引用该库的可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库那样被拷贝和嵌入到引用它们的可执行文件中。其次,在存储器中,一个共享库的.text节的一个副本可以被不同的正在运行的进程共享。

3.1 制作共享库

不同的编译器,生成共享库的方法不一样,下面仅介绍gcc的方法。

$ gcc -c -fPIC -Wall mod1.c mod2.c mod3.c
$ gcc -shared -o libfoo.so mod1.o mod2.o mod3.o

也可以用下面一个命令生成共享库.so文件:

$ gcc -fPIC -Wall mod1.c mod2.c mod3.c -shared -o libfoo.so

3.2 使用共享库

方法1:链接时,直接指定共享库即可。

$ gcc -Wall -o prog prog.c libfoo.so

方法2:通过-l指定共享库的库名(库名为文件名去掉lib前缀和.so后缀)。

$ gcc -Wall -o prog prog.c -lfoo

如果共享库不在标准的搜索路径中,可以通过-L指定共享库的路径。
如果找不到共享库,gcc会尝试找对应的静态库libfoo.a。

运行程序时,如果共享库不在标准路径中,需要指定环境变量LD_LIBRARY_PATH。

$ LD_LIBRARY_PATH=. ./prog

4 加载共享库的两种机制(Load-time Relocation和PIC)

共享库是一个目标模块,在运行时,可以加载到任意的存储器地址,并和一个存储器中的程序链接起来。这个过程称为动态链接。
有两种主要的机制来处理动态链接:
方法1、Load-time Relocation。
方法2、Position Independent Code。

如果没有使用 -fPIC 编译共享库,会使用方法1:加载时重定位。

4.1 Load-time Relocation

Load-time Relocation是一种直观的方法,在加载共享库时把共享库中的符号地址地址修改为当前进程地址空间的一个地址(这就是重定位过程)。
这种方法有两个缺点。
缺点一:性能问题,当一个程序使用很多共享库时,在启动时要花费较多的时间在来对每个共享库进行重定位。
缺点二:共享库的代码段没有办法在内存中共享。由于进行重定位过程时共享库的代码段被修改了,所以无法在多个进程的地址空间中共享(除非所有进程加载共享库到地址空间的相同位置,但会带来管理问题,Linux没有这么做)。

参考: Load-time relocation of shared libraries

4.2 Position Independent Code (PIC)

The –fPIC option specifies that the compiler should generate position-independent code. It allow the code to be located at any virtual address at run time (rather than load time).

现在一般使用这种方法。它在动态链接过程中不会修改共享库中代码段。
如何访问PIC共享库中的全局变量符号?利用GOT (Global Offset Table)。
如何访问PIC共享库中的函数符号?利用GOT (Global Offset Table)和PLT(Procedure Linkage Table)。

说明:PIC解决了Load-time relocation方法的两个缺点,但PIC代码也有性能缺陷——访问全局变量需要更多的指令(因为它通过GOT间接地访问全局变量)。

参考:
深入理解计算机系统(原书第2版)7.12 与位置无关代码
Position Independent Code (PIC) in shared libraries

4.2.1 检测.o文件是否是用-fPIC选项编译

如何检测.o文件是否是用-fPIC选项编译?
先做个测试,不用或用-fPIC选项编译时,目标文件中的符号有什么不同?

$ cat mod1.c
extern int i;

void test(void)
{
    i = 1;
}
$ gcc -c mod1.c
$ nm mod1.o
                 U i
0000000000000000 T test
$ gcc -c -o mod1-PIC.o -fPIC mod1.c
$ nm mod1-PIC.o
                 U _GLOBAL_OFFSET_TABLE_
                 U i
0000000000000000 T test

由上面测试可以知道, 当目标文件用-fPIC选项编译生成时,会多个符号 _GLOBAL_OFFSET_TABLE_
所以可以用下面任一个命令来检测.o文件是否是用-fPIC选项编译,如果有输出,则说明.o文件用-fPIC选项编译生成。

$ nm mod1.o | grep _GLOBAL_OFFSET_TABLE_
$ readelf -s mod1.o | grep _GLOBAL_OFFSET_TABLE_

Global Offset Table参考:http://www.bottomupcs.com/global_offset_tables.html

5 把“库搜索目录”写死在目标文件中 (-Wl,-rpath,/path/to/lib)

We have already seen two ways of informing the dynamic linker of the location of shared libraries: using the LD_LIBRARY_PATH environment variable and installing a shared library into one of the standard library directories (/lib, /usr/lib, or one of the directories listed in /etc/ld.so.conf).
There is a third way: during the static editing phase, we can insert into the executable a list of directories that should be searched at run time for shared libraries. This is useful if we have libraries that reside in fixed locations that are not among the standard locations searched by the dynamic linker. To do this, we employ the –rpath linker option when creating an executable:

$ gcc -g -Wall -Wl,-rpath,/home/mtk/pdir -o prog prog.c libdemo.so

The above command copies the string /home/mtk/pdir into the run-time library path (rpath) list of the executable prog, so, that when the program is run, the dynamic linker will also search this directory when resolving shared library references.

5.1 编译时指定LD_RUN_PATH

An alternative to the –rpath option is the LD_RUN_PATH environment variable. This variable can be assigned a string containing a series of colon-separated directories that are to be used as the rpath list when building the executable file. LD_RUN_PATH is employed only if the –rpath option is not specified when building the executable.

5.2 指定动态库相对搜索路径(在rpath中使用$ORIGIN)

The dynamic linker interprets $ORIGIN to mean “the directory containing the application.”
This means that we can, for example, build an application with the following command:

$ gcc -Wl,-rpath,'$ORIGIN'/lib  ...

This presumes that at run time the application’s shared libraries will reside in the subdirectory lib under the directory that contains the application executable.

6 运行时符号解析 (-Wl,-Bsymbolic)

Suppose that a global symbol (i.e., a function or variable) is defined in multiple locations, such as in an executable and in a shared library, or in multiple shared libraries. How is a reference to that symbol resolved?

假设有下面程序,符号xyz同时出现在主程序和一个动态库中。

You might expect this:

           prog                               libfoo.so
+----------------------------+      +----------------------------+
|  #include<stdio.h>         |      |  #include<stdio.h>         |
|  extern void func(void);   |      |                            |
|                            |      |  void xyz() {              |
|  void xyz() {              |      |      printf("foo-xyz\n");  |
|      printf("main-xyz\n"); |      |  }                         |
|  }                         |      |        ^                   |
|                            |      |        |                   |
|  int main() {              |      |        |                   |
|      xyz();                |      |        |                   |
|      func();        -------+----> |  void func() {             |
|      return 0;             |      |      xyz();                |
|  }                         |      |  }                         |
+----------------------------+      +----------------------------+


But what you get may be this:

           prog                               libfoo.so
+----------------------------+      +----------------------------+
|  #include<stdio.h>         |      |  #include<stdio.h>         |
|  extern void func(void);   |      |                            |
|                            |      |  void xyz() {              |
|  void xyz() {              |<-+   |      printf("foo-xyz\n");  |
|      printf("main-xyz\n"); |  |   |  }                         |
|  }                         |  |   |                            |
|                            |  +---+--------+                   |
|  int main() {              |      |        |                   |
|      xyz();                |      |        |                   |
|      func();        -------+----> |  void func() {             |
|      return 0;             |      |      xyz();                |
|  }                         |      |  }                         |
+----------------------------+      +----------------------------+

测试1:

$ gcc -g -c -fPIC -Wall foo.c
$ gcc -g -shared -o libfoo.so foo.o
$ gcc -g -o prog prog.c libfoo.so   # 把libfoo.so放在前面不会影响结果 gcc -g -o prog libfoo.so prog.c
$ LD_LIBRARY_PATH=. ./prog
main-xyz
main-xyz

通过上面的结果可知,func()调用的是prog中的xyz(),而没有调用动态库libfoo.so中的xyz()。

The –Bsymbolic linker option specifies that references to global symbols within a shared library should be preferentially bound to definitions (if they exist) within that library. (Note that, regardless of this option, calling xyz() from the main program would always invoke the version of xyz() defined in the main program.)

测试2:

$ gcc -g -c -fPIC -Wall foo.c
$ gcc -g -shared -Wl,-Bsymbolic -o libfoo.so foo.o    # libfoo.so中的符号优先调用同一库中的符号
$ gcc -g -o prog prog.c libfoo.so
$ LD_LIBRARY_PATH=. ./prog
main-xyz
foo-xyz

说明:上面的实验在Linux中测试通过,其它平台可能不一样,如默认地Mac OS X中库中的函数优先使用同一个库中的函数。
说明:仅就上面的问题而言,我们还有其它办法来使func()调用动态库libfoo.so中的xyz()。
方法1:把prog.c中的函数xyz()声明为static;
方法2:利用gcc的 __attribute__ 把prog.o中的符号xyz()隐藏起来。

#include<stdio.h>
extern void func(void);

__attribute__ ((visibility ("hidden")))
void xyz() {
    printf("main-xyz\n");
}

int main() {
    xyz();
    func();
    return 0;
}

7 动态加载共享库

当开始程序执行时,所有的共享库都会被加载,如果找不到会直接报错。但有时候我们只想在需要的时候进行加载(如插件)。这可以通过在程序运行过程中动态加载共享库来实现。

7.1 dlopen API

The dlopen API enables a program to open a shared library at run time, search for a function by name in that library, and then call the function. A shared library loaded at run time in this way is commonly referred to as a dynamically loaded library, and is created in the same way as any other shared library.

The core dlopen API consists of the following functions (all of which are specified in SUSv3):

  • The dlopen() function opens a shared library, returning a handle used by subsequent calls.
  • The dlsym() function searches a library for a symbol (a string containing the name of a function or variable) and returns its address.
  • The dlclose() function closes a library previously opened by dlopen().
  • The dlerror() function returns an error-message string, and is used after a failure return from one of the preceding functions.

注:在Linux上,如果程序使用了dlopen/dlsym/dlclose/dlerror这些API,则编译时必须使用 -ldl 选项以链接到libdl库。

7.2 让动态加载库能访问主程序中符号 (-rdynamic)

Suppose that we use dlopen() to dynamically load a shared library, use dlsym() to obtain the address of a function x() from that library, and then call x(). If x() in turn calls a function y(), then y() would normally be sought in one of the shared libraries loaded by the program.
Sometimes, it is desirable instead to have x() invoke an implementation of y() in the main program. (This is similar to a callback mechanism.)
In order to do this, we must make the (global-scope) symbols in the main program available to the dynamic linker, by linking the program using the –rdynamic linker option:

$ gcc -rdynamic main.c

gcc的下面4个选项的含义一样:
-rdynamic
-export-dynamic
-Wl,--export-dynamic
-Wl,-E

8 控制共享库的导出符号

如果我们不想公开共享库中的某个符号,可通过把它设置为static来实现。但有时这种方法不可行,如:

// file foo.c
void foo() {    // foo()是libfoo的主要函数,想公开
  bar();
}

// file bar.c
void bar() {     // bar()仅是内部函数,不想公开。但却不能把它设置为static,因为另一文件foo.c中调用了它。
}

可把上面的文件foo.c和bar.c分别编译,链接生成libfoo.so。

$ gcc -fPIC -c foo.c
$ gcc -fPIC -c bar.c
$ gcc -shared -o libfoo.so foo.o bar.o
$ nm -D libfoo.so |grep -v '_'
00000000000006b0 T bar
00000000000006a0 T foo

可以看到有两个符号foo和bar对外可见,但bar并不是我们想公开的。如何隐藏符号bar呢?请看下文分解。

参考:
Binary Hacks: 黑客秘笈100选,Hack#29 控制对外公开库的符号
How To Write Shared Libraries, by Ulrich Drepper: http://www.akkadia.org/drepper/dsohowto.pdf
How To Write Shared Libraries, (slides) by Ulrich Drepper Slides: http://www.akkadia.org/drepper/ukuug2002slides.pdf
Shared Library Symbol Conflicts: https://holtstrom.com/michael/blog/post/437/Shared-Library-Symbol-Conflicts.html

8.1 控制共享库导出符号——visibility属性

可用通过定义符号的 visibility 属性来控制它的对外可见性。

具体方法是:
对想要公开的符号声明为 __attribute__ ((visibility("default"))) ,编译.o时,默认使用选项 -fvisibility=hidden
当然也可使用这样的策略:对所有不想公开的符号声明为 __attribute__ ((visibility("hidden"))) ,编译.o时,默认使用选项 -fvisibility=default

// file foo.c
__attribute__ ((visibility("default")))
void foo() {    // foo()是libfoo的主要函数,想公开
  bar();
}
$ gcc -fPIC -c -fvisibility=hidden foo.c
$ gcc -fPIC -c -fvisibility=hidden bar.c
$ gcc -shared -o libfoo.so foo.o bar.o
$ nm -D libfoo.so |grep -v '_'
0000000000000660 T foo

可以看到,符号bar已经对外不可见了。

参考:
https://gcc.gnu.org/onlinedocs/gcc-5.2.0/gcc/Code-Gen-Options.html#index-fvisibility-2829

8.1.1 visibility属性的实现基础

The ELF application binary interface (ABI) defines the visibility of symbols. Generally, it defines four classes, but in most cases, only two of them are more commonly used:
STV_DEFAULT - Symbols defined with it will be exported. In other words, it declares that symbols are visible everywhere.
STV_HIDDEN - Symbols defined with it will not be exported and cannot be used from other objects

The ELF ABI also defines other visibility modes:
STV_PROTECTED - The symbol is visible outside the current executable or shared object, but it may not be overridden. In other words, if a protected symbol in a shared library is referenced by an other code in the shared library, the other code will always reference the symbol in the shared library, even if the executable defines a symbol with the same name.
STV_INTERNAL - The symbol is not accessible outside the current executable or shared library.

参考:
man 5 elf
Controlling symbol visibility for shared libraries: http://www.ibm.com/developerworks/aix/library/au-aix-symbol-visibility/

8.2 控制共享库导出符号——LD Version Script (--version-script)

使用GNU链接器的版本控制脚本可以控制对外公开的符号。

如,可以准备下面文件来实现仅导出符号foo:

$ cat libfoo.map
{
  global:
     foo;             # only export symbol foo

  local: *;
};

使用链接选项 --version-script 来指定这个文件即可。

$ gcc -fPIC -c foo.c
$ gcc -fPIC -c bar.c
$ gcc -shared -o libfoo.so foo.o bar.o -Wl,--version-script,libfoo.map
$ nm -D libfoo.so  |grep -v '_'
00000000000005a0 T foo

可以看到,符号bar已经对外不可见了。

参考:https://sourceware.org/binutils/docs/ld/VERSION.html

8.2.1 Version Script的格式

LD Version Script的格式和Sun's linker in Solaris 2.5相同。

参考:
Sun's Linker and Libraries Guide: http://docs.oracle.com/cd/E19253-01/817-1984/
http://docs.oracle.com/cd/E19683-01/817-3677/817-3677.pdf

8.2.2 Solaris系统(-M<mapfile>)

Solaris系统中C编译器(cc)不支持 --version-script 选项,可以使用 -M<mapfile> 选项。

$ cc -flags|grep -w M
-M<file>                      Pass <file> mapfile to linker

测试实例如下(源文件及map文件和前面实例相同):

$ cc -fPIC -c foo.c
$ cc -fPIC -c bar.c
$ cc -shared -o libfoo.so foo.o bar.o
$ nm libfoo.so |grep GLOB
[49]    |     66288|       0|OBJT |GLOB |0    |11     |_DYNAMIC
[50]    |     66220|       0|OBJT |GLOB |0    |9      |_GLOBAL_OFFSET_TABLE_
[40]    |     66224|       0|OBJT |GLOB |0    |10     |_PROCEDURE_LINKAGE_TABLE_
[48]    |     66440|       0|OBJT |GLOB |0    |13     |_edata
[44]    |     66440|       0|OBJT |GLOB |0    |14     |_end
[42]    |       684|       0|OBJT |GLOB |0    |8      |_etext
[41]    |       668|      12|FUNC |GLOB |0    |7      |_fini
[43]    |       656|      12|FUNC |GLOB |0    |6      |_init
[47]    |       680|       4|OBJT |GLOB |0    |8      |_lib_version
[45]    |       632|      20|FUNC |GLOB |0    |5      |bar
[46]    |       584|      28|FUNC |GLOB |0    |5      |foo

$ cc -shared -o libfoo.so foo.o bar.o -M libfoo.map
$ nm libfoo.so |grep GLOB
[45]    |     66136|       0|OBJT |GLOB |0    |11     |_DYNAMIC
[48]    |     66132|       0|OBJT |GLOB |0    |10     |_GLOBAL_OFFSET_TABLE_
[47]    |         0|       0|OBJT |GLOB |0    |ABS    |_PROCEDURE_LINKAGE_TABLE_
[49]    |     66248|       0|OBJT |GLOB |0    |13     |_edata
[44]    |     66248|       0|OBJT |GLOB |0    |14     |_end
[46]    |       596|       0|OBJT |GLOB |0    |9      |_etext
[50]    |       496|      28|FUNC |GLOB |0    |6      |foo

从上面的测试结果可以看到使用 -M libfoo.map 后,符号bar被隐藏了。

8.2.3 AIX系统(-bE:exportlist)

AIX系统中用来控制导出符号的选项是 -bE:exportlist

但是对应的“导出符号配置文件”的格式和Linux/Solaris不同。AIX使用的格式更简单,把需要导出的符号一行行地列到文件中即可。如:

$ cat libfoo.exp
foo

测试实例如下(源文件和前面实例相同):

$ xlc -c foo.c
$ xlc -c bar.c
$ xlc -G -o libfoo.so foo.o bar.o
$ nm -g  libfoo.so
.bar                 T   268435840
.bar                 T   268435912
.foo                 T   268435776
.foo                 T   268435872
bar                  D   536871420          12
foo                  D   536871408          12

$ xlc -G -o libfoo.so foo.o bar.o -bE:libfoo.exp
$ nm -g  libfoo.so
.bar                 T   268435840
.foo                 T   268435776
.foo                 T   268435872
foo                  D   536871368          12

从上面的测试结果可以看到使用 -bE:libfoo.exp 后,符号bar被隐藏了。

参考:
Compiling a shared library: http://www.ibm.com/support/knowledgecenter/SSGH2K_13.1.2/com.ibm.xlc131.aix.doc/proguide/compiling_shared_aix.html?lang=zh

8.3 隐藏共享库内部符号的好处

It is useful because

  • It prevents abuse of undocumented APIs of your library. Symbols that are not exported from the library cannot be used. This eliminates the problem that when the maintainer of the library changes internals of the library, maintainers of other projects cry "breakage". Instead, these maintainers are forced to negotiate the desired API from the maintainer of the library.
  • It reduces the risk of symbol collision between your library and other libraries. For example, the symbol 'readline' is defined in several libraries, most of which don’t have the same semantics and the same calling convention as the GNU readline library.
  • It reduces the startup time of programs linked to the library. This is because the dynamic loader has less symbols to process.
  • It allows the compiler to generate better code. Within a shared library, a call to a function that is a global symbol costs a "call" instruction to a code location in the so-called PLT (procedure linkage table) which contains a "jump" instruction to the actual function’s code. (This is needed so that the function can be overridden, for example by a function with the same name in the executable or in a shared library interposed with LD_PRELOAD.) Whereas a call to a function for which the compiler can assume that it is in the same shared library is just a direct "call" instructions. Similarly for variables: A reference to a global variable fetches a pointer in the so-called GOT (global offset table); this is a pointer to the variable’s memory. So the code to access it is two memory load instructions. Whereas for a variable which is known to reside in the same shared library, it is just a direct memory access: one memory load instruction.

参考:https://www.gnu.org/software/gnulib/manual/html_node/Exported-Symbols-of-Shared-Libraries.html

8.4 查看共享库的导出符号 (nm -D or readelf -s -W)

在Linux中,可以用下面命令查看共享库的导出符号:
readelf -W --dyn-syms /path/to/libfoo.so 或者:
nm -D /path/to/libfoo.so

如以前面生成的libfoo.so为例:

$ readelf -s -W libfoo.so

Symbol table '.dynsym' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000458     0 SECTION LOCAL  DEFAULT    9
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2           .5 (2)
     5: 000000000000057c     6 FUNC    GLOBAL DEFAULT   11 bar
     6: 000000000000056c    16 FUNC    GLOBAL DEFAULT   11 foo
     7: 0000000000200858     0 NOTYPE  GLOBAL DEFAULT  ABS _end
     8: 0000000000200848     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
     9: 0000000000200848     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    10: 0000000000000458     0 FUNC    GLOBAL DEFAULT    9 _init
    11: 00000000000005c8     0 FUNC    GLOBAL DEFAULT   12 _fini

Symbol table '.symtab' contains 54 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000190     0 SECTION LOCAL  DEFAULT    1
     2: 00000000000001b8     0 SECTION LOCAL  DEFAULT    2
     3: 00000000000001f8     0 SECTION LOCAL  DEFAULT    3
     4: 0000000000000318     0 SECTION LOCAL  DEFAULT    4
     5: 000000000000038e     0 SECTION LOCAL  DEFAULT    5
     6: 00000000000003a8     0 SECTION LOCAL  DEFAULT    6
     7: 00000000000003c8     0 SECTION LOCAL  DEFAULT    7
     8: 0000000000000428     0 SECTION LOCAL  DEFAULT    8
     9: 0000000000000458     0 SECTION LOCAL  DEFAULT    9
    10: 0000000000000470     0 SECTION LOCAL  DEFAULT   10
    11: 00000000000004a0     0 SECTION LOCAL  DEFAULT   11
    12: 00000000000005c8     0 SECTION LOCAL  DEFAULT   12
    13: 00000000000005d8     0 SECTION LOCAL  DEFAULT   13
    14: 00000000000005f8     0 SECTION LOCAL  DEFAULT   14
    15: 0000000000200658     0 SECTION LOCAL  DEFAULT   15
    16: 0000000000200668     0 SECTION LOCAL  DEFAULT   16
    17: 0000000000200678     0 SECTION LOCAL  DEFAULT   17
    18: 0000000000200680     0 SECTION LOCAL  DEFAULT   18
    19: 0000000000200688     0 SECTION LOCAL  DEFAULT   19
    20: 0000000000200808     0 SECTION LOCAL  DEFAULT   20
    21: 0000000000200820     0 SECTION LOCAL  DEFAULT   21
    22: 0000000000200848     0 SECTION LOCAL  DEFAULT   22
    23: 0000000000000000     0 SECTION LOCAL  DEFAULT   23
    24: 00000000000004a0     0 FUNC    LOCAL  DEFAULT   11 call_gmon_start
    25: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    26: 0000000000200658     0 OBJECT  LOCAL  DEFAULT   15 __CTOR_LIST__
    27: 0000000000200668     0 OBJECT  LOCAL  DEFAULT   16 __DTOR_LIST__
    28: 0000000000200678     0 OBJECT  LOCAL  DEFAULT   17 __JCR_LIST__
    29: 00000000000004c0     0 FUNC    LOCAL  DEFAULT   11 __do_global_dtors_aux
    30: 0000000000200848     1 OBJECT  LOCAL  DEFAULT   22 completed.6338
    31: 0000000000200850     8 OBJECT  LOCAL  DEFAULT   22 dtor_idx.6340
    32: 0000000000000540     0 FUNC    LOCAL  DEFAULT   11 frame_dummy
    33: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    34: 0000000000200660     0 OBJECT  LOCAL  DEFAULT   15 __CTOR_END__
    35: 0000000000000650     0 OBJECT  LOCAL  DEFAULT   14 __FRAME_END__
    36: 0000000000200678     0 OBJECT  LOCAL  DEFAULT   17 __JCR_END__
    37: 0000000000000590     0 FUNC    LOCAL  DEFAULT   11 __do_global_ctors_aux
    38: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS foo.c
    39: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS bar.c
    40: 0000000000200820     0 OBJECT  LOCAL  DEFAULT  ABS _GLOBAL_OFFSET_TABLE_
    41: 0000000000200680     0 OBJECT  LOCAL  DEFAULT   18 __dso_handle
    42: 0000000000200670     0 OBJECT  LOCAL  DEFAULT   16 __DTOR_END__
    43: 0000000000200688     0 OBJECT  LOCAL  DEFAULT  ABS _DYNAMIC
    44: 000000000000057c     6 FUNC    GLOBAL DEFAULT   11 bar
    45: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    46: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
    47: 00000000000005c8     0 FUNC    GLOBAL DEFAULT   12 _fini
    48: 000000000000056c    16 FUNC    GLOBAL DEFAULT   11 foo
    49: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@@GLIBC_2.           2.5
    50: 0000000000200848     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    51: 0000000000200858     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    52: 0000000000200848     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    53: 0000000000000458     0 FUNC    GLOBAL DEFAULT    9 _init

比较新的readelf支持'–dyn-syms'选项来仅输出'.dynsym':

$ readelf --dyn-syms -W libfoo.so

Symbol table '.dynsym' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000458     0 SECTION LOCAL  DEFAULT    9
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2           .5 (2)
     5: 000000000000057c     6 FUNC    GLOBAL DEFAULT   11 bar
     6: 000000000000056c    16 FUNC    GLOBAL DEFAULT   11 foo
     7: 0000000000200858     0 NOTYPE  GLOBAL DEFAULT  ABS _end
     8: 0000000000200848     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
     9: 0000000000200848     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    10: 0000000000000458     0 FUNC    GLOBAL DEFAULT    9 _init
    11: 00000000000005c8     0 FUNC    GLOBAL DEFAULT   12 _fini

$ nm -D libfoo.so
                 w _Jv_RegisterClasses
0000000000200848 A __bss_start
                 w __cxa_finalize
                 w __gmon_start__
0000000000200848 A _edata
0000000000200858 A _end
00000000000005c8 T _fini
0000000000000458 T _init
000000000000057c T bar
000000000000056c T foo

参考:
http://stackoverflow.com/questions/1237575/how-do-i-find-out-what-all-symbols-are-exported-from-a-shared-object


Author: cig01

Created: <2015-03-15 Sun 00:00>

Last updated: <2017-12-13 Wed 11:55>

Creator: Emacs 25.3.1 (Org mode 9.1.4)