Solaris, gcc에서 dlopen로 shared object 붙일 때 오류
서버 프로그램이 하나 있고,
서버 프로그램에서 사용하는 각 모듈을 그때그때 사용자의 요청이 있을 때마다 동적으로 생성해서 dlopen과 dlsym을 이용해서 붙이는 구조로 돌아가는 프로그램을 짜고 있는 중입니다. 환경파일을 통해 초기에 몇몇 모듈들은 서버와 같이 올라가고요. server_func()는 서버 프로그램에 정의되어 있는 함수입니다.
server:
server_func()
module1.so
call server_func();
module2.so
call server_func();
...
각 모듈에서는 서버에 있는 루틴을 호출하고 있는 부분이 있는데,
Solaris cc 에서는 전혀 문제가 없이 돌아갔었는데, 포팅때문에 (server컴파일, 모듈 컴파일 모두) 컴파일러를 gcc로 바꾸면서 dlopen()시에 referenced symbol not found 라는 에러를 내면서 실패를 합니다.
nm 등으로 보면 분명히 server에 server_func 가 정의되어 있거든요.
일단 시간이 급해서 server_func 루틴을 별도의 라이브러리로 뽑아서 수행이 되게는 했지만 영 찜찜합니다.
간단하게 테스트 코드를 짜 봤습니다.
dlopen.c
#include <stdio.h> #include <unistd.h> #include <dlfcn.h> int func_in_main(int x) { printf("I'm func in main. %d\n", x); } main(int argc, char **argv) { int (*func)(int x); void *handle; int rc; handle = dlopen("dlroutine.so", RTLD_NOW); if (handle == NULL) { fprintf(stderr, "dlopen error: %s\n", dlerror()); exit(2); } func = (int (*)(int))dlsym(handle, "dl_func"); if (func == NULL) { fprintf(stderr, "dlsym error: %s\n", dlerror()); exit(2); } func(50); rc = dlclose(handle); if (rc != 0) { fprintf(stderr, "dlclose error: %s", dlerror()); exit(2); } }
dlroutine.c
#include <stdio.h> int dl_func(int x) { printf("hello, world %d times\n", x); func_in_main(x); }
shared object는 cc, gcc에 따라서 다음과 같이 컴파일했고요,
gcc인 경우 gcc -fPIC -shared -g -o dlroutine.so dlroutine.c cc인 경우 cc -KPIC -g -G -h dlroutine.so -o dlroutine.so dlroutine.c
메인프로그램은 cc, gcc상관없이 다음과 같이 컴파일했습니다.
cc -o dlopen dlopen.c -ldl gcc -o dlopen dlopen.c -ldl
테스트해 본 결과 shared object는 gcc로 했는지 cc로 컴파일했는지 상관이 없었고, 메인 프로그램을 어떤 컴파일러를 썼는지에 따라서만 차이가 났습니다.
gcc의 결과
dlopen error: ld.so.1: dlopen: fatal: relocation error: file ./dlroutine.so: symbol func_in_main: referenced symbol not found
gcc의 경우에는 LD_LIBRARY_PATH에 gcc 라이브러리가 있는 경로 (/usr/local/lib )을 추가해 주어야 하던데, 이것도 좀 이상한 느낌이 들긴합니다. gcc로 컴파일된 shared object를 위해서 필요한건가요?
dll에 관한 자세한 사항은 GNU libtool info 파일에 아주
dll에 관한 자세한 사항은 GNU libtool info 파일에 아주 잘 나와 있습니다.
("info libtool"해보세요)
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://cinsk.github.io/cfaqs/
아마도..
이런 제 답변이 잘 못 되었군요. 삭제는 안되네요.
문제는 포함한 동적객체가 실행파일의 심볼을 찾지못해서입니다. 실행파
문제는 포함한 동적객체가 실행파일의 심볼을 찾지못해서
입니다. 실행파일은 (dl*() 함수를 사용하여) 직접 혹은
(ld-linux.so를 통해) 간접으로 동적객체를 포함하면
동적객체에서 자신이 원하는 심볼을 찾을 수 있습니다.
문제는 반대로 동적객체가 실행파일의 심볼을 찾을 경우인데,
플래폼에 따라 차이가 있어서 여러 플래폼에 portable한
프로그램을 작성할때 자주 발생하는 문제중 하나입니다.
가장 portable하지만 번거로운 방법은 함수포인터를 넘겨주는
것입니다. dl_func()이 func_in_main()을 부르는 것이
일종의 callback이므로, dl_func()을 부를때 아규먼트로
함수포인터 func_in_main를 넘겨주고 dl_func()에서는
받은 함수포인터로 함수를 호출하면 됩니다.
상당히 portable하고 손쉬운 다른 방법은 실행파일에서
func_in_main 심볼을 export하는 것입니다.
심볼을 export하는 방법은 다양합니다.
gcc -Wl,--export-dynamic -o dlopen dlopen.c -ldl
마지막으로 GNU ld의 -rpath 옵션을 사용하여 라이브러리경로를
직접 실행파일에 기록할 수 있습니다. 솔라리스 컴파일러는
아마도 자동으로 이 작업을 할 겁니다.
gcc -Wl,-rpath . -Wl,--export-dynamic -o dlopen dlopen.c -ldl
) Solaris 2.6에서 gcc 2.95.3으로 테스트 해봤는데
)
Solaris 2.6에서 gcc 2.95.3으로 테스트 해봤는데 정상적으로 so에서 메인 프로그램의 심볼을 참조 합니다.
Linux에서는 메인 프로그램을 링크할 때 -rdynamic 옵션을 주지 않으면 so에서 심볼읍 못 찾습니다.
질문하신것은 Solaris에서 사용했다고 되어 있는데 혹시 리눅스에서 테스트 하신것은 아닌지?
질문자입니다. 됩니다
실행환경은 Solaris 7과 Solaris 2.6 이었고요, gcc는 Solaris 7은 3.0.3이었고, 2.6은 2.95.3이었습니다.
-Wl,--export-dynamic 컴파일 옵션(링크 옵션이겠죠?)을 쓰니까 깔끔하게 되네요.
이 옵션을 붙였을 때와 안 붙있을 때 두 실행파일의 차이가 거의 없던데,
(nm으로 보았을 때 앞부분의 SECT들의 크기가 좀 다른 것하고, -Wl... 옵션을 안 준 경우에
[66] | 133504| 0|OBJT |GLOB |2 |14 |__dso_handle
라는 정보가 추가되는 것 말고는 func_in_main 의 정의나 다른 부분의 차이가 없는 것 같습니다.)
export되는 심볼을 저장하는 곳(SECTION인가요?)을 분석해 보면 무언가 차이점을 찾을 수 있겠죠? 이쪽 관련정보를 조금 더 알려주시면 고맙겠습니다.
(info libtool 에 나와있나요? 쓸 수 있는 리눅스장비가 없어서 당장 찾아보기가 조금 어렵네요. 되는대로 읽어봐야겠네요.)
프로그램이 여러개의 소스파일로 구성이 되어있고, 그중 하나의 파일에만 이런 함수들이 들어가있는데, 그 파일을 컴파일하는 과정에 저 옵션을 넣어주어야 할까요? 아니면 맨 마지막에 실행파일을 만들때 넣어주어야 할까요?
링킹과정의 옵션이라서 실행파일만들 때만 넣어주면 될 듯 한데, 테스트 해보고 올리도록 하겠습니다.
차이점은 func_in_main이란 동적심볼의 유무입니다.파일내용
차이점은 func_in_main이란 동적심볼의 유무입니다.
파일내용을 nm이나 objdump로 확인할 수 있지만,
"dynamic" 섹션의 내용을 해석할 수 있는 readelf를
예로 들면,
--export-dynamic 옵션과 관계는 없지만 다른 것들도
확인할 수 있습니다.
같은 gcc라도 기본 옵션이 차이가 날 수 있습니다.
libtool은 라이브러리를 컴파일할때 컴파일도구를
플래폼에 알맞은 옵션으로 실행하는 도구로,
주로 autoconf와 같이 사용합니다.
소스파일이 여러개인 경우 마지막 링크할때만 옵션을
사용하면 됩니다. 자신이 원하는 심볼만을 선택해서
export할 수도 있습니다.
ps) -rdynamic이 --export-dynamic의 별칭이더군요.
(gcc specs 파일 참고)
댓글 달기