ELF file parser

preface

Recently, I chose the elective course of Linux kernel principle. Although the class is relatively short, the content involved can only cover a small part of Linux knowledge, the teacher's level is really high and the knowledge is very in-depth. The small assignment this time is to write C language program under Linux platform to realize the following functions:

Imitate and realize some functions of readelf tool under Linux, and be able to simply analyze ELF executable files. (at least support the - h, - s and - s command options of readelf tool)

About the principle part, I think I don't have the ability to speak as thoroughly and clearly as the bosses, so the reference article link is posted at the end of the article, and I will understand it after reading it.

1.ELF document introduction

In Linux system, an ELF file is mainly used to represent three types of files:

  • Executable file: it is read from the hard disk by the loader in the operating system and loaded into the memory for execution;
  • Target file: read by the linker to generate an executable file or shared library file;
  • Shared library files: during dynamic link, LD Linux So to read;

2.readelf command

-a : --all Show all information,Equivalent to -h -l -S -s -r -d -V -A -I
-h : --file-header display elf Header information at the beginning of the file. 
-l : --program-headers  ;--segments Display program header (segment header) information(If any).  
-S : --section-headers  ;--sections Display section header information(If any).  
-g : --section-groups Display section group information(If any). 
-t : --section-details Show section details(-S of).  
-s : --syms  ;--symbols Displays the items in the symbol table segment, if any. 
-e : --headers Display all header information, equivalent to: -h -l -S 
-n : --notes display note Section (kernel comment). 
-r : --relocs Displays information about relocatable segments. 
-u : --unwind display unwind Segment information. Currently only supported IA64 ELF of unwind Segment information. 
-d : --dynamic Displays information about the dynamic segment. 
-V : --version-info Displays information about the version segment. 
-A : --arch-specific display CPU Architecture information. 
-D : --use-dynamic Use the symbol table in the dynamic segment to display symbols instead of using symbol segments. 
-x <number or name> : --hex-dump=<number or name> Displays the contents of the specified segment in hexadecimal mode. number Specifies the index of the segment in the segment table,Or string to specify the segment name in the file. 
-w[liaprmfFsoR]perhaps
-debugdump[=line,=info,=abbrev,=pubnames,=aranges,
=macro,=frames,=frames-interp,=str,=loc,=Ranges] Displays the contents specified in the debugging section. 
-I : --histogram When the symbol is displayed, the bucket list Histogram of length. 
-v : --version display readelf Version information for. 
-H : --help display readelf Supported command line options. 
-W : --wide Wide line output. 

3. Code implementation

Kakaluoto/ELFReader (github.com)

This time I divided it into five file headers h ; main. cpp ; readelf_ symbol. cpp ; readelf_ Section. cpp ; readelf_ header. cpp

Header file header h

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <elf.h>
#ifndef ELFREADER_HEADER_H
#define ELFREADER_HEADER_H
using namespace std;
void readelf_h(const char* filename);

void readelf_S(const char* filename);

void readelf_s(const char* filename);
#endif //ELFREADER_HEADER_H

Main function main cpp

#include "header.h"

int main(int argc, char* argv[]) {
    if (argc < 2) { exit(0); }
    //argv[0] is the current executable path
    //arv[1] is a parameter, and the parameter is separated by a space
    //argv[2] is the resolved executable name
    if (strcmp(argv[1], "-h") == 0)
        readelf_h(argv[2]);
    else if (strcmp(argv[1], "-S") == 0)
        readelf_S(argv[2]);
    else if (strcmp(argv[1], "-s") == 0)
        readelf_s(argv[2]);
    else
        printf("invalid argument!\n");
    return 0;
}

Imitate the implementation of readelf -h_ header. cpp

#include "header.h"

