gcc生成ダイナミックリンクライブラリ

7071 ワード

Makefileファイルは次のとおりです.
OBJS_DIR=./objs
CCFLAGS= -shared -Wall -fPIC -Wl,-soname,libcudart.so.4 -g
LDFLAGS=

ifeq ($(hook),1)
CCFLAGS+=-DRUN_REAL_LIBCUDA
LDFLAGS+= -ldl
endif

RTAPI_SRCS = runtime_api.c driver_intf.c 
RTAPI_OBJS = $(addprefix ${OBJS_DIR}/, $(RTAPI_SRCS:.c=.o))


$(OBJS_DIR)/%.o: %.c
	gcc $(CCFLAGS) -I./  -c $< -o $@

.PHONY:

all:mk-dirs rtapi

rtapi: mk-dirs $(RTAPI_OBJS)
	gcc $(CCFLAGS) -o  libcudart.so.4.0.0  $(RTAPI_OBJS) $(LDFLAGS)
	ln -sf libcudart.so.4.0.0 libcudart.so.4

mk-dirs:
	mkdir -p $(OBJS_DIR)

clean:
	rm -rf $(OBJS_DIR)
	rm -rf libcudart.so*

ここでは主にCCFlags変数の意味を解析します.
-shared共有ライブラリの生成
-Wall出力コンパイル中の警告
-fPICはアドレスに関係のないバイナリコードを生成し、このようなダイナミックライブラリをすべてのプロセスがアクセスできるメモリにロードすることができ、各プロセスがそれぞれのメモリにダイナミックライブラリのcopyを保持する必要がなく、メモリの浪費をもたらすが、linuxは書き込み時コピー(copy-on-write技術)を採用するため、ダイナミックライブラリにグローバル変数を定義することでプロセス間通信を実現することを妄想しないでください.プロセスがダイナミックライブラリ内のグローバル変数を変更しようとすると、linuxはダイナミックライブラリのグローバル変数をプロセスのプライベートメモリ領域にコピーします(読み取り専用で書かない場合はコピーしません)、このパスでプロセス間通信を実現できません.参照:
    http://www.cnblogs.com/lovevivi/archive/2013/01/10/2854584.html
    https://blog.csdn.net/zxh821112/article/details/8969541この記事では、-fPICオプションが適用されない結果について説明しています.ロード時にリダイレクトされるコード(コード内のすべてのアドレスを変更)が生成され、ダイナミックライブラリが1回だけロードされ、すべてのプロセスがこのメモリを共有することは不可能になります.
-Wl,-soname-Wlは次のパラメータがリンクに渡されることを示し、-sonameは共有ライブラリのsoname(単純共有名、short for shared object name)を制定し、sonameはライブラリのファイル名ではなくライブラリの唯一の表示であるが、一般的にsonameとライブラリのファイル名は関連している(ライブラリ名には詳細バージョン情報が含まれ、sonameにはプライマリバージョン情報のみが含まれている)ため、同じプライマリバージョンではこのバージョンの更新が容易にアップグレードされ、異なるバージョンのライブラリはファイル名が異なるがsonameは必ず一致しなければならない.プライマリ・バージョンが更新されると、通常は前のバージョンと互換性がないため、このライブラリを使用する実行可能プログラムはソースコードを書き換えてコンパイルする.
上のライブラリのファイル名はlibcudartです.so.4.0.0、ライブラリのsonameはlibcudartです.so.4ライブラリのソフトリンクと同じ
-gはgdbデバッグに使用されます.
 
LDFLAGS変数の意味:
-ldlリンクの名前はlibdlです.aのライブラリ.このライブラリにはいくつかの関数dlopen、dlsym、dlclose、dlerrorがあり、表示される動的ライブラリを開き、その中の関数アドレスを取得することができます.
 
RTAPI_OBJS = $(addprefix ${OBJS_DIR}/, $(RTAPI_SRCS:.c=.o))
この言葉の効果はまずRTAPIをSRCS変数のすべてc末尾の文字列を置換する.o末尾の文字列、makefileを呼び出すaddprefix関数は.o接頭辞を追加./objs/
https://blog.csdn.net/zhoudengqing/article/details/41775353
 
