在没有 libelf 或任何其他库的情况下使用 C 创建可重定位的 elf 二进制文件

Creating a relocatable elf binary with C without libelf nor any other libraries

所以我正在尝试用 c 创建一个可重定位的 elf 二进制文件。我不想使用和库或类似的东西。我已经写了一些代码。这是我一直在阅读的资源:https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html。我让小精灵 header 看起来正确。当涉及到部分和部分 headers 时,我只是遇到错误。 这是我的主要代码

#include <stdio.h>
#include <stdlib.h>
#include "elf.h"

const unsigned char strtab[] = {
    '[=10=]', 
    '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', '[=10=]',
    '.', 's', 't', 'r', 't', 'a', 'b', '[=10=]',
    '.', 'd', 'a', 't', 'a', '[=10=]',
    '.', 't', 'e', 'x', 't', '[=10=]',
};

int strtab_size = sizeof(strtab);
int strtab_index = 1;
int section_n = 2;
Elf32_Shdr * create_shdr(){
    Elf32_Shdr * header = calloc(sizeof(Elf32_Shdr), 1);

    if (header == NULL){
        return NULL;
    }

    return header;
}
Elf32_Ehdr * create_ehdr(){
    Elf32_Ehdr * header = calloc(sizeof(Elf32_Ehdr), 1);

    if (header == NULL){
        return NULL;
    }


    header->e_ident[EI_MAG0] = 0x7f;
    header->e_ident[EI_MAG1] = 'E';
    header->e_ident[EI_MAG2] = 'L';
    header->e_ident[EI_MAG3] = 'F';
    // set the object to 32 bit
    header->e_ident[EI_CLASS] = ELFCLASS32;
    header->e_ident[EI_DATA] = ELFDATA2LSB;
    // set the file version
    header->e_ident[EI_VERSION] = EV_CURRENT;
    // set sysv abi
    header->e_ident[EI_OSABI] = ELFOSABI_SYSV;
    header->e_ident[EI_ABIVERSION] = ELFOSABI_NONE;
    header->e_ident[EI_PAD] = 0; // needs to be changed
    header->e_shentsize = sizeof(Elf32_Shdr);
    // we default to relocatable
    header->e_type = ET_REL;
    header->e_machine = EM_X86;
    header->e_version = EV_CURRENT;
    header->e_ehsize = sizeof(Elf32_Ehdr);
    header->e_phentsize = sizeof(Elf32_Phdr);
    header->e_phnum = 0;

    return header;
}

int main()
{
    FILE * fout;

    Elf32_Ehdr *elf_header;
    Elf32_Shdr *NULL_Section; // this should all be null
    Elf32_Shdr *strtab_Section;
    printf("%d\n", strtab_size);
    fout = fopen("output.elf", "wb");

    if (fout == NULL){
        printf("Failed to open output.elf\n");
        return 1;
    }

    if ((elf_header = create_ehdr()) == NULL){
        printf("Failed to create new header\n");
        return 1;
    }

    if ((NULL_Section = create_shdr()) == NULL){
        printf("Failed to create new section header\n");
        return 1;
    }
    if ((strtab_Section = create_shdr()) == NULL){
        printf("Failed to create new section header\n");
        return 1;
    }
    elf_header->e_shstrndx = strtab_index;    // Point to the shstrtab section
    elf_header->e_shnum = section_n;
    elf_header->e_shoff = sizeof(Elf32_Ehdr)-1;
    strtab_Section->sh_type = SHT_STRTAB;
    strtab_Section->sh_offset = ((sizeof(Elf32_Shdr))*1)+sizeof(Elf32_Ehdr);
    strtab_Section->sh_addr = 80;
    //strtab_Section->sh_addralign = 1;
    strtab_Section->sh_size = strtab_size;

    fwrite(elf_header, sizeof(Elf32_Ehdr), 1, fout);
    fwrite(NULL_Section, sizeof(Elf32_Shdr), 1, fout);
    fwrite(strtab_Section, sizeof(Elf32_Shdr), 1, fout);
    fwrite(strtab, strtab_size, 1, fout);

    return 0;
}

我的 elf.h 文件是

#pragma once

enum EI{
        EI_MAG0 = 0,
        EI_MAG1,
        EI_MAG2,
        EI_MAG3,
        EI_CLASS,
        EI_DATA,
        EI_VERSION,
        EI_OSABI,
        EI_ABIVERSION,
        EI_PAD,
};

#define ELFOSABI_NONE           0       //No extensions or unspecified
#define ELFOSABI_HPUX           1       //Hewlett-Packard HP-UX
#define ELFOSABI_NETBSD         2       //NetBSD
#define ELFOSABI_LINUX          3       //Linux
#define ELFOSABI_SOLARIS        6       //Sun Solaris
#define ELFOSABI_AIX            7       //AIX
#define ELFOSABI_IRIX           8       //IRIX
#define ELFOSABI_FREEBSD        9       //FreeBSD
#define ELFOSABI_TRU64          10      //Compaq TRU64 UNIX
#define ELFOSABI_MODESTO        11      //Novell Modesto
#define ELFOSABI_OPENBSD        12      //Open BSD
#define ELFOSABI_OPENVMS        13      //Open VMS
#define ELFOSABI_NSK            14      //Hewlett-Packard Non-Stop Kernel




#define SHF_WRITE       0x1
#define SHF_ALLOC       0x2
#define SHF_EXECINSTR   0x4
#define SHF_MERGE       0x10
#define SHF_STRINGS     0x20
#define SHF_INFO_LINK   0x40
#define SHF_LINK_ORDER  0x80
#define SHF_OS_NONCONFORMING    0x100
#define SHF_GROUP       0x200
#define SHF_TLS         0x400
#define SHF_MASKOS      0x0ff00000
#define SHF_MASKPROC    0xf0000000