void readelf_h(const char* filename) {
    FILE* fp;//Definition file pointer
    Elf64_Ehdr elf_header;//Define elf header to store
    fp = fopen(filename, "r");
    if (fp == NULL) { exit(0); }
    fread(&elf_header, sizeof(Elf64_Ehdr), 1, fp);//Read header
    if (elf_header.e_ident[0] != 0x7f || elf_header.e_ident[1] != 'E') { exit(0); }//Determine whether it is an elf file
    printf("ELF Header:\n");
    printf("  Magic:\t");
    for (unsigned char i : elf_header.e_ident) {
        printf("%02x ", i);
    }
    printf("\n");
    printf("  Class:\t\t\t\t");
    switch (elf_header.e_ident[EI_CLASS]) {
        case 1:
            printf("ELF%d\n", 32);
            break;
        case 2:
            printf("ELF%d\n", 64);
            break;
        default:
            printf("Error!\n");
    }
    printf("  Data:\t\t\t\t\t");
    switch (elf_header.e_ident[EI_DATA]) {
        case 1:
            printf("2's complement, little endian\n");
            break;
        case 2:
            printf("2's complement, big endian\n");
            break;
        default:
            printf("Error!\n");
    }
    printf("  Version:\t\t\t\t%d (current)\n", elf_header.e_ident[EI_VERSION]);
    printf("  OS/ABI:\t\t\t\t");
    switch (elf_header.e_ident[EI_OSABI]) {
        case ELFOSABI_NONE:
            printf("UNIX System V ABI\n");
            break;
        case ELFOSABI_HPUX:
            printf("HP-UX\n");
            break;
        case ELFOSABI_NETBSD:
            printf("NetBSD.\n");
            break;
        case ELFOSABI_GNU:
            printf("Object uses GNU ELF extensions.\n");
            break;
        case ELFOSABI_SOLARIS:
            printf("Sun Solaris.\n");
            break;
        case ELFOSABI_AIX:
            printf("IBM AIX.\n");
            break;
        case ELFOSABI_IRIX:
            printf("SGI Irix.\n");
            break;
        case ELFOSABI_FREEBSD:
            printf("FreeBSD.\n");
            break;
        case ELFOSABI_TRU64:
            printf("Compaq TRU64 UNIX.\n");
            break;
        case ELFOSABI_MODESTO:
            printf("Novell Modesto.\n");
            break;
        case ELFOSABI_OPENBSD:
            printf("OpenBSD.\n");
            break;
        case ELFOSABI_ARM_AEABI:
            printf("ARM EABI\n");
            break;
        case ELFOSABI_ARM:
            printf("ARM\n");
            break;
        case ELFOSABI_STANDALONE:
            printf("Standalone (embedded) application\n");
            break;
        default:
            printf("Error!\n");
    }
    printf("  ABI Version:\t\t\t\t%d\n", elf_header.e_ident[EI_ABIVERSION]);
    printf("  Type:\t\t\t\t\t");
    switch (elf_header.e_type) {
        case ET_REL:
            printf("REL (Relocatable file)\n");
            break;
        case ET_EXEC:
            printf("EXEC (Executable file)\n");
            break;
        case ET_DYN:
            printf("DYN (Shared object file)\n");
            break;
        default:
            printf("Error!\n");
    }
    printf("  Machine:\t\t\t\t");
    switch (elf_header.e_machine) {
        case EM_NONE:
            printf("No Machine!\n");
            break;
        case EM_386:
            printf("Intel 80386\n");
            break;
        case EM_860:
            printf("Intel 80860\n");
            break;
        case EM_ARM:
            printf("ARM\n");
            break;
        case EM_X86_64:
            printf("AMD x86-64 architecture\n");
            break;
        case EM_AVR:
            printf("Atmel AVR 8-bit microcontroller\n");
            break;
        case EM_MSP430:
            printf("Texas Instruments msp430\n");
            break;
        case EM_ALTERA_NIOS2:
            printf("Altera Nios II\n");
            break;
        case EM_MICROBLAZE:
            printf("Xilinx MicroBlaze\n");
            break;
        case EM_8051:
            printf("Intel 8051 and variants\n");
            break;
        case EM_STM8:
            printf("STMicroelectronics STM8\n");
            break;
        case EM_CUDA:
            printf("NVIDIA CUDA\n");
            break;
        case EM_AMDGPU:
            printf("AMD GPU\n");
            break;
        case EM_RISCV:
            printf("RISC-V\n");
            break;
        case EM_BPF:
            printf("Linux BPF -- in-kernel virtual machine\n");
            break;
        default:
            printf("Unknown Machine!\n");
    }
    printf("  Version:\t\t\t\t%x\n", elf_header.e_ident[EI_VERSION]);
    printf("  Entry point address:\t\t\t0x%016lx\n", elf_header.e_entry);
    printf("  Start of program headers:\t\t%ld (bytes into file)\n", elf_header.e_phoff);
    printf("  Start of section headers:\t\t%ld (bytes into file)\n", elf_header.e_shoff);
    printf("  Flags:\t\t\t\t0x%x\n", elf_header.e_flags);
    printf("  Size of this header:\t\t\t%d (bytes)\n", elf_header.e_ehsize);
    printf("  Size of program headers:\t\t%d (bytes)\n", elf_header.e_phentsize);
    printf("  Number of program headers:\t\t%d\n", elf_header.e_phnum);
    printf("  Size of section headers:\t\t%d (bytes)\n", elf_header.e_shentsize);
    printf("  Number of section headers:\t\t%d\n", elf_header.e_shnum);
    printf("  Section header string table index:\t%d\n", elf_header.e_shstrndx);
}

Imitate the implementation of readelf -S_ Section. cpp

#include "header.h"

