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를 위해서 필요한건가요?

cinsk의 이미지

dll에 관한 자세한 사항은 GNU libtool info 파일에 아주 잘 나와 있습니다.
("info libtool"해보세요)

errai의 이미지

이런 제 답변이 잘 못 되었군요. 삭제는 안되네요.

maczniak의 이미지

문제는 포함한 동적객체가 실행파일의 심볼을 찾지못해서
입니다. 실행파일은 (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

mtorange의 이미지

)
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 에 나와있나요? 쓸 수 있는 리눅스장비가 없어서 당장 찾아보기가 조금 어렵네요. 되는대로 읽어봐야겠네요.)

프로그램이 여러개의 소스파일로 구성이 되어있고, 그중 하나의 파일에만 이런 함수들이 들어가있는데, 그 파일을 컴파일하는 과정에 저 옵션을 넣어주어야 할까요? 아니면 맨 마지막에 실행파일을 만들때 넣어주어야 할까요?
링킹과정의 옵션이라서 실행파일만들 때만 넣어주면 될 듯 한데, 테스트 해보고 올리도록 하겠습니다.

maczniak의 이미지

차이점은 func_in_main이란 동적심볼의 유무입니다.

파일내용을 nm이나 objdump로 확인할 수 있지만,
"dynamic" 섹션의 내용을 해석할 수 있는 readelf를
예로 들면,

readelf -S dlopen    # dyn* 섹션들의 크기가 커졌음
readelf -s -D dlopen # 동적심볼 func_in_main 존재

--export-dynamic 옵션과 관계는 없지만 다른 것들도
확인할 수 있습니다.

readelf -d dlopen       # rpath는 dynamic 섹션에 기록됨
readelf -r dlroutine.so # func_in_main에 대한 relocation(재배치) 항목 존재

같은 gcc라도 기본 옵션이 차이가 날 수 있습니다.
libtool은 라이브러리를 컴파일할때 컴파일도구를
플래폼에 알맞은 옵션으로 실행하는 도구로,
주로 autoconf와 같이 사용합니다.

소스파일이 여러개인 경우 마지막 링크할때만 옵션을
사용하면 됩니다. 자신이 원하는 심볼만을 선택해서
export할 수도 있습니다.

ps) -rdynamic이 --export-dynamic의 별칭이더군요.
(gcc specs 파일 참고)

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.