다음과 같은 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]