void readelf_S(const char* filename) {
    FILE* fp;
    Elf64_Ehdr elf_header;
    fp = fopen(filename, "r");
    if (fp == NULL) { exit(0); }
    fread(&elf_header, sizeof(Elf64_Ehdr), 1, fp);
    if (elf_header.e_ident[0] != 0x7f || elf_header.e_ident[1] != 'E') { exit(0); }
    //The definition array is used to store each section in the segment table_ Header, number of segments: elf_header.e_shnum
    Elf64_Shdr* sec_headers = new Elf64_Shdr[elf_header.e_shnum];
    //Move the pointer to the starting address of the segment table, and the starting address of the segment elf_header.e_shoff is the offset relative to the entire ELF file, SEEK_SET offset from the beginning of the file
    fseek(fp, elf_header.e_shoff, SEEK_SET);
    //Read section_header. The size of each header is sizeof(Elf64_Shdr), and elf is read in total_ header. e_ Shnum header segments
    fread(sec_headers, sizeof(Elf64_Shdr), elf_header.e_shnum, fp);
    printf("There are %d section headers, starting at offset 0x%lx\n\n", elf_header.e_shnum, elf_header.e_shoff);
    printf("Section Headers:\n");

    int str_tab_ind = elf_header.e_shstrndx;//Gets the index elf of the string table in the segment table_ header. e_ Shstrndx, which is used to read the segment name
    fseek(fp, sec_headers[str_tab_ind].sh_offset, SEEK_SET);//Move pointer to string table
    char* string_table = new char[sec_headers[str_tab_ind].sh_size];//Construct a character array to store the characters in the string table
    fread(string_table, 1, sec_headers[str_tab_ind].sh_size, fp);//Read out all the characters in the string table

    //Get the type of section segment, enter the value corresponding to the type, and return the type name of string type
    auto get_sh_type = [](int sh_type, string& sec_header_name) {
        switch (sh_type) {
            case SHT_NULL:
                sec_header_name = "NULL";
                break;
            case SHT_PROGBITS:
                sec_header_name = "PROGBITS";
                break;
            case SHT_SYMTAB:
                sec_header_name = "SYMTAB";
                break;
            case SHT_STRTAB:
                sec_header_name = "STRTAB";
                break;
            case SHT_RELA:
                sec_header_name = "RELA";
                break;
            case SHT_HASH:
                sec_header_name = "HASH";
                break;
            case SHT_DYNAMIC:
                sec_header_name = "DYNAMIC";
                break;
            case SHT_NOTE:
                sec_header_name = "NOTE";
                break;
            case SHT_NOBITS:
                sec_header_name = "NOBITS";
                break;
            case SHT_REL:
                sec_header_name = "REL";
                break;
            case SHT_SHLIB:
                sec_header_name = "SHLIB";
                break;
            case SHT_DYNSYM:
                sec_header_name = "DYNSYM";
                break;
            case SHT_INIT_ARRAY:
                sec_header_name = "INIT_ARRAY";
                break;
            case SHT_FINI_ARRAY:
                sec_header_name = "FINI_ARRAY";
                break;
            case SHT_PREINIT_ARRAY:
                sec_header_name = "PREINIT_ARRAY";
                break;
            case SHT_GROUP:
                sec_header_name = "GROUP";
                break;
            case SHT_SYMTAB_SHNDX:
                sec_header_name = "SYMTAB_SHNDX";
                break;
            case SHT_NUM:
                sec_header_name = "NUM";
                break;
            case SHT_GNU_HASH:
                sec_header_name = "GNU_HASH";
                break;
            case SHT_GNU_versym:
                sec_header_name = "VERSYM";
                break;
            case SHT_GNU_verneed:
                sec_header_name = "VERNEED";
                break;
            default:
                sec_header_name = "UnknownType";
        }
    };

    //Get the flag flag of the section section, enter the value corresponding to the type, and return the string flag
    //Because multiple flags may be satisfied at the same time, judge whether the corresponding flag is satisfied according to whether the corresponding bit is 1. If yes, piece up the flag string
    auto get_sh_flags = [](unsigned int sh_flags, string& sec_header_name) {
        if ((sh_flags & SHF_WRITE) >> 0)
            sec_header_name += "W";
        if ((sh_flags & SHF_ALLOC) >> 1)
            sec_header_name += "A";
        if ((sh_flags & SHF_EXECINSTR) >> 2)
            sec_header_name += "X";
        if ((sh_flags & SHF_MERGE) >> 4)
            sec_header_name += "M";
        if ((sh_flags & SHF_STRINGS) >> 5)
            sec_header_name += "S";
        if ((sh_flags & SHF_INFO_LINK) >> 6)
            sec_header_name += "I";
        if ((sh_flags & SHF_LINK_ORDER) >> 7)
            sec_header_name += "L";
        if ((sh_flags & SHF_OS_NONCONFORMING) >> 8)
            sec_header_name += "O";
        if ((sh_flags & SHF_GROUP) >> 9)
            sec_header_name += "G";
        if ((sh_flags & SHF_TLS) >> 10)
            sec_header_name += "T";
        if ((sh_flags & SHF_COMPRESSED) >> 11)
            sec_header_name += "C";
        //Special flags can be processed separately because the corresponding bits do not overlap with the corresponding bits of the above flag
        switch (sh_flags) {
            case SHF_MASKOS:
                sec_header_name = "o";
                break;
            case SHF_MASKPROC:
                sec_header_name = "p";
                break;
            case SHF_EXCLUDE:
                sec_header_name = "E";
                break;
        }
    };

    printf("  [Nr]\tName\t\t\tType\t\tAddr\t\tOffset\t\tSize\t\t"
           "EntSize\t\tFlags\tLink\tInfo\tAlign\n");
    //Traverse section_ For each section in the headers section table, the corresponding information is output
    for (int i = 0; i < elf_header.e_shnum; i++) {
        printf("  [%2d]\t", i);
        printf("%-24s", &string_table[sec_headers[i].sh_name]);
        string sh_type;
        get_sh_type(sec_headers[i].sh_type, sh_type);
        printf("%-16s", sh_type.data());
        printf("0x%08lx\t", sec_headers[i].sh_addr);
        printf("0x%08lx\t", sec_headers[i].sh_offset);
        printf("0x%08lx\t", sec_headers[i].sh_size);
        printf("0x%08lx\t", sec_headers[i].sh_entsize);
        string sh_flags;
        get_sh_flags(sec_headers[i].sh_flags, sh_flags);
        printf("%-8s", sh_flags.data());
        printf("%-8d", sec_headers[i].sh_link);
        printf("%-8d", sec_headers[i].sh_info);
        printf("%-8ld", sec_headers[i].sh_addralign);
        printf("\n");
    }
    printf("Key to Flags:\n"
           "\tW (write), A (alloc), X (execute), M (merge), S (strings), I (info),\n"
           "\tL (link order), O (extra OS processing required), G (group), T (TLS),\n"
           "\tC (compressed), x (unknown), o (OS specific), E (exclude),\n"
           "\tl (large), p (processor specific)\n");

    //Free heap memory
    delete[] string_table;
    delete[] sec_headers;
    fclose(fp);
}

