내 실행 파일이 실행이 안되는 이유 (feat. ldd 활용법)

sycho·2024년 7월 14일
0

OS related

목록 보기
5/5

파일이 없대요

다음과 같은 main.c 파일을 우분투에서 만들었다고 해보자.

#include <math.h>
#include <stdio.h>

int main() {
	double val = sqrt(pow(2, 2) + pow(3, 2));
    printf("return value : %lf\n", ret);
    return 0;
}

이걸 컴파일을 해보자.

$ gcc -c *.c

이후 링킹을 이렇게 해보자.

ld *.o

아마 위가 안 된다는 것은 많이들 알 것이다. 이유는 libm이랑 libc, 그리고 CRT를 추가하지 않았기 때문.

그래서 관련 정보를 추가해 링커에 보내보자. 그러면 나중에 동적 링커가 알아서 CRT랑 저 두 라이브러리를 링킹해서 실행파일을 실행할 것이다. 우분투 기준 다음과 같다.

ld *.o /usr/lib/x86_64-linux-gnu/crt1.o -lm -lc

이제 될까? 일단 a.out은 나온다. 실행해보면...

bash: ./a.out: No such file or directory

없다고 한다. 음? 분명 만들었는데 왜 없다고 할까.

ldd로 의존관계 확인

여러 이유가 있을 수 있지만, 이 경우는 의존관계에 관한 문제로 인해 발생했다.

ldd a.out을 해보도록 하자.

linux-vdso.so.1 (...)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (...)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (...)
/lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (...)

ldd는 해당 executable의 의존관계를 나타낸다. 좌측은 의존관계가 있는 라이브러리의 이름이고 우측은 그 라이브러리의 실제 위치 경로다.

좌측은 링커가 동적으로 연결할 때 사용하는 이름이기도 하다.

vdso는 궁금하면 이 글 참고 여기서는 그리 중요한 내용은 아니고, 보통 커널이 자주 호출되는 시스템콜을 미리 제공한다고 생각하면 된다.

여기서 중요한건 마지막 줄. 저 우측 경로는 실제로 존재하는 경로로 이 실행 파일이 사용하는 링커가 위치한 경로...인데 희한하게 얘도 의존관계에 존재하고 있다.

그리고 좌측에 링커가 링커를 찾는데 활용할(?) 링커 찾는 이름(???)이 존재하고 있다. /lib/ld64.so.1

뭔 상황?

이 명령어를 실행해보자.

readelf -a a.out | grep 'program interpreter'

C의 executable은 ELF 형식을 지키는 것을 알것이다. readelf는 그 구조를 분석하는데 활용되는데, 이 중 program interpreter이라는 부분이 사용하는 링커의 이름을 알려준다.

출력은 다음과 같이 나온다.

[Requesting program interpreter: /lib/ld64.so.1]

이 말은 즉 활용할 동적 링커의 경로가 저것이라는거다. 그런데 정작 저 파일은 존재하지 않는다. 사실 사용해야 하는 링커는 /lib64/ld-linux-x86-64.so.2다. 그런데 저걸 링커로 사용해버려서 링커도 뭔가 의존관계가 있는 파일이고 분석해보니 원래 링커가 있는 경로의 파일이랑 관계가 있다고 결론을 내버린채 오류없이 링킹이 된 것이다.

즉 사용할 동적 링커 자체가 제대로 정의가 안되어버렸다.

어떻게 해결?

사용할 동적 링커 경로를 명시하면 된다. 구별을 위해 이번에는 work.out이라고 이름을 지어보겠다.

ld *.o /usr/lib/x86_64-linux-gnu/crt1.o -lm -lc -dynamic-linker=/lib64/ld-linux-x86-64.so.2 -o work.out

이러면 실행이 되고, ldd의 경우 출력은

linux-vdso.so.1 (...)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (...)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (...)
/lib64/ld-linux-x86-64.so.2 (...)

이 된다. 보다시피 화살표가 사라졌다. 실제로 링커의 경우 안 될 때의 ldd결과물에서처럼 화살표가 나오는 상황이 이상하다는 점 유의바란다.

readelf를 써보면 다음과 같이 나온다.

[Requesting program interpreter: /ㅣlib64/ld-linux-x86-64.so.2]
profile
안 흔하고 싶은 개발자. 관심 분야 : 임베디드/컴퓨터 시스템 및 아키텍처/웹/AI

0개의 댓글