次に、ダイナミックライブラリの生成とダイナミックライブラリの使用に関する注意点について説明します.
-Wl,-soname,xxxオプションは、共有ライブラリの一意のIDを指定するために使用されます:単純な共有名、指定しない場合はデフォルト:
1.-oが指定する生成するライブラリのファイル名が標準のlib***.so.x.x.xというフォーマットでは、sonameはバージョン情報を削除する部分、すなわちlib**である.so(バージョン情報がない場合、ライブラリファイル名とsonameは同名)
2.-oが非標準形式で指定すると、gccはライブラリファイル名の本体部分(ポイント番号の前の部分、ポイント番号がない場合はすべてのファイル名)にlib接頭辞和を付ける.so接尾辞はsonameとして使用されます.
sonameは、プライマリ・プログラムの実行時にダイナミック・ライブラリをロードする唯一のIDであるため、プログラムがダイナミック・ライブラリを正常にロードするには、sonameと同じ名前のダイナミック・ライブラリ・ファイルへのソフト接続を作成するか、ダイナミック・ライブラリの名前を直接sonameに変更する必要があります(推奨しません)
 
メインプログラムのコンパイル時の注意事項:
コンパイル時にダイナミックライブラリを使用するには-lコマンドを使用し、コンパイルに成功するには、-lという名前の文字列にlib接頭辞とを付けるソフト接続を確立する必要があります.so接尾辞.
次に例を示します.
shared.cファイルは動的ライブラリファイルlibsharedを生成する.so.1.0.0、sonameをlibsharedと指定します.so.1
gcc -shared -fpic -Wall -Wl,-soname,libshared.so.1 -o libshared.so.1.0.0 shared.c

main.c exeファイルの生成
まずソフト接続を作成します.
ln -s libshared.so.1.0.0 libshared.so
ln -s libshared.so.1.0.0 libshared.so

再コンパイル、名前はありません.soのソフト接続、コンパイルエラー.
gcc -lshared -L. -o exe main.c

exeファイルを実行するときは、sonameと同じ名前でなければならない別のソフトリンクを作成します.
ln -s libshared.so.1.0.0 libshared.so.1

また、実行時にソフトリンクのあるパスを環境変数LD_に追加するLIBRARY_PATH中に行けば正しく動作する
export LD_LIBRARY_PATH=$(pwd):LD_LIBRARY_PATH

まとめ:
ダイナミックライブラリには3つのファイル名があります
1.libxxx.so(一般的にはソフト接続)は、プログラムをコンパイルする際に使用するダイナミックライブラリがこの名前である必要があります.gccの-lオプション、例えば-lsharedはリンクファイル名libsharedを意味します.soのダイナミックライブラリ
2.libxxx.so.x(一般的にはソフト接続で、メインバージョン番号のみを含む)は、コンパイルされたプログラムが、ユーザーに渡された実行時に、必ずユーザーのパソコンにlibxxxという名前があることを保証する役割を果たす.so.xダイナミックライブラリであり、この名前を保証するダイナミックライブラリの一意の識別子もlibxxxである.so.x、例えばユーザのパソコンにlibsharedが存在する.so.1のソフト接続は、実際のファイルlibsharedを指します.so.1.0.0、実際のファイルの一意の識別子がlibsharedであることを保証します.so.1、すなわちコンパイル生成リアルファイルlibshared.so.1.0.0の場合、gccの-WL、-sonameオプションの後に指定するパラメータもlibshared.so.1、それ以外の場合、プログラム実行時にダイナミックライブラリがロードされたときにこの名前のダイナミックライブラリが見つかったとしても、正常にロードできません.
3.libxxx.so.x.xx(プライマリ・バージョン番号とセカンダリ・バージョン番号を含む実際のダイナミック・ライブラリ・ファイル)、ダイナミック・ライブラリを開発する企業が生成したダイナミック・ライブラリのファイル名
4.上記から分かるように:
a.ダイナミックライブラリの主バージョン番号はダイナミックライブラリのファイル名に現れるだけでなく、gccの-Wl、-sonameオプションを使用して、バイナリデータの形式で、ダイナミックライブラリに保存し、ダイナミックライブラリの実行可能プログラムを使用して実行する場合、まずファイル名libxxxを使用する.so.x動的ライブラリを見つけ、動的ライブラリの一意の識別子がlibxxxであるかどうかを確認します.so.x、そうであれば正常にロードできます(検証待ち)
b.ダイナミックライブラリのセカンダリバージョン番号はダイナミックライブラリのファイル名にのみ表示されるので、プライマリバージョン番号は同じで、セカンダリバージョン番号の異なるダイナミックライブラリは、互いに互換性があります.
 
