zoukankan      html  css  js  c++  java
  • UNIX环境下用C语言写静态库与动态库

    静态库,动态库用UNIX 的术语来说,或者叫做归档文件(archive 常以.a 结尾)和共享对象(share object 常以lib 开头.so 结尾)更为准确。静态库,动态库可能是WINDOWS 下的术语,但两者的概念是一样的。下面统一说静态库和动态库。

    静态库,就是一大堆object (CC ,CC 在LINUX 下其实是软件链接到GCC 的,编译后默认为.o 结尾的)的集合。静态库就是用ar 等工具集合在一起。在编译的时候,连接器就会将这部分代码嵌入到目标代码里。

    动态库,也是一大堆object 的集合(编译成动态或静态,只是编译选项的不同)。不同的时,在编译的时候,连接器并没有将这部分代码嵌入到目标代码里,而在运行时的时候,才加载代码。

    由此,一般情况下用静态库一般都比动态库大。

    一、静态库编写

    以简单起见,写个非常简单例子。

    //hello.h

    #ifndef _HELLO_H

    #define _HELLO_H

    void sayhello(const char* msg);

    void saygoodbye(const char* msg);

    #endif

    //hello.c

    #include "hello.h"

    #include <unistd.h>

    #include <string.h>

    #include <stdio.h>

    void sayhello(const char* msg)

    {

    char wel[] = "welcome to invoke sayhello function/n";

    char hellomsg[BUFSIZ] = "hello, ";

    write(STDOUT_FILENO, wel, strlen(wel)+1);

    strcat(hellomsg, msg);

    strcat(hellomsg, "/n");

    write(STDOUT_FILENO, hellomsg, strlen(hellomsg)+1);

    }

    void saygoodbye(const char* msg)

    {

    char wel[] = "welcome to invoke saygoodbye function/n";

    char goodbyemsg[BUFSIZ] = "goodbye, ";

    write(STDOUT_FILENO, wel, strlen(wel)+1);

    strcat(goodbyemsg, msg);

    strcat(goodbyemsg, "/n");

    write(STDOUT_FILENO, goodbyemsg, strlen(goodbyemsg)+1);

    }

    先编译

    [heidong@bogon hellolib]$ gcc -Wall -c hello.c

    上面命令将产生hello.o 的obj 文件。(PS 一下:注意到bogon 了吗?你可以猜测我的网络环境)。-Wall 表示打开所有的编译警告。-c 表示只编译不连接。

    再创建静态库

    [heidong@bogon hellolib]$ ar -cru libhello.a hello.o

    一般库文件是以lib 开头的,链接的时候不必指定lib 前缀。-cru 表示创建并替换为最新的。具体各个参数参看ar 的man 页。为了方便,抄袭一些来,鲁迅说拿来主义。

    Option Name Example

    -d Delete ar -d <archive> <objects>

    -r Replace ar -r <archive> <objects>

    -t Table list ar -t <archive>

    -x Extract ar -x <archive> <objects>

    -v Verbose ar -v

    -c Create ar -c <archive>

    -ru Update object ar -ru <archive> <objects>

    至此,文件如下

    [heidong@bogon hellolib]$ ls

    hello.c hello.h hello.o libhello.a

    测试

    //statictest.c

    #include "hello.h"

    int main(int argc, char**argv)

    {

    char msg[] = "heidong";

    sayhello(msg);

    saygoodbye(msg);

    return 0;

    }

    编译

    [heidong@bogon hellolib]$ gcc -o statictest.o statictest.c -L. -lhello

    -L. 表示将当前目录添加到库查找目录里。系统标准的库目录是/usr/lib, /lib, /usr/local/lib 等等。-lhello 表示连接libhello 这个静态库。

    测试

    [heidong@bogon hellolib]$ ./statictest.o

    welcome to invoke sayhello function

    hello, heidong

    welcome to invoke saygoodbye function

    goodbye, heidong

    工作了。

    二、动态库

    代码依然是用上面的,但是编译的选项却不同。

    编译

    [heidong@bogon hellolib]$ gcc -c hello.c -fPIC

    [heidong@bogon hellolib]$ gcc -shared hello.o -o libhello.so

    [heidong@bogon hellolib]$ gcc -o statictest.o statictest.c -L. -lhello

    -fPIC 表示位置独立的。具体是什么的缩写不知道。第一行生成 hello.o ,第二行将生成动态库libhello.so 。第三行的意思我上面所说的一样。

    测试

    [heidong@bogon hellolib]$ ./statictest.o

    ./statictest.o: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

    咦,出错,原因就是找不到 libhello.so 这个动态库。有两个解决办法。第一,最简单的就是将库文件复制到系统库目录。需要根用户权限。第二种办法,添加 LD_LIBRARY_PATH 环境变量为所在库的目录。

    再次测试

    [heidong@bogon hellolib]$ export LD_LIBRARY_PATH=./

    [heidong@bogon hellolib]$ ./statictest.o

    welcome to invoke sayhello function

    hello, heidong

    welcome to invoke saygoodbye function

    goodbye, heidong

    Ok 啦。

    可以用ldd 查看依赖于哪些库。

    [heidong@bogon hellolib]$ ldd statictest.o

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

    libhello.so => ./libhello.so (0x00e98000)

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

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

    可见,除了我们的库外,还有额外三个。 libc.so.6 为标准C 的库, lib/ld-linux.so.2为动态连接/加载的库。至于第一个,不太清楚,网上说是“linux-gate.so.1 文件目前在文件系统中根本就不被支持  它只是一个虚拟的DSO (译者注virtual DSO :dynamically shared object ),一个在每个进程的存储空间(process’ memory )指定的地址点被内核暴露出来的共享对象”

    三、运行时加载动态库

    就是在程序运行的过程中动态地加载动态库(和WINDOWS 下的LoadLibrary 一样)。四个关键的函数:

    #include <dlfcn.h>

    void *dlopen( const char *filename, int flag );

    const char *dlerror( void );

    void *dlsym( void *handle, char *symbol );

    int dlclose( void *handle );

    例子:

    #include <unistd.h>

    #include <stdio.h>

    #include <dlfcn.h>

    #include <stdlib.h>

    int main(int argc, char**argv)

    {

    void (*sayhello)(const char*);

    void (*saygoodbye)(const char*);

    void* handle = NULL;

    char* err;

    handle = dlopen("libhello.so", RTLD_LAZY);

    if(handle == NULL){

    fprintf(stderr, "%s/n", dlerror());

    exit(-1);

    }

    sayhello = dlsym(handle, "sayhello");

    err = dlerror();

    if(err != NULL){

    fprintf(stderr, "%s/n", err);

    }else{

    sayhello("heidong");

    }

    saygoodbye = dlsym(handle, "saygoodbye");

    err = dlerror();

    if(err != NULL){

    fprintf(stderr, "%s/n", err);

    }else{

    saygoodbye("heidong");

    }

    saygoodbye = dlsym(handle, "sssssssaygoodbye");

    err = dlerror();

    if(err != NULL){

    fprintf(stderr, "%s/n", err);

    }else{

    saygoodbye("heidong");

    }

    dlclose(handle);

    return 0;

    }

    编译+测试

    [heidong@bogon hellolib]$ gcc -Wall -ldl dynamicload.c -o dynamicload.out

    [heidong@bogon hellolib]$ ./dynamicload.out

    welcome to invoke sayhello function

    hello, heidong

    welcome to invoke saygoodbye function

    goodbye, heidong

    ./libhello.so: undefined symbol: sssssssaygoodbye

    [heidong@bogon hellolib]$

    最后一个没有,所以出错。

    四、一些常用工具

    4.1 file, 测试文件类型:

    [heidong@bogon hellolib]$ file libhello.so

    libhello.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped

    [heidong@bogon hellolib]$ file statictest.o

    statictest.o: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped

    [heidong@bogon hellolib]$

    4.2 size 测试可执行文件的代码。

    [heidong@bogon hellolib]$ size statictest.o

    text data bss dec hex filename

    1167 264 8 1439 59f statictest.o

    text 为代码段,data 为已经初始化的变量,bbs 为未初始为的变量。Dec 和hex 分别为前三个之和的十进制和十六进制表示。

    4.3 nm 查看有哪些导出函数。

    [heidong@bogon hellolib]$ nm libhello.so | grep " T "

    00000784 T _fini

    00000308 T _init

    000005cb T saygoodbye

    0000046c T sayhello

    4.4 objdump 类似windows 下的dumpbin 。

    [heidong@bogon hellolib]$ objdump -d libhello.so

    libhello.so: file format elf32-i386

    Disassembly of section .init:

    00000308 <_init>:

    308: 55 push %ebp

    309: 89 e5 mov %esp,%ebp

    30b: 83 ec 08 sub $0x8,%esp

  • 相关阅读:
    Java基础--day04
    Java基础--day03
    Java基础--day02
    高斯键盘设置指南
    博客园主题配置
    算法笔记--二分
    Test2反思
    树链剖分【模板】
    7.20关于莫队算法的一些初步理解
    分块(n根n复杂度)
  • 原文地址:https://www.cnblogs.com/tianlangshu/p/3994457.html
Copyright © 2011-2022 走看看