공유 라이브러리에서 해결되지 않은 기호를 쉽게 확인 하시겠습니까?


82

상당히 큰 C ++ 공유 객체 라이브러리를 작성 중이며 디버깅을 어렵게 만드는 작은 문제가 발생했습니다.

헤더 파일에 함수 / 메서드를 정의하고이를위한 스텁을 만드는 것을 잊은 경우 (개발 중), 실행 파일이 아닌 공유 객체 라이브러리로 빌드하고 있기 때문에 컴파일 타임에 오류가 발생하지 않는다는 오류가 나타나지 않습니다. 그 기능을 구현하는 것을 잊었습니다. 내가 뭔가 잘못되었음을 알아내는 유일한 방법은 런타임에이 라이브러리에 연결하는 응용 프로그램이 결국 '정의되지 않은 기호'오류로 넘어갈 때입니다.

컴파일 타임에 필요한 모든 기호가 있는지 확인하는 쉬운 방법을 찾고 있습니다. 아마도 Makefile에 추가 할 수있는 것입니다.

내가 생각 해낸 한 가지 해결책은 컴파일 된 라이브러리를 실행하여 nm -C -U정의되지 않은 모든 참조 목록을 가져 오는 것입니다. 문제는 GLibC와 같은 다른 라이브러리에있는 모든 참조 목록과 함께 나타납니다. 물론 최종 응용 프로그램이 함께 작성 될 때이 라이브러리와 함께 연결됩니다. 내 모든 헤더 파일 nmgrep통해 to 의 출력을 사용하고 해당하는 이름이 있는지 확인할 수 있습니다. 확실히 이것은 드문 문제가 아니며 더 나은 해결 방법이 있습니까?


3
nm -C -u저를 여러 번 구했습니다! ( -u내 시스템 의 소문자 에 유의하십시오 .) 다음에 필요할 때 찾을 수 있도록 여기에이 주석을 남겨 둡니다.
dpritch

답변:


93

링커 옵션 -z defs/을 확인하십시오 --no-undefined. 공유 객체를 만들 때 해결되지 않은 심볼이 있으면 링크가 실패합니다.

gcc를 사용하여 링커를 호출하는 경우 컴파일러 -Wl옵션을 사용 하여 옵션을 링커에 전달합니다.

gcc -shared ... -Wl,-z,defs

예를 들어 다음 파일을 고려하십시오.

#include <stdio.h>

void forgot_to_define(FILE *fp);

void doit(const char *filename)
{
    FILE *fp = fopen(filename, "r");
    if (fp != NULL)
    {
        forgot_to_define(fp);
        fclose(fp);
    }
}

이제 공유 객체로 빌드하면 성공합니다.

> gcc -shared -fPIC -o libsilly.so silly.c && echo succeeded || echo failed
succeeded

그러나을 추가 -z defs하면 링크가 실패하고 누락 된 기호에 대해 알려줍니다.

> gcc -shared -fPIC -o libsilly.so silly.c -Wl,-z,defs && echo succeeded || echo failed
/tmp/cccIwwbn.o: In function `doit':
silly.c:(.text+0x2c): undefined reference to `forgot_to_define'
collect2: ld returned 1 exit status
failed

3
+1이 답변은 컴파일 시간 동안 DLL 외부 기호를 해결해야하는 Windows 배경에서 온 사람들을 도울 수 있습니다. 멋진 코드 스 니펫을 찾는 방법!
Shmil The Cat

@ShmilTheCat : 안녕하세요, 내 make 파일에 -Wl, -z, defs를 넣어 보았습니다. 여전히 실행 중이지만 컴파일 중에는 정의되지 않은 기호 오류가 발생합니다. 어떡해?. 내 시나리오에서는 두 개의 폴더가 다른 폴더 안에 있습니다 (예 : A / B, 즉 B가 A 안에 있음). "libA.so"에서 기호 또는 B가 거의 표시되지 않습니다. B의 모든 기호가 "libA.so"에서 사용 가능한지 확실하지 않습니다. 어떻게 확인합니까?
Vinay