Imitate the implementation of readelf -s_ symbol. cpp

#include "header.h"

void readelf_s(const char* filename) {
    FILE* fp;
    Elf64_Ehdr elf_header;
    fp = fopen(filename, "r");
    if (fp == NULL) { exit(0); }
    fread(&elf_header, sizeof(Elf64_Ehdr), 1, fp);
    if (elf_header.e_ident[0] != 0x7f || elf_header.e_ident[1] != 'E') { exit(0); }
    Elf64_Shdr* sec_headers = new Elf64_Shdr[elf_header.e_shnum];//Store each section_ Array of headers
    fseek(fp, elf_header.e_shoff, SEEK_SET);//Move the pointer to the offset address corresponding to the segment table
    fread(sec_headers, sizeof(Elf64_Shdr), elf_header.e_shnum, fp);//Read segment table data into the opened array sec_ In headers

    int str_tab_ind = elf_header.e_shstrndx;//Get string table Index of shstrtab in segment table
    fseek(fp, sec_headers[str_tab_ind].sh_offset, SEEK_SET);//Move pointer to string table Offset address corresponding to shstrtab
    char* string_table = new char[sec_headers[str_tab_ind].sh_size];//Open up heap memory to store string tables shstrtab
    fread(string_table, 1, sec_headers[str_tab_ind].sh_size, fp);//String table The data at the corresponding address of shstrtab is read into the string array

    int dynsym_ind = -1;//Default dynsym symbol table index is - 1
    int symtab_ind = -1;//Default symtab symbol table index is - 1
    int dynstr_ind = -1;//Default dynstr string table index is - 1
    int strtab_ind = -1;//Default strtab string index is - 1

    //Traversal segment table section_headers gets the symbol table dynsym;. symtab;. dynstr;. Index of four strtab tables in segment table
    for (int i = 0; i < elf_header.e_shnum; i++) {
        if (sec_headers[i].sh_type == SHT_DYNSYM)//Yes dynsym symbol table
            dynsym_ind = i;
        else if (sec_headers[i].sh_type == SHT_SYMTAB)//Yes symtab symbol table
            symtab_ind = i;
        if (strcmp(&string_table[sec_headers[i].sh_name], ".strtab") == 0)//Yes strtab string table
            strtab_ind = i;
        else if (strcmp(&string_table[sec_headers[i].sh_name], ".dynstr") == 0)//Yes dynstr string table
            dynstr_ind = i;
    }
    //Get the st corresponding to the symbol table entry_ Info section, which is used to calculate symbol type and binding information
    auto get_st_info = [](unsigned int st_info, string& symbol_type, string& symbol_binding) {
        unsigned char st_type = st_info & 0x0000000f;//The lower 4 bits indicate the symbol type
        unsigned char st_binding = (st_info & (~0x0000000f)) >> 4;//The upper 28 bits represent symbol binding information
        switch (st_binding) {
            case STB_LOCAL:
                symbol_binding = "LOCAL";
                break;
            case STB_GLOBAL:
                symbol_binding = "GLOBAL";
                break;
            case STB_WEAK:
                symbol_binding = "WEAK";
                break;
            case STB_NUM:
                symbol_binding = "NUM";
                break;
            case STB_LOOS:
                symbol_binding = "LOOS";
                break;
            case STB_HIOS:
                symbol_binding = "HIOS";
                break;
            case STB_LOPROC:
                symbol_binding = "LOPROC";
                break;
            case STB_HIPROC:
                symbol_binding = "HIPROC";
                break;
            default:
                symbol_binding = "UnknownType";
        }
        switch (st_type) {
            case STT_NOTYPE:
                symbol_type = "NOTYPE";
                break;
            case STT_OBJECT:
                symbol_type = "OBJECT";
                break;
            case STT_FUNC:
                symbol_type = "FUNC";
                break;
            case STT_SECTION:
                symbol_type = "SECTION";
                break;
            case STT_FILE:
                symbol_type = "FILE";
                break;
            case STT_COMMON:
                symbol_type = "COMMON";
                break;
            case STT_TLS:
                symbol_type = "TLS";
                break;
            case STT_NUM:
                symbol_type = "NUM";
                break;
            case STT_LOOS:
                symbol_type = "LOOS";
                break;
            case STT_HIOS:
                symbol_type = "HIOS";
                break;
            case STT_LOPROC:
                symbol_type = "LOPROC";
                break;
            case STT_HIPROC:
                symbol_type = "HIPROC";
                break;
            default:
                symbol_type = "UnknownBinding";
        }
    };

    //Get the index of the segment in which the symbol is located in the segment table, and handle special symbols
    auto get_st_shndx = [](unsigned int st_shndx, string& Ndx) {
        switch (st_shndx) {
            case SHN_UNDEF:
                Ndx = "UNDEF";break;
            case SHN_COMMON:
                Ndx = "COMMON";break;
            case SHN_ABS:
                Ndx = "ABS";break;
            default:
                Ndx = to_string(st_shndx);
        }
    };

    //Output the symbol table information and input the index sym of the symbol table in the segment table_ Ind, number of symbol table entries_ Num, string table corresponding to symbol table_ table
    auto show_symbol_table = [&](int sym_ind, unsigned long entry_num, char* string_table) {
        fseek(fp, sec_headers[sym_ind].sh_offset, SEEK_SET);//Move the pointer to the offset address corresponding to the symbol table

        Elf64_Sym* sym_entries = new Elf64_Sym[entry_num];//Heap memory is used to store all entries in the symbol table
        fread(sym_entries, sizeof(Elf64_Sym), entry_num, fp);//Read symbol table
        printf("  Num:\t\tValue\t\tSize\tType\tBind\tVis\t"
               "Ndx\t\tName\n");
        //Traverse each entry in the symbol table and output the information of the entry
        for (int i = 0; i < entry_num; i++) {
            printf("  %3d:\t", i);
            printf("0x%016lx:\t", sym_entries[i].st_value);
            printf("%4ld\t", sym_entries[i].st_size);
            string symbol_type;
            string symbol_binding;
            get_st_info(sym_entries[i].st_info, symbol_type, symbol_binding);
            printf("%s\t", symbol_type.data());
            printf("%s\t", symbol_binding.data());
            printf("DEFAULT\t");
            string Ndx;
            get_st_shndx(sym_entries[i].st_shndx, Ndx);
            printf("%4s\t", Ndx.data());
            //According to the st of entry_ The name attribute finds the name of the entry in the string table corresponding to the symbol table
            printf("%s", &string_table[sym_entries[i].st_name]);
            printf("\n");
        }
        //Free heap memory
        delete[] sym_entries;
    };

    //If dynsym segment exists and dynstr exists
    if ((dynsym_ind != -1) && (dynstr_ind != -1)) {
        //Symbol table size sec_headers[dynsym_ind].sh_size each entry size sec_headers[dynsym_ind].sh_entsize
        // Calculate the number of entries_ num
        unsigned long entry_num = sec_headers[dynsym_ind].sh_size / sec_headers[dynsym_ind].sh_entsize;
        printf("Symbol table '.dynsym' contains %ld entries\n", entry_num);
        fseek(fp, sec_headers[dynstr_ind].sh_offset, SEEK_SET);//Move the pointer to The offset address corresponding to the dynstr string table
        //Heap memory is used to store string tables
        char* dynstr_string_table = new char[sec_headers[dynstr_ind].sh_size];
        //Read data into string table
        fread(dynstr_string_table, 1, sec_headers[dynstr_ind].sh_size, fp);
        show_symbol_table(dynsym_ind, entry_num, dynstr_string_table);
        //Release string table
        delete[] dynstr_string_table;
    } else {
        printf("No Dynamic linker symbol table!\n");
    }
    printf("\n");
    //If symtab segment exists and strtab exists
    if ((symtab_ind != -1) && (strtab_ind != -1)) {
        unsigned long entry_num = sec_headers[symtab_ind].sh_size / sec_headers[symtab_ind].sh_entsize;
        printf("Symbol table '.symtab' contains %ld entries\n", entry_num);
        fseek(fp, sec_headers[strtab_ind].sh_offset, SEEK_SET);
        char* strtab_string_table = new char[sec_headers[strtab_ind].sh_size];
        fread(strtab_string_table, 1, sec_headers[strtab_ind].sh_size, fp);
        show_symbol_table(symtab_ind, entry_num, strtab_string_table);
        delete[] strtab_string_table;
    } else {
        printf("No symbol table!\n");
    }

    //release
    delete[] string_table;
    delete[] sec_headers;
    fclose(fp);
}

