linux shared library global/local functionを設定する方法
C++言語の関数を共有ライブラリとして作成する場合、linuxではデフォルトですべての関数がglobalで共有されてしまいます。そのため、共有したい関数/共有したくない関数を定義し共有ライブラリを作成する方法を記述します。
以下はRedhat 8.6環境で確認しました。
# cat test1.c
#include <stdio.h>
#include <stdlib.h>
#define RETURN_VAL 1
int sub() {
return RETURN_VAL;
}
extern int test1() {
int rc = sub();
return rc;
}
#include <stdio.h>
#include <stdlib.h>
#define RETURN_VAL 1
int sub() {
return RETURN_VAL;
}
extern int test1() {
int rc = sub();
return rc;
}
# cat makefile.1.default
#
SRCDIR = .
TARGETDIR = .
TARGETLIB = $(TARGETDIR)/libtest1.so
CODESRC = $(SRCDIR)/test1.c
TARGETOBJ = $(TARGETDIR)/test1.o
CC = g++
CCOPTIONS = -c -g -O0 -fPIC -Wno-deprecated -m64 -mtune=generic -mcmodel=small
LIBCC = $(CC)
LDOPTIONS = -shared -m64
RM = rm
all: $(TARGETLIB)
$(TARGETLIB): $(TARGETOBJ)
$(LIBCC) $(LDOPTIONS) $(TARGETOBJ) -o $(TARGETLIB)
$(TARGETOBJ): $(CODESRC)
$(CC) $(CCOPTIONS) -c $(CODESRC) -o $(TARGETOBJ)
clean:
$(RM) $(TARGETLIB) $(TARGETOBJ)
# make -f makefile.1.default
g++ -c -g -O0 -fPIC -Wno-deprecated -m64 -mtune=generic -mcmodel=small -c ./test1.c -o ./test1.o
g++ -shared -m64 ./test1.o -o ./libtest1.so
# nm libtest1.so
0000000000200df0 d _DYNAMIC
0000000000201000 d _GLOBAL_OFFSET_TABLE_
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
00000000000005e9 T _Z3subv
00000000000005f4 T _Z5test1v
00000000000006c0 r __FRAME_END__
000000000000061c r __GNU_EH_FRAME_HDR
0000000000201028 d __TMC_END__
0000000000201028 B __bss_start
w __cxa_finalize@@GLIBC_2.2.5
00000000000005a0 t __do_global_dtors_aux
0000000000200de0 t __do_global_dtors_aux_fini_array_entry
0000000000200de8 d __dso_handle
0000000000200dd8 t __frame_dummy_init_array_entry
w __gmon_start__
0000000000201028 D _edata
0000000000201030 B _end
000000000000060c t _fini
00000000000004e0 t _init
0000000000201028 b completed.7303
0000000000000530 t deregister_tm_clones
00000000000005e0 t frame_dummy
0000000000000560 t register_tm_clones
sub, test1関数がTとなりglobal symbolであることがわかります。
int test1()だけを外部からリンク可能なglobal symbolにするには、以下のようなmapファイルを作成しリンカーに渡します。
# cat test1.map
{
global:
_Z5test1v;
local: *;
};
makefileへの指定は--version-scriptで指定します。
# cat makefile.1
#
SRCDIR = .
TARGETDIR = .
TARGETLIB = $(TARGETDIR)/libtest1.so
CODESRC = $(SRCDIR)/test1.c
TARGETOBJ = $(TARGETDIR)/test1.o
CC = g++
CCOPTIONS = -c -g -O0 -fPIC -Wno-deprecated -m64 -mtune=generic -mcmodel=small
LIBCC = $(CC)
LDOPTIONS = -shared -m64
RM = rm
all: $(TARGETLIB)
$(TARGETLIB): $(TARGETOBJ)
$(LIBCC) $(LDOPTIONS) $(TARGETOBJ) -o $(TARGETLIB) -Wl,--version-script,$(TARGETDIR)/test1.map
$(TARGETOBJ): $(CODESRC)
$(CC) $(CCOPTIONS) -c $(CODESRC) -o $(TARGETOBJ)
clean:
$(RM) $(TARGETLIB) $(TARGETOBJ)
# make -f makefile.1
g++ -c -g -O0 -fPIC -Wno-deprecated -m64 -mtune=generic -mcmodel=small -c ./test1.c -o ./test1.o
g++ -shared -m64 ./test1.o -o ./libtest1.so -Wl,--version-script,./test1.map
# nm libtest1.so
0000000000200df0 d _DYNAMIC
0000000000201000 d _GLOBAL_OFFSET_TABLE_
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000000529 t _Z3subv
0000000000000534 T _Z5test1v
0000000000000600 r __FRAME_END__
000000000000055c r __GNU_EH_FRAME_HDR
0000000000201020 d __TMC_END__
0000000000201020 b __bss_start
w __cxa_finalize@@GLIBC_2.2.5
00000000000004e0 t __do_global_dtors_aux
0000000000200de0 t __do_global_dtors_aux_fini_array_entry
0000000000200de8 d __dso_handle
0000000000200dd8 t __frame_dummy_init_array_entry
w __gmon_start__
0000000000201020 d _edata
0000000000201028 b _end
000000000000054c t _fini
0000000000000430 t _init
0000000000201020 b completed.7303
0000000000000470 t deregister_tm_clones
0000000000000520 t frame_dummy
00000000000004a0 t register_tm_clones
sub関数がtとなりlocal、test1関数がTとなりglobal symbolであることがわかります。
本件は同じsub関数を含んだ別の共有ライブラリがある場合に問題が発生します。
以下はその例でlibtest2.soを作成すると、sub関数がglobal symbolとして外部に公開されます。
すると、実行時の共有ライブラリ:libtest1.so,libtest2.soの読み込み順により結果が異なることになります。
# cat test2.c
#include <stdio.h>
#include <stdlib.h>
#define RETURN_VAL 2
int sub() {
return RETURN_VAL;
}
int test2() {
int rc = sub();
return rc;
}
# cat makefile.2
#
SRCDIR = .
TARGETDIR = .
TARGETLIB = $(TARGETDIR)/libtest2.so
CODESRC = $(SRCDIR)/test2.c
TARGETOBJ = $(TARGETDIR)/test2.o
CC = g++
CCOPTIONS = -c -g -O0 -fPIC -Wno-deprecated -m64 -mtune=generic -mcmodel=small
LIBCC = $(CC)
LDOPTIONS = -shared -m64
RM = rm
all: $(TARGETLIB)
$(TARGETLIB): $(TARGETOBJ)
$(LIBCC) $(LDOPTIONS) $(TARGETOBJ) -o $(TARGETLIB)
$(TARGETOBJ): $(CODESRC)
$(CC) $(CCOPTIONS) -c $(CODESRC) -o $(TARGETOBJ)
clean:
$(RM) $(TARGETLIB) $(TARGETOBJ)
このようにデフォルトで作成するとsub関数がglobalで公開されます。
# nm -D libtest2.so
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000000529 T _Z3subv
0000000000000534 T _Z5test2v
w __cxa_finalize
w __gmon_start__
実際に以下のメインプログラムから呼び出します。
# cat testmain.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern int test1();
extern int test2();
int main() {
int ret1 = test1();
printf("test1 return = %d\n",ret1);
int ret2 = test2();
printf("test2 return = %d\n",ret2);
return 0;
}
# cat makefile.main
#
SRCDIR = .
TARGETDIR = .
TARGETLIB = $(TARGETDIR)/testmain
CODESRC = $(SRCDIR)/testmain.c
TARGETOBJ = $(TARGETDIR)/testmain.o
CC = g++
CCOPTIONS = -c -g -O0 -fPIC -Wno-deprecated -m64 -mtune=generic -mcmodel=small
LIBCC = $(CC)
LOOKUPDIR = .
LOOKUPLIB1 = test1
LOOKUPLIB2 = test2
RM = rm
all: $(TARGETLIB)
$(TARGETLIB): $(TARGETOBJ)
$(LIBCC) $(LDOPTIONS) -L$(LOOKUPDIR) -l$(LOOKUPLIB1) -l$(LOOKUPLIB2) $(TARGETOBJ) -o $(TARGETLIB)
$(TARGETOBJ): $(CODESRC)
$(CC) $(CCOPTIONS) -c $(CODESRC) -o $(TARGETOBJ)
clean:
$(RM) $(TARGETLIB) $(TARGETOBJ)
実行結果
1):libtest1.so, libtest2.soをデフォルトで作成し実行時のアクセス順をlibtest1.so, libtest2.soとすると
# ./testmain.sh
test1 return = 1
test2 return = 1
2):libtest1.so, libtest2.soをデフォルトで作成し実行時のアクセス順をlibtest2.so, libtest1.soとすると
# ./testmain.sh
test1 return = 2
test2 return = 2
3):libtest1.so, libtest2.soをsub関数をlocalで作成するとlibtest1.so, libtest2.soの順番によらず
# ./testmain.sh
test1 return = 1
test2 return = 2
となります。
コメント
コメントを投稿