ElfN_Addr Unsigned program address, uintN_t
ElfN_Off Unsigned file offset, uintN_t
ElfN_Section Unsigned section index, uint16_t
ElfN_Versym Unsigned version symbol information, uint16_t
Elf_Byte unsigned char
ElfN_Half uint16_t
ElfN_Sword int32_t
ElfN_Word uint32_t
ElfN_Sxword int64_t
ElfN_Xword uint64_t
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
ElfN_Addr e_entry;
ElfN_Off e_phoff;
ElfN_Off e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} ElfN_Ehdr;
16바이트의 크기로 이루어져 있다.
파일을 어떤 식으로 해석해야 하는지에 대한 정보가 담겨 있다.
\x7f\x45\x4c\x46
으로 이루어져 있다.현재 파일의 타입을 나타낸다.
요구되는 아키텍쳐에 대한 정보가 담겨있다.
파일 버전? 을 의미한다.
프로그램의 시작 지점의 virtual address를 의미
만약 시작 지점이 없다면 0으로 세팅
현재 파일에서 program header table의 시작지점 offset을 의미
program header table이 없다면 0으로 세팅.
현재 파일에서 section header table의 시작지점 offset을 의미
section header table이 없다면 0으로 세팅.
processor-specific 플래그 값들이 저장된다.
ELF header의 사이즈를 나타낸다.
program header table의 한 entry의 사이즈
program header table의 총 entry 수
section header table의 한 entry 사이즈
section header table의 총 entry 수
각 section의 name에 대한 string을 담고 있는 section name string table이 있는데, 이 table 또한 하나의 section으로써 존재한다.
이 때, 이 table이 위치해 있는 section header table에서의 index 값을 의미한다.
program header table의 각 entry들은 다음과 같은 구조를 가진다.
typedef struct {
uint32_t p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} Elf32_Phdr;
typedef struct {
uint32_t p_type;
uint32_t p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
} Elf64_Phdr;
어떤 종류의 세그먼트인지, 혹은 어떤 식으로 해석해야 하는지에 대한 정보가 저장된다.
segment의 첫 바이트가 파일의 처음으로부터 얼마나 떨어져 있는지 (offset)의 값.
segment의 첫 바이트가 메모리상에서 어떤 virtual address에서 시작하는지의 값.
physical address가 사용되는 경우 사용된다. p_vaddr와 의미상 동등하다.
segment의 file image의 크기를 나타낸다.
segment의 memory image의 크기를 나타낸다.
해당 segment의 권한 설정을 나타낸다.
loadable process segment는 p_vaddr와 p_offset을 page size로 나눈 나머지가 동일해야 함.
0이나 1은 alignment가 필요없다는 뜻이다.
나머지 경우 : p_align은 양수이면서, 2의 제곱수이면서, p_vaddr와 p_offset은 p_align으로 나눈 나머지가 동일해야 한다.
typedef struct {
uint32_t sh_name;
uint32_t sh_type;
uint32_t sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
uint32_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint32_t sh_addralign;
uint32_t sh_entsize;
} Elf32_Shdr;
typedef struct {
uint32_t sh_name;
uint32_t sh_type;
uint64_t sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
uint64_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint64_t sh_addralign;
uint64_t sh_entsize;
} Elf64_Shdr;
section의 name을 의미하는 멤버변수이다.
section header string table에서의 index 값이 저장된다.
section의 내용과 구성이 어떻게 되어있는지 알려준다.
null-terminated character sequence (string)를 담고 있다.
symbol이나 section name을 표시하기 위해 쓰인다.
첫 byte (index zero)는 null byte, 마지막 byte 또한 null byte이다.
각 symbol의 이름, 종류, 크기 위치 등의 정보들이 저장되어 있는 테이블이다.
typedef struct {
uint32_t st_name;
Elf32_Addr st_value;
uint32_t st_size;
unsigned char st_info;
unsigned char st_other;
uint16_t st_shndx;
} Elf32_Sym;
typedef struct {
uint32_t st_name;
unsigned char st_info;
unsigned char st_other;
uint16_t st_shndx;
Elf64_Addr st_value;
uint64_t st_size;
} Elf64_Sym;
symbol string table의 index 값이 저장된다.
값이 0이 아니라면, string table index를 통해서 symbol의 이름을 찾을 수 있다.
-> string table의 0번째 index의 값이 null인 이유.
symbol의 값이 저장된다.
symbol의 size값이 저장된다.
size가 없거나 unknown 이라면 0이 저장된다.
symbol의 type과 binding 특성이 저장된다. (둘 중 하나가 아니라 둘 다 저장된다.)
type
binding
아래의 매크로들로 binding과 type field를 추출해낼 수 있다.
symbol의 visibility를 정의한다.
-> STV_DEFAULT와 STV_PROTECTED의 차이 : 당연히 local symbol은 볼 수 없다. 하지만 global / weak symbol은 둘 다 볼 수 있는데, 대신 참조가 preempt되지 않는다. 즉, 같은 이름의 다른 module에서 정의된 2개의 전역변수가 있는 경우, 현재 module에서 정의된 전역변수만을 참조한다는 뜻이다.
visibility type을 추출하는 매크로
모든 symbol table entry는 몇 개의 section과 연관되어 있다.
이 연관된 section header index 값이 저장된다.
typedef struct {
Elf32_Addr r_offset;
uint32_t r_info;
} Elf32_Rel;
typedef struct {
Elf64_Addr r_offset;
uint64_t r_info;
} Elf64_Rel;
typedef struct {
Elf32_Addr r_offset;
uint32_t r_info;
int32_t r_addend;
} Elf32_Rela;
typedef struct {
Elf64_Addr r_offset;
uint64_t r_info;
int64_t r_addend;
} Elf64_Rela;
relocation action을 취해야 할 location을 의미한다.
두 가지의 정보가 담겨 있다.
다음 매크로로 각각을 얻을 수 있다.
ELF32_R_SYM(info) ((info) >> 8)
ELF32_R_TYPE(info) ((unsigned char)(info))
ELF64_R_SYM(info) ((info) >> 32)
ELF64_R_TYPE(info) ((ELF64_Word)(info))
relocation reference를 찾을 때 더해지는 constant 값을 의미한다.
의문점
relocated types는 아키텍쳐마다 굉장히 다르고 다양하다.
relocation type 마다 relocated value가 계산되는 방식이 다르다.
굉장히 많다..
대충 이런 방식으로 relocation이 계산된다는 것만 이해하고 넘어가면 충분할 듯.
정의 : 여러 object file을 합쳐서 하나의 shared library나 executable을 만드는 과정.
컴파일이 끝나면 링킹이 완료된다.
내용은 간단하다. 관련된 여러 object 파일들을 relocation을 적용시켜 하나의 binary로 만든다.
물론 그만큼 binary의 크기는 커질 수 있다.
시스템에 의해 load time에 링킹이 완료된다.
shared library를 통해 dynamically linked executable이 생성된다.
output에는 executable의 code와 shared libraries의 name이 들어가게 된다.
https://velog.io/@junttang/SP-7.1-Fundamentals-of-Linking
https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
https://deepfield.blog/kr/ctf/basic/elf%20%ED%8C%8C%EC%9D%BC%20%ED%98%95%EC%8B%9D%EC%97%90%EC%84%9C%20%EC%9E%AC%EB%B0%B0%EC%B9%98(relocation),%20%EB%A7%81%ED%82%B9(linking)%20%EA%B9%8C%EC%A7%80/
https://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries/
https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/