ELF 파일 형식에 대한 심층적 이해(1)

머리말

ELF는 Executable and Linking Format의 약자로 Linux 플랫폼의 일반적인 바이너리 파일 형식입니다. Android NDK 개발에서는 다음과 같은 거의 모든 ELF를 처리합니다.

  • c/c++ 파일에서 컴파일된 .o(또는 .obj) 파일은 ELF 형식의 파일입니다.
  • 동적 라이브러리(.so) 파일과 실행 파일도 ELF 파일입니다.
  • ELF는 동적 라이브러리 문자열 삭제, 동적 라이브러리 패킹, 동적 라이브러리 복구 등과 분리할 수 없습니다.

저자가 ELF 형식을 배울 때 ELF 형식에 대한 이해를 심화하기 위해 ELF 파일을 각각 C와 Java로 파싱하는 Android 프로젝트를 만들었으며 관심있는 파트너는 링크를 직접 확인할 수 있습니다.

ELF 파일 형식

앞에서 언급했듯이 ELF는 Executable and Linking Format의 약자입니다. 이름의 Executable및는 LinkingELF에 두 가지 중요한 기능이 있음을 나타냅니다.

  • 실행 가능: 실행 가능. ELF 파일은 프로그램의 실행(Execution) 프로세스에 참여하게 됩니다. 바이너리 프로그램의 작동과 동적 라이브러리 .so 파일의 로딩을 포함합니다.

  • 연결: 연결 가능. ELF 파일은 컴파일 및 링크 프로세스에 참여합니다.

    ELF 파일 보기

위의 두 가지 보기는 ELF 형식을 두 가지 각도(View)에서 분석할 수 있음을 나타냅니다. 개인적으로 이 두 가지 보기는 ELF 형식이 어떻게 배치되는지를 제공할 뿐이라고 생각 table하지만 실제로는 Linking View있는 그대로 Program Header TableELF 헤더 파일에 의해 결정됩니다 program_table_element. 즉 Program Header Table, 개념적 의미이며 실제로는 존재하지 않습니다. 물론 Section Header table마찬가지입니다.

ELF 헤더

ELF 파일은 64비트 및 32비트 CPU 명령 아키텍처를 지원하고 ELF는 더 긴 필드 유형(32비트에 비해)을 정의하여 64비트를 지원합니다. 다음은 주로 32비트 ELF 파일 규격 분석을 통한 것이다.

#define EI_NIDENT 16
typedef struct elf32_hdr {
    
    
  unsigned char e_ident[EI_NIDENT];
  Elf32_Half e_type;
  Elf32_Half e_machine;
  Elf32_Word e_version;
  Elf32_Addr e_entry;
  Elf32_Off e_phoff;
  Elf32_Off e_shoff;
  Elf32_Word e_flags;
  Elf32_Half e_ehsize;
  Elf32_Half e_phentsize;
  Elf32_Half e_phnum;
  Elf32_Half e_shentsize;
  Elf32_Half e_shnum;
  Elf32_Half e_shstrndx;
} Elf32_Ehdr;

위의 코드 조각은 다음과 같이 구문 분석됩니다.

e_ident[EI_NIDENT]는 e_ident 길이가 16바이트인 배열입니다.

  • e_ident[0-3]의 처음 4바이트는 각각 '0x7f', 'E', 'L', 'F'인 Magic Number를 나타내며 일반적으로 ELF 파일인지 여부를 확인하는 데 사용됩니다.
  • e_ident[EI_CLASS=4]는 ELF 파일이 32비트 파일(값 1)인지 64비트 파일(값 2)인지를 나타냅니다.
  • e_ident[EI_DATA=5]는 ELF 파일에 있는 데이터의 바이트 순서가 little-endian(값 1)인지 big-endian(값 2)인지를 나타냅니다.
  • e_ident[EI_VERSION=5]는 ELF 파일의 버전을 나타내며 일반적으로 값은 1입니다.
  • e_ident[6-15]는 현재 바이트 정렬을 위해 0으로 설정되어 있습니다.

**e_type ** 이 필드의 길이는 2바이트이며 ELF의 유형을 나타냅니다.
e_machine 필드는 길이가 2바이트이며 ELF 파일이 해당하는 CPU 아키텍처를 나타냅니다.

e_version 이 필드는 e_ident[EI_VERSION=5]와 동일한 값을 사용합니다.

e_entry 이 필드는 프로그램 항목의 가상 주소를 나타냅니다. ELF 파일이 실행 파일인 경우 운영 체제는 e_entry 위치로 이동하여 프로그램을 로드한 후 해당 코드를 실행합니다.

e_phoff ph는 프로그램 헤더의 약자입니다. e_phoff는 프로그램 헤더의 첫 번째 요소의 시작 위치를 나타냅니다.(오프셋이 기록됨) 프로그램 헤더의 요소가 연속적이라는 점은 주목할 가치가 있습니다.

e_shoff sh는 섹션 헤더의 약자입니다. e_phoff와 마찬가지로 ELF에 Section이 포함된 경우 이 변수는 파일에서 Section 요소의 시작 위치를 나타냅니다.

e_flags는 프로세서별 플래그를 나타냅니다.

e_ehsize eh는 elf header의 약자로 ELF 파일 헤더의 크기를 나타낸다.

e_phentsize 는 프로그램 헤더의 항목 크기입니다.

