UNIX下C语言动态链接库SO

时间:2022-06-04 06:22:27

UNIX下C语言动态链接库SO

2009-11-22 专题

大恒 daheng99@qq.com

 

相关技术

gcc编译加-fPIC

Ø  以动态链接库的形式给出的。所以编译这个库的时候我们需要指定-fPIC,也就是指定生成的代码是位置无关的(position independent code)。

Ø  在-fPIC情况下,编译器将把所有的绝对位移用相对于0地址的相对位移代替,然后在引用这些位移的时候用库的装载地址作为基址来寻址。

Ø  动态连接, 这样程序可以支持二进制文件接口, 比如连接libc时一般都使用.so而不是.a, 你总不想在libc更新后重新链接你的程序吧? 实际上这种功能比所谓的share更重要, 应用也更广泛.

Ø  也就是说你的应用程序所使用的so文件可以更新,但应用程序无需重新编译。

 

 

动态库的生成

 

动态库的生成可分为三个步骤,设计库源码、编译位置无关码(PIC)型.o文件和链接动态库。链接动态库的命令包含特殊标志,与链接静态库和链接可执行目标文件有区别,而且不同的Unix系统,其实现的细节也不尽相同。

编译PIC.o中间文件的方法一般是采用C语言编译器的“-KPIC”或者“-fpic”选项,有的Unix版本C语言编译器缺省带上了PIC标志。创建最终动态库的方法一般是采用C语言编译器的“-G”或者“-shared”选项,或者直接使用工具ld创建。

本处设计一个创建并调用动态库的例子,分别将源文件d1.cd2.c编译为动态库d1.sod2.so,并在主程序中调用之。

1. 设计库源码

程序pr1.c提供了函数print和变量p,如代码3-4所示:

代码3-4动态库源码(节自/code/chapter3/d1.c

int p = 2;

void print()

{

        printf("This isthe first dll src!\n");

}

程序pr2.c也提供了自己的函数print和变量p,如代码3-5所示:

代码3-5动态库源码(节自/code/chapter3/d2.c

int p = 3;

void print()

{

        printf("This isthe second dll src!\n");

}

2. ScoSolaris系列

    通过cc的“-KPIC”选项生成PIC中间文件,再通过cc的“-G”选项链接成动态库:

cc –O -KPIC -c d1.c d2.c     /*编译以“.o”为扩展名的中间目标文件d1.od2.o */

cc -G -o d1.so d1.o      /*根据中间目标文件d1.o创建动态库文件d1.so*/

cc -G -o d2.so d2.o      /*根据中间目标文件d2.o创建动态库文件d2.so*/

或者直接一步到位,直接创建动态库文件d1.sod2.so

cc -O -KPIC -G -o d1.so d1.c                           /*创建动态库文件d1.so*/

cc -O -KPIC -G -o d2.so d2.c                           /*创建动态库文件d2.so*/

3. HP-UNIX系列

创建 PIC 中间文件的编译器选项是+z,创建动态库的链接器标志是-b

cc +z -c d1.c d2.c        /*编译以“.o”为扩展名的中间目标文件d1.od2.o */

ld -b -o d1.so d1.o       /*根据中间目标文件d1.o创建动态库文件d1.so*/

ld -b -o d2.so d2.o       /*根据中间目标文件d2.o创建动态库文件d2.so*/

与大部分系统不同的是,HP-UNIX缺省使用“.sl”做动态库的扩展名。

4. AIX系列

AIX中,ccxlc系列都是C语言编译器。

xlc_r4 -c d1.c d2.c       /*编译以“.o”为扩展名的中间目标文件d1.od2.o */

ld -G -bnoentry -bexpall -lc d1.o -o d1.so    /*创建动态库文件d1.so */

ld -G -bnoentry -bexpall -lc d2.o -o d2.so    /*创建动态库文件d2.so */

ld是创建动态库的工具,它的常用选项与cc类似,比如“-l”标识链接库,“-o”标识目标文件名称等。本处“-G”选项等价于指定带有-b标记的 erokrtlnortllibnosymbolicnoautoexp M:SRE选项,它将创建动态库对象。更多的ld信息请参考相关文档。

5. Linux和其他使用gcc编译器的Unix

gcc -fpic -c d1.c d2.c    /*编译以“.o”为扩展名的中间目标文件d1.od2.o */

gcc -shared -o d1.so d1.o   /*根据中间目标文件d1.o创建动态库文件d1.so*/

gcc -shared -o d2.so d2.o   /*根据中间目标文件d2.o创建动态库文件d2.so*/

或者直接一步到位:

gcc -O -fpic -shared -o d1.so d1.c              /*创建动态库文件d1.so*/

gcc -O -fpic -shared -o d2.so d2.c              /*创建动态库文件d2.so*/

某些版本的gcc上也可以使用“-G”替换“-shared选项。

 

 

 

Makefile示例

Linux-so

#此编译文件用于在linux下编译静态以及动态库

 

SSMDIR=..

INCLUDEPATH = $(SSMDIR)/include

LIBPATH = $(SSMDIR)/lib

WORKDIR = .

#-fPIC表示编译为位置独立的代码

CFLAG = -fPIC

 

all: $(LIBPATH)/libswssm.a $(LIBPATH)/libsmapi.a  libsmapi.so

 

$(LIBPATH)/libswssm.a: swqdes.o swstr.o swssm.o swtea.o swxchg20.o

      ar -v -r $(LIBPATH)/libswssm.a swqdes.o swstr.o swssm.o swtea.o swxchg20.o

 

$(LIBPATH)/libsmapi.a: AscBcd.o hsmbj.o sec.o swqdes.o swstr.o swssm.o swtea.o swxchg20.o smapi.o

      ar -v -r $(LIBPATH)/libsmapi.a AscBcd.o hsmbj.o sec.o swqdes.o swstr.o swssm.o swtea.o swxchg20.o smapi.o

 

 

swstr.o: swstr.c $(INCLUDEPATH)/stepwork.h

      cc -I $(INCLUDEPATH)  -c swstr.c $(CFLAG)

 

swqdes.o: swqdes.c $(INCLUDEPATH)/stepwork.h

      cc -I $(INCLUDEPATH) -c swqdes.c $(CFLAG)

 

swtea.o: swtea.c $(INCLUDEPATH)/stepwork.h

      cc -I $(INCLUDEPATH) -c swtea.c $(CFLAG)

 

swssm.o: swssm.c $(INCLUDEPATH)/stepwork.h $(INCLUDEPATH)/swssm.h

      cc -I $(INCLUDEPATH) -c swssm.c $(CFLAG)

 

swxchg20.o: swxchg20.c $(INCLUDEPATH)/stepwork.h

      cc -I $(INCLUDEPATH) -c swxchg20.c $(CFLAG)

 

smapi.o: smapi.c $(INCLUDEPATH)/stepwork.h $(INCLUDEPATH)/smapi.h

      cc -I $(INCLUDEPATH) -c smapi.c $(CFLAG)

 

AscBcd.o: smapi.c $(INCLUDEPATH)/hsmbj.h $(INCLUDEPATH)/smapi.h

      cc -I $(INCLUDEPATH) -c AscBcd.c $(CFLAG)

     

hsmbj.o: smapi.c $(INCLUDEPATH)/hsmbj.h $(INCLUDEPATH)/smapi.h

      cc -I $(INCLUDEPATH) -c hsmbj.c $(CFLAG)

 

sec.o: smapi.c $(INCLUDEPATH)/sec.h $(INCLUDEPATH)/sec_api.h $(INCLUDEPATH)/smapi.h

      cc -I $(INCLUDEPATH) -c sec.c $(CFLAG)

 

rm:

      rm *.o

 

SOFLAG = -shared

 

OBJDYN = AscBcd.o hsmbj.o sec.o smapi.o swqdes.o swssm.o swstr.o swtea.o swxchg20.o

 

libsmapi.so: $(OBJDYN)

      cc $(SOFLAG) -o $@ $(OBJDYN)

      mv $@ ../lib

      rm *.o

 

Aix-so

#                                                                  

# makefile for application preconsole program

#

#

.SUFFIXES:.c

.SUFFIXES:.cpre

 

 

SYBASEDIR=/sybase

CICSDIR=/usr/lpp/cics

PDTDIR=$(HOME)/appl/pdt

APPLDIR=$(HOME)/appl

DLLEXPDIR=../exp

DLLLIBDIR=$(HOME)/bin

 

BSCDIR=$(APPLDIR)/sc/bsc

SCDIR=$(APPLDIR)/sc/

ABSDIR=$(APPLDIR)/cs/abs

APPDIR=$(APPLDIR)/cs/app

BILDIR=$(APPLDIR)/cs/bil

BUGDIR=$(APPLDIR)/cs/bug

CHKDIR=$(APPLDIR)/cs/chk

COMDIR=$(APPLDIR)/cs/com

CTMDIR=$(APPLDIR)/cs/ctm

ERRDIR=$(APPLDIR)/cs/err

EUMDIR=$(APPLDIR)/cs/eum

FMTDIR=$(APPLDIR)/cs/fmt

PDGDIR=$(APPLDIR)/cs/pdg

RPTDIR=$(APPLDIR)/cs/rpt

SECDIR=$(APPLDIR)/cs/sec

SYSDIR=$(APPLDIR)/cs/sys

TABDIR=$(APPLDIR)/cs/tab

XMLDIR=$(APPLDIR)/cs/xml

BTCDIR=$(APPLDIR)/cs/btc

CFGDIR=$(APPLDIR)/cfglist

CSLIBDIR=$(APPLDIR)/cslib

 

 

INCDIRS=-I$(PDTDIR)/include -I$(SYBASEDIR)/include -I$(CICSDIR)/include \

        -I../include -I$(APPLDIR)/include -I$(BSCDIR)/include\

        -I$(SCDIR)/include -I$(SCDIR)/include \

      

LIBDIRS=-L$(PDTDIR)/lib -L$(SYBASEDIR)/lib -L$(CICSDIR)/lib \

        -L/usr/lib/dce -L../lib -L$(APPLDIR)/lib -L$(BSCDIR)/lib  \

        -L$(SCDIR)/lib -L$(CSLIBDIR)/lib

 

LIBS= -s  -lcs_r.so -lct_r.so -ltcl_dce.so -lcomn_dce.so \

    -lintl_r.so -ldce -lcicsecico -ldcelibc_r -ldcepthreads -lc_r \

    -lpthreads -lm -lld -lcicsrt -lcur -lcurses

 

SOFLAG=-bnoentry -bE:$(DLLEXPDIR)/$@.exp -bM:SRE -lc

CICSREDO=$(PDTDIR)/bin/cicsline

CICSLINE=$(PDTDIR)/bin/cicsline

CC=xlc_r4

CICSTRAN=$(CICSDIR)/bin/cicstran

CPRE=$(SYBASEDIR)/bin/cpre -y -V CS_VERSION_110 $(INCDIR)

CFLAGS=-bI:/usr/lpp/cics/lib/cicsprC.exp -qmaxmem=8192 \

      -O -DUNIX  -DLIB -D__PATH__='"$(PWD)/"'  -DDEBUG

EDF=

 

help:

      @echo Please input filename that will be made

      @echo 'syntax: make <help|all|"filename">'

 

.c:

      $(CC) -c $? $(CFLAG) $(INCDIR)

      @if [ -f $(DLLLIBDIR)/$@.so ]; then rm $(DLLLIBDIR)/$@.so; fi

      @ld -o $(DLLLIBDIR)/$@.so $@.o $(SOFLAG) $(LIBDIR) $(LIB)

      @if [ -f $*.o ]; then rm $*.o; fi

      if [ -f $*.cpre ]; then rm $*.c; fi

      if [ -f $*.cpre.cp ]; then rm $*.cpre.cp; fi

 

.cpre.c:

      $(CICSREDO) $< < $< > $<.cp

      $(CPRE) $<.cp

      mv $<.c $@

 

 

SO调用示例

 

2、AIX下动态链接库的使用
2.1
重要的dlfcn.h头文件

AIX下使用动态链接库,源程序需要包含dlfcn.h头文件,此文件定义了调用动态链接库的函数的原型。下面详细说明一下这些函数。
2.1.1 dlerror

原型为:const char *dlerror(void);
当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
2.1.2 dlopen

原型为:void *dlopen (const char *filename, int flag);
dlopen
用于打开指定名字(filename)的动态链接库,并返回操作句柄。
filename: 如果名字不以/开头,则非绝对路径名,将按下列先后顺序查找该文件。
(1) 用户环境变量中的LD_LIBRARY值;
(2) 动态链接缓冲文件/etc/ld.so.cache
(3) 目录/lib,/usr/lib
flag表示在什么时候解决未定义的符号(调用)。取值有两个:
1) RTLD_LAZY : 表明在动态链接库的函数代码执行时解决。
2) RTLD_NOW : 表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。
dlopen调用失败时,将返回NULL值,否则返回的是操作句柄。
2.1.3 dlsym : 取函数执行地址

