Chinaunix首页 | 论坛 | 博客
  • 博客访问: 196933
  • 博文数量: 63
  • 博客积分: 1970
  • 博客等级: 上尉
  • 技术积分: 600
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-22 14:26
文章分类

全部博文(63)

文章存档

2013年(2)

2012年(11)

2011年(19)

2010年(6)

2007年(11)

2006年(14)

分类: LINUX

2010-03-07 10:34:02

 

(2006-10-08 10:47:35)
Windows有API能够动态的加载DLL(动态连接库)文件,具体用法这里就不说明了。
动态加载DLL能够实现类似于“插件”的功能。
Windows有的,Linux与Unix也肯定有。今天碰到这个问题,到网上没有搜到。看了一些Unix的Man文档,知道了一个用法
主要是调用
void * dlopen(const char *pathname, int mode);
void *dlsym(void *handle, const char *name);
int dlclose(void *handle);
这三个函数。很简单,以下用例子说明

myadd.c

int myadd(int a, int b)
{
    return b;
}

编译
  gcc -Wall -shared -o libtry.so myadd.c


dlmain.c

#include 
#include 
#include 
#include 

void pdlerr()
{
    fprintf(stderr, "*** ERROR: ");
    fprintf(stderr, dlerror());
    fprintf(stderr, "\n");
    exit(1);
}

int main()
{
    void *dlh NULL;
    int (*myadd)(intint);
    int result;
    
    printf("Load libaray\n");
    if ((dlh dlopen("./libtry.so"RTLD_LAZY)) == NULL)
    {
        pdlerr();
    }
    
    printf("Serach function\n");
    if ((myadd (int (*)(intint))dlsym(dlh, "myadd")) == NULL)
    {
        dlclose(dlh);
        pdlerr();
    }
    
    printf("Call function\n");
    result (*myadd)(34);
    printf("The result is %d\n"result);
    
    dlclose(dlh);    // 用完了就需要关闭
    
    return 0;
}

编译
  gcc -Wall -o dlmain -ldl dlmain.c


运行
  ./dlmain

结果

Load libaray
Serach function
Call function
The result is 7
 
-------------------------------------------------------
 Linux下文件的类型是不依赖于其后缀名的,但一般来讲:
    .o,是目标文件,相当于windows中的.obj文件
    .so 为共享库,是shared object,用于动态连接的,和dll差不多
    .a为静态库,是好多个.o合在一起,用于静态连接
    .la为libtool自动生成的一些共享库,vi编辑查看,主要记录了一些配置信息。可以用如下命令查看*.la文件的格式   $file *.la

*.la: ASCII English text

-------------------------------

是否动态连接,如果列出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文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题 了。

    makefile里面怎么正确的编译和连接生成.so库文件,然后又是在其他程序的makefile里面如何编译和连接才能调用这个库文件的函数????
    答:
     你需要告诉动态链接器、加载器ld.so在哪里才能找到这个共享库,可以设置环境变量把库的路径添加到库目录/lib和/usr/lib,LD_LIBRARY_PATH=$(pwd),这种方法采用命令行方法不太方便,一种替代方法,是把库路径添加到/etc/ld.so.conf,然后以root身份运行ldconfig

 

^^^^^^^^^^^^^^^^^^^6

调用当前目录下的SO的写法:

  $ gcc -o test test.c ./libmytools.so

  $ldd test 可以看到能找到当前目录下libmytools.so。

编译生成test可执行文件。如上编译条件的最后一条需要是所调用的标准对象文件名,注意必须含有路径。如果只是使用libmyso.so,则必须确保这个文件在可访问的PATH下面。本例所使用的文件名"./libmytools.so"是当前路径下的,使用了相对路径。

^^^^^^^^^^^^^^^^^^6

 

 

 ^^^^^^^^^^^^^^^^^^^^^^^^^^^注释^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    LD_LIBRARY_PATH可以在/etc/profile还是 ~/.profile还是 ./bash_profile里设置,或者.bashrc里,改完后运行source /etc/profile或 . /etc/profile
    更好的办法是添入/etc/ld.so.conf, 然后执行 /sbin/ldconfig

^^^^^^^^^^^^^^^^^^^^^^^^注释^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  是把库路径添加到/etc/ld.so.conf,然后以root身份运行ldconfig
     也可以在连接的时候指定文件路径和名称 -I -L.

GCC=gcc
    CFLAGS=-Wall -ggdb -fPIC
    #CFLAGS=
    all: libfunc test
    libfunc:func.o func1.o
            $(GCC) -shared -Wl,-soname,libfunc.so.1 -o libfunc.so.1.1 $<
            ln -sf libfunc.so.1.1 libfunc.so.1
            ln -sf libfunc.so.1 libfunc.so
    *************************注释

    ln -s是用来创建软链接,也就相当于windows中的快捷方式,在当前目录中创建上一级目录中的文件ttt的命名为ttt2软链接的命令是ln -s ../ttt ttt2,如果原文件也就是ttt文件删除的话,ttt2也变成了空文件。
    ln -d是用来创建硬链接,也就相当于windows中文件的副本,当原文件删除的时候,并不影响“副本”的内容。

编译目标文件时使用gcc的-fPIC选项,产生与位置无关的代码并能被加载到任何地址:
    gcc –fPIC –g –c liberr.c –o liberr.o
    使用gcc的-shared和-soname选项;
    使用gcc的-Wl选项把参数传递给连接器ld;
    使用gcc的-l选项显示的连接C库,以保证可以得到所需的启动(startup)代码,从而避免程序在使用不同的,可能不兼容版本的C库的系统上不能启动执行。
    gcc –g –shared –Wl,-soname,liberr.so –o liberr.so.1.0.0 liberr.o –lc

