GCC编译,库的编译使用及Makefile

我们一般用: ar -rsv obj.o obj2.o libobj.a 命令来将 obj.o obj2.o 生成一个静态库 libobj.a
然后在使用: gcc -L/path/ lobj 来连接静态库文件 libobj.a

动态库的生成:gcc -O -fpic -shared -o obj.so obj.c 命令来将生成一个动态库 obj.so
然后的链接使用:gcc -o main main.c ./obj.so 来连接动态库文件 obj.so

=======================================================================================

..3.. man as:
NAME
AS - the portable GNU assembler.
编译:as -gstabs -o helloworld.o helloworld.s
链接:ld -o helloworld helloworld.o
执行:./helloworld

=======================================================================================

..4.. man objdump:
NAME
objdump - display information from object files.

反汇编: objdump -S -d helloworld
-S 表示 source ,即反汇编的同时显示源码;-d 表示 disassemble 即反汇编。
一、静态库
静态库文件也成为“文档文件”,它是一些 .o 文件的集合。
在linux中,使用 ar 维护和管理。
使用库函数时,需include 对应的 .h 头文件。
库文件 libName.a 的库名是 NAME

在 linux 中静态库是以 .a 为后缀的文件,共享库是以 .so 为后缀的文件。
在windows中静态库是以 .lib 为后缀的文件,共享库是以 .dll 为后缀的文件。

二、库文件的使用

要用 libtest.so 库里的函数,需要与libtest.so配套的头文件
包含头文件有两种方法,多用第一种方法
(1)源码里用 #include xxxxxx 包含头文件
(2)用 gcc 的 -include 包含头文件

gcc 参数
-l: 指定库文件。
-L: 指定搜索位置。
-I: 指定头文件搜索位置。
查看默认搜索位置:$gcc -print-search-dirs
其中,libraries 是库文件的搜索列表。
e.g. gcc main.c -L. -lstack -Istack -o main

库文件:libstack.a

gcc 搜索路径的优先级

头文件

※搜寻会从-I开始
※然后找gcc的环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH
※再找内定目录

    /usr/include 

    /usr/local/include
        /usr/lib/gcc-lib/i386-linux/2.95.2/include
       /usr/lib/gcc-lib/i386-linux/2.95.2/include/g++-3
       /usr/lib/gcc-lib/i386-linux/2.95.2/i386-linux/include

库文件
※先找-L
※再找gcc的环境变量LIBRARY_PATH
※再找内定目录 /lib:/usr/lib: /usr/local/lib:这是当初compile gcc时写在程序内的

include "FILE.h" 先搜索当前目录,后搜索系统头文件目录。

include <FILE.h> 只搜索系统头文件目录。

linux下 默认搜索位置:
头文件: /usr/include(Unix)

三、静态库的创建
(1)将源文件编译成目标文件:gcc –c Add.c MakeEmpty.c
(2)生成静态库:ar –rc liblist.a Add.o MakeEmpty.o

四、ar命令
ar功能:集合许多文件,成为单一的备存文件。在备存文件中,所有成员文件皆保有原来的属性与权限

  -d  删除备存文件中的成员文件。   
  -p  显示备存文件中的成员文件内容。
  -r  将文件插入备存文件中。 
  -t  显示备存文件中所包含的文件。 
  -x  自备存文件中取出成员文件。    

通过make,可以方便的维护个人静态函数库。

在windows下动态链接库是以.dll后缀的文件,而在Linux中,是以.so作后缀的文件。

动态链接库的好处就是节省内存空间。

1、Linux下创建动态链接库

在使用GCC编译程序时,只需加上-shared选项即可,这样生成的执行程序即为动态链接库。

例如有文件:hello.c x.h main.c

编译:gcc hello.c -fPIC -o libhello.so

其中-fPIC选项的作用是:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的,

所以动态载入时是通过代码拷贝的方式来满足不同的调用,而不能达到真正的代码段共享的目的.

将main.c与hello.so动态库gcc main.c -L. -lhello -o main

一、动态链接库

1.创建hello.so动态库

#include <stdio.h>

