在window平台下模拟Liunx使用GCC环境进行编译C的SO库。

来源:互联网 发布:linux自动部署脚本 编辑:程序博客网 时间:2024/05/17 22:16

在Liunx下的文件:

.o,是目标文件,相当于windows中的.obj文件
.so 为共享库, 是shared object,用于动态连接的,和dll差不多
.a为静态库, 是好多个.o合在一起,用于静态连接
.la为libtool自动生成的一些共享库, vi编辑查看

 

先打开VS2008 新建一个win32 控制台工程,然后新建两个文件(mathunits.h和mathunits.c)文件内容如下:

mathunits.h

#ifndef MATHUNITS_H#define MATHUNITS_Hint add(int a,int b);int sub(int x,int y);void WriteSysLog(char *str);#endif


mathunits.c

#include "mathunits.h"#include "time.h"#include "stdio.h"#include "stdlib.h"int add(int a,int b){return a + b;}int sub(int a,int b){return a - b;}void WriteSysLog(char *str){char buf[512];long MAXLEN = 10*1024*1024;//10MBtime_t timep; FILE *fp = NULL;struct tm *p; time(&timep); p = localtime(&timep); memset(buf,0,sizeof(buf));sprintf(buf,"%d-%d-%d %d:%d:%d : ",(1900+p->tm_year),(1+p->tm_mon),\p->tm_mday,p->tm_hour, p->tm_min, p->tm_sec); //星期p->tm_wdaystrcat(buf,str);strcat(buf,"\r\n");fp = fopen("./syslog.log","r");if(fp==NULL){fp = fopen("./syslog.log","w+");}else{fseek(fp,0,2);if(ftell(fp) >= MAXLEN){fclose(fp);fp = fopen("./syslog.log","w+");//大于10MB则清空原日志文件}else{fclose(fp);fp = fopen("./syslog.log","a");}}fwrite(buf,1,strlen(buf),fp);fflush(fp);//fsync(fileno(fp));fclose(fp);}


然后在主函数进行测试试用。(注:这是在VS2008 环境下的main)

#include "stdafx.h"#include "stdlib.h"#include "mathunits.h"int _tmain(int argc, _TCHAR* argv[]){int c = add(7,9);printf("c = %d\n",c);c = sub(8,19);printf("c = %d\n",c);WriteSysLog("test log out.");system("pause");return 0;}

在编译前记得先将VS2008 的配置要设为C的编译。

1、以免产生C/C++的编译冲突,先关闭预编译头:在项目上右建--》属性--》配置属性--》C/C++--》预编译头,设置为不使用预编译头。


2、设置为编译为C代码。在项目上右建--》属性--》配置属性--》C/C++--》高级--》编译为--》编译为 C 代码(/TC)

以上完成后,可以在VS下编译看看有没有问题。如果有问题自己处理一下BUG了。无问题的话就OK。

 

下面将使用GCC进行编译:

将(mathunits.h和mathunits.c)拷到一个简单的目录这里以F:\SO 作讲解,这步不是必须的,可以不移动文件。

运行CygWin.bat,将弹出命令行空口

设置编译路径到.h和.c的路径(F:\so)

输入cd /cygdrive/f/so 回车

下面先将.c文件编译为.o文件,再通过o文件编译成为静态库(.a)文件

gcc -c mathunits.c 回车,然后可以使用ls命令查看编译的文件。

图片中有些警告还有ls后有一个cmain.c这些先不理会,警告是因为我的c代码中用到了一些旧接口的函数。而cmain.c是我预先放到f:\so中的main测试文件,后面用到。

cmain.c

#include "stdio.h"#include "mathunits.h"int main(int agrc,char *argv){ int c = add(7,9);printf("c = %d\n",c);c = sub(8,19);printf("c = %d\n",c);WriteSysLog("ok,good");return 0;}

再将.o文件编译为.a静态库文件。

ar -crv libunits.a mathunits.o

说明:其中libunits为自命名的,以lib为前缀。

a:库名格式lib<name>.a,例如libmytest.a     

 b:ar命令创建静态库文件
d -----从指定的静态库文件中删除文件
m -----把文件移动到指定的静态库文件中
p -----把静态库文件中指定的文件输出到标准输出
q -----快速地把文件追加到静态库文件中
r -----把文件插入到静态库文件中
t -----显示静态库文件中文件的列表
x -----从静态库文件中提取文件

 

ar 命令的命令行格式如下:
ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files... 参数archive定义库的名称, files是库文件中包含的目标文件的清单, 用空格分隔每个文件

下面测试一下静态库的使用,将前面的哪个cmail.c放到f:\so目录下。

在GCC下进行静态库的连接使用。

命令:

方法一:

gcc  -o testlib cmain.c -L. -lunits

说明:testlib为最终编译出来的EXE文件名。 -L.为搜索当前路径(.)下的.a文件,即表示在当前目录中搜索连接库。

-lunits其中units为库的名去掉了lib前缀.

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

运行一下输出的testlib.exe

输入命令:

./testlib 回车

为了验证静态库在使用时是否被成功集入(链接到EXE)程序中。我们将删除libunits.a文件后再来运行./testlib。

如果成功输出则表示成功集入。

 

静态库使用的方法二:

gcc cmain.c libunits.a -o testlib

方法三:先将cmain.c生成.o文件,再生成可执行文件。

gcc  -c cmain.c    //生成.o

gcc  -o testlib cmain.o  libunits.a

 

以上是生成静态库.a文件。

下面是生成动态库.so文件。

在有.o文件之后就可以进行打so了。

命令:

gcc  -shared -fPCI -o libdyunits.so mathunits.o

其中-o是不可少;

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

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

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刷新。这样,加入的目录下的所有库文件都可见。

 

动态库的连接使用跟静库使用命令一样,如果静态库和动态库同时存在,优先调用动态库。

gcc cmain.c libdyunits.so -o testlib

 

使用下面命令时有个错误,小弟未能解决。试了几个网上的解决方法均不能解除。

上以是路径设置问题引起。

1:在unix/linux环境下
  1.1:生成.o文件
  gcc -I/usr/lib/j2sdk1.5-ibm/include -fPIC -c example.c
  1.2:生成动态库.so文件
  gcc -shared -WI -soname example.o -o  libexample.so
  2:在windows环境下
  2.1:生成.o文件
  gcc -c -I"D:\Program Files\Java\jdk1.6.0_10\include" -I"D:\Program Files\Java\jdk1.6.0_10\include\win32" -o Hello.o Hello.c
  2.2:生成dll文件
  gcc -I"D:\Program Files\Java\jdk1.6.0_10\include" -Wl,--add-stdcall-alias -shared -o Hello.dll Hello.o