본문 바로가기
임베디드 개발/TMS320F2838x (C28x)

CCS ] .out(ELF) 파일에서 섹션 정보 파싱하기

by eteo 2024. 12. 7.

 

 

 

CCS에서 프로젝트를 빌드하면 .out 확장자의 ELF 형식 실행파일이 생성된다. 해당 파일을 파싱하여 섹션 정보를 획득해보자.

 

 

 

 

 

CCS 폴더 경로에서 주운 elf.h 파일, 여기서 함수 원형은 무시하고 구조체 정의랑 매크로 상수만 활용하자. 근데 또 여기엔 Elf32_Phdr 정보가 없어서 다른데서 구해왔다.

 

/*
 *  ======== elf.h ========
 */
#ifndef ELF_
#define ELF_    1

/*
 *  ======== Elf32_Handle ========
 */
typedef struct Elf32_Object *Elf32_Handle;

typedef Bits32  Elf32_Addr;
typedef Bits16  Elf32_Half;
typedef Bits32  Elf32_Off;
typedef Bits32  Elf32_Word;
typedef Int32   Elf32_Sword;

/*
 *  ======== Elf32_Ehdr ========
 */
#define Elf32_EI_NIDENT 16
typedef struct {
    unsigned char e_ident[Elf32_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_type values */
#define Elf32_ET_NONE 0         /* No file type */
#define Elf32_ET_REL 1          /* Relocatable file */
#define Elf32_ET_EXEC 2         /* Executable file */
#define Elf32_ET_DYN 3          /* Shared object file */
#define Elf32_ET_CORE 4         /* Core file */
#define Elf32_ET_LOPROC 0xff00  /* Processor-specific */
#define Elf32_ET_HIPROC 0xffff  /* Processor-specific */

/* e_machine values */
#define Elf32_ET_NONE 0         /* No machine */
#define Elf32_EM_M32 1          /* AT&T WE 32100 */
#define Elf32_EM_SPARC 2        /* SPARC */
#define Elf32_EM_386 3          /* Intel Architecture */
#define Elf32_EM_68K 4          /* Motorola 68000 */
#define Elf32_EM_88K 5          /* Motorola 88000 */
#define Elf32_EM_860 7          /* Intel 80860 */
#define Elf32_EM_MIPS 8         /* MIPS RS3000 Big-Endian */
#define Elf32_EM_MIPS_RS4_BE 10 /* MIPS RS4000 Big-Endian */

#define Elf32_EM_ARM 40         /* Advanced RISC Machines ARM */

#define Elf32_EM_MSP430   105   /* Texas Instruments msp430*/
#define Elf32_EM_TI_C6000 140   /* Texas Instruments TMS320C6000 DSP family */
#define Elf32_EM_TI_C2000 141   /* Texas Instruments TMS320C2000 DSP family */
#define Elf32_EM_TI_C5500 142   /* Texas Instruments TMS320C55x DSP family */
#define Elf32_EM_TI_T16   143   /* Texas Instruments T16 family */

/* e_version values */
#define Elf32_EV_NONE 0         /* Invalid version */
#define Elf32_EV_CURRENT 1      /* Current version */

/* elf ids (http://uw714doc.sco.com/en/SDK_cprog/OF_ELFID.html) */
#define Elf32_EI_MAG0 0         /* File identification */
#define Elf32_EI_MAG1 1         /* File identification */
#define Elf32_EI_MAG2 2         /* File identification */
#define Elf32_EI_MAG3 3         /* File identification */
#define Elf32_EI_CLASS 4        /* File class */
#define Elf32_EI_DATA 5         /* Data encoding */
#define Elf32_EI_VERSION 6      /* File version */
#define Elf32_EI_OSABI 7        /* Operating system / ABI */
#define Elf32_EI_ABIVERSION 8   /* ABI version */
#define Elf32_EI_PAD 9          /* Start of padding bytes */
#define Elf32_EI_NIDENT 16      /* Size of e_ident[] */

typedef enum {
    ELFDATANONE = 0,  /* Invalid data encoding */
    ELFDATA2LSB = 1,  /* Little-endian data    */
    ELFDATA2MSB = 2   /* Big-endian data       */
} Elf32_EI_Data;

/* EI_CLASS values */
#define Elf32_ELFCLASSNONE  0   /* Invalid class */
#define Elf32_ELFCLASS32    1   /* 32-bit objects */
#define Elf32_ELFCLASS64    2   /* 64-bit objects */

/* EI_DATA values */
#define Elf32_ELFDATANONE 0     /* Invalid data encoding */
#define Elf32_ELFDATA2LSB 1     /* LSB occupies the lowest address */
#define Elf32_ELFDATA2MSB 2     /* MSB occupies the lowest address */

/* elf magic numbers */
#define Elf32_ELFMAG0 0x7f      /* e_ident[EI_MAG0] */
#define Elf32_ELFMAG1 'E'       /* e_ident[EI_MAG1] */
#define Elf32_ELFMAG2 'L'       /* e_ident[EI_MAG2] */
#define Elf32_ELFMAG3 'F'       /* e_ident[EI_MAG3] */

#define Elf32_SHN_UNDEF 0
#define Elf32_SHN_LORESERVE 0xff00
#define Elf32_SHN_LOPROC 0xff00
#define Elf32_SHN_HIPROC 0xff1f
#define Elf32_SHN_ABS 0xfff1
#define Elf32_SHN_COMMON 0xfff2
#define Elf32_SHN_HIRESERVE 0xffff

/*
 *  ======== Elf32_Shdr ========
 */
typedef struct {
    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 types */
#define Elf32_SHT_NULL 0
#define Elf32_SHT_PROGBITS 1
#define Elf32_SHT_SYMTAB 2
#define Elf32_SHT_STRTAB 3
#define Elf32_SHT_RELA 4
#define Elf32_SHT_HASH 5
#define Elf32_SHT_DYNAMIC 6
#define Elf32_SHT_NOTE 7
#define Elf32_SHT_NOBITS 8
#define Elf32_SHT_REL 9
#define Elf32_SHT_SHLIB 10
#define Elf32_SHT_DYNSYM 11
#define Elf32_SHT_LOPROC 0x70000000
#define Elf32_SHT_HIPROC 0x7fffffff
#define Elf32_SHT_LOUSER 0x80000000
#define Elf32_SHT_HIUSER 0xffffffff

/* section flags */
#define Elf32_SHF_WRITE     (1 << 0)    /* Writable */
#define Elf32_SHF_ALLOC     (1 << 1)    /* Occupies memory during execution */
#define Elf32_SHF_EXECINSTR (1 << 2)    /* Executable */
#define Elf32_SHF_MASKPROC  0xf0000000  /* Processor-specific */

/*
 *  ======== Elf32_create ========
 */
extern Elf32_Handle Elf32_create(String fileName, String *status);

/*
 *  ======== Elf32_delete ========
 */
extern Void Elf32_delete(Elf32_Handle elf);

/*
 *  ======== Elf32_getEhdr ========
 */
extern String Elf32_getEhdr(Elf32_Handle elf, Elf32_Ehdr *ehdr);

/*
 *  ======== Elf32_getShdr ========
 */
extern String Elf32_getShdr(Elf32_Handle elf, Int sNum, Elf32_Shdr *sHdr);

/*
 *  ======== Elf32_getShdrByName ========
 */
extern String Elf32_getShdrByName(Elf32_Handle elf, String name,
    Elf32_Shdr *sHdr, Int *sNum);

/*
 *  ======== Elf32_getSName ========
 */
extern String Elf32_getSName(Elf32_Handle elf, Elf32_Shdr *shdr);

/*
 *  ======== Elf32_getSize ========
 */
extern String Elf32_getSize(Elf32_Handle elf, Long *size);

/*
 *  ======== Elf32_read ========
 */
extern Bool Elf32_read(Elf32_Handle elf, Elf32_Off off, Char *buf, SizeT n);

/*
 *  ======== Elf32_readSect ========
 */
extern Char *Elf32_readSect(Elf32_Handle elf, Int sNum);

/*
 *  ======== Elf32_redefineSect ========
 */
extern String Elf32_redefineSect(Elf32_Handle elf, Int sNum, String fileName);

/*
 *  ======== Elf32_writeSect ========
 */
extern Int Elf32_writeSect(Elf32_Handle elf, Int sNum, Char *buf, Int len);

#endif

 

 

 

 

 

 

ELF 파일의 구조

 

ELF 파일의 주요 구성 요소는 다음과 같다.

+------------------+
| ELF Header       |   <- 파일의 기본 정보 (파일 타입, 아키텍처, 엔디안 등)
+------------------+
| Program Headers  |   <- 실행 가능한 파일에서 메모리에 로드할 때 사용 (옵션)
+------------------+
| Section Headers  |   <- 각 섹션의 메타데이터 (섹션의 위치, 크기 등)
+------------------+
| .text Section    |   <- 실행 코드 (기계어)
+------------------+
| .data Section    |   <- 초기화된 전역/정적 변수
+------------------+
| .bss Section     |   <- 초기화되지 않은 변수 (런타임 시 0으로 초기화)
+------------------+
| .rodata Section  |   <- 읽기 전용 데이터 (문자열 리터럴 등)
+------------------+
| .shstrtab Section|   <- 섹션 이름 테이블
+------------------+
| Other Sections...|   <- 기타 추가 정보가 저장된 섹션들
+------------------+

 

  • ELF 헤더 (ELF Header) : ELF 파일의 전반적인 정보를 담고 있는 헤더로 파일의 타입, 아키텍처, 엔디안 방식, 프로그램 헤더와 섹션 헤더 테이블의 위치 등을 정의한다.
  • 프로그램 헤더 (Program Header) : 실행 가능한 ELF 파일에서 사용되며 메모리에 로드할 세그먼트 정보를 담고있다.
  • 섹션 헤더 (Section Header) : ELF 파일의 각 섹션(예: .text, .data)에 대한 메타데이터를 담고 있으며, 파일 내에서 섹션이 위치한 오프셋과 크기, 타입 등을 정의한다.
  • 섹션 내용 (Sections) : 실제 코드와 데이터가 저장된 영역으로, 프로그램이 실행될 때 메모리에 로드되는 데이터이다.

 

✔️ ELF 헤더의 경우 파일의 시작 위치에서 고정된 52바이트(32비트 ELF의 경우)를 차지하지만 그 외의 구성요소는 항상 일정한 위치에 존재하지 않으며, ELF 헤더를 읽어 파일 오프셋 값을 확인해 그 위치를 알아낼 수 있다.

 

실제로 Program Header의 경우 ELF Header에서 e_phoff가 가리키는 위치에 있고, Section Header의 경우 ELF Header에서 e_shoff가 가리키는 위치에 있다. 그리고 Section의 경우 Section Header에서 sh_offset가 가리키는 위치에 존재한다.

 

 

 

 

 

파싱 결과

  • ELF Header

 

 

  • Program Header

 

  • Type : 세그먼트의 타입
  • Offset : 파일 내에서 세그먼트가 시작하는 오프셋
  • VirtAddr : 세그먼트가 로드될 메모리의 가상 주소
  • PhysAddr : 세그먼트가 로드될 메모리의 물리 주소
  • FileSize : ELF 파일 내에서 세그먼트의 킉
  • MemSize : 메모리 내에서 세그먼트의 크기
  • Flag : 세그먼트의 플래그 (읽기, 쓰기, 실행 등)
  • Align : 메모리와 파일에서의 정렬 기준

 

 

  • Section Header

 

0번째 섹션은 이름이 비어있는데 NULL 타입을 가지며, ELF 형식의 규칙에 따라 유효한 섹션의 인덱스가 1로 시작하게끔 하기 위해 0번째 섹션으로 자리 잡고 있을 뿐 아무런 정보를 담고 있지 않는 빈 섹션이다.

  • Num: 각 섹션의 번호(인덱스)
  • Name: 섹션의 이름
  • Addr: 메모리에 로드될 때 섹션의 시작 주소
  • Off: ELF 파일 내에서 해당 섹션이 시작하는 파일 오프셋
  • Size: 섹션의 크기
  • Type: 섹션의 타입
    • PROGBITS : 실제 데이터를 포함한 섹션 (.text, .data, .rodata)
    • NOBITS : 메모리 공간만 차지하며 ELF 파일 내 실제 데이터가 저장되지 않는 섹션 (.bss)
    • STRTAB : 문자열 테이블로 심볼 이름을 저장하는 테이블과 섹션 이름을 저장하는 테이블이 이에 해당한다.(.strtab, .shstrtab)
    • SYMTAB : 프로그램 내 변수, 함수, 객체에 대한 정보를 담고 있다. 심볼 테이블은 링킹 과정에서 다른 오브젝트 파일에 정의된 심볼을 찾고 연결하는데 사용되며, 디버거가 심볼 이름을 메모리 주소로 매핑할 수 있도록 해준다. (.systab )

 

 

 

위 파싱 결과 출력에 사용한 소스코드

#include "elf.h"
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>

using namespace std;

void print_elf_header(const Elf32_Ehdr& ehdr) {

	cout << "ELF Header:" << endl;
	// ELF Magic
	cout << "  Magic:   ";
	for (int i = 0; i < Elf32_EI_NIDENT; ++i) {
		cout << hex << setw(2) << setfill('0') << static_cast<int>(ehdr.e_ident[i]) << " ";
	}
	cout << dec << endl;

	// ELF 클래스, 데이터, 버전, OS ABI 등
	cout << left;
	cout << "  Class:                             " << (ehdr.e_ident[Elf32_EI_CLASS] == 1 ? "ELF32" : "ELF64") << endl;
	cout << "  Data:                              " << (ehdr.e_ident[Elf32_EI_DATA] == 1 ? "2's complement, little-endian" : "2's complement, big-endian") << endl;
	cout << "  Version:                           " << static_cast<int>(ehdr.e_ident[Elf32_EI_VERSION]) << endl;
	cout << "  OS/ABI:                            " << static_cast<int>(ehdr.e_ident[Elf32_EI_OSABI]) << endl;
	cout << "  ABI Version:                       " << static_cast<int>(ehdr.e_ident[Elf32_EI_ABIVERSION]) << endl;

	// ELF 타입, 아키텍처, 버전, 엔트리 포인트, 오프셋 정보 등
	string type_str;
	switch (ehdr.e_type) {
	case 0: type_str = "No file type"; break;
	case 1: type_str = "Relocatable"; break;
	case 2: type_str = "Executable"; break;
	case 3: type_str = "Shared object"; break;
	case 4: type_str = "Core file"; break;
	default: type_str = "Unknown"; break;
	}

	cout << "  Type:                              " << type_str << endl;

	string machine_str;
	switch (ehdr.e_machine) {
	case 0: machine_str = "No machine"; break;
	case 3: machine_str = "Intel Architecture"; break;
	case 40: machine_str = "Advanced RISC Machines ARM"; break;
	case 62: machine_str = "AMD x86-64 architecture"; break;
	case 105: machine_str = "Texas Instruments msp430"; break;
	case 140: machine_str = "Texas Instruments TMS320C6000 DSP family"; break;
	case 141: machine_str = "Texas Instruments TMS320C2000 DSP family"; break;
	case 142: machine_str = "Texas Instruments TMS320C55x DSP family"; break;
	case 143: machine_str = "Texas Instruments T16 family"; break;
	default: machine_str = "Unknown"; break;
	}

	cout << "  Machine:                           " << machine_str << endl;
	cout << "  Version:                           0x" << hex << ehdr.e_version << dec << endl;
	cout << "  Entry point address:               0x" << hex << ehdr.e_entry << dec << endl;
	cout << "  Start of program headers:          " << ehdr.e_phoff << " (bytes into file)" << endl;
	cout << "  Start of section headers:          " << ehdr.e_shoff << " (bytes into file)" << endl;
	cout << "  Flags:                             0x" << hex << ehdr.e_flags << dec << endl;
	cout << "  Size of this header:               " << ehdr.e_ehsize << " (bytes)" << endl;
	cout << "  Size of program headers:           " << ehdr.e_phentsize << " (bytes)" << endl;
	cout << "  Number of program headers:         " << ehdr.e_phnum << endl;
	cout << "  Size of section headers:           " << ehdr.e_shentsize << " (bytes)" << endl;
	cout << "  Number of section headers:         " << ehdr.e_shnum << endl;
	cout << "  Section header string table index: " << ehdr.e_shstrndx << endl;

	cout << setfill(' ') << endl;
}

void print_program_headers(ifstream& file, const Elf32_Ehdr& ehdr) {

	file.seekg(ehdr.e_phoff, ios::beg);
	cout << "Program Headers:\n";
	cout << "Type           Offset    VirtAddr  PhysAddr  FileSize  MemSize  Flag  Align\n";
	cout << string(75, '-') << endl;

	for (int i = 0; i < ehdr.e_phnum; ++i) {
		Elf32_Phdr phdr;
		file.read(reinterpret_cast<char*>(&phdr), sizeof(phdr));

		string type_str;
		switch (phdr.p_type) {
		case 0: type_str = "NULL"; break;
		case 1: type_str = "LOAD"; break;
		case 2: type_str = "DYNAMIC"; break;
		case 3: type_str = "INTERP"; break;
		case 4: type_str = "NOTE"; break;
		case 5: type_str = "SHLIB"; break;
		case 6: type_str = "PHDR"; break;
		case 0x6474e550: type_str = "GNU_EH_FRAME"; break;
		case 0x6474e551: type_str = "GNU_STACK"; break;
		case 0x6474e552: type_str = "GNU_RELRO"; break;
		default: type_str = "UNKNOWN"; break;
		}

		cout << left << setw(15) << type_str
			<< "0x" << setw(8) << hex << phdr.p_offset
			<< "0x" << setw(8) << phdr.p_vaddr
			<< "0x" << setw(8) << phdr.p_paddr
			<< "0x" << setw(8) << phdr.p_filesz
			<< "0x" << setw(6) << phdr.p_memsz;

		cout << " ";
		cout << ((phdr.p_flags & 0x4) ? 'R' : ' ');
		cout << ((phdr.p_flags & 0x2) ? 'W' : ' ');
		cout << ((phdr.p_flags & 0x1) ? 'E' : ' ');
		cout << "  ";

		cout << " 0x" << hex << phdr.p_align << dec << '\n';
	}
	cout << endl;
}

string get_section_name(ifstream& file, const Elf32_Shdr& shstrtab, uint32_t name_offset) {
	file.seekg(static_cast<streamoff>(shstrtab.sh_offset) + static_cast<streamoff>(name_offset), ios::beg);
	string name;
	char ch;
	while (file.get(ch) && ch != '\0') {
		name += ch;
	}
	return name;
}

void print_section_headers(ifstream& file, const Elf32_Ehdr& ehdr) {

	// 섹션 이름 테이블(.shstrtab) 섹션 읽기
	file.seekg(ehdr.e_shoff + ehdr.e_shstrndx * sizeof(Elf32_Shdr), ios::beg);
	Elf32_Shdr shstrtab;
	file.read(reinterpret_cast<char*>(&shstrtab), sizeof(Elf32_Shdr));

	cout << "Section Headers:\n";
	cout << left
		<< setw(5) << "Num"
		<< setw(22) << "Name"
		<< setw(12) << "Addr"
		<< setw(10) << "Off"
		<< setw(10) << "Size"
		<< setw(10) << "Type" << endl;
	cout << string(65, '-') << endl;

	// 모든 섹션 헤더를 반복하며 섹션 정보를 출력
	for (int i = 0; i < ehdr.e_shnum; i++) {
		Elf32_Shdr shdr;
		file.seekg(ehdr.e_shoff + i * sizeof(Elf32_Shdr), ios::beg);
		file.read(reinterpret_cast<char*>(&shdr), sizeof(Elf32_Shdr));

		string section_name = get_section_name(file, shstrtab, shdr.sh_name);

		cout << left << dec
			<< setw(5) << i
			<< setw(22) << section_name
			<< setw(12) << hex << shdr.sh_addr
			<< setw(10) << hex << shdr.sh_offset
			<< setw(10) << hex << shdr.sh_size;

		string type_str;
		switch (shdr.sh_type) {
		case Elf32_SHT_NULL: type_str = "NULL"; break;
		case Elf32_SHT_PROGBITS: type_str = "PROGBITS"; break;
		case Elf32_SHT_SYMTAB: type_str = "SYMTAB"; break;
		case Elf32_SHT_STRTAB: type_str = "STRTAB"; break;
		case Elf32_SHT_RELA: type_str = "RELA"; break;
		case Elf32_SHT_HASH: type_str = "HASH"; break;
		case Elf32_SHT_DYNAMIC: type_str = "DYNAMIC"; break;
		case Elf32_SHT_NOTE: type_str = "NOTE"; break;
		case Elf32_SHT_NOBITS: type_str = "NOBITS"; break;
		case Elf32_SHT_REL: type_str = "REL"; break;
		case Elf32_SHT_SHLIB: type_str = "SHLIB"; break;
		case Elf32_SHT_DYNSYM: type_str = "DYNSYM"; break;
		case Elf32_SHT_LOPROC: type_str = "LOPROC"; break;
		case Elf32_SHT_HIPROC: type_str = "HIPROC"; break;
		case Elf32_SHT_LOUSER: type_str = "LOUSER"; break;
		case Elf32_SHT_HIUSER: type_str = "HIUSER"; break;
		default: type_str = "UNKNOWN"; break;
		}

		cout << setw(10) << type_str << endl;
	}
}

int main(int argc, char* argv[]) {

	if (argc < 2) {
		cerr << "Usage: " << argv[0] << " <elf_file>" << endl;
		return 1;
	}

	const string filename = argv[1];

	ifstream file(filename, ios::in | ios::binary);
	if (!file.is_open()) {
		cerr << "Failed to open file: " << filename << endl;
		return 1;
	}

	// ELF 헤더 읽기
	Elf32_Ehdr ehdr;
	file.read(reinterpret_cast<char*>(&ehdr), sizeof(ehdr));
	if (!file) {
		cerr << "Failed to read ELF header" << endl;
		file.close();
		return 1;
	}

	// ELF Magic 확인
	if (ehdr.e_ident[Elf32_EI_MAG0] != Elf32_ELFMAG0 ||
		ehdr.e_ident[Elf32_EI_MAG1] != Elf32_ELFMAG1 ||
		ehdr.e_ident[Elf32_EI_MAG2] != Elf32_ELFMAG2 ||
		ehdr.e_ident[Elf32_EI_MAG3] != Elf32_ELFMAG3) {
		cerr << "This is not a valid ELF file." << endl;
		file.close();
		return 1;
	}

	// ELF 헤더 출력
	print_elf_header(ehdr);

	// 프로그램 헤더 출력
	print_program_headers(file, ehdr);

	// 섹션 헤더 출력
	print_section_headers(file, ehdr);

	file.close();

	return 0;
}