void hello(){

        printf("hello world
");

}

编译:gcc -fPIC -shared hello.c -o libhello.so

2.hello.h头文件

void hello();

3.链接动态库

#include <stdio.h>

#include "hello.h"

int main(){

        printf("call hello()");

        hello();

}

复制代码

编译:gcc main.c -L. -lhello -o main这里-L的选项是指定编译器在搜索动态库时搜索的路径,告诉编译器hello库的位置。"."意思是当前路径.

3.编译成够后执行./main,会提示:

In function main": main.c.text+0x1d): undefined reference to hello"collect2: ld returned 1 exit status这是因为在链接hello动态库时,编译器没有找到。

解决方法:

sudo cp libhello.so /usr/lib/这样,再次执行就成功输入:

call hello()

二、静态库

文件有:main.c、hello.c、hello.h

1.编译静态库hello.o:

gcc hello.c -o hello.o #这里没有使用-shared

2.把目标文档归档

ar -r libhello.a hello.o #这里的ar相当于tar的作用,将多个目标打包。程序ar配合参数-r创建一个新库libhello.a,并将命令行中列出的文件打包入其中。这种方法,如果libhello.a已经存在,将会覆盖现在文件,否则将新创建。

3.链接静态库

gcc main.c -lhello -L. -static -o main这里的-static选项是告诉编译器,hello是静态库。

或者:

gcc main.c libhello.a -L. -o main这样就可以不用加-static

4.执行./main

输出:call hello()

三、借助自带的ldd实现程序来分析动态库搜索情况

ldd main

结果:

linux-gate.so.1 => (0x00efd000)

libhello.so => /usr/lib/libhello.so (0x00f6b000)

libc.so.6 => /lib/libc.so.6 (0x001a5000)

/lib/ld-linux.so.2 (0x00eb8000)

如果目标程序没有链接动态库,则打印“not a dynamic executable”

  1. 当在同一个目录下静态库和共享库同名时,共享库优先
    hello.h头文件

ifndef HELLO_H

define HELLO_H

void print_hello();

endif

hello.c源文件

include "hello.h"

include <stdio.h>

int main(int argc,char *argv[])
{
printf("hello world!");

测试使用源文件main.c

include "hello.h"

int main(int argc,char argv[])
{
printf_hello();
}
1.1 编译静态库和共享库
[test@hadoop hello]$ ls
hello.c hello.h main.c
[test@hadoop hello]$ gcc -c hello.c
[test@hadoop hello]$ ar crs libhello.a hello.o
[test@hadoop hello]$ gcc -shared -fPIC -o libhello.so hello.o
[test@hadoop hello]$ ls
hello.c hello.h hello.o libhello.a libhello.so main.c
1.2 使用hello库编译 main.c
[test@hadoop hello]$ gcc main.c -o main -L. -lhello -I.
[test@hadoop hello]$ ldd main
linux-gate.so.1 => (0x00a11000)
libhello.so => /home/test/programs/c/hello/libhello.so (0x0024b000)
libc.so.6 => /lib/libc.so.6 (0x0024d000)
/lib/ld-linux.so.2 (0x0011f000)
看的出, gcc选择的是libhello.so, 即共享库优先
2. 当同一目录下存在同名静态库和共享库, 那么怎样选择静态库那?
[test@hadoop hello]$ ls
hello.c hello.h hello.o libhello.a libhello.so main.c
[test@hadoop hello]$ gcc -static main.c -o main -L. -lhello -I. #使用-static 参数阻止链接共享库
[test@hadoop hello]$ ls
hello.c hello.h hello.o libhello.a libhello.so main main.c
[test@hadoop hello]$ ldd main
not a dynamic executable
看的出, gcc此次选择的是libhello.a, 即静态库
3. 编译程序中同时包含静态库和共享库
hello.h代码同上
********
hello.c代码同上*********

calculate.h头文件

//calculate.h

ifndef CALCULATE_H

define CALCULATE_H

int add(int a,int b);

endif

calculate.c源文件

//calculate.c

include "calculate.h"

int add(int a,int b)
{
return a+b;
}
新的测试文件main.c

include "hello.h"

include "calculate.h"

include <stdio.h>

int main(int argc,char *argv[])
{
print_hello();
int res=add(1,2);
printf(" 1+2=%d ",res);
}
3.1 将hello的静态库文件libhello.a删除,保留共享库文件libhello.so
3.2 编译calculate成为静态库libcalculate.a
[test@hadoop hello]$ gcc -c calculate.c
[test@hadoop hello]$ ar crs libcalculate.a calculate.o
[test@hadoop hello]$ ls
calculate.c calculate.h calculate.o hello.c hello.h libcalculate.a libhello.so main.c

3.3 使用libcalculate.a和libhello.so编译main
[test@hadoop hello]$ gcc main.c -o main -L. -lhello -lcalculate -I.
[test@hadoop hello]$ ls
calculate.c calculate.h calculate.o hello.c hello.h libcalculate.a libhello.so main main.c
[test@hadoop hello]$ ldd main
linux-gate.so.1 => (0x00769000)
libhello.so => /home/test/programs/c/hello/libhello.so (0x00c6d000)
libc.so.6 => /lib/libc.so.6 (0x0013e000)
/lib/ld-linux.so.2 (0x0011f000)
看得出, 我们成功了:)

前言

我们通常把一些公用函数制作成函数库,供其他程序使用。函数库分为静态库和动态库两种。本文讲解如何制作属于自己的静态库。

什么是静态库?

通常来说,静态库以.a作为后缀,且以lib开头。类似于libxxx.a。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

ar命令详解

Linux ar命令用于创建或者操作静态库。

ar命令的参数如下:

参数 意义
-r 将objfile文件插入静态库尾或者替换静态库中同名文件
-x 从静态库文件中抽取文件objfile
-t 打印静态库的成员文件列表
-d 从静态库中删除文件objfile
-s 重置静态库文件索引
-v 显示详细信息
-c 创建静态库文件
制作静态库

test.c

include <stdio.h>

include "test.h"

void test(){

printf("This is a static library
");

}

1
2
3
4
5
6
7
8
9
10
test.h

define TEST_H

ifndef TEST_H

void test();

endif

1
2
3
4
5
6
编译成可重定位文件,即生成.o文件:

在这里插入图片描述

为了制作成静态库,我们需要使用ar命令。

ar -rcs libtest.a test.o #库名一般以.a为扩展名,以lib开头
ar -t libtest.a #查看内容
test.o
1
2
3
在这里插入图片描述输出信息可以看到,静态库以.a作为后缀,且以lib开头,这时候就制作好了自己的静态库了。

制作好了静态库,下面来使用它。

静态库的使用

编写一个main.c文件进行测试:

在这里插入图片描述
main.c

include <stdio.h>

include "test.h"

int main(int argc, char const *argv[])
{
test();

return 0;

}

1
2
3
4
5
6
7
8
9
10
在这里插入图片描述
出现信息为test未定义引用,原因是test已经编译成静态库。

解决办法为:
在这里插入图片描述

静态库的代码在编译时链接到应用程序中,因此编译时库文件必须存在,并且需要通过"-L"参数传递路径给编译器。

链接的库名为libtest.a,在链接的时候,去掉开头的lib和后缀.a,前面再加l,就变成了-ltest,其他库也是类似。

例如,你如果看到程序链接使用-lm,说明它使用了名为libm.a的库。可以参考这一篇在编译时时为什么要链接 -lm

总结

编译静态库时先使用-rcs选项,再利用ar工具产生,然后把一些文件可重定位文件打包在一起。

https://www.cnblogs.com/stewarttzy/p/3932507.html?utm_source=tuicool
make -f binding.Makefile
make -f Makefile
make -f base64_sse42.target.mk

将持续更新

一,gcc和g++编译命令基础

gcc/g++在执行编译工作的时候,总共需要4步

1.预处理,生成.i的文件[预处理器cpp]
2.将预处理后的文件不转换成汇编语言,生成文件.s[编译器egcs]
3.有汇编变为目标代码(机器代码)生成.o的文件[汇编器as]
4.连接目标代码,生成可执行程序[链接器ld]

[参数详解]
-x language filename
   设定文件所使用的语言,使后缀名无效,对以后的多个有效.也就是根
   据约定C语言的后缀名称是.c的,而C++的后缀名是.C或者.cpp,如果
   你很个性,决定你的C代码文件的后缀名是.pig 哈哈,那你就要用这
   个参数,这个参数对他后面的文件名都起作用,除非到了下一个参数
   的使用。
   可以使用的参数吗有下面的这些
     c', objective-c', c-header', c++', cpp-output',      assembler', and `assembler-with-cpp'.
   看到英文,应该可以理解的。
   例子用法:
   gcc -x c hello.pig
  
-x none filename
  关掉上一个选项,也就是让gcc根据文件名后缀,自动识别文件类型
  例子用法:
  gcc -x c hello.pig -x none hello2.c
  
-c
  只激活预处理,编译,和汇编,也就是他只把程序做成obj文件
  例子用法:
  gcc -c hello.c
  他将生成.o的obj文件

-S
  只激活预处理和编译,就是指把文件编译成为汇编代码。
  例子用法
  gcc -S hello.c
  他将生成.s的汇编代码,你可以用文本编辑器察看

-E
  只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里
  面.
  例子用法:
  gcc -E hello.c > pianoapan.txt
  gcc -E hello.c | more
  慢慢看吧,一个hello word 也要与处理成800行的代码

-o
  指定目标名称,缺省的时候,gcc 编译出来的文件是a.out,很难听,如果
  你和我有同感,改掉它,哈哈
  例子用法
  gcc -o hello.exe hello.c (哦,windows用习惯了)
  gcc -o hello.asm -S hello.c

-pipe
  使用管道代替编译中临时文件,在使用非gnu汇编工具的时候,可能有些问
  题
  gcc -pipe -o hello.exe hello.c

-ansi
  关闭gnu c中与ansi c不兼容的特性,激活ansi c的专有特性(包括禁止一
  些asm inline typeof关键字,以及UNIX,vax等预处理宏,

-fno-asm
  此选项实现ansi选项的功能的一部分,它禁止将asm,inline和typeof用作
  关键字。
    
-fno-strict-prototype
  只对g++起作用,使用这个选项,g++将对不带参数的函数,都认为是没有显式
  的对参数的个数和类型说明,而不是没有参数.
  而gcc无论是否使用这个参数,都将对没有带参数的函数,认为城没有显式说
  明的类型
  
-fthis-is-varialble
  就是向传统c++看齐,可以使用this当一般变量使用.
  
-fcond-mismatch
  允许条件表达式的第二和第三参数类型不匹配,表达式的值将为void类型
  
-funsigned-char
-fno-signed-char
-fsigned-char
-fno-unsigned-char
  这四个参数是对char类型进行设置,决定将char类型设置成unsigned char(前
  两个参数)或者 signed char(后两个参数)
  
-include file
  包含某个代码,简单来说,就是便以某个文件,需要另一个文件的时候,就可以
  用它设定,功能就相当于在代码中使用#include
  例子用法:
  gcc hello.c -include /root/pianopan.h
  
-imacros file
  将file文件的宏,扩展到gcc/g++的输入文件,宏定义本身并不出现在输入文件
  中
  
-Dmacro
  相当于C语言中的#define macro
  
-Dmacro=defn
  相当于C语言中的#define macro=defn
  
-Umacro
  相当于C语言中的#undef macro

-undef
  取消对任何非标准宏的定义
  
-Idir
  在你是用#include"file"的时候,gcc/g++会先在当前目录查找你所制定的头
  文件,如果没有找到,他回到缺省的头文件目录找,如果使用-I制定了目录,他
  回先在你所制定的目录查找,然后再按常规的顺序去找.
  对于#include,gcc/g++会到-I制定的目录查找,查找不到,然后将到系
  统的缺省的头文件目录查找
  
-I-
  就是取消前一个参数的功能,所以一般在-Idir之后使用
  
-idirafter dir
  在-I的目录里面查找失败,讲到这个目录里面查找.
  
-iprefix prefix
-iwithprefix dir
  一般一起使用,当-I的目录查找失败,会到prefix+dir下查找
  
-nostdinc
  使编译器不再系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确
  限定头文件的位置
  
-nostdin C++
  规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,.此选项在创建
  libg++库使用
  
-C
  在预处理的时候,不删除注释信息,一般和-E使用,有时候分析程序,用这个很
  方便的
  
-M
  生成文件关联的信息。包含目标文件所依赖的所有源代码
  你可以用gcc -M hello.c来测试一下,很简单。
  
-MM
  和上面的那个一样,但是它将忽略由#include造成的依赖关系。
  
-MD
  和-M相同,但是输出将导入到.d的文件里面
  
-MMD
  和-MM相同,但是输出将导入到.d的文件里面
  
-Wa,option
  此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选
  项,然后传递给会汇编程序
  
-Wl.option
  此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选
  项,然后传递给会连接程序.
  

-llibrary
  制定编译的时候使用的库
  例子用法
  gcc -lcurses hello.c
  使用ncurses库编译程序
  
-Ldir
  制定编译的时候,搜索库的路径。比如你自己的库,可以用它制定目录,不然
  编译器将只在标准库的目录找。这个dir就是目录的名称。
  
-O0
-O1
-O2
-O3
  编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最
  高  
  
-g
  只是编译器,在编译的时候,产生条是信息。
  
-gstabs
  此选项以stabs格式声称调试信息,但是不包括gdb调试信息.
  
-gstabs+
  此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息.
  
-ggdb
  此选项将尽可能的生成gdb的可以使用的调试信息.
-static
  此选项将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么
动态连接库,就可以运行.
-share
  此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
-traditional
  试图让编译器支持传统的C语言特性

二,静态库,共享库的编译与使用

1.静态库创建及使用

示例:

复制代码
  base.h->a.cpp , baes.h->b.cpp

  g++ -c a.cpp
  g++ -c b.cpp
  ar -r libsay.a a.o b.o  # 编译静态库:将所有目标文件打包入库中

  g++ main.cpp libsay.a -o run
复制代码

2.共享库(动态库)创建及使用

示例:

1   g++ -shared -fpic avg.cpp -o avg.so (编译动态库)
2
3   g++ main.cpp avg.so -o run

三,makefile的编写

1.makefile的规则

target (目标文件或label) : prerequisites (生成target所需要的东西)
command (执行的命令)
解释:

  最终要生成的目标(run.bin) :中间的目标文件(a1.o a2.o a3.o)

      编译命令

2.makefile中常用函数表

(一)、字符串处理函数
1.$(subst FROM,TO,TEXT)
函数名称:字符串替换函数—subst。
函数功能:把字串“TEXT”中的“FROM”字符替换为“TO”。
返回值:替换后的新字符串。

2.$(patsubst PATTERN,REPLACEMENT,TEXT)
函数名称:模式替换函数—patsubst。
函数功能:搜索“TEXT”中以空格分开的单词,将否符合模式“TATTERN”替换为“REPLACEMENT”。参数“PATTERN”中可以使用模
式通配符“%”来代表一个单词中的若干字符。如果参数“REPLACEMENT”中也包含一个“%”,那么“REPLACEMENT”中的“%”将是
“TATTERN”中的那个“%”所代表的字符串。在“TATTERN”和“REPLACEMENT”中,只有第一个“%”被作为模式字符来处理,后续的作为字符本上来处理。在两个参数中当使用第一个“%”本是字符本身时,可使用反斜杠“”对它进行转义处理。
返回值:替换后的新字符串。
函数说明:参数“TEXT”单词之间的多个空格在处理时被合并为一个空格,但前导和结尾空格忽略。

3.$(strip STRINT)
函数名称:去空格函数—strip。
函数功能:去掉字串(若干单词,使用若干空字符分割)“STRINT”开头和结尾的空字符,并将其中多个连续空字符合并为一个空字符。
返回值:无前导和结尾空字符、使用单一空格分割的多单词字符串。
函数说明:空字符包括空格、[Tab]等不可显示字符。

4.$(findstring FIND,IN)
函数名称:查找字符串函数—findstring。
函数功能:搜索字串“IN”,查找“FIND”字串。
返回值:如果在“IN”之中存在“FIND”,则返回“FIND”,否则返回空。
函数说明:字串“IN”之中可以包含空格、[Tab]。搜索需要是严格的文本匹配。

5.$(filter PATTERN…,TEXT)
函数名称:过滤函数—filter。
函数功能:过滤掉字串“TEXT”中所有不符合模式“PATTERN”的单词,保留所有符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符“%”。存在多个模式时,模式表达式之间使用空格分割。
返回值:空格分割的“TEXT”字串中所有符合模式“PATTERN”的字串。
函数说明:“filter”函数可以用来去除一个变量中的某些字符串,我们下边的例子中就是用到了此函数。

6.$(filter-out PATTERN...,TEXT)
函数名称:反过滤函数—filter-out。
函数功能:和“filter”函数实现的功能相反。过滤掉字串“TEXT”中所有符合模式“PATTERN”的单词,保留所有不符合此模式的单词。可以有多个模式。存在多个模式时,模式表达式之间使用空格分割。。
返回值:空格分割的“TEXT”字串中所有不符合模式“PATTERN”的字串。
函数说明:“filter-out”函数也可以用来去除一个变量中的某些字符串,(实现和“filter”函数相反)。

7.$(sort LIST)
函数名称:排序函数—sort。
函数功能:给字串“LIST”中的单词以首字母为准进行排序(升序),并取掉重复的单词。
返回值:空格分割的没有重复单词的字串。
函数说明:两个功能,排序和去字串中的重复单词。可以单独使用其中一个功能。

8.$(word N,TEXT)
函数名称:取单词函数—word。
函数功能:取字串“TEXT”中第“N”个单词(“N”的值从1开始)。
返回值:返回字串“TEXT”中第“N”个单词。
函数说明:如果“N”值大于字串“TEXT”中单词的数目,返回空字符串。如果“N”为0,出错!

9.$(wordlist S,E,TEXT)
函数名称:取字串函数—wordlist。
函数功能:从字串“TEXT”中取出从“S”开始到“E”的单词串。“S”和“E”表示单词在字串中位置的数字。
返回值:字串“TEXT”中从第“S”到“E”(包括“E”)的单词字串。
函数说明:“S”和“E”都是从1开始的数字。
当“S”比“TEXT”中的字数大时,返回空。如果“E”大于“TEXT”字数,返回从“S”开始,到“TEXT”结束的单词串。如果“S”大于“E”,返回空。

10.$(words TEXT)
函数名称:统计单词数目函数—words。
函数功能:字算字串“TEXT”中单词的数目。
返回值:“TEXT”字串中的单词数。

11.$(firstword NAMES…)
函数名称:取首单词函数—firstword。
函数功能:取字串“NAMES…”中的第一个单词。
返回值:字串“NAMES…”的第一个单词。
函数说明:“NAMES”被认为是使用空格分割的多个单词(名字)的序列。函数忽略“NAMES…”中除第一个单词以外的所有的单词。

(二)、文件名处理函数
1.$(dir NAMES…)
函数名称:取目录函数—dir。
函数功能:从文件名序列“NAMES…”中取出各个文件名目录部分。文件名的目录部分就是包含在文件名中的最后一个斜线(“/”)(包括斜线)之前的部分。
返回值:空格分割的文件名序列“NAMES…”中每一个文件的目录部分。
函数说明:如果文件名中没有斜线,认为此文件为当前目录(“./”)下的文件。

2.$(notdir NAMES…)
函数名称:取文件名函数——notdir。
函数功能:从文件名序列“NAMES…”中取出非目录部分。目录部分是指最后一个斜线(“/”)(包括斜线)之前的部分。删除所有文件名中的目录部分,只保留非目录部分。
返回值:文件名序列“NAMES…”中每一个文件的非目录部分。
函数说明:如果“NAMES…”中存在不包含斜线的文件名,则不改变这个文件名。以反斜线结尾的文件名,是用空串代替,因此当“NAMES…”中存在多个这样的文件名时,返回结果中分割各个文件名的空格数目将不确定!这是此函数的一个缺陷。

3.$(suffix NAMES…)
函数名称:取后缀函数—suffix。
函数功能:从文件名序列“NAMES…”中取出各个文件名的后缀。后缀是文件名中最后一个以点“.”开始的(包含点号)部分,如果文件名中不包含一个点号,则为空。
返回值:以空格分割的文件名序列“NAMES…”中每一个文件的后缀序列。
函数说明:“NAMES…”是多个文件名时,返回值是多个以空格分割的单词序列。如果文件名没有后缀部分,则返回空。

4.$(basename NAMES…)
函数名称:取前缀函数—basename。
函数功能:从文件名序列“NAMES…”中取出各个文件名的前缀部分(点号之后的部分)。前缀部分指的是文件名中最后一个点号之前的部分。
返回值:空格分割的文件名序列“NAMES…”中各个文件的前缀序列。如果文件没有前缀,则返回空字串。
函数说明:如果“NAMES…”中包含没有后缀的文件名,此文件名不改变。如果一个文件名中存在多个点号,则返回值为此文件名的最后一个点号之前的文件名部分。

5.$(addsuffix SUFFIX,NAMES…)
函数名称:加后缀函数—addsuffix。
函数功能:为“NAMES…”中的每一个文件名添加后缀“SUFFIX”。参数“NAMES…”为空格分割的文件名序列,将“SUFFIX”追加到此序列的每一个文件名的末尾。
返回值:以单空格分割的添加了后缀“SUFFIX”的文件名序列。

6.$(addprefix PREFIX,NAMES…)
函数名称:加前缀函数—addprefix。
函数功能:为“NAMES…”中的每一个文件名添加前缀“PREFIX”。参数“NAMES…”是空格分割的文件名序列,将“SUFFIX”添加到此序列的每一个文件名之前。
返回值:以单空格分割的添加了前缀“PREFIX”的文件名序列。

7.$(join LIST1,LIST2)
函数名称:单词连接函数——join。
函数功能:将字串“LIST1”和字串“LIST2”各单词进行对应连接。就是将“LIST2”中的第一个单词追加“LIST1”第一个单词字后合并为一个单词;将“LIST2”中的第二个单词追加到“LIST1”的第一个单词之后并合并为一个单词,……依次列推。
返回值:单空格分割的合并后的字(文件名)序列。
函数说明:如果“LIST1”和“LIST2”中的字数目不一致时,两者中多余部分将被作为返回序列的一部分。

8.$(wildcard PATTERN)
函数名称:获取匹配模式文件名函数—wildcard
函数功能:列出当前目录下所有符合模式“PATTERN”格式的文件名。
返回值:空格分割的、存在当前目录下的所有符合模式“PATTERN”的文件名。
函数说明:“PATTERN”使用shell可识别的通配符,包括“?”(单字符)、“*”(多字符)等。

(三)、其它函数
1.$(foreach VAR,LIST,TEXT)
函数功能:函数“foreach”不同于其它函数。它是一个循环函数。类似于Linux的shell中的循环(for语句)。这个函数的工作过程是这样
的:如果必要(存在变量或者函数的引用),首先展开变量“VAR”和“LIST”;而表达式“TEXT”中的变量引用不被展开。执行时把“LIST”中使
用空格分割的单词依次取出赋值给变量“VAR”,然后执行“TEXT”表达式。重复直到“LIST”的最后一个单词(为空时结束)。“TEXT”中的变量
或者函数引用在执行时才被展开,因此如果在“TEXT”中存在对“VAR”的引用,那么“VAR”的值在每一次展开式将会到的不同的值。
返回值:空格分割的多次表达式“TEXT”的计算的结果。

2.$(if CONDITION,THEN-PART[,ELSE-PART])
函数功能:函数“if”提供了一个在函数上下文中实现条件判断的功能。就像make所支持的条件语句—ifeq。第一个参数“CONDITION”,在函
数执行时忽略其前导和结尾空字符并展开。“CONDITION”的展开结果非空,则条件为真,就将第二个参数“THEN_PATR”作为函数的计算表达
式,函数的返回值就是第二表达式的计算结果;“CONDITION”的展开结果为空,将第三个参数
“ELSE-PART”作为函数的表达式,返回结果为第三个表达式的计算结果。
返回值:根据条件决定函数的返回值是第一个或者第二个参数表达式的计算结果。当不存在第三个参数“ELSE-PART”,并且“CONDITION”展开为空,函数返回空。
函数说明:函数的条件表达式“CONDITION”决定了,函数的返回值只能是“THEN-PART”或者“ELSE-PART”两个之一的计算结果。

3.((call VARIABLE,PARAM,PARAM,...) 函数功能:“call”函数是唯一一个可以创建定制参数化的函数的引用函数。我们可以将一个变量定义为一个复杂的表达式,用“call”函数根据不同的参数对它进行展开来获得不同的结果。 在执行时,将它的参数“PARAM”依次赋值给临时变量“)(1)”、“((2)”(这些临时变量定义在“VARIABLE”的值中,参考下边的例 子)…… call函数对参数的数目没有限制,也可以没有参数值,没有参数值的“call”没有任何实际存在的意义。执行时变量“VARIABLE”被展开为在函数 上下文有效的临时变量,变量定义中的“)(1)”作为第一个参数,并将函数参数值中的第一个参数赋值给它;变量中的“((2)”一样被赋值为函数的第二个 参数值;依此类推(变量)(0)代表变量“VARIABLE”本身)。之后对变量“VARIABLE” 表达式的计算值。
返回值:参数值“PARAM”依次替换“((1)”、“)(2)”…… 之后变量“VARIABLE”定义的表达式的计算值。
函数说明:1.
函数中“VARIBLE”是一个变量名,而不是对变量的引用。因此,通常“call”函数中的“VARIABLE”中不包含“$”(当然,除了此变量名是
一个计算的变量名)。2.
当变量“VARIBLE”是一个make内嵌的函数名时(如“if”、“foreach”、“strip”等),对“PARAM”参数的使用需要注意,因
为不合适或者不正确的参数将会导致函数的返回值难以预料。3. 函数中多个“PARAM”之间使用逗号分割。4.
变量“VARIABLE”在定义时不能定义为直接展开式!只能定义为递归展开式。

4.value函数
((value VARIABLE) 函数功能:不对变量“VARIBLE”进行任何展开操作,直接返回变量“VARIBALE”代表的值。这里“VARIABLE”是一个变量名,一般不包含“)”(当然,除了计算的变量名),
返回值:变量“VARIBALE”所定义文本值(不展开其中的变量或者函数应用)。

5.eval函数
函数功能:函数“eval”是一个比较特殊的函数。使用它我们可以在我们的Makefile中构造一个可变的规则结构关系(依赖关系链),其中可以使用其
它变量和函数。函数“eval”对它的参数进行展开,展开的结果作为Makefile的一部分,make可以对展开内容进行语法解析。展开的结果可以包含
一个新变量、目标、隐含规则或者是明确规则等。也就是说此函数的功能主要是:根据其参数的关系、结构,对它们进行替换展开。
返回值:函数“eval”的返回值时空,也可以说没有返回值。
函数说明:“eval”函数执行时会对它的参数进行两次展开。第一次展开过程发是由函数本身完成的,第二次是函数展开后的结果被作为Makefile内容
时由make解析时展开的。明确这一点对于使用“eval”函数非常重要。在理解了函数“eval”二次展开的过程后。实际使用时,当函数的展开结果中存
在引用(格式为:((x))时,那么在函数的参数中应该使用“)(”来代替“)”。因为这一点,所以通常它的参数中会使用函数“value”来取一个变量
的文本值。

6.origin函数
((origin VARIABLE) 函数功能:函数“origin”查询参数“VARIABLE”(通常是一个变量名)的出处。 函数说明:“VARIABLE”是一个变量名而不是一个变量的引用。因此通常它不包含“)”(当然,计算的变量名例外)。
返回值:返回“VARIABLE”的定义方式。用字符串表示。
. undefined
变量“VARIABLE”没有被定义。
. default
变量“VARIABLE”是一个默认定义(内嵌变量)。如“CC”、“MAKE”、“RM”等变量。如果在Makefile中重新定义这些变量,函数返回值将相应发生变化。
. environment
变量“VARIABLE”是一个系统环境变量,并且make没有使用命令行选项“-e”(Makefile中不存在同名的变量定义,此变量没有被替代)。
. environment override
变量“VARIABLE”是一个系统环境变量,并且make使用了命令行选项“-e”。Makefile中存在一个同名的变量定义,使用“make -e”时环境变量值替代了文件中的变量定义。
. file
变量“VARIABLE”在某一个makefile文件中定义。
. command line
变量“VARIABLE”在命令行中定义。
. override
变量“VARIABLE”在makefile文件中定义并使用“override”指示符声明。
. automatic
变量“VARIABLE”是自动化变量。

7.shell函数
不同于除“wildcard”函数之外的其它函数。make可以使用它来和外部通信。
函数功能:函数“shell”所实现的功能和shell中的引用(``)相同。实现了命令的扩展。意味着需要一个shell
命令作为它的参数,而返回的结果是此命令在shell中的执行结果。make仅仅对它的回返结果进行处理;make将函数的返回结果中的所有换行符
(“ ”)或者一对“ ”替换为单空格;并去掉末尾的回车符号(“ ”)或者“ ”。函数展开式时,它所调用的命令(它的参数)得到执
行。除了对它的引用出现在规则的命令行中和递归的变量定义引用以外,其它决大多数情况下,make在读取Makefile时函数shell就被扩展。
返回值:函数“shell”的参数在shell中的执行结果。
函数说明:函数本身的返回值是其参数的执行结果,没有进行任何处理。对结果的处理是由make进行的。当对函数的引用出现在规则的命令行中,命令行在执行
时函数引用才被展开。展开过程函数参数的执行时在另外一个shell进程中完成的,因此对于出现在规则命令行的多级“shell”函数引用需要谨慎处理,
否则会影响效率(每一级的“shell”函数的参数都会有各自的shell进程)。

8.error 函数
$(error TEXT…)
函数功能:产生致命错误,并提示“TEXT…”信息给用户,之后退出make的执行。需要说明的是:“error”函数是在函数展开式(函数被调用时)才
提示信息并结束make进程。因此如果函数出现在命令中或者一个递归的变量定义中时,在读取Makefile时不会出现错误。而只有包含
“error”函数引用的命令被执行,或者定义中引用此函数的递归变量被展开时,才会提示致命信息“TEXT…”同时make退出执行。
返回值:空字符
函数说明:“error”函数一般不出现在直接展开式的变量定义中,否则在make读取Makefile时将会提示致命错误。

  1. warning 函数
    $(warning TEXT…)
    函数功能:函数“warning”类似于函数“error”,区别在于它不会导致致命错误(make不退出),而只是提示“TEXT…”,make的执行过程继续。
    返回值:空字符
    函数说明:用法和“error”类似,展开过程相同。

3.makefile模板

复制代码
GXX=g++
GCC=gcc
CXXFLAG=-fopenmp -march=core2 -O3 -fomit-frame-pointer -pipe
CFLAG=((CXXFLAG) DIR_INC=./include DIR_SRC=./src DIR_OBJ=./obj DIR_BIN=./bin DIR_LIB=/home/your_dir/env/lib_dir1/ LDFLAG=-Wl,--rpath=)(DIR_LIB)/lib,--rpath=/home/your_dir/env/lib,--rpath=$(PWD)/lib,-O3 -fopenmp

指定include的路径

INCLUDE=-I$(DIR_INC) -I./lib/include -I ((DIR_LIB)/include -I)(DIR_LIB)/include/h_file_dir

指定lib的路径

LIBS=-L $(DIR_LIB)/lib -lxxx1 -lxxx2 -lxxx3 -L/home/your_dir/env/lib -L $(PWD)/lib -lxxx4

TARGET=((DIR_BIN)/exe_name DEF= -DLINUX #-DPATH #-DDEBUG SRC=)(wildcard ({DIR_SRC}/*.cpp) OBJS=)(SRC:((DIR_SRC)/%.cpp=)(DIR_OBJ)/%.o)

((TARGET):)(OBJS)
$(GXX) ((GXXFLAG))(LDFLAG) -o $@ $(OBJS) $(LIBS)

((TARGET):)(OBJS)

ar cru $(TARGET) $(OBJS)

((DIR_OBJ)/%.o:)(DIR_SRC)/%.cpp
$(GXX) -o $@ -c $< $(CXXFLAG) $(INCLUDE) $(DEF)

clean:
rm -fr $(OBJS) $(TARGET);
复制代码

说明:(1)利用自动化变量“$@”,这个变量表示着目前规则中所有的目标的集合。

    (2)(<是第一个"prerequisite", 不知道怎么翻译,就是target:后面的列表里的第一个. (Often the prerequisites include header files as well, which you do not want to mention in the recipe. The automatic variable `)<' is just the first prerequisite:)
  示例:

 VPATH = src:../headers
 foo.o : foo.c defs.h hack.h
         cc -c $(CFLAGS) $< -o $@

  这里$<就是foo.c

四,其他问题

1.rpath链接选项

SDK库的名称为liba.so, 其依赖于libb.so和libc.so,那么在编译应用程序的时候使用以下命令:

+++++++++++++++++++++++++++++++++++++++++++++

gcc -o test test.c -I. -L. -la -lb -lc

+++++++++++++++++++++++++++++++++++++++++++++

将SDK库即liba.so交给其他的开发人员,其他的开发人员可不想编译的时候,显示的链接liba.so所依赖的库。他们更愿意编译的时候,只显示地链接liba.so。

rpath链接选项主要有两个功能:

(1)程序运行时,优先到rpath指定的目录去寻找依赖库

(2)程序链接时,在指定的目录中,隐式的链接那些动态库所需要的链接库。

往往我们都熟知第一个功能,忽略第二个功能。而第二个功能正是现在所需要的。

我们将liba.so,libb.so 和libc.so拷贝的同一个目录中,然后利用rpath链接应用程序,这样编译便不需要显示的去链接liba.so所依赖的库了。

+++++++++++++++++++++++++++++++++++++++++++++

gcc -o test test.c -I. -L. -la -Wl,-rpath=.

+++++++++++++++++++++++++++++++++++++++++++++

Linux命令之ar - 创建静态库.a文件和动态库.so
 转自:http://blog.csdn.net/eastonwoo/article/details/8241693

用途说明
  创建静态库。a文件。用C/C++开发程序时经常用到,但我很少单独在命令行中使用ar命令,一般写在makefile中,有时也会在shell脚 本中用到。关于Linux下的库文件、静态库、动态库以及怎样创建和使用等相关知识,参见本文后面的相关资料【3】《关于Linux静态库和动态库的分析》。
  常用参数
  格式:ar rcs libxxx.a xx1.o xx2.o
  参数r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。【1】
  参数c:创建一个库。不管库是否存在,都将创建。
  参数s:创建目标文件索引,这在创建较大的库时能加快时间。(补充:如果不需要创建索引,可改成大写S参数;如果。a文件缺少索引,可以使用ranlib命令添加)
  格式:ar t libxxx.a
  显示库文件中有哪些目标文件,只显示名称。
  格式:ar tv libxxx.a
  显示库文件中有哪些目标文件,显示文件名、时间、大小等详细信息。
  格式:nm -s libxxx.a
  显示库文件中的索引表。
  格式:ranlib libxxx.a
  为库文件创建索引表。
  使用示例
  示例一 在shell脚本中使用
  Bash代码
  OS=uname -r
  ar rcs libhycu.a.$OS *.o

  示例二 在makefile中使用
  Makefile代码
  $(BIN1): $(BIN1_OBJS)
  ar rcs $@ $^

  示例三 创建并使用静态库
  第一步:编辑源文件,test.h test.c main.c。其中main.c文件中包含main函数,作为程序入口;test.c中包含main函数中需要用到的函数。
  vi test.h test.c main.c
  第二步:将test.c编译成目标文件。
  gcc -c test.c
  如果test.c无误,就会得到test.o这个目标文件。
  第三步:由。o文件创建静态库。
  ar rcs libtest.a test.o
  第四步:在程序中使用静态库。
  gcc -o main main.c -L. -ltest
  因为是静态编译,生成的执行文件可以独立于。a文件运行。
  第五步:执行。
  ./main

  示例四 创建并使用动态库
  第一步:编辑源文件,test.h test.c main.c。其中main.c文件中包含main函数,作为程序入口;test.c中包含main函数中需要用到的函数。
  vi test.h test.c main.c
  第二步:将test.c编译成目标文件。
  gcc -c test.c
  前面两步与创建静态库一致。
  第三步:由。o文件创建动态库文件。
  gcc -shared -fPIC -o libtest.so test.o
  第四步:在程序中使用动态库。
  gcc -o main main.c -L. -ltest
  当静态库和动态库同名时,gcc命令将优先使用动态库。
  第五步:执行。
  LD_LIBRARY_PATH=. ./main

     动态库除了在默认的的路径/lib 和 /usr/lib 下还可以通过"在配置文件/etc/ld.so.conf中指定动态库搜索路径",“环境变量的方式”和“在编译目标代码时指定该程序的动态库搜索路径(通过gcc 的参数"-Wl,-rpath,"指定)”三种,他们的优先级也不一样;

     下面介绍第二种:

例如:

export LD_LIBRARY_PATH=.

但本文为了举例方便,使用另一种设置环境变量的方法,既在命令前加环境变量设置,该环境变量只对该命令有效,当该命令执行完成后,该环境变量就无效了。如下述命令:

LD_LIBRARY_PATH=. ./main

  示例五 查看静态库中的文件
  [root@node56 lib]# ar -t libhycu.a
  base64.c.o
  binbuf.c.o
  cache.c.o
  chunk.c.o
  codec_a.c.o
  …
  xort.c.o
  [root@node56 lib]#
  [root@node56 lib]# ar -tv libhycu.a
  rw-r--r-- 0/0 7220 Jul 29 19:18 2011 base64.c.o
  rw-r--r-- 0/0 2752 Jul 29 19:18 2011 binbuf.c.o
  rw-r--r-- 0/0 19768 Jul 29 19:18 2011 cache.c.o
  …
  rw-r--r-- 0/0 4580 Jul 29 19:18 2011 xort.c.o
  [root@node56 lib]#
  [root@node56 lib]# nm -s libhycu.a | less
  Archive index:
  Base64Enc in base64.c.o
  GetBase64Value in base64.c.o
  Base64Dec in base64.c.o
  encode64 in base64.c.o
  decode64 in base64.c.o
  check64 in base64.c.o
  test64 in base64.c.o
  …
  chunk_alloc in chunk.c.o
  [root@node56 lib]#

本篇文章来源于 黑基网-中国最大的网络安全站点 原文链接:http://www.hackbase.com/tech/2011-08-09/64867.html

Linux下gcc编译生成动态链接库.so文件并调用它
动态库
.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一点帮助。
1、动态库的编译

下面通过一个例子来介绍如何生成一个动态库。这里有一个头文件:so_test.h,三个.c文件:test_a.c、test_b.c、test_c.c,我们将这几个文件编译成一个动态库:libtest.so。

//so_test.h:

include "stdio.h"

void test_a();
void test_b();
void test_c();

//test_a.c:

include "so_test.h"

void test_a()
{
printf("this is in test_a... ");
}

//test_b.c:

include "so_test.h"

void test_b()
{
printf("this is in test_b... ");
}

//test_c.c:

include "so_test.h"

void test_c()
{
printf("this is in test_c... ");
}
将这几个文件编译成一个动态库:libtest.so
$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

2、动态库的链接
在1、中,我们已经成功生成了一个自己的动态链接库libtest.so,下面我们通过一个程序来调用这个库里的函数。程序的源文件为:test.c。

test.c:

include "so_test.h"

int main()
{
test_a();
test_b();
test_c();
return 0;
}
将test.c与动态库libtest.so链接生成执行文件test:
$ gcc test.c -L. -ltest -o test
测试是否动态连接,如果列出libtest.so,那么应该是连接正常了
$ ldd test
执行test,可以看到它是如何调用动态库中的函数的。
3、编译参数解析
最主要的是GCC命令行的一个选项:
-shared该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件

-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

-L.:表示要连接的库在当前目录中

-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称

LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。

当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。

4、注意

调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。

在linux下可以用export命令来设置这个值,在linux终端下输入:
export LD_LIBRARY_PATH=/opt/au1200_rm/build_tools/bin: (LD_LIBRARY_PATH:    然后再输入:export    即会显示是否设置正确    export方式在重启后失效,所以也可以用 vim /etc/bashrc ,修改其中的LD_LIBRARY_PATH变量。    例如:LD_LIBRARY_PATH=)LD_LIBRARY_PATH:/opt/au1200_rm/build_tools/bin。

gcc 生成 .a静态库和 .so动态库
Posted on 2012-04-13 22:30 网名还没想好 阅读(5911) 评论(1) 编辑 收藏
我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两
种。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态
库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运
行时还需要动态库存在。本文主要通过举例来说明在Linux中如何创建静态库和动态库,以
及使用它们。

在创建函数库前,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。

第1步:编辑得到举例的程序--hello.h、hello.c和main.c;

hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出"
Hello XXX!"。hello.h(见程序1)为该函数库的头文件。main.c(见程序3)为测试库文件的
主程序,在主程序中调用了公用函数hello。

程序1: hello.h

ifndef HELLO_H

define HELLO_H

void hello(const char *name);

endif //HELLO_H

程序2: hello.c

include <stdio.h>

void hello(const char *name)
{
printf("Hello %s! ", name);
}

程序3: main.c

include "hello.h"

int main()
{
hello("everyone");
return 0;
}

第2步:将hello.c编译成.o文件;

无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过g
cc先编译成.o文件。

在系统提示符下键入以下命令得到hello.o文件。

gcc -c hello.c

我们运行ls命令看看是否生存了hello.o文件。

ls

hello.c hello.h hello.o main.c

在ls命令结果中,我们看到了hello.o文件,本步操作完成。

下面我们先来看看如何创建静态库,以及使用它。

第3步:由.o文件创建静态库;

静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将
创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,
需要注意这点。创建静态库用ar命令。

在系统提示符下键入以下命令将创建静态库文件libmyhello.a。

ar -crv libmyhello.a hello.o

我们同样运行ls命令查看结果:

ls

hello.c hello.h hello.o libmyhello.a main.c

ls命令结果中有libmyhello.a。

第4步:在程序中使用静态库;

静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包
含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从
静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追
加扩展名.a得到的静态库文件名来查找静态库文件。

在程序3:main.c中,我们包含了静态库的头文件hello.h,然后在主程序main中直接调用公
用函数hello。下面先生成目标程序hello,然后运行hello程序看看结果如何。

法一 # gcc -o hello main.c -L. –lmyhello,自定义的库时,main.c还可放在-L.和 –lmyhello之间,但是不能放在它俩之后,否则会提示myhello没定义,但是是系统的库时,如g++ -o main(-L/usr/lib) -lpthread main.cpp就不出错。

法二 #gcc main.c libmyhello.a -o hello

法三:先生成main.o:gcc -c main.c ,再生成可执行文件:gcc -o hello main.o libmyhello.a,动态库连接时也可以这样做。

./hello

Hello everyone!

我们删除静态库文件试试公用函数hello是否真的连接到目标文件 hello中了。

rm libmyhello.a

rm: remove regular file `libmyhello.a'? y

./hello

Hello everyone!

程序照常运行,静态库中的公用函数已经连接到目标文件中了。

我们继续看看如何在Linux中创建动态库。我们还是从.o文件开始。

第5步:由.o文件创建动态库文件;

动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其
文件扩展名为.so。例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyh
ello.so。用gcc来创建动态库。

在系统提示符下键入以下命令得到动态库文件libmyhello.so。

gcc -shared -fPCI -o libmyhello.so hello.o (-o不可少)

我们照样使用ls命令看看动态库文件是否生成。

ls

hello.c hello.h hello.o libmyhello.so main.c

第6步:在程序中使用动态库;

在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含
这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们
先运行gcc命令生成目标文件,再运行它看看结果。

gcc -o hello main.c -L. -lmyhello

(或 #gcc main.c libmyhello.so -o hello 不会出错(没有libmyhello.so的话,会出错),但是接下来./hello 会提示出错,因为虽然连接时用的是当前目录的动态库,但是运行时,是到/usr/lib中找库文件的,将文件libmyhello.so复制到目录 /usr/lib中就OK了)

./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

哦!出错了。快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,
会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提
示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中,再试
试。

mv libmyhello.so /usr/lib

./hello

Hello everyone!

成功了。这也进一步说明了动态库在程序运行时是需要的。

我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样,
那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?抱着对问题必究到底的心情,
来试试看。

先删除除.c和.h外的所有文件,恢复成我们刚刚编辑完举例程序状态。

rm -f hello hello.o /usr/lib/libmyhello.so

ls

hello.c hello.h main.c

在来创建静态库文件libmyhello.a和动态库文件libmyhello.so。

gcc -c hello.c

ar -cr libmyhello.a hello.o (或-cvr )

gcc -shared -fPCI -o libmyhello.so hello.o

ls

hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.s
o都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标
文件hello,并运行程序 hello。

gcc -o hello main.c -L. –lmyhello (动态库和静态库同时存在时,优先使用动态库, 当然,直接#gcc main.c libmyhello.a -o hello的话,就是指定为静态库了)

./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

从程序hello运行的结果中很容易知道,当静态库和动态库同名时,gcc命令将优先使用动态库,默认去连/usr/lib和/lib等目录中的动态库,将文件libmyhello.so复制到目录/usr/lib中即可。

Note:
编译参数解析
最主要的是GCC命令行的一个选项:
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC 表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

-L. 表示要连接的库在当前目录中;(多个库:在编译命令行中,将使用的静态库文件放在源文件后面就可以了。比如:gcc -L/usr/lib myprop.c libtest.a libX11.a libpthread.a -o myprop
其中-L/usr/lib指定库文件的查找路径。编译器默认在当前目录下先查找指定的库文件,如前面的“法二 #gcc main.c libmyhello.a -o hello”)

-lmyhello 编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so或.a来确定库的名称libmyhello.so或libmyhello.a。
LD_LIBRARY_PATH 这个环境变量指示动态连接器可以装载动态库的路径。
当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。

调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。

另:

从上述可知,如何找到生成的动态库有3种方式:

(1)把库拷贝到/usr/lib和/lib目录下。

(2)在LD_LIBRARY_PATH环境变量中加上库所在路径。

例如动态库libhello.so在/home/example/lib目录下:

(export LD_LIBRARY_PATH=)LD_LIBRARY_PATH:/home/example/lib

(3) 修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾,并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见。

附:像下面这样指定路径去连接系统的静态库,会报错说要连接的库找不到:

g++ -o main main.cpp -L/usr/lib libpthread.a

必须这样g++ -o main main.cpp -L/usr/lib -lpthread才正确 。

自定义的库考到/usr/lib 下时,

g++ -o main main.cpp -L/usr/lib libpthread.a libthread.a libclass.a 会出错,但是这样 g++ -o main main.cpp -L/usr/lib -lpthread -lthread -lclass 就正确了。

转自:http://hi.baidu.com/������/blog/item/e58ed2f142913ea7a50f525e.html

=================================

以下内容,转载自

http://www.newsmth.net/bbsanc.php?path=%2Fgroups%2Fcomp.faq%2FCProgramming%2Fyuanchuang%2FM.1129250654.M0

=================================

在GCC下实现自己的静态库

静态库的有关知识,请自行了解,这里只是讲一点简单的实现过程。

1、$mkdir static_lib_demo
2、用vim新产生文件main.c welcome.c
//---------------main.c------------------

include <stdio.h>

int main()
{
printf("Hello,world! ");
disp();
return 0;
}
//-------------welcome.c------------------

include <stdio.h>

void disp()
{
printf("welcome to NEWSMTH! ");
}

3、编译产生obj文件
(gcc -c main.c welcome.c 4、产生.a库文件 )ar -r libwelcome.a welcome.o
5、用生成的库文件进行链接
$gcc main.o -o main -L/home/zgj/static_lib_demo -lwelcome
其中:/home/zgj/static_lib_demo是libwelcome.a所在的目录
6、./main
Hello,world!
welcome to NEWSMTH!
完成演示

其它:
1、察看.a里有哪些obj文件
(ar -t libwelcome.a welcome.o [zgj@localhost static_lib_demo]) ar -vt libwelcome.a
rw-rw-r-- 507/507 792 Oct 14 08:01 2005 welcome.o
2、.a库文件包含多个obj文件
(ar -r libwelcome.a welcome.o welcome2.o 如果libwelcome.a存在,则libwelcome.a被更新。 如果已存在的libwelcome.a内已经包含welcome.o,则libwelcome.a内welcome.o被更新。 如果已存在的libwelcome.a内没有包含welcome.o,则添加welcome.o到libwelcome.a内,libwelcome.a内原其他obj文件不变。 从libwelcome.a内删除welcome.o ) ar -d libwelcome.a welcome.o
3、如果libwelcome.a在当前目录,还可以直接用libwelcome.a进行链接
$gcc -o main main.o libwelcome.a
4、其它参数详细内容请看gcc(1)、ar(1)

本文转自:https://blog.csdn.net/youqika/article/details/54617525

1. 坑多的办法

-static

如果需要链接成不依赖任何so文件的程序,用ldd查看显示为"not a dynamic executable",但是这个选项时不推荐的。

即使像这样链接(c++):“-static-libgcc -static-libstdc++ -static”

  一是会出现警告,比如我使用了系统调用getaddrinfo,“Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for l
inking”

  二是会挂(c代码没试过),类似如下的调用栈,在main函数之前,知道原因的同学请留言:-)

0 0x0000000000000000 in ?? ()

1 0x0000000000437c61 in std::locale::_S_initialize() ()

2 0x0000000000437ca3 in std::locale::locale() ()

3 0x0000000000435ce4 in std::ios_base::Init::Init() ()

4 0x0000000000401925 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at /usr/include/c++/5/iostream:74

5 0x000000000040194f in _GLOBAL__sub_I__Z4addrv () at src/cotasia.cpp:49

6 0x00000000004cf377 in __libc_csu_init ()

7 0x00000000004ce97e in generic_start_main ()

8 0x00000000004cebca in __libc_start_main ()

9 0x0000000000400d89 in _start ()

  1. 普遍的做法

就是直接连接.a文件全路径,这没啥好说的,就把它当.o文件一样链接。

  1. 优雅的做法

既然是库,-l和-L才是正派的做法,比如同一目录下有libxxx.a文件和libxxx.so文件,gcc默认会链接so,改变这一默认行为的方法就是:将"-lxxx"改为"-l:libxxx.a"

另外如果程序链接的是静态库(libxxx.a),那么用ldd查不到,ldd只能查看链接的动态库(libxxx.so)。

gcc 生成 .a静态库和 .so动态库
Posted on 2012-04-13 22:30 网名还没想好 阅读(5912) 评论(1) 编辑 收藏
我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两
种。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态
库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运
行时还需要动态库存在。本文主要通过举例来说明在Linux中如何创建静态库和动态库,以
及使用它们。

在创建函数库前,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。

第1步:编辑得到举例的程序--hello.h、hello.c和main.c;

hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出"
Hello XXX!"。hello.h(见程序1)为该函数库的头文件。main.c(见程序3)为测试库文件的
主程序,在主程序中调用了公用函数hello。

程序1: hello.h

ifndef HELLO_H

define HELLO_H

void hello(const char *name);

endif //HELLO_H

程序2: hello.c

include <stdio.h>

void hello(const char *name)
{
printf("Hello %s! ", name);
}

程序3: main.c

include "hello.h"

int main()
{
hello("everyone");
return 0;
}

第2步:将hello.c编译成.o文件;

无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过g
cc先编译成.o文件。

在系统提示符下键入以下命令得到hello.o文件。

gcc -c hello.c

我们运行ls命令看看是否生存了hello.o文件。

ls

hello.c hello.h hello.o main.c

在ls命令结果中,我们看到了hello.o文件,本步操作完成。

下面我们先来看看如何创建静态库,以及使用它。

第3步:由.o文件创建静态库;

静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将
创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,
需要注意这点。创建静态库用ar命令。

在系统提示符下键入以下命令将创建静态库文件libmyhello.a。

ar -crv libmyhello.a hello.o

我们同样运行ls命令查看结果:

ls

hello.c hello.h hello.o libmyhello.a main.c

ls命令结果中有libmyhello.a。

第4步:在程序中使用静态库;

静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包
含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从
静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追
加扩展名.a得到的静态库文件名来查找静态库文件。

在程序3:main.c中,我们包含了静态库的头文件hello.h,然后在主程序main中直接调用公
用函数hello。下面先生成目标程序hello,然后运行hello程序看看结果如何。

法一 # gcc -o hello main.c -L. –lmyhello,自定义的库时,main.c还可放在-L.和 –lmyhello之间,但是不能放在它俩之后,否则会提示myhello没定义,但是是系统的库时,如g++ -o main(-L/usr/lib) -lpthread main.cpp就不出错。

法二 #gcc main.c libmyhello.a -o hello

法三:先生成main.o:gcc -c main.c ,再生成可执行文件:gcc -o hello main.o libmyhello.a,动态库连接时也可以这样做。

./hello

Hello everyone!

我们删除静态库文件试试公用函数hello是否真的连接到目标文件 hello中了。

rm libmyhello.a

rm: remove regular file `libmyhello.a'? y

./hello

Hello everyone!

程序照常运行,静态库中的公用函数已经连接到目标文件中了。

我们继续看看如何在Linux中创建动态库。我们还是从.o文件开始。

第5步:由.o文件创建动态库文件;

动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其
文件扩展名为.so。例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyh
ello.so。用gcc来创建动态库。

在系统提示符下键入以下命令得到动态库文件libmyhello.so。

gcc -shared -fPCI -o libmyhello.so hello.o (-o不可少)

我们照样使用ls命令看看动态库文件是否生成。

ls

hello.c hello.h hello.o libmyhello.so main.c

第6步:在程序中使用动态库;

在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含
这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们
先运行gcc命令生成目标文件,再运行它看看结果。

gcc -o hello main.c -L. -lmyhello

(或 #gcc main.c libmyhello.so -o hello 不会出错(没有libmyhello.so的话,会出错),但是接下来./hello 会提示出错,因为虽然连接时用的是当前目录的动态库,但是运行时,是到/usr/lib中找库文件的,将文件libmyhello.so复制到目录 /usr/lib中就OK了)

./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

哦!出错了。快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,
会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提
示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中,再试
试。

mv libmyhello.so /usr/lib

./hello

Hello everyone!

成功了。这也进一步说明了动态库在程序运行时是需要的。

我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样,
那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?抱着对问题必究到底的心情,
来试试看。

先删除除.c和.h外的所有文件,恢复成我们刚刚编辑完举例程序状态。

rm -f hello hello.o /usr/lib/libmyhello.so

ls

hello.c hello.h main.c

在来创建静态库文件libmyhello.a和动态库文件libmyhello.so。

gcc -c hello.c

ar -cr libmyhello.a hello.o (或-cvr )

gcc -shared -fPCI -o libmyhello.so hello.o

ls

hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.s
o都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标
文件hello,并运行程序 hello。

gcc -o hello main.c -L. –lmyhello (动态库和静态库同时存在时,优先使用动态库, 当然,直接#gcc main.c libmyhello.a -o hello的话,就是指定为静态库了)

./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

从程序hello运行的结果中很容易知道,当静态库和动态库同名时,gcc命令将优先使用动态库,默认去连/usr/lib和/lib等目录中的动态库,将文件libmyhello.so复制到目录/usr/lib中即可。

Note:
编译参数解析
最主要的是GCC命令行的一个选项:
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC 表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

-L. 表示要连接的库在当前目录中;(多个库:在编译命令行中,将使用的静态库文件放在源文件后面就可以了。比如:gcc -L/usr/lib myprop.c libtest.a libX11.a libpthread.a -o myprop
其中-L/usr/lib指定库文件的查找路径。编译器默认在当前目录下先查找指定的库文件,如前面的“法二 #gcc main.c libmyhello.a -o hello”)

-lmyhello 编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so或.a来确定库的名称libmyhello.so或libmyhello.a。
LD_LIBRARY_PATH 这个环境变量指示动态连接器可以装载动态库的路径。
当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。

调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。

另:

从上述可知,如何找到生成的动态库有3种方式:

(1)把库拷贝到/usr/lib和/lib目录下。

(2)在LD_LIBRARY_PATH环境变量中加上库所在路径。

例如动态库libhello.so在/home/example/lib目录下:

(export LD_LIBRARY_PATH=)LD_LIBRARY_PATH:/home/example/lib

(3) 修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾,并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见。

附:像下面这样指定路径去连接系统的静态库,会报错说要连接的库找不到:

g++ -o main main.cpp -L/usr/lib libpthread.a

必须这样g++ -o main main.cpp -L/usr/lib -lpthread才正确 。

自定义的库考到/usr/lib 下时,

g++ -o main main.cpp -L/usr/lib libpthread.a libthread.a libclass.a 会出错,但是这样 g++ -o main main.cpp -L/usr/lib -lpthread -lthread -lclass 就正确了。

转自:http://hi.baidu.com/������/blog/item/e58ed2f142913ea7a50f525e.html

=================================

以下内容,转载自

http://www.newsmth.net/bbsanc.php?path=%2Fgroups%2Fcomp.faq%2FCProgramming%2Fyuanchuang%2FM.1129250654.M0

=================================

在GCC下实现自己的静态库

静态库的有关知识,请自行了解,这里只是讲一点简单的实现过程。

1、$mkdir static_lib_demo
2、用vim新产生文件main.c welcome.c
//---------------main.c------------------

include <stdio.h>

int main()
{
printf("Hello,world! ");
disp();
return 0;
}
//-------------welcome.c------------------

include <stdio.h>

void disp()
{
printf("welcome to NEWSMTH! ");
}

3、编译产生obj文件
(gcc -c main.c welcome.c 4、产生.a库文件 )ar -r libwelcome.a welcome.o
5、用生成的库文件进行链接
$gcc main.o -o main -L/home/zgj/static_lib_demo -lwelcome
其中:/home/zgj/static_lib_demo是libwelcome.a所在的目录
6、./main
Hello,world!
welcome to NEWSMTH!
完成演示

其它:
1、察看.a里有哪些obj文件
(ar -t libwelcome.a welcome.o [zgj@localhost static_lib_demo]) ar -vt libwelcome.a
rw-rw-r-- 507/507 792 Oct 14 08:01 2005 welcome.o
2、.a库文件包含多个obj文件
(ar -r libwelcome.a welcome.o welcome2.o 如果libwelcome.a存在,则libwelcome.a被更新。 如果已存在的libwelcome.a内已经包含welcome.o,则libwelcome.a内welcome.o被更新。 如果已存在的libwelcome.a内没有包含welcome.o,则添加welcome.o到libwelcome.a内,libwelcome.a内原其他obj文件不变。 从libwelcome.a内删除welcome.o ) ar -d libwelcome.a welcome.o
3、如果libwelcome.a在当前目录,还可以直接用libwelcome.a进行链接
$gcc -o main main.o libwelcome.a
4、其它参数详细内容请看gcc(1)、ar(1)

linux下生成.so文件和.a文件
test.h

复制代码
1 #ifndef TEST_H
2 #define TEST_H
3
4 void TestA();
5 void TestB();
6
7 #endif
复制代码

test_a.cpp

复制代码
1 #include <stdio.h>
2 #include "test.h"
3
4 void TestA()
5 {
6 printf("TestA func ");
7 }
复制代码

test_b.cpp

复制代码
1 #include <stdio.h>
2 #include "test.h"
3
4 void TestB()
5 {
6 printf("TestB func ");
7 }
复制代码

生成so文件的命令

g++ test_a.cpp test_b.cpp -fPIC -shared -o libtest.so
生成.a文件的命令

1 gcc -c test_a.cpp
2 gcc -c test_b.cpp
3 ar -r libtest.a test_a.o test_b.o

test.cpp

复制代码
1 #include "test.h"
2
3 int main()
4 {
5 TestA();
6 TestB();
7
8 return 0;
9 }
复制代码

采用动态库编译命令

g++ test.cpp -o test -L. -ltest

执行

export LD_LIBRARY_PATH=./
./test
执行结果如下。

采用静态库编译命令

g++ -static -o test -L. -ltest test.cpp
执行效果

静态库的嵌套调用,有时候我想做一个自己的静态库,它里面要调用其他静态库里面的函数,经过试验

这个好像用ar -r不行,所以就在链接的时候需要两个库文件都包含,同时要有这一个头文件才行。。。

Linux下gcc编译生成动态连接库*.so文件并调用它 node

动态库*.so在linux下用c和c++编程时常常会碰到,最近在网站找了几篇文章介绍动态库的编译和连接,总算搞懂了这个以前一直不太了解得东东,这里作个笔记,也为其它正为动态库连接库而苦恼的兄弟们提供一点帮助。
1、动态库的编译

下面经过一个例子来介绍如何生成一个动态库。这里有一个头文件:so_test.h,三个.c文件:test_a.c、test_b.c、test_c.c,咱们将这几个文件编译成一个动态库:libtest.so。

//so_test.h:

include "stdio.h"

void test_a();
void test_b();
void test_c();

//test_a.c:

include "so_test.h"

void test_a()
{
printf("this is in test_a... ");
}

//test_b.c:

include "so_test.h"

void test_b()
{
printf("this is in test_b... ");
}

//test_c.c:

include "so_test.h"

void test_c()
{
printf("this is in test_c... ");
}
将这几个文件编译成一个动态库:libtest.so
$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

2、动态库的连接
在1、中,咱们已经成功生成了一个本身的动态连接库libtest.so,下面咱们经过一个程序来调用这个库里的函数。程序的源文件为:test.c。

test.c:

include "so_test.h"

int main()
{
test_a();
test_b();
test_c();
return 0;
}
将test.c与动态库libtest.so连接生成执行文件test:
$ gcc test.c -L. -ltest -o test
测试是否动态链接,若是列出libtest.so,那么应该是链接正常了
$ ldd test
执行test,能够看到它是如何调用动态库中的函数的。
3、编译参数解析
最主要的是GCC命令行的一个选项:
-shared该选项指定生成动态链接库(让链接器生成T类型的导出符号表,有时候也生成弱链接W类型的导出符号),不用该标志外部程序没法链接。至关于一个可执行文件

-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的因此动态载入时是经过代码拷贝的方式来知足不一样进程的须要,而不能达到真正代码段共享的目的。

-L.:表示要链接的库在当前目录中

-ltest:编译器查找动态链接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来肯定库的名称

LD_LIBRARY_PATH:这个环境变量指示动态链接器能够装载动态库的路径。

固然若是有root权限的话,能够修改/etc/ld.so.conf文件,而后调用 /sbin/ldconfig来达到一样的目的,不过若是没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。

4、注意

调用动态库的时候有几个问题会常常碰到,有时,明明已经将库的头文件所在目录 经过 "-I" include进来了,库所在文件经过 "-L"参数引导,并指定了"-l"的库名,但经过ldd命令察看时,就是死活找不到你指定连接的so文件,这时你要做的就是经过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。一般这样作就能够解决库没法连接的问题了。

在linux下能够用export命令来设置这个值,在linux终端下输入:
export LD_LIBRARY_PATH=/opt/au1200_rm/build_tools/bin: (LD_LIBRARY_PATH:    而后再输入:export    即会显示是否设置正确    export方式在重启后失效,因此也能够用 vim /etc/bashrc ,修改其中的LD_LIBRARY_PATH变量。    例如:LD_LIBRARY_PATH=)LD_LIBRARY_PATH:/opt/au1200_rm/build_tools/bin。 mysql

Linux下静态库,动态库,以及arm平台下库的基本概念 linux

1、基本概念 c++

1.1、什么是库 sql

在 windows 平台和 Linux 平台下都大量存在着库。 编程

本质上来讲库是 一种可执行代码的二进制形式,能够被操做系统载入内存执行。 vim

因为 windows 和 linux 的平台不一样(主要是编译器、汇编器和链接器 的不一样),所以两者库的二进制是不兼容的。 windows

本文仅限于介绍 linux 下的库。 bash

1.2、 库的种类 函数

linux 下的库有两种:静态库和共享库(动态库)。

两者的不一样点在于代码被载入的时刻不一样。

静态库的代码在编译过程当中已经被载入可执行程序,所以体积较大。

静态用.a为后缀, 例如: libhello.a

共享库(动态库)的代码是在可执行程序运行时才载入内存的,在编译过程当中仅简单的引用,所以代码体积较小。

动态一般用.so为后缀, 例如:libhello.so

共享库(动态库)的好处是,不一样的应用程序若是调用相同的库,那么在内存里只须要有一份该共享库的实例。

为了在同一系统中使用不一样版本的库,能够在库文件名后加上版本号为后缀,例如: libhello.so.1.0,因为程序链接默认以.so为文件后缀名。因此为了使用这些库,一般使用创建符号链接的方式。

ln -s libhello.so.1.0 libhello.so.1 ln -s libhello.so.1 libhello.so

1.三、静态库,动态库文件在linux下是如何生成的:

如下面的代码为例,生成上面用到的hello库:

/* hello.c */

include "hello.h"

void sayhello()

{

printf("hello,world ");

}

首先用gcc编绎该文件,在编绎时可使用任何合法的编绎参数,例如-g加入调试代码等:

$gcc -c hello.c -o hello.o

一、生成静态库 生成静态库使用ar工具,其实ar是archive的意思

$ar cqs libhello.a hello.o

二、生成动态库 用gcc来完成,因为可能存在多个版本,所以一般指定版本号:

$gcc -shared -o libhello.so.1.0 hello.o

1.四、库文件是如何命名的,有没有什么规范:

在 linux 下,库文件通常放在/usr/lib和/lib下,

静态库的名字通常为libxxxx.a,其中 xxxx 是该lib的名称;

动态库的名字通常为libxxxx.so.major.minor,xxxx 是该lib的名称,major是主版本号,minor是副版本号

1.五、可执行程序在执行的时候如何定位共享库(动态库)文件 :

当系统加载可执行代码(即库文件)的时候,可以知道其所依赖的库的名字,可是还须要知道绝对路径,此时就须要系统动态载入器 (dynamic linker/loader)

对于 elf 格式的可执行程序,是由 ld-linux.so* 来完成的,它前后搜索 elf 文件的 DT_RPATH 段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache 文件列表— /lib/,/usr/lib 目录找到库文件后将其载入内存

如: export LD_LIBRARY_PATH='pwd'

将当前文件目录添加为共享目录

1.六、使用ldd工具,查看可执行程序依赖那些动态库或着动态库依赖于那些动态库:

ldd 命令能够查看一个可执行程序依赖的共享库,

例如 # ldd /bin/lnlibc.so.6

=> /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

=> /lib/ld- linux.so.2 (0×40000000)

能够看到 ln 命令依赖于 libc 库和 ld-linux 库

使用如下的命令查看arm平台的依赖关系

注意:arm-linux-readelf -d busybox | grep Shared grep后面能够不要,注意大小Shared第一个字母大写

1.七、使用nm工具,查看静态库和动态库中有那些函数名(T类表示函数是当前库中定义的,U类表示函数是被调用的,在其它库中定义的,W类是当前库中定义,被其它库中的函数覆盖)。:

有时候可能须要查看一个库中到底有哪些函数,nm工具能够打印出库中的涉及到的全部符号,这里的库既能够是静态的也能够是动态的。

nm列出的符号有不少, 常见的有三种::

一种是在库中被调用,但并无在库中定义(代表须要其余库支持),用U表示;

一种是在库中定义的函数,用T表示,这是最多见的;

另一种是所 谓的"弱态"符号,它们虽然在库中被定义,可是可能被其余库中的同名符号覆盖,用W表示。

例如,假设开发者但愿知道上文提到的hello库中是否引用了 printf():

$nm libhello.so | grep printf

发现printf是U类符号,说明printf被引用,可是并无在库中定义。

由此能够推断,要正常使用hello库,必须有其它库支持,使用ldd工具查看hello依赖于哪些库:

$ldd hello libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)

从上面的结果能够继续查看printf最终在哪里被定义,有兴趣能够Go on

1.八、使用ar工具,能够生成静态库,同时能够查看静态库中包含那些.o文件,即有那些源文件构成。

可使用 ar -t libname.a 来查看一个静态库由那些.o文件构成。

可使用 ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o 生成静态库

Linux下进行程序设计时,关于库的使用:

1、gcc/g++命令中关于库的参数:

-shared: 该选项指定生成动态链接库(让链接器生成T类型的导出符号表,有时候也生成弱链接W类型的导出符号),不用该标志外部程序没法链接。至关于一个可执行文件

-fPIC:表示编译为位置独立(地址无关)的代码,不用此选项的话,编译后的代码是位置相关的,因此动态载入时,是经过代码拷贝的方式来知足不一样进程的须要,而不能达到真正代码段共享的目的。

-L:指定连接库的路径,-L. 表示要链接的库在当前目录中

-ltest:指定连接库的名称为test,编译器查找动态链接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来肯定库的名称

LD_LIBRARY_PATH:这个环境变量指示动态链接器能够装载动态库的路径。

固然若是有root权限的话,能够修改/etc/ld.so.conf文件,而后调用 /sbin/ldconfig来达到一样的目的,

不过若是没有root权限,那么只能采用修改LD_LIBRARY_PATH环境变量的方法了。

调用动态库的时候,有几个问题会常常碰到:

一、有时,明明已经将库的头文件所在目录 经过 "-I" include进来了,库所在文件经过 "-L"参数引导,并指定了"-l"的库名,但经过ldd命令察看时,就是死活找不到你指定连接的so文件,这时你要做的就是经过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。一般这样作就能够解决库没法连接的问题了。

2、静态库连接时搜索路径的顺序:

  1. ld会去找gcc/g++命令中的参数-L;

  2. 再找gcc的环境变量LIBRARY_PATH,它指定程序静态连接库文件搜索路径;

export LIBRARY_PATH=$LIBRARY_PATH:data/home/billchen/lib

  1. 再找默认库目录 /lib /usr/lib /usr/local/lib,这是当初compile gcc时写在程序内的。

3、动态连接时、执行时搜索路径顺序:

  1. 编译目标代码时指定的动态库搜索路径;

  2. 环境变量LD_LIBRARY_PATH指定动态库搜索路径,它指定程序动态连接库文件搜索路径;

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:data/home/billchen/lib

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

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

  3. 默认的动态库搜索路径/usr/lib。

4、静态库和动态连接库同时存在的问题:

当一个库同时存在静态库和动态库时,好比libmysqlclient.a和libmysqlclient.so同时存在时:

在Linux下,动态库和静态库同事存在时,gcc/g++的连接程序,默认连接的动态库。

可使用下面的方法,给链接器传递参数,看是否连接动态库仍是静态库。

-WI,-Bstatic -llibname //指定让gcc/g++连接静态库

使用:

gcc/g++ test.c -o test -WI,-Bstatic -llibname

-WI,-Bdynamic -llibname //指定让gcc/g++连接动态库

使用:

gcc/g++ test.c -o test -WI,-Bdynamic -llibname

若是要彻底静态加在,使用-static参数,即将全部的库以静态的方式链入可执行程序,这样生成的可执行程序,再也不依赖任何库,同事出现的问题是,这样编译出来的程序很是大,占用空间。

5、有关环境变量:

LIBRARY_PATH环境变量:指定程序静态连接库文件搜索路径

LD_LIBRARY_PATH环境变量:指定程序动态连接库文件搜索路径

6、动态库升级问题:

在动态连接库升级时,

不能使用cp newlib.so oldlib.so,这样有可能会使程序core掉;

而应该使用:

rm oldlib.so 而后 cp newlib.so oldlib.so

或者

mv oldlib.so oldlib.so_bak 而后 cp newlib.so oldlib.so

为何不能用cp newlib.so oldlib.so ?

在替换so文件时,若是在不停程序的状况下,直接用 cp new.so old.so 的方式替换程序使用的动态库文件会致使正在运行中的程序崩溃。

解决方法:

解决的办法是采用"rm+cp" 或"mv+cp" 来替代直接"cp" 的操做方法。

linux系统的动态库有两种使用方法:运行时动态连接库,动态加载库并在程序控制之下使用。

一、为何在不停程序的状况下,直接用 cp 命令替换程序使用的 so 文件,会使程序崩溃?

不少同窗在工做中遇到过这样一个问题,在替换 so 文件时,若是在不停程序的状况下,直接用cp new.so old.so的方式替换程序使用的动态库文件会致使正在运行中的程序崩溃,退出。

这与 cp 命令的实现有关,cp 并不改变目标文件的 inode,cp 的目标文件会继承被覆盖文件的属性而非源文件。实际上它是这样实现的:

strace cp libnew.so libold.so 2>&1 |grep open.lib..so

open("libnew.so", O_RDONLY|O_LARGEFILE) = 3

open("libold.so", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4

在 cp 使用"O_WRONLY|O_TRUNC" 打开目标文件时,原 so 文件的镜像被意外的破坏了。这样动态连接器 ld.so 不能访问到 so 文件中的函数入口。从而致使 Segmentation fault,程序崩溃。ld.so 加载 so 文件及"再定位"的机制比较复杂。

二、怎样在不中止程序的状况下替换so文件,而且保证程序不会崩溃?

答案是采用"rm+cp" 或"mv+cp" 来替代直接"cp" 的操做方法。

在用新的so文件 libnew.so 替换旧的so文件 libold.so 时,若是采用以下方法:

rm libold.so //若是内核正在使用libold.so,那么inode节点不会马上别删除掉。

cp libnew.so libold.so

采用这种方法,目标文件 libold.so 的 inode 其实已经改变了,原来的 libold.so 文件虽然不能用 "ls"查看到,但其 inode 并无被真正删除,直到内核释放对它的引用。

(即: rm libold.so,此时,若是ld.so正在加在libold.so,内核就在引用libold.so的inode节点,rm libold.so的inode并无被真正删除,当ld.so对libold.so的引用结束,inode才会真正删除。这样程序就不会崩溃,由于它还在使用旧的libold.so,当下次再使用libold.so时,已经被替换,就会使用新的libold.so)

同理,mv只是改变了文件名,其 inode 不变,新文件使用了新的 inode。这样动态连接器 ld.so 仍然使用原来文件的 inode 访问旧的 so 文件。于是程序依然能正常运行。

(即: mv libold.so ***后,若是程序使用动态库,仍是使用旧的inode节点,当下次再使用libold.so时,就会使用新的libold.so)

到这里,为何直接使用"cp new_exec_file old_exec_file"这样的命令时,系统会禁止这样的操做,而且给出这样的提示"cp: cannot create regular file `old': Text file busy"。这时,咱们采用的办法仍然是用"rm+cp"或者"mv+cp"来替代直接"cp",这跟以上提到的so文件的替换有一样的道理。

可是,为何系统会阻止 cp 覆盖可执行程序,而不阻止覆盖 so 文件呢?

这是由于 Linux 有个 Demand Paging 机制,所谓"Demand Paging",简单的说,就是系统为了节约物理内存开销,并不会程序运行时就将全部页(page)都加载到内存中,而只有在系统有访问需求时才将其加载。"Demand Paging"要求正在运行中的程序镜像(注意,并不是文件自己)不被意外修改,所以内核在启动程序后会锁定这个程序镜像的 inode。

对于 so 文件,它是靠 ld.so 加载的,而ld.so毕竟也是用户态程序,没有权利去锁定inode,也不该与内核的文件系统底层实现耦合。

gcc指定头文件路径及动态连接库路径

本文详细介绍了linux 下gcc头文件指定方法,以及搜索路径顺序的问题。另外,还总结了,gcc动态连接的方法以及路径指定,一样也讨论了搜索路径的顺序问题。本文包含了不少的例子,具备很强的操做性,但愿读者本身去走一遍。
一.#include <>与#include ""

include <>直接到系统指定的某些目录中去找某些头文件。

include ""先到源文件所在文件夹去找,而后再到系统指定的某些目录中去找某些头文件。

二.gcc指定头文件的三种状况:

1.会在默认状况下指定到/usr/include文件夹(更深层次的是一个相对路径,gcc可执行程序的路径是/usr/bin/gcc,那么它在实际工做时指定头文件头径是一种相对路径方法,换算成绝对路径就是加上/usr/include,如#include 就是包含/usr/include/stdio.h)

2.GCC还使用了-I指定路径的方式,即
gcc -I 头文件所在文件夹(绝对路径或相对路径都可) 源文件
举一个例子:
设当前路径为/root/test,其结构以下:
include_test.c
include/include_test.h
有两种方法访问到include_test.h。

  1. include_test.c中#include "include/include_test.h"而后gcc include_test.c便可

  2. include_test.c中#include 或者#include 而后gcc –I include include_test.c也可

  3. 参数:-nostdinc使编译器再也不系统缺省的头文件目录里面找头文件,通常和-I联合使用,明确限定头文件的位置。

在编译驱动模块时,因为非凡的需求必须强制GCC不搜索系统默认路径,也就是不搜索/usr/include要用参数-nostdinc,还要本身用-I参数来指定内核头文件路径,这个时候必须在Makefile中指定。

头文件搜索顺序:
1.由参数-I指定的路径(指定路径有多个路径时,按指定路径的顺序搜索)

2.而后找gcc的环境变量 C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH

3.再找内定目录
/usr/include
/usr/local/include
/usr/lib/gcc-lib/i386-linux/2.95.2/include
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g++-3
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include

库文件,可是若是装gcc的时候,是有给定的prefix的话,那么就是
/usr/include
prefix/include
prefix/xxx-xxx-xxx-gnulibc/include
prefix/lib/gcc-lib/xxxx-xxx-xxx-gnulibc/2.8.1/include

三.Linux指定动态库路径

众所周知,Linux动态库的默认搜索路径是/lib和/usr/lib。动态库被建立后,通常都复制到这两个目录中。当程序执行时须要某动态库, 而且该动态库还未加载到内存中,则系统会自动到这两个默认搜索路径中去查找相应的动态库文件,而后加载该文件到内存中,这样程序就可使用该动态库中的函 数,以及该动态库的其它资源了。在Linux 中,动态库的搜索路径除了默认的搜索路径外,还能够经过如下三种方法来指定。

1.在配置文件/etc/ld.so.conf中指定动态库搜索路径。
能够经过编辑配置文件/etc/ld.so.conf来指定动态库的搜索路径,该文件中每行为一个动态库搜索路径。每次编辑完该文件后,都必须运行命令ldconfig使修改后的配置生效。

举一个例子:
全部源文件:
源文件1: lib_test.c

include

void prt()
{
printf("You found me!!!/n");
}
源文件2: main.c
void prt();
int main()
{
prt();
return 0;
}
操做过程:
咱们经过如下命令用源程序lib_test.c来建立动态库 lib_test.so。

gcc –o lib_test.o -c lib_test.c

gcc -shared -fPIC -o lib_test.so lib_test.o

或者直接一条指令:

gcc –shared –fPIC –o lib_test.so lib_test.c

注意:
-fPIC参数声明连接库的代码段是能够共享的,
-shared参数声明编译为共享库。请注意此次咱们编译的共享库的名字叫作
lib_test.so,这也是Linux共享库的一个命名的惯例了:后缀使用so,而名称使用libxxxx格式。

接着经过如下命令编译main.c,生成目标程序main.out。

gcc -o main.out -L. –l_test main.c

请注意为何是-l_test?

而后把库文件移动到目录/root/lib中。

mkdir /root/lib

mv lib_test.so /root/lib/ lib_test.so

最后编辑配置文件/etc/ld.so.conf,在该文件中追加一行/root/lib。

运行程序main.out:

./main.out

./main.out: error while loading shared libraries: lib_test.so: cannot open shared object file: No such file or directory

出错了,系统未找到动态库lib_test.so。找找缘由,原来在编辑完配置文件/etc/ld.so.conf后,没有运行命令ldconfig,因此刚才的修改还未生效。咱们运行ldconfig后再试试。

ldconfig

./main.out

You found me!!!

程序main.out运行成功,而且打印出正确结果。

2.经过环境变量LD_LIBRARY_PATH指定动态库搜索路径。
经过设定环境变量LD_LIBRARY_PATH也能够指定动态库搜索路径。当经过该环境变量指定多个动态库搜索路径时,路径之间用冒号":"分隔。下面经过例2来讲明本方法。

举一个例子:
此次咱们把上面获得的文件lib_test.so移动到另外一个地方去,如/root下面,而后设置环境变量LD_LIBRARY_PATH找到lib_test.so。设置环境变量方法以下:

export LD_LIBRARY_PATH=/root

而后运行:

./main.out

You found me!!!

注意:设置环境变量LD_LIBRARY_PATH=/root是不行的,非得export才行。

3.在编译目标代码时指定该程序的动态库搜索路径。
还能够在编译目标代码时指定程序的动态库搜索路径。-Wl,表示后面的参数将传给link程序ld(由于gcc可能会自动调用ld)。这里经过gcc 的参数"-Wl,-rpath,"指定
举一个例子:
此次咱们还把上面获得的文件lib_test.so移动到另外一个地方去,如/root/test/lib下面,
由于咱们须要在编译目标代码时指定可执行文件的动态库搜索路径,因此须要用gcc命令从新编译源程序main.c(见程序2)来生成可执行文件main.out。

gcc -o main.out -L. –l_test -Wl,-rpath,/root/test/lib main.c

运行结果:

./main.out

You found me!!!

程序./main.out运行成功,输出的结果正是main.c中的函数prt的运行结果。所以程序main.out搜索到的动态库是/root/test/lib/lib_test.so。

关于-Wl,rpath的使用方法我再举一个例子,应该不难从中看出指定多个路径的方法:
gcc -Wl,-rpath,/home/arc/test,-rpath,/lib/,-rpath,/usr/lib/,-rpath,/usr/local/lib test.c

以上介绍了三种指定动态库搜索路径的方法,加上默认的动态库搜索路径/lib和/usr/lib,共五种动态库的搜索路径,那么它们搜索的前后顺序是什么呢?读者能够用下面的方法来试验一下:
(1) 用前面介绍的方法生成5个lib_test.so放在5个不一样的文件夹下面,要求每个lib_test.so都惟一对应一个搜索路径,并注意main.out程序输出的不一样。
(2) 运行main.out,便可看出他是那个搜索路径下的,而后删除这个路径下的lib_test.so,而后再运行。依此类推操做,便可推出搜索顺序。

能够得出动态库的搜索路径搜索的前后顺序是:

1.编译目标代码时指定的动态库搜索路径;

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

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

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

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

在上述1、2、3指定动态库搜索路径时,均可指定多个动态库搜索路径,其搜索的前后顺序是按指定路径的前后顺序搜索的。有兴趣的读者本身验证。

PS:此文网上搜得,原始出处已经没法访问,故没法给出原文连接。

原文地址:https://www.cnblogs.com/marklove/p/14269331.html