4. Compile and execute with cmake make

Enter the directory where elfront is located and execute the following command

./ELFReader -h filename
./ELFReader -S filename
./ELFReader -s filename

Where filename is the path of the elf file being parsed.

Example:

Execute cmake... And then make in the build folder. Finally, the project directory structure is as follows. Elfront is the elf file parser, libmylib The so shared library and helloworld executable are the test files to be parsed

.
├── build
│   ├── bin
│   │   ├── ELFReader
│   │   ├── helloworld
│   │   └── libMylib.so
......
├── CMakeLists.txt
├── readme.md
└── src
    ├── CMakeLists.txt
    ├── header.h
    ├── main.cpp
    ├── readefl_h.cpp
    ├── readelf_s.cpp
    └── readelf_S.cpp

Root directory cmakelists txt

cmake_minimum_required(VERSION 3.10)
project(ELFReader)
set(CMAKE_CXX_STANDARD 11)
ADD_SUBDIRECTORY(./src)

Cmakelists. In the src directory txt

cmake_minimum_required(VERSION 3.10)

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
AUX_SOURCE_DIRECTORY(./ DIR_SRCS)
ADD_EXECUTABLE(ELFReader ${DIR_SRCS})

Enter the bin directory and execute

cd build/bin/
./ELFReader -h helloworld

Output:

ELF Header:
  Magic:	7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:				ELF64
  Data:					2's complement, little endian
  Version:				1 (current)
  OS/ABI:				UNIX System V ABI
  ABI Version:				0
  Type:					DYN (Shared object file)
  Machine:				AMD x86-64 architecture
  Version:				1
  Entry point address:			0x00000000000010c0
  Start of program headers:		64 (bytes into file)
  Start of section headers:		36440 (bytes into file)
  Flags:				0x0
  Size of this header:			64 (bytes)
  Size of program headers:		56 (bytes)
  Number of program headers:		13
  Size of section headers:		64 (bytes)
  Number of section headers:		36
  Section header string table index:	35

