编译与链接的概念

编译与链接的概

 

前言:

先说明项目的需求:因为c++有高效的STL库,所以我想用c++编写一个缓存帧图像的JitterBuffer。利用该JitterBuffer缓存的一些措施,来实现帧部分丢失RTP包后对这些丢失的RTP包做重传。这篇文档的小Demo是为上述目的做铺垫的。该demo涉及了Pjsip的C代码编译链接C++代码相关的重要概念。本人选了一个不同的文档风格,来表达我对底层开发的编译链接装载库的理解。直接上代码附带注释,来说明编译库过程中可能遇到与库概念相关的问题。比如最近的对项目pjsip的升级,重新编译,可能会遇到各式各样的编译链接问题。我觉得最重要的理解是:“中间文件的全局符号是暴露给其它中间文件的引用接口”。

文档框架:main.c文件引用JSF_JitterBuffer.cpp文件,而JSF_JitterBuffer.cpp文件又引用了部分C语言的库函数。

 

/*

JSF_JitterBuffer.cpp文件

*/

#include <iostream>

#include <vector>

#include "JSF_JitterBuffer.h"

int jsf = 9;//强符号,若是 int jsf; 则是弱符号。

//多个同 弱符号 不会产生 .o 链接符号冲突问题

int  JitterBuffer(unsigned int count,unsigned char value)

{

unsigned int privCount =count;

printf("c:%d,v:%d\n", count, value);

unsigned char *p = (unsigned char *)malloc(count*sizeof(unsigned char));

memset(p, value, count);

while (count > 0) { --count; printf("%c\n", *p); ++p; }

return privCount*value;

}

//本 JSF_JitterBuffer.cpp 文件 和 JSF_JitterBuffer.h 文件 编译生成一个“独立”的 .o 单元

/*

JSF_JitterBuffer.h文件

*/

#ifndef JSF_JB_H_

#define JSF_JB_H_

//判断本文件是 c 还是 c++ ;

//若是 c 文件,则直接生成 memset 符号,链接时 与 c 库的 memset 符号相连接。

// 若是 c++ 文件,则利用 extern "C" 语法(c++ 专属),不要生成 c++ 类型的 memset 符号,

// 而是生成 c 类型的 memset 符号,链接时 与 c 库的 memset 符号相连接。

#ifdef __cplusplus 

extern "C" {

#endif

//c++语言,生成的中间文件的接口名是 C 语言的,可与 c 中间文件相连接

int JitterBuffer(unsigned int ts,unsigned char seq);

//c++语言,生成 c 类型的 memset 符号,可与 c 库的 memset 符号相连接。

void *memset(void *,int,size_t);

void *malloc(size_t size);

int printf(const char *format, ...);

extern int jsf;

#ifdef __cplusplus

}

#endif

#endif

//注意:main.c 中 声明 extern JitterBuffer 与 本文件中的 memset 的区别关系?

/*

main.c文件

*/

#include <stdio.h>

//#include "JSF_JitterBuffer.h"  

//本文件如果作了JSF_JitterBuffer.cpp中 定义的符号 的 符号声明 ,就没有必要再包含

//JSF_JitterBuffer.h 头文件

//而 JSF_JitterBuffer.h 头文件中的 #ifndef XXX  #define XXX  #endif 的

//目的就是为了避免 可能 的重复 定义。

//****现在要转变视角,把编译的一个(x.cpp + include<x.h>)文件看成是一个单独的 x.o 单元,x.cpp中的全局符号,

//是该 x.o 单元暴露给其它 *.o 单元的符号接口,可被 *.o 引用。******//

//链接的时候,去其它的中间文件找该函数的实现。plus:加不加 extern 都能正常链接???

extern int JitterBuffer(unsigned int,unsigned char);//answer:函数或变量可以声明多次,但是只能定义一次!

  //int JitterBuffer(unsigned int, unsigned char);

int jsf;  //为什么这个没有重复定义???(注意,这里的 jsf 是 “弱符号”),不会与 JSF_JitterBuffer.o 中的jsf冲突

extern  int jsf;//这里只是对 jsf 进行声明。

 //若是 int jsf = 8; 则链接错误! 与 JSF_JitterBuffer.o 中 定义 的 “强符号” jsf 冲突

 //int jsf = 8;

int main()

{

int a = JitterBuffer(8, 102);

//若是 main.o 与 libJB.a(.o 文件集合) 链接成 exe ,则 libJB.a 成为 exe 的一部分。

//若是 main.o 与 libJB.so 链接成 exe ,

//则 exe 仅仅是将 main.o 中的使用的库函数 标记 为一个对 libJB.so 动态链接符号 的引用。

//待到运行装载时,(“动态链接器”) 再把 libJB.so 载入到内存,然后进行对 exe 中的符号进行 符号重定位。

printf("rlt:%d\n", a); //该函数已经在 stdio.h 中声明了,在包含了<stdio.h>后,main.c可以直接使用。

printf("jsf:%d\n", ++jsf);

return 0;

}

//概念:强符号,函数定义 及 用值初始化了的全局变量。

//      弱符号,未初始化的全局变量。

/*

编译shell文本

*/

#!/bin/sh

#几个有用的概念

#在linux系统中;.o文件,.a文件,.so文件,.exe文件均是以 elf 标准格式存储。