原型为: void *dlsym(void *handle, char*symbol);
dlsym
根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。
如程序代码: void (*add)(int x,int y); /* 说明一下要调用的动态函数add */
add=dlsym("xxx.so","add"); /* 打开xxx.so共享库,取add函数地址 */
add(89,369); /* 带两个参数89和369调用add函数 */
2.1.4 dlclose : 关闭动态链接库
原型为: int dlclose (void *handle);
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
2.2 在程序中使用动态链接库函数
2.2.1 程序范例

下面的程序装载了动态链接库share_str_tools.so,并用allTrim格式化字符串。

文件名: newtest.c

文件存放地址 src/test/
----------------------------------------------------------------------

  1. #include <stdio.h>
  2. #include <dlfcn.h>
  3.  
  4. #include "../lib/share_str_tools.h"
  5.  
  6. /*typedef int (*dll_func)();*/
  7.  
  8. int main() {
  9.        void *dp = NULL;
  10.        /*dll_func allTrim;*/
  11.        int (*allTrim)();
  12.  
  13.        char aa[50];
  14.        char *error;
  15.  
  16.         strcpy(aa, "        this is a test         ");
  17.  
  18.        if (!(dp = dlopen("../lib/share_str_tools.so", RTLD_LAZY))) {
  19.                fputs(dlerror(), stderr);
  20.               return 1;
  21.         }
  22.        /*allTrim = (dll_func) dlsym(dp, "allTrim");*/
  23.         allTrim = (int (*)()) dlsym(dp, "allTrim");
  24.  
  25.         error=dlerror(); /* 检测错误 */
  26.        if (error) { /* 若出错则退出 */
  27.                fputs(error,stderr);
  28.               return 1;
  29.         }
  30.  
  31.         printf("1==%s==\n", aa);
  32.         allTrim(aa);
  33.         printf("2==%d, %s==\n", allTrim(aa), aa);
  34.  
  35.         dlclose(dp);
  36.  
  37.        return 0;
  38. }
  39.  

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


2.2.2

  1. cc –c newtest.c
  2. cc newtest.o –c newtest
  3.  

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

当删除my.so文件时,将出现以下信息:
动态链接库应用示范
my.so: cannot open shared object file: 文件或目录不存在
3、小结
AIX创建与使用动态链接库并不是一件难事。
编译函数源程序时选用-shared选项即可创建动态链接库,注意应以.so后缀命名,最好放到公用库目录(如/lib,/usr/lib等)下面,并要写好用户接口文件,以便其它用户共享。
使用动态链接库,源程序中要包含dlfcn.h头文件,写程序时注意dlopen等函数的正确调用。编译文件
----------------------------------------------------------------------