Execute - S command

./ELFReader -S helloworld

Output:

There are 36 section headers, starting at offset 0x8e58

Section Headers:
  [Nr]	Name			Type		Addr		Offset		Size		EntSize		Flags	Link	Info	Align
  [ 0]	                        NULL            0x00000000	0x00000000	0x00000000	0x00000000	        0       0       0       
  [ 1]	.interp                 PROGBITS        0x00000318	0x00000318	0x0000001c	0x00000000	A       0       0       1       
  [ 2]	.note.gnu.property      NOTE            0x00000338	0x00000338	0x00000020	0x00000000	A       0       0       8       
  [ 3]	.note.gnu.build-id      NOTE            0x00000358	0x00000358	0x00000024	0x00000000	A       0       0       4       
  [ 4]	.note.ABI-tag           NOTE            0x0000037c	0x0000037c	0x00000020	0x00000000	A       0       0       4       
  [ 5]	.gnu.hash               GNU_HASH        0x000003a0	0x000003a0	0x00000028	0x00000000	A       6       0       8       
  [ 6]	.dynsym                 DYNSYM          0x000003c8	0x000003c8	0x00000138	0x00000018	A       7       1       8       
  [ 7]	.dynstr                 STRTAB          0x00000500	0x00000500	0x00000163	0x00000000	A       0       0       1       
  [ 8]	.gnu.version            VERSYM          0x00000664	0x00000664	0x0000001a	0x00000002	A       6       0       2       
  [ 9]	.gnu.version_r          VERNEED         0x00000680	0x00000680	0x00000040	0x00000000	A       7       2       8       
  [10]	.rela.dyn               RELA            0x000006c0	0x000006c0	0x00000120	0x00000018	A       6       0       8       
  [11]	.rela.plt               RELA            0x000007e0	0x000007e0	0x00000060	0x00000018	AI      6       24      8       
  [12]	.init                   PROGBITS        0x00001000	0x00001000	0x0000001b	0x00000000	AX      0       0       4       
  [13]	.plt                    PROGBITS        0x00001020	0x00001020	0x00000050	0x00000010	AX      0       0       16      
  [14]	.plt.got                PROGBITS        0x00001070	0x00001070	0x00000010	0x00000010	AX      0       0       16      
  [15]	.plt.sec                PROGBITS        0x00001080	0x00001080	0x00000040	0x00000010	AX      0       0       16      
  [16]	.text                   PROGBITS        0x000010c0	0x000010c0	0x00000205	0x00000000	AX      0       0       16      
  [17]	.fini                   PROGBITS        0x000012c8	0x000012c8	0x0000000d	0x00000000	AX      0       0       4       
  [18]	.rodata                 PROGBITS        0x00002000	0x00002000	0x00000013	0x00000000	A       0       0       4       
  [19]	.eh_frame_hdr           PROGBITS        0x00002014	0x00002014	0x00000054	0x00000000	A       0       0       4       
  [20]	.eh_frame               PROGBITS        0x00002068	0x00002068	0x00000148	0x00000000	A       0       0       8       
  [21]	.init_array             INIT_ARRAY      0x00003d78	0x00002d78	0x00000010	0x00000008	WA      0       0       8       
  [22]	.fini_array             FINI_ARRAY      0x00003d88	0x00002d88	0x00000008	0x00000008	WA      0       0       8       
  [23]	.dynamic                DYNAMIC         0x00003d90	0x00002d90	0x00000200	0x00000010	WA      7       0       8       
  [24]	.got                    PROGBITS        0x00003f90	0x00002f90	0x00000070	0x00000008	WA      0       0       8       
  [25]	.data                   PROGBITS        0x00004000	0x00003000	0x00000010	0x00000000	WA      0       0       8       
  [26]	.bss                    NOBITS          0x00004040	0x00003010	0x00000118	0x00000000	WA      0       0       64      
  [27]	.comment                PROGBITS        0x00000000	0x00003010	0x0000002a	0x00000001	MS      0       0       1       
  [28]	.debug_aranges          PROGBITS        0x00000000	0x0000303a	0x00000030	0x00000000	        0       0       1       
  [29]	.debug_info             PROGBITS        0x00000000	0x0000306a	0x00002c34	0x00000000	        0       0       1       
  [30]	.debug_abbrev           PROGBITS        0x00000000	0x00005c9e	0x00000649	0x00000000	        0       0       1       
  [31]	.debug_line             PROGBITS        0x00000000	0x000062e7	0x00000406	0x00000000	        0       0       1       
  [32]	.debug_str              PROGBITS        0x00000000	0x000066ed	0x00001b08	0x00000001	MS      0       0       1       
  [33]	.symtab                 SYMTAB          0x00000000	0x000081f8	0x00000780	0x00000018	        34      55      8       
  [34]	.strtab                 STRTAB          0x00000000	0x00008978	0x00000381	0x00000000	        0       0       1       
  [35]	.shstrtab               STRTAB          0x00000000	0x00008cf9	0x0000015a	0x00000000	        0       0       1       
