
.a.so-ldl, -rdynamic 지정 컴파일,함수 사용법 등이 다름

$ gcc [options] [src] -o [output_file]
gcc -Iinclude src/userprog.c
src/userprog.c를 바이너리로 만들면 collect2 링커가 print 심볼을 찾지 못해 에러
-I 옵션은 include할 파일의 위치를 지정한다.-I<header file path>$ gcc -c -Iinclude src/userprog.c -o obj/userprog.o
readelf -s userprog.o 로 symbol을 보자
userprog.o 내에 print 심볼 을 찾는다.print 심볼을 찾을 것이다.print심볼은 src/print.c에 print함수이다. 이에 대한 object를 만들어야한다.$ gcc -c -Iinclude src/print.c -o obj/print.o
$ gcc -c -Iinclude src/arith.c -o obj/arith.o
선택의 기로...
1. print.o, arith.o가 심볼로 사용되긴 하는데 다른 프로그램(userprog.c)에 하나의 바이너리로 '합쳐서' 만들 것인가?
2. print.o arith.o를 심볼로 사용하긴 하는데, 라이브러리 로 만들어 다른 프로그램(userprog.c)에서 가져와 사용하게 할 것인가?
-> 여기서 obj를 하나의 라이브러리로 만들어 다른 프로그램에서 가져와서 사용케 할 수 있다.
= 라이브러리
.alib[라이브러리_이름].aOpenCL, 라이브러리 파일 명: libOpenCL.agcc -L'libOpenCL.a 위치'userprog.c-lOpenCL -o userprog$ gcc -Linclude userprog.c -lOpenCL -o userprog
arar [option] | description | examples |
|---|---|---|
r | 아카이브에 새로운 오브젝트 추가 오래된 오브젝트는 새 오브젝트로 교체 | ar r [lib] [object] |
c | 아카이브가 존재하지 않으면 생성 | ar rcs [lib] [object] |
t | 아카이브에 있는 파일 리스트 출력 | ar t [library] |
x | 아카이브에서 오브젝트 파일 추출 | ar x [library] |
s | 아카이브에 오브젝트파일 idx 넣기이 index는 library에서 lookup-symbol로 컴파일러에서 사용됨 | ar rcs [lib] [object] |
$ ar rcs <library name> <src1.o> [src2.o ...]
e.g.
$ ar rcs libmyhello.a arith.o print.o
-static 으로 컴파일 시, 모든 라이브러리를 static으로# ====== sample_native.c ====== #
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
printf("hello world!\n");
return 0;
}
$ gcc sample_native.c -o sample_native

ld-linux-*.so)를 사용한다.
17KB
-static으로 빌드하여, 전부 static으로 링크해보자.
$ gcc -static sample_native.c -o sample_native
크기가 852KB로 늘었다.libc.so에서 libc.a로 대체되어 함께 빌드되었기 때문이다.libc.a는 /usr/liblibc.so는 /lib.so/etc/ld.so.conf를 관리lib[라이브러리_이름].soOpenCL, 라이브러리 파일 명: libOpenCL.sogcc -L'libOpenCL.a 위치'userprog.c-lOpenCL -o userprog$ gcc -Linclude userprog.c -lOpenCL -o userprog
-fPIC): .c->.o$ gcc -fPIC -c print.c # print.o 생성
$ gcc -fPIC -c arith.c # arith.o 생성
-fPIC : Position Independent Code로 compile.o-> .so$ gcc -shared -Wl,-soname,<SONAME> -o <Create lib Name> <src1.o> [src2.o ...]
e.g.
$ gcc -shared -Wl,-soname,libmyhello.so.1 -o libmyhello.so.1.0 print.o arith.o