建立相应的符号连接:
    ln –s liberr.so.1.0.0 liberr.so.1;
    ln –s liberr.so.1.0.0 liberr.so;

在MAKEFILE中:
    $@ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
    $% 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],下是[.lib]),那么,其值为空。
    $< 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
    $? 所有比目标新的依赖目标的集合。以空格分隔。
    $^ 所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
    **************************注释**************************************
    test: test.o libfunc
            $(GCC) -o test test.o -L. -lfunc
    %.o:%.c
            $(GCC) -c $(CFLAGS) -o $@ $<
    clean:
            rm -fr *.o
            rm -fr *.so*
            rm -fr test

    要生成.so文件,cc要带-shared 参数;要调用.so的文件,比如libfunc.so,可以在cc命令最后加上-lfunc,还要视情况加上-L/usr/xxx 指出libfunc.so的路径;这样,在你要编译的源文件中就可以调用libfunc.so这个库文件的函数.
        前面的都说的差不多了,最后提醒一下最好提供一个接口头文件
        动态加载,用dlopen,dlclose,dlsym


 
 
 
 
 
--------------------------一个调试so的例子
  
  1)文件列表:   
  >   ls   -l   
  -rw-r--r--     1   hchen   wheel   209   Nov   16   11:08   get.c   
  -rw-r--r--     1   hchen   wheel     28   Nov   16   11:07   get.h   
  -rw-r--r--     1   hchen   wheel   237   Nov   16   11:08   main.c   
  -rw-r--r--     1   hchen   wheel   213   Nov   16   11:52   makefile   
    
  2)Makefile的样子   
  >   cat   ./makefile   
  all:main   
    
  libget.so:get.c   
                  gcc   -g   -c   -o   get.o   get.c   
                  gcc   -g   -shared   -o   libget.so   get.o   
    
  main:main.c   libget.so   
                  gcc   -g   -c   -o   main.o   main.c   
                  gcc   -g   -o   main   main.o   -L./   -lget   
    
  clean:   
                  @rm   libget.so   get.o   main.o   main   
    
  3)编译程序   
  >   make   
  gcc   -g   -c   -o   get.o   get.c   
  gcc   -g   -shared   -o   libget.so   get.o   
  gcc   -g   -c   -o   main.o   main.c   
  gcc   -g   -o   main   main.o   -L./   -lget   
    
  4)设置LD_LIBRARY_PATH环境变量   
  >   setenv   LD_LIBRARY_PATH   ${PWD}   
  >   env   |   grep   LD   
  LD_LIBRARY_PATH=/home/hchen/test/so   
    
  5)用GDB调试(step   in)   
  >   gdb   ./main   
    
  (gdb)   break   main   
  Breakpoint   1   at   0x80484d0:   file   main.c,   line   5.   
  (gdb)   r   
  Starting   program:   /home/hchen/test/so/main   
    
  Breakpoint   1,   main   (argc=1,   argv=0xbfe62fc4)   at   main.c:5   
  5                               int   a   =   100;   
  (gdb)   n   
  6                               int   b   =   get   ();   
  (gdb)   s         <-------   进入动态库   
  get   ()   at   get.c:7   
  7                               printf   ("get   x=%d\n",   x);   
  (gdb)   s   
  get   x=0   
  8                               return   x;   
    
  6)用GDB调试(break   set)   
  (gdb)   break   set   
  Function   "set"   not   defined.   
  Make   breakpoint   pending   on   future   shared   library   load?   (y   or   [n])   y   
    
  Breakpoint   1   (set)   pending.   
    
    
  我的GDB版本如下:(操作系统是RedHat   Enterprise   Linux   Advance   Server   4)   
  (gdb)   show   version   
  GNU   gdb   Red   Hat   Linux   (6.1post-1.20040607.62rh)   
  Copyright   2004   Free   Software   Foundation,   Inc.   
  GDB   is   free   software,   covered   by   the   GNU   General   Public   License,   and   you   are   
  welcome   to   change   it   and/or   distribute   copies   of   it   under   certain   conditions.   
  Type   "show   copying"   to   see   the   conditions.   
  There   is   absolutely   no   warranty   for   GDB.     Type   "show   warranty"   for   details.   
  This   GDB   was   configured   as   "i386-redhat-linux-gnu".   
 
-----------------------------------
 
 
设置LD_LIBRARY_PATH环境变量   
  >   setenv   LD_LIBRARY_PATH   ${PWD}   
  >   env   |   grep   LD   
  LD_LIBRARY_PATH=/home/hchen/test/so   
 
 
 
一个调试案例:
 
 
 
 
一个文章,
 

动态连接库
2008年10月08日 星期三 20:52
mylib.c
----------------------------------
#include

int add(int a, int b)
{
    int c = a + b;
    printf("c = %d\n", c);
    return c;
}


mylib.h
----------------------------------
#ifndef MYLIB_H
#define MYLIB_H

int add(int a, int b);

#endif



Makefile
----------------------------------
CC = gcc -Wall
LD = ld

MY_OBJECTS = mylib.o

all: $(MY_OBJECTS)
    @$(CC) -shared -o libmylib.so $(MY_OBJECTS)
clean:
    rm -fr *.so *.o



testlib.c
----------------------------------
#include "mylib.h"

int main()
{
    add(10, 20);
}



readme.sh
----------------------------------
make
gcc testlib.c -L./ -lmylib 
export LD_LIBRARY_PATH=./
./a.out


-Ldir
    Add directory dir to the list of directories to be searched for -l.
 

阅读(2545) | 评论(0) | 转发(0) |
0

上一篇:VIM常用命令

下一篇:GvDvB调v试

给主人留下些什么吧!~~