Key to Flags:
	W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
	L (link order), O (extra OS processing required), G (group), T (TLS),
	C (compressed), x (unknown), o (OS specific), E (exclude),
	l (large), p (processor specific)

Execute the - s command

./ELFReader -s helloworld

Output:

Symbol table '.dynsym' contains 13 entries
  Num:		Value		Size	Type	Bind	Vis	Ndx		Name
    0:	0x0000000000000000:	   0	NOTYPE	LOCAL	DEFAULT	UNDEF	
    1:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    2:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	__cxa_atexit
    3:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    4:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZNSolsEPFRSoS_E
    5:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZNSt8ios_base4InitC1Ev
    6:	0x0000000000000000:	   0	NOTYPE	WEAK	DEFAULT	UNDEF	_ITM_deregisterTMCloneTable
    7:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	__libc_start_main
    8:	0x0000000000000000:	   0	NOTYPE	WEAK	DEFAULT	UNDEF	__gmon_start__
    9:	0x0000000000000000:	   0	NOTYPE	WEAK	DEFAULT	UNDEF	_ITM_registerTMCloneTable
   10:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZNSt8ios_base4InitD1Ev
   11:	0x0000000000000000:	   0	FUNC	WEAK	DEFAULT	UNDEF	__cxa_finalize
   12:	0x0000000000004040:	 272	OBJECT	GLOBAL	DEFAULT	  26	_ZSt4cout