| CFLAGS | description |
|---|---|
-shared | compiler에게 executable한 object에게 link를 걸어 줄 수 있는 shared library를 만든다고 set 하는 flag |
| LDFLAGS | description |
|---|---|
-Wl,[options] | -Wl,은 링커(collect2)에게 gcc 거치지 않고 바로 전해주는 옵션. 뒤에 [options]에 링크 옵션을 넣어준다. |
-Wl,-soname,lib[name].so.1 | 생성하려는 shared library의 soname을 뒤에 지어준다. soname에는 major 버전만 명시 |
major.minor.release nojpeg| Naming Convention | e.g. | Description | 필요할 때 |
|---|---|---|---|
| (static link) | libexample.a | 정적 링크 | 빌드시 호스트 그래서 호스트에 있음, 그래도 sysroot 내 파일 링크 |
| Linker name | libexample.so | 링커가 '-lexample' 과 같이 링킹할 때 사용되는 이름libexample.so -> libexample.so.1 소프트링크 | 빌드시 호스트 그래서 호스트에 있음, 그래도 아래 Soname file을 symlink함 (결국 Target 용 바이너리 파일 링크) |
| Soname | libexample.so.1 | 모든 shared library 가 가지고 있는 soname 'libexample.so.1' 처럼 major 버전 명시 libexample.so.1 -> libexample.so.1.2 소프트링크 | 실행 시 타겟 그래서 sysroot/ 에 있음 |
| Real Name | libexample.so.1.2 | 실제 shared library 파일. 내부에 soname을 갖고 있음 | 실행 시 타겟 그래서 sysroot/에 있음 |

$ readelf -d: soname, rpath 찾기$ readelf -d <library>

