So im trying to create a relocatable elf binary with c. I don't want to use and libraries or anything like that. I have some code already that i have been writing. This has been the resource i have been reading: https://refspecs.linuxfoundation.org/elf/gabi4 /ch4.eheader.html . i have got the elf headers looking correct. Im just getting errors when it comes to sections and section headers. This is my main code
#include <stdio.h>
#include <stdlib.h>
#include "elf.h"
const unsigned char strtab[] = {
'\0',
'.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', '\0',
'.', 's', 't', 'r', 't', 'a', 'b', '\0',
'.', 'd', 'a', 't', 'a', '\0',
'.', 't', 'e', 'x', 't', '\0',
};
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;
}
My elf.h file is
#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;
I have been testing if my binaries are all correct with readelf. This is the current output when using the command 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
The offset to the NULL section header seems all good, so i don't think it is that problem. The size of the headers are correct, so i don't think its that. Im really puzzled why this is giving me errors. Does anyone see anything im doing incorrectly? If you need more info please put a comment and i will add anything you need.
Update: this are the changes i have made to my code
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;
CodePudding user response:
i have got the elf headers looking correct.
I don't think so:
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
The start of section headers is obviously bogus. Removing bogus -1
from this line:
elf_header->e_shoff = sizeof(Elf32_Ehdr)-1;
produces an output.elf
binary which looks better:
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.
Update:
why my string table is not working then?
For that, you need to set strtab_Section->sh_name = 1;
and set its sh_offset
to ((sizeof(Elf32_Shdr))*2) sizeof(Elf32_Ehdr)
(you have two sections, not one).
As-is, .sh_name == 0
, which corresponds to empty string. With above change, I get:
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)