Symbol table '.symtab' contains 80 entries
  Num:		Value		Size	Type	Bind	Vis	Ndx		Name
    0:	0x0000000000000000:	   0	NOTYPE	LOCAL	DEFAULT	UNDEF	
    1:	0x0000000000000318:	   0	SECTION	LOCAL	DEFAULT	   1	
    2:	0x0000000000000338:	   0	SECTION	LOCAL	DEFAULT	   2	
    3:	0x0000000000000358:	   0	SECTION	LOCAL	DEFAULT	   3	
    4:	0x000000000000037c:	   0	SECTION	LOCAL	DEFAULT	   4	
    5:	0x00000000000003a0:	   0	SECTION	LOCAL	DEFAULT	   5	
    6:	0x00000000000003c8:	   0	SECTION	LOCAL	DEFAULT	   6	
    7:	0x0000000000000500:	   0	SECTION	LOCAL	DEFAULT	   7	
    8:	0x0000000000000664:	   0	SECTION	LOCAL	DEFAULT	   8	
    9:	0x0000000000000680:	   0	SECTION	LOCAL	DEFAULT	   9	
   10:	0x00000000000006c0:	   0	SECTION	LOCAL	DEFAULT	  10	
   11:	0x00000000000007e0:	   0	SECTION	LOCAL	DEFAULT	  11	
   12:	0x0000000000001000:	   0	SECTION	LOCAL	DEFAULT	  12	
   13:	0x0000000000001020:	   0	SECTION	LOCAL	DEFAULT	  13	
   14:	0x0000000000001070:	   0	SECTION	LOCAL	DEFAULT	  14	
   15:	0x0000000000001080:	   0	SECTION	LOCAL	DEFAULT	  15	
   16:	0x00000000000010c0:	   0	SECTION	LOCAL	DEFAULT	  16	
   17:	0x00000000000012c8:	   0	SECTION	LOCAL	DEFAULT	  17	
   18:	0x0000000000002000:	   0	SECTION	LOCAL	DEFAULT	  18	
   19:	0x0000000000002014:	   0	SECTION	LOCAL	DEFAULT	  19	
   20:	0x0000000000002068:	   0	SECTION	LOCAL	DEFAULT	  20	
   21:	0x0000000000003d78:	   0	SECTION	LOCAL	DEFAULT	  21	
   22:	0x0000000000003d88:	   0	SECTION	LOCAL	DEFAULT	  22	
   23:	0x0000000000003d90:	   0	SECTION	LOCAL	DEFAULT	  23	
   24:	0x0000000000003f90:	   0	SECTION	LOCAL	DEFAULT	  24	
   25:	0x0000000000004000:	   0	SECTION	LOCAL	DEFAULT	  25	
   26:	0x0000000000004040:	   0	SECTION	LOCAL	DEFAULT	  26	
   27:	0x0000000000000000:	   0	SECTION	LOCAL	DEFAULT	  27	
   28:	0x0000000000000000:	   0	SECTION	LOCAL	DEFAULT	  28	
   29:	0x0000000000000000:	   0	SECTION	LOCAL	DEFAULT	  29	
   30:	0x0000000000000000:	   0	SECTION	LOCAL	DEFAULT	  30	
   31:	0x0000000000000000:	   0	SECTION	LOCAL	DEFAULT	  31	
   32:	0x0000000000000000:	   0	SECTION	LOCAL	DEFAULT	  32	
   33:	0x0000000000000000:	   0	FILE	LOCAL	DEFAULT	 ABS	crtstuff.c
   34:	0x00000000000010f0:	   0	FUNC	LOCAL	DEFAULT	  16	deregister_tm_clones
   35:	0x0000000000001120:	   0	FUNC	LOCAL	DEFAULT	  16	register_tm_clones
   36:	0x0000000000001160:	   0	FUNC	LOCAL	DEFAULT	  16	__do_global_dtors_aux
   37:	0x0000000000004150:	   1	OBJECT	LOCAL	DEFAULT	  26	completed.8060
   38:	0x0000000000003d88:	   0	OBJECT	LOCAL	DEFAULT	  22	__do_global_dtors_aux_fini_array_entry
   39:	0x00000000000011a0:	   0	FUNC	LOCAL	DEFAULT	  16	frame_dummy
   40:	0x0000000000003d78:	   0	OBJECT	LOCAL	DEFAULT	  21	__frame_dummy_init_array_entry
   41:	0x0000000000000000:	   0	FILE	LOCAL	DEFAULT	 ABS	main.cpp
   42:	0x0000000000002004:	   1	OBJECT	LOCAL	DEFAULT	  18	_ZStL19piecewise_construct
   43:	0x0000000000004151:	   1	OBJECT	LOCAL	DEFAULT	  26	_ZStL8__ioinit
   44:	0x00000000000011e0:	  77	FUNC	LOCAL	DEFAULT	  16	_Z41__static_initialization_and_destruction_0ii
   45:	0x000000000000122d:	  25	FUNC	LOCAL	DEFAULT	  16	_GLOBAL__sub_I_main
   46:	0x0000000000000000:	   0	FILE	LOCAL	DEFAULT	 ABS	crtstuff.c
   47:	0x00000000000021ac:	   0	OBJECT	LOCAL	DEFAULT	  20	__FRAME_END__
   48:	0x0000000000000000:	   0	FILE	LOCAL	DEFAULT	 ABS	
   49:	0x0000000000002014:	   0	NOTYPE	LOCAL	DEFAULT	  19	__GNU_EH_FRAME_HDR
   50:	0x0000000000001000:	   0	FUNC	LOCAL	DEFAULT	  12	_init
   51:	0x0000000000003d90:	   0	OBJECT	LOCAL	DEFAULT	  23	_DYNAMIC
   52:	0x0000000000003d88:	   0	NOTYPE	LOCAL	DEFAULT	  21	__init_array_end
   53:	0x0000000000003d78:	   0	NOTYPE	LOCAL	DEFAULT	  21	__init_array_start
   54:	0x0000000000003f90:	   0	OBJECT	LOCAL	DEFAULT	  24	_GLOBAL_OFFSET_TABLE_
   55:	0x0000000000004010:	   0	NOTYPE	GLOBAL	DEFAULT	  25	_edata
   56:	0x0000000000004000:	   0	NOTYPE	WEAK	DEFAULT	  25	data_start
   57:	0x0000000000002000:	   4	OBJECT	GLOBAL	DEFAULT	  18	_IO_stdin_used
   58:	0x0000000000000000:	   0	FUNC	WEAK	DEFAULT	UNDEF	__cxa_finalize@@GLIBC_2.2.5
   59:	0x00000000000011a9:	  55	FUNC	GLOBAL	DEFAULT	  16	main
   60:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@@GLIBCXX_3.4
   61:	0x0000000000004008:	   0	OBJECT	GLOBAL	DEFAULT	  25	__dso_handle
   62:	0x00000000000012c8:	   0	FUNC	GLOBAL	DEFAULT	  17	_fini
   63:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	__cxa_atexit@@GLIBC_2.2.5
   64:	0x00000000000010c0:	  47	FUNC	GLOBAL	DEFAULT	  16	_start
   65:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4
   66:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZNSolsEPFRSoS_E@@GLIBCXX_3.4
   67:	0x0000000000004010:	   0	OBJECT	GLOBAL	DEFAULT	  25	__TMC_END__
   68:	0x0000000000004040:	 272	OBJECT	GLOBAL	DEFAULT	  26	_ZSt4cout@@GLIBCXX_3.4
   69:	0x0000000000004000:	   0	NOTYPE	GLOBAL	DEFAULT	  25	__data_start
   70:	0x0000000000004158:	   0	NOTYPE	GLOBAL	DEFAULT	  26	_end
   71:	0x0000000000004010:	   0	NOTYPE	GLOBAL	DEFAULT	  26	__bss_start
   72:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
   73:	0x0000000000001250:	 101	FUNC	GLOBAL	DEFAULT	  16	__libc_csu_init
   74:	0x0000000000000000:	   0	NOTYPE	WEAK	DEFAULT	UNDEF	_ITM_deregisterTMCloneTable
   75:	0x00000000000012c0:	   5	FUNC	GLOBAL	DEFAULT	  16	__libc_csu_fini
   76:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	__libc_start_main@@GLIBC_2.2.5
   77:	0x0000000000000000:	   0	NOTYPE	WEAK	DEFAULT	UNDEF	__gmon_start__
   78:	0x0000000000000000:	   0	NOTYPE	WEAK	DEFAULT	UNDEF	_ITM_registerTMCloneTable
   79:	0x0000000000000000:	   0	FUNC	GLOBAL	DEFAULT	UNDEF	_ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4

Reference articles

ELF file structure description - yooo - blog Park (cnblogs.com)

ELF file, the cornerstone of compiling and linking in Linux system: take off its layers and explore from the granularity of bytecode (qq.com)

C/C + + implementation of ELF Structure Parsing tool - lysark - blog Garden (cnblogs.com)

Implementation of elf file parsing in C language_ Yuhan's blog - CSDN blog_ elf file reading

Self cultivation of programmers: linking, loading and library Yu Jiazi, Shi fan, pan Aimin

Keywords: Linux Operation & Maintenance server

Added by jeboy on Fri, 11 Feb 2022 17:35:13 +0200