e_phnum은 프로그램 헤더 번호를 나타냅니다.

e_shentsize 는 섹션 헤더의 항목 크기입니다.

e_shnum은 섹션 헤더의 수를 나타냅니다.

e_shstrndx는 각 섹션 이름의 문자열 테이블을 연결하고 섹션을 sh_name첨자 인덱스로 사용합니다. 섹션에 이름이 없으면 이 값을 SHN_UNDEF로 설정해야 합니다.

프로그램 헤더

typedef struct elf32_phdr {
    
    
  Elf32_Word p_type;
  Elf32_Off p_offset;
  Elf32_Addr p_vaddr;
  Elf32_Addr p_paddr;
  Elf32_Word p_filesz;
  Elf32_Word p_memsz;
  Elf32_Word p_flags;
  Elf32_Word p_align;
} Elf32_Phdr;

Execution View의 ELF는 Program Header 요소를 포함해야 합니다. 프로그램 헤더의 각 필드는 다음과 같이 구문 분석됩니다.

p_type program_table_element(세그먼트) 유형.
p_offset program_table_element(세그먼트)는 파일의 시작 부분에 있습니다.

p_vaddr 가상 메모리에 로드된 program_table_element(세그먼트)는 상대 메모리 위치로 지정됩니다.
p_paddr은 program_table_element(세그먼트)에 해당하는 물리적 주소를 나타냅니다. 실행 파일 및 동적 라이브러리의 경우 이 값은 의미가 없습니다.
p_filesz는 파일에서 program_table_element(세그먼트)가 차지하는 크기를 나타내며 그 값은 0일 수 있습니다. 세그먼트는 섹션으로 구성되어 있고 일부 섹션은 공간을 차지하지 않기 때문입니다.
p_memsz 메모리에서 program_table_element(세그먼트)가 차지하는 공간, 그 값은 0이 될 수 있습니다.
p_flags 세그먼트 플래그.
p_align program_table_element(세그먼트)가 메모리에 로드된 후 p_align요구 사항에 따라 정렬해야 합니다.

섹션 헤더

typedef struct elf32_shdr {
    
    
  Elf32_Word sh_name;
  Elf32_Word sh_type;
  Elf32_Word sh_flags;
  Elf32_Addr sh_addr;
  Elf32_Off sh_offset;
  Elf32_Word sh_size;
  Elf32_Word sh_link;
  Elf32_Word sh_info;
  Elf32_Word sh_addralign;
  Elf32_Word sh_entsize;
} Elf32_Shdr;

Section Header 요소의 데이터 구조는 위의 코드 스니펫에 표시되어 있습니다.
sh_name 이 변수는 섹션의 이름을 지정합니다. ELF에는 섹션 이름을 저장하는 섹션(섹션 헤더 문자열 테이블 섹션, 약어로 shstrtab)이 있습니다. 여기서 sh_name은 섹션 이름의 문자열을 저장하는 shstrtab의 특정 위치를 가리킵니다(sh_name은 shstrtab의 첨자임을 알 수 있습니다).
sh_type 섹션 유형, 다른 유형의 섹션은 다른 내용을 저장합니다. 섹션의
sh_flags 속성.
sh_addr 섹션이 메모리(실행 프로그램 또는 동적 라이브러리)에 로드되면 sh_addr은 메모리에 로드되어야 하는 위치를 나타냅니다.
sh_offset은 섹션이 파일의 시작 위치에 상대적임을 나타냅니다.
sh_size Section 자체의 크기.

스트링 테이블

위에서 언급한 바와 같이 ELF에는 Section의 이름을 저장하는 Section(Section Header String Table Section, 줄여서 shstrtab)이 있으며 이 Section은 스트링 테이블의 정보를 나타낸다. 개체 파일에서 이러한 문자열은 일반적으로 기호 또는 섹션의 이름입니다. 오브젝트 파일의 다른 부분에서 특정 문자열을 참조해야 하는 경우 문자열 테이블에 있는 문자열의 일련 번호만 제공하면 됩니다.

문자열 테이블의 첫 번째 문자열(숫자 0)은 항상 빈 문자열입니다. 즉 null, 빈 이름 또는 이름 없음을 나타내는 데 사용할 수 있습니다. 따라서 문자열 테이블의 첫 번째 바이트는 \0. 모든 문자열은 로 끝나므로 null문자열 테이블의 마지막 바이트도 이어야 합니다 null.

문자열 테이블은 문자열 없이 비어 있을 수도 있지만 ELF 파일 헤더의 sh_size는 0이어야 합니다.

스트링 테이블

위의 그림에서 알 수 있듯이 완전히 정의된 문자열은 첨자를 통해 참조할 수 있습니다. 즉, \0감싸진 문자열 전체 또는 일부를 참조할 수 있습니다.

요약

이 문서는 주로 ELF 형식 사양을 자세히 설명하기 위해 ELF 형식을 중심으로 진행되며 아직 모든 세부 사항을 다루지는 않았습니다. 주요 목적은 ELF 파일 형식의 대략적인 레이아웃 보기를 먼저 제공하여 나중에 지식 포인트를 더 잘 보완하고 개선할 수 있도록 하는 것입니다. 위의 주요 참고 자료는 elf - ELF(Executable and Linking Format) 파일의 형식 입니다 .

Supongo que te gusta

Origin blog.csdn.net/HongHua_bai/article/details/122288032
Recomendado
Clasificación