#define ELFCLASSNONE 0
#define ELFCLASS32 1
#define ELFCLASS64 2

#define ELFDATANONE 0
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2


#define ELFOSABI_SYSV 0
#define ELFOSABI_HPUX 1
#define ELFOSABI_STANDALONE 255

#define ET_NONE 0
#define ET_REL 1 // relocatable file
#define ET_EXEC 2 // executable file
#define ET_DYN 3 // shared object file
#define ET_CORE 4 // core file
#define ET_LOOS 0xfe00 // operating system-specific
#define ET_HIOS 0xfeff // operating system specific
#define ET_LOPROC 0xff00 // processor-specific
#define ET_HIPROC 0xffff // processor-specific


#define SHT_NULL        0
#define SHT_PROGBITS    1
#define SHT_SYMTAB      2
#define SHT_STRTAB      3
#define SHT_RELA        4
#define SHT_HASH        5
#define SHT_DYNAMIC     6
#define SHT_NOTE        7
#define SHT_NOBITS      8
#define SHT_REL         9
#define SHT_SHLIB       10
#define SHT_DYNSYM      11
#define SHT_INIT_ARRAY  14
#define SHT_FINI_ARRAY  15
#define SHT_PREINIT_ARRAY       16
#define SHT_GROUP       17
#define SHT_SYMTAB_SHNDX        18
#define SHT_LOOS        0x60000000
#define SHT_HIOS        0x6fffffff
#define SHT_LOPROC      0x70000000
#define SHT_HIPROC      0x7fffffff
#define SHT_LOUSER      0x80000000
#define SHT_HIUSER      0xffffffff


#define EM_X86 0x03
#define EM_AMD_X86_64 0x3E


#define EV_NONE 0
#define EV_CURRENT 1



#define EI_NIDENT 16

typedef unsigned int Elf32_Addr;
typedef unsigned short Elf32_Half;
typedef unsigned int Elf32_Off;
typedef signed int Elf32_Sword;
typedef unsigned int Elf32_Word;


typedef struct{ // elf file header
        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;



typedef struct { // program header
        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;


typedef struct{ // section header
    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;

我一直在用 readelf 测试我的二进制文件是否全部正确。这是使用命令 readelf --section-headers output.elf:

时的当前输出
There are 2 section headers, starting at offset 0x33:
readelf: Error: Reading 7936 bytes extends past end of file for string table

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0] <no-strings>      NULL            00000000 000000 000000 00      0   0  0
readelf: Warning: Size of section 1 is larger than the entire file!
  [ 1] <no-strings>      00000300: <unkn 00005000 005c00 001f00 00      0   0  0
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),
  D (mbind), p (processor specific)
readelf: Error: Reading 7936 bytes extends past end of file for string table
readelf: Error: Reading 7936 bytes extends past end of file for string table
readelf: Error: Reading 7936 bytes extends past end of file for string table
readelf: Error: Reading 7936 bytes extends past end of file for string table
readelf: Error: Reading 7936 bytes extends past end of file for string table

NULL 部分的偏移量 header 似乎一切正常,所以我认为这不是问题所在。 header 的大小是正确的,所以我不认为是这样。我真的很困惑为什么这会给我错误。有没有人看到我做错了什么? 如果您需要更多信息,请发表评论,我会添加您需要的任何内容。

更新: 这是我对代码所做的更改

elf_header->e_shstrndx = strtab_index;    // Point to the shstrtab section
elf_header->e_shnum = section_n;
elf_header->e_shoff = sizeof(Elf32_Ehdr);
strtab_Section->sh_type = SHT_STRTAB;
strtab_Section->sh_offset = ((sizeof(Elf32_Shdr))*1)+sizeof(Elf32_Ehdr);
strtab_Section->sh_addr = sizeof(Elf32_Shdr)*2;
strtab_Section->sh_addralign = 1;
strtab_Section->sh_size = sizeof(*strtab_Section) + strtab_size;

i have got the elf headers looking correct.

我不这么认为:

readelf -Wh output.elf
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          51 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         2
  Section header string table index: 1
readelf: Error: Reading 7936 bytes extends past end of file for string table
readelf: Error: Reading 7936 bytes extends past end of file for string table
readelf: Error: Reading 7936 bytes extends past end of file for string table
readelf: Error: Reading 7936 bytes extends past end of file for string table
readelf: Error: Reading 7936 bytes extends past end of file for string table
readelf: Error: Reading 7936 bytes extends past end of file for string table

headers 部分的开头显然 是假的。从此行中删除伪造的 -1

    elf_header->e_shoff = sizeof(Elf32_Ehdr)-1;

生成一个看起来更好的 output.elf 二进制文件:

 readelf -W --all  output.elf
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          52 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         2
  Section header string table index: 1

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1]                   STRTAB          00000050 00005c 00001f 00      0   0  0
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),
  D (mbind), p (processor specific)

There are no section groups in this file.

There are no program headers in this file.

There is no dynamic section in this file.

There are no relocations in this file.
No processor specific unwind information to decode

No version information found in this file.

更新:

why my string table is not working then?

为此,您需要设置 strtab_Section->sh_name = 1; 并将其 sh_offset 设置为 ((sizeof(Elf32_Shdr))*2)+sizeof(Elf32_Ehdr)(您有 两个 部分,而不是一个) .

As-is、.sh_name == 0,对应空串。通过以上更改,我得到:

There are 2 section headers, starting at offset 0x34:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .shstrtab         STRTAB          00000050 000084 00001f 00      0   0  0
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),
  D (mbind), p (processor specific)