天天看點

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

在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_H

int 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;//10MB
	time_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_wday
	strcat(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++--》預編譯頭,設定為不使用預編譯頭。

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

2、設定為編譯為C代碼。在項目上右建--》屬性--》配置屬性--》C/C++--》進階--》編譯為--》編譯為 C 代碼(/TC)

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

以上完成後,可以在VS下編譯看看有沒有問題。如果有問題自己處理一下BUG了。無問題的話就OK。

下面将使用GCC進行編譯:

将(mathunits.h和mathunits.c)拷到一個簡單的目錄這裡以F:\SO 作講解,這步不是必須的,可以不移動檔案。

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

運作CygWin.bat,将彈出指令行空口

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

設定編譯路徑到.h和.c的路徑(F:\so)

輸入cd /cygdrive/f/so 回車

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。
在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

下面先将.c檔案編譯為.o檔案,再通過o檔案編譯成為靜态庫(.a)檔案

gcc -c mathunits.c 回車,然後可以使用ls指令檢視編譯的檔案。

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

圖檔中有些警告還有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是庫檔案中包含的目标檔案的清單, 用空格分隔每個檔案

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

下面測試一下靜态庫的使用,将前面的哪個cmail.c放到f:\so目錄下。

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

在GCC下進行靜态庫的連接配接使用。

指令:

方法一:

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

說明:testlib為最終編譯出來的EXE檔案名。 -L.為搜尋目前路徑(.)下的.a檔案,即表示在目前目錄中搜尋連接配接庫。

-lunits其中units為庫的名去掉了lib字首.

-l:編譯器查找動态連接配接庫時有隐含的命名規則,即在給出的名字前面加上lib,後面加上.so來确定庫的名稱,如果是靜态庫,在後面加上.a來确定名稱.

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

運作一下輸出的testlib.exe

輸入指令:

./testlib 回車

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

為了驗證靜态庫在使用時是否被成功集入(連結到EXE)程式中。我們将删除libunits.a檔案後再來運作./testlib。

如果成功輸出則表示成功集入。

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

靜态庫使用的方法二:

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

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

其中-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

使用下面指令時有個錯誤,小弟未能解決。試了幾個網上的解決方法均不能解除。

在window平台下模拟Liunx使用GCC環境進行編譯C的SO庫。

上以是路徑設定問題引起。

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