@ShmilTheCat Windows DLL과 같은 것을 더 만들기 위해 -Bsymbolic링커 명령 줄에 추가 할 수 있습니다 . 검사 forgot_to_define덕분에 라이브러리에 현재 존재 하더라도 -z실행 파일은 여전히 ​​자체 정의로이를 재정의 할 수 있으며 라이브러리의 자체 정의는 해당 재정의로 이동합니다. -Bsymbolic라이브러리의 기능에 대한 자체 정의가 기능으로 이동하도록 강제합니다.
Kaz

실제로 사용되는 기능에 대해서만 작동합니다. 그렇게 하면 오류 // forgot_to_define(fp);
가보고

17

Linux (사용중인 것으로 보이는) ldd -r a.out에서는 원하는 답을 정확하게 제공해야합니다.

업데이트 : 확인할 사소한 방법 a.out:

 echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so
 ldd -r ./a.out

4
그것은 내가 원래 포스트에서 제안한 nm -C -U와 거의 똑같은 일을한다. 문제는 말할 수있는 'a.out'애플리케이션이없고, 공유 라이브러리이므로 ldd -r mylibrary.so는 다양한 다른 동적 라이브러리의 기호를 사용하기 때문에 전체 출력 힙을 제공합니다. 특히 관심이 있습니다. 외부 라이브러리가 아닌 헤더 파일에 정의 된 누락 된 기호에서 .
David Claridge

1
이것은 허용되어야합니다. 질문은 정의되지 않은 기호를 피하는 방법이 아니라 정의되지 않은 기호를 확인하는 쉬운 방법이 무엇인지입니다
workplaylifecycle

8

testsuite는 어떻습니까? 필요한 기호에 연결되는 모의 실행 파일을 만듭니다. 연결이 실패하면 라이브러리 인터페이스가 불완전 함을 의미합니다.


3

나는 한 번 같은 문제가 있었다. 저는 C ++로 구성 요소 모델을 개발하고 있었으며, 물론 구성 요소는 런타임에 동적으로로드되어야합니다. 세 가지 해결책이 떠오르는데 그게 제가 적용한 것입니다.

  1. 정적으로 컴파일 할 수있는 빌드 시스템을 정의하는 데 시간이 걸립니다. 엔지니어링에 약간의 시간을 잃게 될 것이지만 이러한 성가신 런타임 오류를 포착하는 데 많은 시간을 절약 할 수 있습니다.
  2. 잘 알려져 있고 잘 이해 된 섹션으로 함수를 그룹화하여 함수 / 스텁을 그룹화하여 각 해당 함수에 해당 스텁이 있는지 확인할 수 있습니다. 문서화하는 데 시간을 할애하면 정의를 확인하는 스크립트 (예 : doxygen 주석을 통해)를 작성하고 해당 .cpp 파일을 확인할 수 있습니다.
  3. 동일한 라이브러리 세트를로드하고 RTLD_NOW 플래그를 dlopen에 지정하는 여러 테스트 실행 파일을 수행하십시오 (* NIX에있는 경우). 그들은 누락 된 기호를 알릴 것입니다.

도움이되기를 바랍니다.


RTLD_NOW 줄에 즉시 (지연이 아닌) 바인딩을 강제하기 위해 env var가 있습니까?
Stefano Borini

1
예. 여기서는 LD_BIND_NOW (libc5; glibc 2.1.1 이후)입니다. 비어 있지 않은 문자열로 설정하면 동적 링커가 처음 참조 될 때 함수 호출 확인을 지연하는 대신 프로그램 시작시 모든 기호를 확인합니다. 디버거를 사용할 때 유용합니다.
Stefano Borini

이것은 OP의 목적에도 유용 할 수 있습니다. LD_WARN (ELF 전용) (2.1.3 이후 glibc) 비어 있지 않은 문자열로 설정되면 해결되지 않은 기호에 대해 경고합니다.
Stefano Borini

스테파노 : 네, 맞습니다. 이러한 변수가 존재합니다. 그러나 동적로드를 나중에 (예 : 사용자가 모듈을로드 할 때)까지 연기하는 경우 의도 된 (미래) 라이브러리를로드하는 테스트 프로그램을 실제로 작성하지 않는 한 이러한 변수는 유용하지 않습니다.
Diego Sevilla
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.