静态库lib.a结构简介

静态库文件是使用打包工具ar将一个或者多个可重定位的目标文件组成一个静态库文件,也可以看做是多个可重定位的目标文件的一个简单包装文件。

1. 静态库的生成

假设我们要构建一个四则运算的而函数库

(1)其源代码如下:

[root@localhost liba]# cat add.c
int
add (int a, int b)
{
  return a + b;
}

[root@localhost liba]# cat sub.c
int
sub (int a, int b)
{
  return a - b;
}

[root@localhost liba]# cat mult.c
int
mult (int a, int b)
{
  return a * b;
}

[root@localhost liba]# cat div.c
int
div (int a, int b)
{
  int d = a / b;
  return d;
}

(2) 构建Makefile

[root@localhost liba]# cat Makefile
AR = ar
STATIC_LIB = ./libmath.a
DYNAMIC_LIB = ./libmath.so
objects = add.o sub.o mult.o div.o
pic_objects = add.o sub.o mult.o div.o

all: $(STATIC_LIB) $(DYNAMIC_LIB)

obj: $(objects)

$(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

$(pic_objects): %.o: %.c
        $(CC) -c $(CFLAGS) -fpic $< -o $@

$(STATIC_LIB) : $(objects)
        $(AR) rcs $@ $^

$(DYNAMIC_LIB): $(pic_objects)
        $(CC) -shared -o $@ $^

clean:
        rm -f $(objects)
        rm -f $(STATIC_LIB) $(DYNAMIC_LIB)

(3) 编译生成libmath.a静态库文件

执行make all,生成libmath.a和libmath.so。

[root@localhost liba]# make all

 2. 静态库文件的基本结构

    静态库的结构在/usr/include/ar.h文件中描述了ar文件结构中的几个重要数据结构,从中可以分析出ar文件的结构主要包括两大部分:

(1)存档文件的魔数

存档文件的魔数(Magic Number)标识ARMAG,也可以看做是ar文件的首部,该首部是一个8字节的字符序列,其定义为:

#define ARMAG   "!<arch>\n"     /* String that begins an archive file.  */

魔数ARMAG的字节大小SARMAG,其值为整数值8,其定义为:

#define SARMAG  8               /* Size of that string.  */

(2)每个成员文件的相关信息

每个成员文件的信息包括也两部分:

A. 成员文件首部

使用struct ar_hdr结构体来描述该成员文件的基本信息,包括文件名称、日期、用户ID、组ID、文件存取属性、文件大小以及一个结构体结束标记。该结构体的定义如下:

#define ARFMAG  "`\n"           /* String in ar_fmag at end of each header.  */

struct ar_hdr
  {
    char ar_name[16];           /* Member file name, sometimes / terminated. */
    char ar_date[12];           /* File date, decimal seconds since Epoch.  */
    char ar_uid[6], ar_gid[6];  /* User and group IDs, in ASCII decimal.  */
    char ar_mode[8];            /* File mode, in ASCII octal.  */
    char ar_size[10];           /* File size, in ASCII decimal.  */
    char ar_fmag[2];            /* Always contains ARFMAG.  */
  };

B.成员文件的内容

如果ar文件是静态库,那么该部分内容就是构造这个静态库的可重定位目标文件(也就是我们常见的o文件)的内容。

图 1.  使用ar工具打包的静态库格式

3. 静态库文件的索引部分

如果在ar打包时使用-s参数,则在ar文件中会添加一个索引部分,索引部分的组织与其他成员文件的组织方式相同,索引成员文件首部中的ar_name的值为“/              ”。

也可以使用nm查看该索引内容。


[root@localhost liba]# nm --print-armap libmath.a

Archive index:
add in add.o
sub in sub.o
mult in mult.o
div in div.o

add.o:
00000000 T add

sub.o:
00000000 T sub

mult.o:
00000000 T mult

div.o:
00000000 T div

索引成员文件的内容一般包括:

(1)符号数量

(2)每个符号所在的成员文件在整个ar文件中的偏移量

(3)所有符号的名称列表

上述libmath.a文件的具体内容分析如下:

 图2. libmath.a 结构图示

也可以使用hexdump查看具体内容。


参考文献

[1] Executable and Linkable Format (ELF) - eLinux.org

[2] ELF - OSDev Wiki

[3] Understanding the ELF File Format

[4] The 101 of ELF files on Linux: Understanding and Analysis

[5] www.skyfree.org/linux/references/ELF_Format.pdf

[6] ELF:From The Programmer's Perspective

<think>我们正在讨论的是静态库文件(LIB.a)对应的符号表。符号表包含调试信息,用于调试。根据之前的上下文,我们知道: - 在Linux/Unix系统中,静态库通常以.a结尾。 - 符号表可以内嵌在文件中,也可以单独存放(如.debug文件)。 参考引用: [^1]:提到Linux下的静态库.a结尾,动态.so或.so.y结尾。 [^3]:提到静态库文件(如libsllofmingw_d.a)是真正的静态库,并且比动态的导入libdllofmingw_d.a)要大,因为它包含了实际的代码和调试信息。 [^4]:区分了静态库(包含实际代码)和动态的导入(仅包含重定位信息)。 因此,对于静态库LIB.a,其符号表可能有以下几种形式: 1. **内嵌在静态库中**:在编译静态库时,如果使用`-g`选项(生成调试信息),那么调试信息会直接包含在.a文件中。这样,当使用这个静态库链接程序时,最终的可执行文件可以包含调试信息(如果最终链接时也用了`-g`)。 2. **单独的调试信息文件**:有时为了减小发布版本的体积,会剥离调试信息,将调试信息单独存放在一个文件中。在Linux系统中,常见的做法是: - 使用`objcopy`工具从静态库中提取调试信息,生成一个单独的调试文件。例如,对于静态库`libexample.a`,可以生成一个`libexample.a.debug`或`libexample.debug`文件。 - 或者,按照系统的调试信息存放规则,将调试信息文件放在特定的目录下(如/usr/lib/debug/...),并保持相同的文件结构。 3. **调试静态库**:有些构建系统可能会生成两个版本的静态库:一个是不带调试信息的发布版,另一个是带调试信息的调试版。调试版的静态库通常以`_debug`为后缀,或者放在不同的目录(如`debug`目录)下。 如何查找或生成LIB.a对应的符号表? ### 步骤: 1. **检查静态库是否已经包含调试信息**: 使用命令`file`和`nm`来检查: ```bash file libexample.a ``` 如果输出中包含“with debug_info”或类似信息,则说明已经包含调试信息。 或者使用: ```bash nm --debug-syms libexample.a ``` 如果有输出符号信息,则说明包含调试信息。 2. **生成单独的调试信息文件**(如果静态库中已有调试信息,但想要剥离出来): 使用`objcopy`: ```bash objcopy --only-keep-debug libexample.a libexample.a.debug ``` 这样就会生成一个单独的调试信息文件`libexample.a.debug`。注意,这个文件不能直接用于链接,仅用于调试。 3. **从源文件重新编译生成带调试信息的静态库**: 如果静态库不包含调试信息,那么需要重新编译源代码并添加`-g`选项。例如,使用gcc编译源文件为带调试信息的对象文件: ```bash gcc -c -g source.c -o source.o ``` 然后使用`ar`命令重新打包静态库: ```bash ar rcs libexample.a source.o ``` 4. **调试信息文件的使用**: 当使用gdb调试最终链接的程序时,如果调试信息是内嵌在静态库中的,那么当程序链接了该静态库,调试信息会被包含在最终的可执行文件中(如果链接时也使用了`-g`)。 如果调试信息是单独的文件,可以在gdb中使用`add-symbol-file`命令来加载,或者将调试信息文件放在gdb可以自动找到的路径(通常需要符合特定的目录结构,例如`/usr/lib/debug/path/to/libexample.a.debug`)。 5. **在构建系统中生成调试静态库**: 在Makefile或CMake中,可以通过设置编译选项来生成带调试信息的静态库。例如,在CMake中: ```cmake add_library(example STATIC source.c) target_compile_options(example PRIVATE -g) ``` ### 注意事项: - 静态库.a)是由多个对象文件(.o)打包而成。每个对象文件可以独立包含调试信息。因此,在创建静态库时,必须确保每个对象文件都是用`-g`编译的。 - 如果静态库已经生成,但不知道是否包含调试信息,可以使用`objdump`或`readelf`来查看其中一个对象文件: ```bash ar x libexample.a # 解压出其中的对象文件 objdump -g example_object.o # 查看调试信息 ``` ### 总结: - **LIB.a对应的符号表**可以是: (1)LIB.a本身(如果编译时加了`-g`)。 (2)单独生成的调试文件,通常命名为LIB.a.debug(或类似的名称)。 (3)在构建系统中生成的另一个调试版本的静态库(如LIB_debug.a)。 因此,具体要看您的构建过程和需求。通常,在开发过程中,我们直接使用带调试信息的静态库(即编译时加入`-g`选项)进行链接,这样最终的可执行文件包含调试信息。 如果您的静态库是从第三方获得的,且不包含调试信息,那么您需要向提供方索取对应的符号表(单独的调试文件或调试版本的静态库)。 如果您是自己编译的,那么请确保在编译静态库时添加`-g`选项。 希望这能解答您的问题。 §§相关问题§§ 1. 如何验证静态库LIB.a中是否包含调试信息? 2. 在GDB中如何加载单独的静态库调试信息文件? 3. 如何减小带调试信息的静态库文件大小? </think>### 查找静态库 LIB.a 对应的符号表 符号表包含调试信息(函数名、变量地址、源码行号等),用于程序调试。根据引用内容,静态库(`.a`)的符号表有以下特点: 1. **符号表存储形式** - **内嵌在静态库中**(主要形式): 当静态库编译时添加 `-g` 选项,调试信息会直接嵌入 `.a` 文件中(如引用[3]中的 `libsllofmingw_d.a`)[^3]。 ```bash gcc -c -g source.c # 编译带调试信息的对象文件 ar rcs libexample.a *.o # 打包成静态库 ``` 生成的 `.a` 文件会显著增大(比无符号表的大一个数量级)[^3]。 - **单独调试文件**(较少见): 可通过 `objcopy` 提取符号表: ```bash objcopy --only-keep-debug libexample.a libexample.a.debug ``` 2. **与动态导入的区别** 需区分真正的静态库和动态的导入(伪静态库): - **真正的静态库**(`.a`):包含完整代码和符号表(如 `libsllofmingw_d.a`)[^3]。 - **动态导入**(`.a` 或 `.lib`):仅含重定位信息(如 `libdllofmingw_d.a`),体积较小,**无符号表**[^3][^4]。 3. **验证符号表存在性** 使用 `nm` 或 `objdump` 检查: ```bash nm --debug-syms libexample.a # 查看调试符号 objdump -g libexample.a # 检查DWARF调试信息 ``` 若输出包含函数名/变量名(如 `T function_name`),则符号表存在。 ### 如何获取符号表 | 场景 | 操作步骤 | |-------------------------------|--------------------------------------------------------------------------| | 需从源码编译新 | 编译时添加 `-g` 选项:<br>`gcc -g -c source.c && ar rcs libnew.a *.o` | | 现有静态库无符号表 | 1. 获取源码重新编译(加 `-g`)<br>2. 联系提供方索取调试版本 | | 从动态导入.a)获取 | **不可行**:此类伪静态库本身不含符号表[^3][^4] | | 提取嵌入式符号表 | 生成单独调试文件:<br>`objcopy --only-keep-debug libexample.a libexample.debug` | > **关键结论**: > LIB.a 的符号表通常直接**内嵌在文件中**,而非单独的"符号表"。要获取带符号表的静态库,必须在编译阶段通过 `-g` 选项生成[^3]。 ### 示例:生成带符号表的静态库 ```bash # 编译带调试信息的对象文件 gcc -g -c src1.c src2.c # 打包成静态库(含符号表) ar rcs libmylib.a src1.o src2.o # 验证符号表 nm --debug-syms libmylib.a | grep "T my_function" ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值