上記総括中の第2条、第4条の検証結果について:
意外なことに、プライマリ・バージョン番号が異なる2つのライブラリでは、2つのライブラリの名前が同じであることを保証する限り、2つのライブラリのインタフェースが同じであること、すなわち、2つのライブラリのバイナリ・データが保存されているプライマリ・バージョン番号が異なることを保証しますが、2つのライブラリのファイル名が同じに変更され、2つのライブラリの関数インタフェースが同じであることを保証すると、プログラムが実行されている間もダイナミック・ライブラリを正常にロードすることができます.検証手順は次のとおりです.
検証プログラムのディレクトリ構造:
sallenkey@ubuntu:~/workspace$ tree ./
./
├── hello
│   ├── hello.c
│   └── Makefile
├── main.c
└── Makefile

ここで./helloディレクトリはコンパイルダイナミックライブラリlibhelloを格納する.soのソースファイル,./ディレクトリ格納呼び出しlibhello.soのソースファイル
./hello/hello.c:
#include 

void hello(void){
    printf("hello world\r
"); }

./hello/Makefile
NO_VER_LINK_OUTPUT := libhello.so
LINK_OUTPUT = $(addsuffix $(VER), $(addsuffix ., $(NO_VER_LINK_OUTPUT)))
OBJS_DIR = ./objs
CCFLAGS = -shared -Wall -fPIC -Wl,-soname,$(LINK_OUTPUT)

HELLO_SRCS = hello.c
HELLO_OBJS = $(addprefix ${OBJS_DIR}/, $(HELLO_SRCS:.c=.o))

SECOND_VER_NU = .0.0
OUTPUT = $(addsuffix ${SECOND_VER_NU}, $(LINK_OUTPUT)) 

$(OBJS_DIR)/%.o:%.c
    gcc $(CCFLAGS) -I./ -c $< -o $@

.PHONY:

all:$(OUTPUT)

$(OUTPUT):$(OBJS_DIR) $(HELLO_OBJS)
    gcc $(CCFLAGS) -o $@ $(HELLO_OBJS)
    #ln -sf $@ $(LINK_OUTPUT)
    ln -sf $@ libhello.so.1 
    ln -sf $@ $(NO_VER_LINK_OUTPUT)

$(OBJS_DIR):
    mkdir -p $@

clean:
    rm -rf $(OBJS_DIR) $(OUTPUT)  $(NO_VER_LINK_OUTPUT) libhello.so.1 #$(LINK_OUTPUT)

./main.c
extern void hello();
#include 

int main(){
    hello();
    return 0;
}

./Makefile
TEST_SRCS:=$(wildcard *.c)
OBJS_DIRS:=./objs
LDFLAGS:=-lhello -L./hello

TEST_OBJS:=$(addprefix $(OBJS_DIRS)/, $(TEST_SRCS:.c=.o))
OUTPUT:=$(basename $(TEST_SRCS))

all:$(OBJS_DIRS) $(OUTPUT)

$(OUTPUT):$(TEST_OBJS)
    gcc -o $@ $^ $(LDFLAGS) -Wl,-rpath=./hello

$(OBJS_DIRS)/%.o:%.c
    gcc -o $@ -c $^

    
$(OBJS_DIRS):
    mkdir -p $@

clean:
    rm -rf $(OBJS_DIRS) $(OUTPUT)

認証方法:
1..に切り替えhelloディレクトリ、実行コマンド:make VER=1、マスターバージョン1のlibhelloを生成する.so
2.に切り替えディレクトリ、実行コマンド:make、実行可能ファイルmainを生成し、実行./main、hello worldの出力に成功したことを発見しました
3..に切り替えhelloディレクトリ、コマンドmake clean VER=1を実行し、マスターバージョン番号1のlibhelloをクリアします.so
4.コマンドmake VER=2を実行し、マスターバージョン番号2のlibhelloを生成する.so
5..に切り替えディレクトリ、実行./main、hello worldの出力に成功したことを発見しました