$ readelf -d userprog | grep RPATH
0x000000000000000f (RPATH) Library rpath: [/library/path]
$ arm-gnueabi-readelf -a <shared_library> | grep "Shared library"
$ arm-gnueabi-readelf -a <shared_library> | grep "program interpreter"
main program build 시, 위에서 생성한 shared library 링크해서 빌드
$ gcc -L<library Path> main.c -l<library> -o main
-L로 shared library 위치를 알려주어 Compile (LDFLAGS)-l로 shared library link (LDLIBS)컴파일 되어도, shared library는 프로그램 실행 시 dynamic으로 링크 되기 때문에,
링커가 shared library 위치를 못찾을 수 있음!!
-L<path>를 지정 했기 때문에 알아먹었지만, 실행 시는 동적으로 찾음그래서 다음과 같이 shared library 관리함
0. 기본적으로 /lib, /usr/lib, /usr/local/lib는 보고 있음
1. 환경 변수 $LD_LIBRARY_PATH에 등록
2. /etc/ld.so.conf, /etc/ld.so.conf.d/libexample.conf, /etc/ld.so.cache
3. 애초에 컴파일 시, -Wl,-rpath,[path] 등록
export LD_LIBRARY_PATH$ export LD_LIBRARY_PATH="$HOME/somewhere:$LD_LIBRARY_PATH"
/etc/ld.so.conf2-1. /etc/ld.so.conf에 직접 위치 작성 (비추천)
최근에는 ld.so.conf 파일에 아래와 같은 문구 만 있음
include /etc/ld.so.conf.d/*.conf
즉, /etc/ld.so.conf.d/에 있는 내용을 include함
2-2. /etc/ld.so.conf.d/libexample.conf 파일 작성
# libexample.conf
/some/where/library/path
작성 후, /etc/ld.so.cache 를 갱신 하는 명령인 ldconfig 실행
$ ldconfig
-rpath 사용$ gcc -L<library/path> -Wl,-rpath,/library/path userprog.c -l<library> -o userprog
$ readelf -d userprog | grep RPATH
0x000000000000000f (RPATH) Library rpath: [/library/path]
lddldd: 응용 프로그램의 의존성을 체크한다.$ ldd [binary]
.so#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
const char *dlerror(void);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle);
dlopen()#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
| parameter | Description |
|---|---|
*filename | 적재하기 원하는 라이브러리 이름 e.g.: /usr/my/lib/libmyhello.so만약 적재시킬 라이브러리의 이름이 절대경로로 지정되어 있지 않으면, 1. $LD_LIBRARY_PATH에 등록된 디렉터리에 있는지 찾음 2. /etc/ld.so.cache에서 찾음 |
flag | <RTLD_LAZY | RTLD_NOW>1. RTLD_LAZY: 라이브러리의 코드가 실행 시간에 정의되지 않은 심볼을 해결2. RTLD_NOW: dlopen이 실행이 끝나기 전, 라이브러리에 정의되지 않은 심볼 해결 |
| return | Description |
|---|---|
| void | - |
dlerror()#include <dlfcn.h>
const char *dlerror(void);
dleooro(), dlsym(), dlclose(), dlopen() 함수 중 마지막 호출된 함수의 에러 메시지를 돌려준다.dlsym()#include <dlfcn.h>
void *dlsym(void *handle, char *symbol);
dlopen()을 통해 열린 라이브러리를 사용할 수 있도록 심볼 값을 찾아준다.| parameter | Description |
|---|---|
*handle | dlopen()에 의해 반환된 값 |
*symbol | 열린 라이브러리에서 실제로 호출할 함수 이름 |
| return | Description |
|---|---|
| 성공 | dlopen()으로 열린 라이브러리의 호출함수 가리키는 포인터void* 형 인데, 호출함수가 리턴하는 형을 직접 명시하면, 유지보수가 훨씬 수월하다. |
| 실패 | NULL |
import subprocess
# Return type (bits):
# 0 - not elf
# 1 - ELF
# 2 - stripped
# 4 - executable
# 8 - shared library
# 16 - kernel module
def is_elf(path):
exec_type = 0
result = subprocess.check_output(["file", "-b", path], stderr=subprocess.STDOUT).decode("utf-8")
if "ELF" in result:
exec_type |= 1
if "not stripped" not in result:
exec_type |= 2
if "executable" in result:
exec_type |= 4
if "shared" in result:
exec_type |= 8
if "relocatable" in result:
if path.endswith(".ko") and path.find("/lib/modules/") != -1 and is_kernel_module(path):
exec_type |= 16
return (path, exec_type)

readelf를 사용하여 프로그램이 어떤 라이브러리와 링크되었는지 알 수 있다.$ arm-cortex_a8-linux-gnueabihf-readelf -a <binary> | grep "Shared library"

$ arm-cortex_a8-linux-gnueabihf-readelf -a <binary> | grep "program interpreter"

-static을 이용해 빌드 하면 모든 라이브러리를 정적으로 빌드한다.sysroot/usr/libsysroot/lib# ===== sample.c ===== #
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
printf("hello world!\n");
return 0;
}
$ arm-cortex_a8-linux-gnueabihf-gcc sample.c -o sample
$ arm-cortex_a8-linux-gnueabihf-readelf -a ./sample | grep "Shared library"

$ arm-cortex_a8-linux-gnueabihf-readelf -a ./sample | grep "interpreter"

sysroot의 정적 링크할 라이브러리가 어디있는지 알아야한다.sysroot/usr/lib -> libc.asysroot/lib -> libc.so.6arm-gcc -print-sysroot를 사용해 빌드한다.$ export SYSROOT=$(arm-cortex_a8-linux-gnueabihf-gcc -print-sysroot)
$ arm-cortex_a8-linux-gnueabihf-gcc -static ./sample.c -o sample
| Naming Convention | e.g. | Description | 필요할 때 |
|---|---|---|---|
| (static link) | libexample.a | 정적 링크 | 빌드시 호스트 그래서 호스트에 있음, 그래도 sysroot 내 파일 링크 |
| Linker name | libexample.so | 링커가 '-lexample' 과 같이 링킹할 때 사용되는 이름libexample.so -> libexample.so.1 소프트링크 | 빌드시 호스트 그래서 호스트에 있음, 그래도 아래 Soname file을 symlink함 (결국 Target 용 바이너리 파일 링크) |
| Soname | libexample.so.1 | 모든 shared library 가 가지고 있는 soname 'libexample.so.1' 처럼 major 버전 명시 libexample.so.1 -> libexample.so.1.2 소프트링크 | 실행 시 타겟 그래서 sysroot/ 에 있음 |
| Real Name | libexample.so.1.2 | 실제 shared library 파일. 내부에 soname을 갖고 있음 | 실행 시 타겟 그래서 sysroot/에 있음 |

$ objcopy --only-keep-debug ./hello ./hello.dbg (hello에서 keep debug 심볼만 가진 hello.dbg 파일 생성)

3. objcopy --add-gnu-debuglink 로 debug링크를 원본 hello 〈----〉 hello.dbg 연결
$ objcopy --add-gnu-debuglink ./hello.dbg ./hello
strip --remove-section=.comment --remove-section=.note ./hello
./hello: ELF 64-bit LSB shared object, x86-64, \
version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, \
BuildID[sha1]=58a2648fe7bf570c9e0e7bec2d74ae69aa1f0d9d, for GNU/Linux 3.2.0, stripped
./hello: file format elf64-x86-64
./hello
architecture: i386:x86-64, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
start address 0x00000000000027e0
Program Header:
PHDR off 0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3
filesz 0x0000000000000268 memsz 0x0000000000000268 flags r--
INTERP off 0x00000000000002a8 vaddr 0x00000000000002a8 paddr 0x00000000000002a8 align 2**0
filesz 0x000000000000001c memsz 0x000000000000001c flags r--
LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**12
filesz 0x0000000000001540 memsz 0x0000000000001540 flags r--
LOAD off 0x0000000000002000 vaddr 0x0000000000002000 paddr 0x0000000000002000 align 2**12
filesz 0x0000000000006c71 memsz 0x0000000000006c71 flags r-x
LOAD off 0x0000000000009000 vaddr 0x0000000000009000 paddr 0x0000000000009000 align 2**12
filesz 0x00000000000022e8 memsz 0x00000000000022e8 flags r--
LOAD off 0x000000000000bb10 vaddr 0x000000000000cb10 paddr 0x000000000000cb10 align 2**12
filesz 0x0000000000000570 memsz 0x0000000000000728 flags rw-
DYNAMIC off 0x000000000000bc18 vaddr 0x000000000000cc18 paddr 0x000000000000cc18 align 2**3
filesz 0x00000000000001f0 memsz 0x00000000000001f0 flags rw-
NOTE off 0x00000000000002c4 vaddr 0x00000000000002c4 paddr 0x00000000000002c4 align 2**2
filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off 0x0000000000009ed4 vaddr 0x0000000000009ed4 paddr 0x0000000000009ed4 align 2**2
filesz 0x000000000000034c memsz 0x000000000000034c flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
RELRO off 0x000000000000bb10 vaddr 0x000000000000cb10 paddr 0x000000000000cb10 align 2**0
filesz 0x00000000000004f0 memsz 0x00000000000004f0 flags r--
Dynamic Section:
NEEDED libc.so.6
INIT 0x0000000000002000
FINI 0x0000000000008c64
INIT_ARRAY 0x000000000000cb10
INIT_ARRAYSZ 0x0000000000000008
FINI_ARRAY 0x000000000000cb18
FINI_ARRAYSZ 0x0000000000000008
GNU_HASH 0x0000000000000308
STRTAB 0x0000000000000998
SYMTAB 0x0000000000000350
STRSZ 0x00000000000002f7
SYMENT 0x0000000000000018
DEBUG 0x0000000000000000
PLTGOT 0x000000000000ce08
PLTRELSZ 0x00000000000004f8
PLTREL 0x0000000000000007
JMPREL 0x0000000000001048
RELA 0x0000000000000d78
RELASZ 0x00000000000002d0
RELAENT 0x0000000000000018
FLAGS 0x0000000000000008
FLAGS_1 0x0000000008000001
VERNEED 0x0000000000000d18
VERNEEDNUM 0x0000000000000001
VERSYM 0x0000000000000c90
RELACOUNT 0x0000000000000013
Version References:
required from libc.so.6:
0x0d696913 0x00 06 GLIBC_2.3
0x09691974 0x00 05 GLIBC_2.3.4
0x06969194 0x00 04 GLIBC_2.14
0x0d696914 0x00 03 GLIBC_2.4
0x09691a75 0x00 02 GLIBC_2.2.5
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 00000000000002a8 00000000000002a8 000002a8 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.gnu.build-id 00000024 00000000000002c4 00000000000002c4 000002c4 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.ABI-tag 00000020 00000000000002e8 00000000000002e8 000002e8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 00000048 0000000000000308 0000000000000308 00000308 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000648 0000000000000350 0000000000000350 00000350 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 000002f7 0000000000000998 0000000000000998 00000998 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 00000086 0000000000000c90 0000000000000c90 00000c90 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000060 0000000000000d18 0000000000000d18 00000d18 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rela.dyn 000002d0 0000000000000d78 0000000000000d78 00000d78 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.plt 000004f8 0000000000001048 0000000000001048 00001048 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 0000001b 0000000000002000 0000000000002000 00002000 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000360 0000000000002020 0000000000002020 00002020 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt.got 00000008 0000000000002380 0000000000002380 00002380 2**3
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 000068d2 0000000000002390 0000000000002390 00002390 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 0000000d 0000000000008c64 0000000000008c64 00008c64 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 00000ed3 0000000000009000 0000000000009000 00009000 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame_hdr 0000034c 0000000000009ed4 0000000000009ed4 00009ed4 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .eh_frame 000010c8 000000000000a220 000000000000a220 0000a220 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .init_array 00000008 000000000000cb10 000000000000cb10 0000bb10 2**3
CONTENTS, ALLOC, LOAD, DATA
19 .fini_array 00000008 000000000000cb18 000000000000cb18 0000bb18 2**3
CONTENTS, ALLOC, LOAD, DATA
20 .data.rel.ro 000000f8 000000000000cb20 000000000000cb20 0000bb20 2**5
CONTENTS, ALLOC, LOAD, DATA
21 .dynamic 000001f0 000000000000cc18 000000000000cc18 0000bc18 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .got 000001e8 000000000000ce08 000000000000ce08 0000be08 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .data 00000080 000000000000d000 000000000000d000 0000c000 2**5
CONTENTS, ALLOC, LOAD, DATA
24 .bss 000001b8 000000000000d080 000000000000d080 0000c080 2**5
ALLOC
SYMBOL TABLE:
no symbols
-rpath.dynamic Section of the ELF executable or shlibs, with the type DT_RPATH, called the DT_RPATH attribute. it can be sorted there at link time by the linker. tools suchas chrpath and patchelfDT_RPATH dynamic section attribute of the binary if present and the DT_RUNPATH attibute does not exists.LD_LIBRARY_PATH, unless the executable is a setuid/setgid binary, in which case it ignored. LD_LIBRARY_PATH can be overridden by calling the dynamic linker with the option --library-path (e.g., /lib/ld-linux.so.2 --library-path $HOME/mylibs myprogram)DT_RUNPATH dynamic section attribute of the binary if present.ldconfig cache file (often located at /etd/ld.so.cache) which contains a compiled list of candidate libraries previously found in the augmented library path (set by /etd/ld.so.conf). if, however, the binary was linked with the -z nodefaultlib linker option, libraries in the default library paths are skipped./lib, and then /usr/lib. if the binary was linked with the -z nodefaultlib linker option, this step is skipped..so를 사용하기 위해서는 명시적으로 dlopen을 이용 혹은 해당 라이브러리의 이름만 링크되어 있는 라이브러리들을 이용해서 실제 자동적으로 실행시에 해당 .so가 로드되도록 하는 방법
.so를 사용하기 위해서 경로 지정에 보통 두가지 방법 사용
1. /etc/ld.so.conf 수정해 /sbin/ldconfig를 이용해 전역적으로 지정하는 방법
2. LD_LIBRARY_PATH를 셋팅해 해당 유저만 사용
자 근데 위에 2가지 방법 모두 문제가 있다. 사용자가 .so의 위치를 어떻게든 지정해야한다.
이는 배포 시 생각보다 큰 문제이다.
-rpath는 프로그램이 빌드될 때, 자동적으로 .so의 위치를 부여 (절대/상대 경로 이용가능)
-rpath .so 경로 -Wl
.so 파일을 찾는다.