####1查看文件name的属性:file name(包含64bit或32bit,x86或arm,文件类型 .o .a .so .exe 或 UTF-8 Unicode text)

####2查看文件name.o的全局符号表:readelf -s name.o(包含链接器对该 .o 扫描得到的可被其它 .o 文件所引用的 全局符号)

####3查看文件name.a中包含的所有中间文件:ar -t name.a(列出.a包含的所有.o文件)

####4查看文件name.a中包含有那些符号段信息:objdump -t name.a(列出包含的所有.o文件的段及符号的详细信息)

####5查看exe或.so所依懒的共享库:ldd exe/.so (列出文件引用符号所依懒的库)

####6查看文件name.so中导出符号:readelf -sD name.so(列出可被应用的全局符号定义,也可以只加 -s 选项)

####7查看exe或.so导入符号(符号定义在其它文件,在运行装载时重定位):readelf -r exe/.so

####8查看exe被执行时动态链接器加载库的打印信息:LD_DEBUG=files exe =libs,显示查找过程,=bindings,显示符号绑定过程等,详见《程序员的自我修养》page 244)

####查看gcc编译链接的所有库的详细信息:加 -verbose 选项

####判断文件name.so是否是PIC的:readelf -d name.so | grep TEXTREL (无任何输出则为 PIC 的,加 -fPIC 选项)

############## ---dynamic exe (compiler linker with.a library) --- ###############

gcc - c JSF_JitterBuffer.cpp # 实际上 gcc 自动接到 g++ , 加 - c 选项只生成.o 中间文件

ar - rc libJSF_JitterBuffer.a JSF_JitterBuffer.o # 将.o 中间文件 打包 成.a 静态库

# 生成的exe1,注意,虽然链接了.a 静态库(.a 仅仅是.o 文件的集合;其.o 中的内容链接成为 exe1 的一部分;所以说.a 库文件改变后,整个用到的.a 系统都要重新编译过一遍!)。但因为没加 -static 选项 exe1 还是运行时加载动态库(例如 libc.so.6)!

gcc - o dynamic_test_exe1 main.c - L. - lJSF_JitterBuffer - lstdc++ #因为是gcc编译,手工指定链接 c++ std 库

############### ---dynamic exe (compiler linker with .so library) --- ###############

gcc - shared - fPIC - o libJSF_JitterBufferSo.so JSF_JitterBuffer.cpp # 若是用上面的 JSF_JitterBuffer.o,则产生.so出错。若用上面的 libJSF_JitterBuffer.a 则可以??(但是,readelf - sD 查看没有对应的被应用全局符号??)。说明? 在语法上可以用.a文件生成.so文件。。。#  - shared 指定生成.so 库, - fPIC 指定 生成的.so 库可被多个不同的进程共享(.txt段,一般是 read only 段)

gcc - o dynamic_test_exe2 main.c - L. - lJSF_JitterBufferSo - lstdc++#将 main.o 中使用的库函数 标记 为对libJSF_JitterBufferSo.so库的 动态链接符号 的引用!!!(详见main.c中的说明)(运行时出错,可以把库拷入 / lib)

gcc - o dynamic_test_exe3 main.c - L. - lJSF_JitterBufferSo - lstdc++ - Wl, -rpath ./ #指定运行时所找的路劲

#注意,dynamic_test_exe2 装载运行时,动态链接器,搜索的动态库路径,的搜索先后顺序是:

#1.编译目标代码时指定的动态库搜索路径;(#"-Wl,-rpath 路径", 运行时 ##"-L 路径",编译时 ##"-Wl,-rpath-link 路径",编译时#)

#2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径;

#3.配置文件 / etc / ld.so.conf中指定的动态库搜索路径;

#4.默认的动态库搜索路径 / lib;

#5.默认的动态库搜索路径 / usr / lib.

#question,为什么dynamic_test_exe1能正常运行,而dynamic_test_exe2报找不

#到error while loading shared libraries: libJSF_JitterBufferSo.so错误?

#因为编译exe1用的JB库是静态库,JB.a已经成为exe1的一部分。而exe2仅仅

#包含JB.so的符号引用。另外exe1中也需要运行时寻找libstdc++.so库,只不过

#它在默认的寻找路径中可以被找到罢了。

############### ----static exe (compiler linker with -static election)----#########################

gcc - o static_test_exe1 main.c - L. - lJSF_JitterBuffer - lstdc++ - static #完全没有运行时动态库的载入,以及运行时有关符号的重定位,exe作为的独立的elf文件被分页映射拷贝到内存。

#gcc - o static_test_exe2 main.c - L. - lJSF_JitterBufferSo - lstdc++ - static #编译出错!!! not found libxxSo

gcc - shared - fPIC - o libJSF_JitterBuffer.so JSF_JitterBuffer.cpp #测试, - static时, - lx 链接 libx.a还是libx.so ?

gcc - o static_test_exe3 main.c - L. - lJSF_JitterBuffer - lstdc++ - static #会成功吗?ok,会自动链接静态版本库 libx.a

#由上得出,单纯由.so 不能制作.a 库 ?

#但.a 库可以生成.so 库吗 ?

#另外.so 库能生成 另一个.so 库吗?

猜你喜欢

转载自blog.csdn.net/sf_jiang/article/details/76273508