美文网首页
linux下静态库 动态库和 gcc gdb Makefile

linux下静态库 动态库和 gcc gdb Makefile

作者: 搬砖写Bug | 来源:发表于2019-08-06 13:56 被阅读0次

    一、静态库和动态库

    定义

    根据链接时期的不同,库有静态库和动态库之分。

    静态库是在链接阶段被链接的,所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行。

    有别于静态库,动态库的链接是在程序执行的时候被链接的。所以,即使程序编译完,库仍须保留在系统上,以供程序运行时调用。
    TODO:链接动态库时链接阶段到底做了什么

    两者的优缺点

    链接静态库其实从某种意义上来说也是一种粘贴复制,只不过它操作的对象是目标代码而不是源码而已。因为静态库被链接后库就直接嵌入可执行文件中了,这样就带来了两个问题。

    首先就是系统空间被浪费了。这是显而易见的,想象一下,如果多个程序链接了同一个库,则每一个生成的可执行文件就都会有一个库的副本,必然会浪费系统空间。

    而动态库的出现正弥补了静态库的以上弊端。因为动态库是在程序运行时被链接的,所以磁盘上只须保留一份副本,因此节约了磁盘空间。如果发现了bug或要升级也很简单,只要用新的库把原来的替换掉就行了。

    正因为动态库在程序运行时被链接,故程序的运行速度和链接静态库的版本相比必然会慢。目前链接程序在链接时一般是优先链接动态库的,除非用-static参数指定链接静态库。

    二、GCC 介绍

    gcc 作为编译工具,主要在 Linux 操作系统中使用,可以编译 C、C++、Object-C、JAVA 等语言。
    编译的流程:
    1)预处理 Pre-Processing
    2)编译 Compiling
    3)汇编 Assembling
    4)链接 Linking

    GCC编译选项

    编译过程中可以带编译选项,选择编译过程:
    1) -c :指编译,不链接,生成目标文件“.o”。
    2) -S :只编译,不汇编,生成汇编代码“.S”。
    3) -E :只进行预编译/预处理,不做其他处理。
    4) -o file:把输出文件输出到file里。
    5) -g :在可执行程序中包含标准调试信息。
    6) -v :打印出编译器内部编译各过程的命令行信息和编译器的版本。7) -I dir :在头文件的搜索路径列表中添加dir目录
    8) -L dir :在库文件的搜索路径列表中添加dir目录
    9) -static :连接静态库(静态库也可以用动态库链接方式链接)
    10) -llibrary :连接名为library的库文件(显示指定需要链接的动态库文件)

    生成静态库

    gcc -c div.c //生成div.o文件
    ar -cr libdiv_sub.a div.o sub.o //生成libdiv_sub.a文件
    使用静态库生成可执行文件:
    gcc main.c -o main_1.exe -L. -ldiv_sub -I ../add/ -I ../sub/ -I ../mul -I ../div/

    -L 表示要使用的静态库的目录,这和前面所讲的 -I (大写 i,指明头文件的目录)差不多,就是用来告诉编译器去哪里找静态库。
    因为可能-L所指明的目录下有很多静态库,所以除了要告诉去哪里找之外,还要告诉编译器找哪一个静态库,此时就要用到 -l (小写L )了,它用来说明链接的时候要用到哪个静态库。

    注意:
    1. 注意是使用 -ldiv_sub,而不是 -llibdiv_sub ,这是因为编译器会自动在库中添加 lib 前缀和 .a 后缀。
    2. 要把 -l 放到命令的尽可能后的位置,必须放到源文件的后面。 

    生成动态库

    gcc -shared -fPIC -o libadd_mul.so add.o mul.o
    使用动态库生成可执行文件:
    gcc main.c -o main_2.exe ./libadd_mul.so -I ../add/ -I ../sub/ -I ../mul -I ../div/
    使用静态库和动态库一起生成可执行文件:
    gcc main.c -o main.exe -L. ./libadd_mul.so -ldiv_sub -I ../add/ -I ../sub/ -I ../mul -I ../div/
    或者把动态库放到/usr/lib下:
    gcc main.c -o main.exe -L. -ladd_mul -ldiv_sub -I ../add/ -I ../sub/ -I ../mul -I ../div/

    三、gdb调式

    生成调式信息:
    gcc  -g
    例如:gcc -g main_add.c -o main_add.exe ./libadd.so -I ../add
    使用gdb命令开始调试:
    gdb ./main_add.exe

    四、Makefile 语法介绍

    ?=  、+= 、:= 的含义

    FOO ?= bar
    其含义是,如果 FOO 没有被定义过,那么变量 FOO 的值就是“bar”,如果 FOO 先前被定义过,那么这条语将什么也不做,其等价于:
    ifeq ($(origin FOO), undefined)
    FOO = bar
    endif

    使用“+=”操作符给变量追加值,如:
    objects = main.o foo.o bar.o utils.o
    objects += another.o
    于是,$(objects)值变成:“main.o foo.o bar.o utils.o another.o”(another.o被追加进去了)
    使用“+=”操作符可以模拟为下面的这种例子:
    objects = main.o foo.o bar.o utils.o
    objects := $(objects) another.o
    所不同的是用“+=”更为简洁。
    如果变量之前没有定义过,那么“+=”会自动变成“=”,如果前面有变量定义,那么“+=”会继承于前次操作的赋值符。如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符,如:
    variable := value
    variable += more
    等价于:
    variable := value
    variable := $(variable) more

    wildcard、patsubs、nodir的含义

    “wildcard”,其用法是:$(wildcard PATTERN...) 。在Makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。如果不存在任何符合此模式的文件,函数会忽略模式字符并返回空。需要注意的是:这种情况下规则中通配符的展开和其他情况下匹配通配符的区别。
    一般用法:我们可以使用“$(wildcard *.c)”来获取工作目录下的所有的.c文件列表。
    复杂一些用法:可以使用“$(patsubst %.c,%.o,$(wildcard *.c))”,首先使用“wildcard”函数获取工作目录下的.c文件列表;之后将列表中所有文件名的后缀.c替换为.o。这样我们就可以得到在当前目录可生成的.o文件列表。因此,在一个目录下,可以使用如下内容的Makefile来将工作目录下的所有.c文件进行编译,并最后连接成为一个可执行文件:
    #举例
    objects := $(patsubst %.c,%.o,$(wildcard *.c))
    foo : $(objects)
    cc -o foo $(objects)
    这里我们使用了make的隐含规则来编译.c的源文件

    变量替换引用

    对于一个已经定义的变量,可以使用“替换引用”将其值中的后缀字符(串)使用指定的字符(字符串)替换。格式为“$(VAR:A=B)”(或者“${VAR:A=B}”),
    意思是,替换变量“VAR”中所有“A”字符结尾的字为“B”结尾的字。“结尾”的含义是空格之前(变量值多个字之间使用空格分开)。而对于变量其它部分的“A”字符不进行替换。

    例如:
    foo := a.o b.o c.o
    bar := $(foo:.o=.c)
    在这个定义中,变量“bar”的值就为“a.c b.c c.c”。使用变量的替换引用将变量“foo”以空格分开的值中的所有的字的尾字符“o”替换为“c”,其他部分不变。
    如果在变量“foo”中如果存在“o.o”时,那么变量“bar”的值为“a.c b.c c.c o.c”而不是“a.c b.c c.c c.c”。

    实战演练

    本地写了一个小demo,生成四个目标文件,打包成静态库和动态库,给main.c调用。
    文件目录如下:

    执行过程:

    生成文件:

    执行main.exe:

    main.c 代码:

    Makefile脚本代码:

    static_obj += $(OBJECT_PATH)/sub.o $(OBJECT_PATH)/div.o
    dynamic_obj += $(OBJECT_PATH)/add.o $(OBJECT_PATH)/mul.o

    #获取makefile的绝对路径
    mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
    pro_path:=$(shell dirname $(mkfile_path))
    $(warning $(mkfile_path))
    $(warning $(pro_path))

    HFLAGS += -I add/ -I sub/ -I mul/ -I div/
    BINARY_PATH ?= out/bin
    LIB_PATH    ?= out/lib
    OBJECT_PATH ?= out/objs

    TARGET1      ?= $(BINARY_PATH)/main_1.exe
    TARGET2      ?= $(BINARY_PATH)/main_2.exe
    TARGET      ?= $(BINARY_PATH)/main.exe

    #创建目录
    $(BINARY_PATH) $(OBJECT_PATH) $(LIB_PATH):
        mkdir -p $@

    #生成目标文件
    $(OBJECT_PATH)/add.o : add/add.c
        cc -c add/add.c -o $(OBJECT_PATH)/add.o
    $(OBJECT_PATH)/sub.o : sub/sub.c
        cc -c sub/sub.c -o $(OBJECT_PATH)/sub.o
    $(OBJECT_PATH)/mul.o : mul/mul.c 
        cc -c mul/mul.c -o $(OBJECT_PATH)/mul.o
    $(OBJECT_PATH)/div.o : div/div.c
        cc -c div/div.c -o $(OBJECT_PATH)/div.o

    #生成动态库
    $(LIB_PATH)/libadd_mul.so :  $(dynamic_obj)
        cc -shared -fPIC -o $(LIB_PATH)/libadd_mul.so  $(OBJECT_PATH)/add.o $(OBJECT_PATH)/mul.o

    #生成静态库
    $(LIB_PATH)/libdiv_sub.a : $(OBJECT_PATH)/div.o $(OBJECT_PATH)/sub.o
        ar -cr -o $(LIB_PATH)/libdiv_sub.a  $(OBJECT_PATH)/div.o $(OBJECT_PATH)/sub.o

    #使用静态库生成可执行文件:
    $(TARGET1) : main.c $(LIB_PATH)/libdiv_sub.a
        cc main.c -o $(BINARY_PATH)/main_1.exe -L out/lib/ -ldiv_sub $(HFLAGS)

    #使用动态库生成可执行文件:
    $(TARGET2) : main.c $(LIB_PATH)/libadd_mul.so
        cc main.c -o $(TARGET2) $(pro_path)/out/lib/libadd_mul.so $(HFLAGS)

    #使用静态库和动态库一起生成可执行文件:
    $(TARGET) : main.c $(LIB_PATH)/libdiv_sub.a $(LIB_PATH)/libadd_mul.so
        cc main.c -o $(TARGET)  $(pro_path)/out/lib/libadd_mul.so  -L out/lib/ -ldiv_sub $(HFLAGS)

    .PHONY: all clean prepare build hello post-build
    all: hello prepare build post-build
    clean: ;rm -rf out
    hello: ;@echo ==== start, $(shell date) ====
    prepare: $(BINARY_PATH) $(OBJECT_PATH) $(LIB_PATH)
    build: $(TARGET)
    post-build: ;@echo ==== done, $(shell date) ====

    相关文章

      网友评论

          本文标题:linux下静态库 动态库和 gcc gdb Makefile

          本文链接:https://www.haomeiwen.com/subject/ostcdctx.html