implement program and section header parsing

This commit is contained in:
mykola2312 2024-08-25 13:10:11 +03:00
parent caf85718e7
commit 44112dc4a7
4 changed files with 206 additions and 19 deletions

View file

@ -6,7 +6,7 @@ LDFLAGS = -z noexecstack
SRC = relf.c SRC = relf.c
OBJ := $(addprefix $(OBJ_DIR)/,$(patsubst %.s,%.o,$(patsubst %.c,%.o,$(SRC)))) OBJ := $(addprefix $(OBJ_DIR)/,$(patsubst %.s,%.o,$(patsubst %.c,%.o,$(SRC))))
DEPS = relf.h DEPS = relf.h relf_debug.h
$(OBJ_DIR)/%.o: %.c $(OBJ_DIR)/%.o: %.c
@mkdir -p $(OBJ_DIR) @mkdir -p $(OBJ_DIR)

View file

@ -1,4 +1,5 @@
#include "relf/relf.h" #include "relf/relf.h"
#include "relf/relf_debug.h"
#include "blackjack/debug.h" #include "blackjack/debug.h"
#include <elf.h> #include <elf.h>
#include <fcntl.h> #include <fcntl.h>
@ -6,8 +7,28 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <errno.h> #include <errno.h>
// returns 1 if size not suitable for 32 bit host
static int check_32bit_limit(off_t st_size)
{
#if __x86_64__
return 0;
#else
// on 32bit hosts we need to check if 64bit file size
// does not exceed 32bit size_t, which mmap/munmap uses
return (st_size > SIZE_MAX);
#endif
}
static void relf_unmap(relf_t* relf)
{
munmap(relf->image, relf->image_size);
relf->image = NULL;
relf->image_size = 0;
}
relf_value_t relf_open(relf_t* relf, const char* path) relf_value_t relf_open(relf_t* relf, const char* path)
{ {
// reset struct // reset struct
@ -17,20 +38,19 @@ relf_value_t relf_open(relf_t* relf, const char* path)
struct stat st = {0}; struct stat st = {0};
if (stat(path, &st)) if (stat(path, &st))
return RELF_ERROR(RELF_FAILED_OPEN); return RELF_ERROR(RELF_FAILED_OPEN);
if (check_32bit_limit(st.st_size))
TRACE("st_size %lu\n", st.st_size); return RELF_ERROR(RELF_TOO_BIG);
relf->image_size = (size_t)st.st_size;
TRACE("image_size %lu\n", relf->image_size);
// open file and read ELF header // open file and read ELF header
int fd = open(path, O_RDONLY); int fd = open(path, O_RDONLY);
if (fd < 0) if (fd < 0)
return RELF_ERROR(RELF_FAILED_OPEN); return RELF_ERROR(RELF_FAILED_OPEN);
union { // read ELF's ident header, which contains magic and type
Elf64_Ehdr hdr64; uint8_t e_ident[EI_NIDENT];
Elf32_Ehdr hdr32; if (read(fd, e_ident, EI_NIDENT) < EI_NIDENT)
} e;
// read biggest value by default
if (read(fd, &e.hdr64, sizeof(e.hdr64)) < sizeof(e.hdr64))
{ {
close(fd); close(fd);
return RELF_ERROR(RELF_FAILED_OPEN); return RELF_ERROR(RELF_FAILED_OPEN);
@ -38,7 +58,7 @@ relf_value_t relf_open(relf_t* relf, const char* path)
// check magic and decide ELF type // check magic and decide ELF type
// we operate here ELF64 variant since it same as in ELF32 // we operate here ELF64 variant since it same as in ELF32
if (!memcmp(e.hdr64.e_ident, ELFMAG, sizeof(ELFMAG))) if (!memcmp(e_ident, ELFMAG, sizeof(ELFMAG)))
{ {
// not an ELF file at all // not an ELF file at all
close(fd); close(fd);
@ -46,7 +66,7 @@ relf_value_t relf_open(relf_t* relf, const char* path)
} }
// 32 bit or 64 bit // 32 bit or 64 bit
switch (e.hdr64.e_ident[EI_CLASS]) switch (e_ident[EI_CLASS])
{ {
case ELFCLASS32: relf->type = RELF_32BIT; break; case ELFCLASS32: relf->type = RELF_32BIT; break;
case ELFCLASS64: relf->type = RELF_64BIT; break; case ELFCLASS64: relf->type = RELF_64BIT; break;
@ -55,7 +75,7 @@ relf_value_t relf_open(relf_t* relf, const char* path)
return RELF_ERROR(RELF_UNSUPPORTED); return RELF_ERROR(RELF_UNSUPPORTED);
} }
if (e.hdr64.e_ident[EI_DATA] != ELFDATA2LSB) if (e_ident[EI_DATA] != ELFDATA2LSB)
{ {
// not little endian, we can't work with that // not little endian, we can't work with that
close(fd); close(fd);
@ -65,7 +85,7 @@ relf_value_t relf_open(relf_t* relf, const char* path)
// we don't care about ABI, OS, machine type or ELF type, // we don't care about ABI, OS, machine type or ELF type,
// as long as we got little endian we're good to go, so // as long as we got little endian we're good to go, so
// let's map file to memory // let's map file to memory
relf->image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); relf->image = mmap(NULL, relf->image_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd); // we can close file after mmap close(fd); // we can close file after mmap
// but still check for errors // but still check for errors
if (relf->image == MAP_FAILED) if (relf->image == MAP_FAILED)
@ -75,6 +95,128 @@ relf_value_t relf_open(relf_t* relf, const char* path)
} }
// now we need to parse segments and section headers // now we need to parse segments and section headers
// get segment and section numbers
if (relf->type == RELF_64BIT)
{
Elf64_Ehdr* elf = (Elf64_Ehdr*)relf->image;
relf->segment_num = elf->e_phnum;
relf->section_num = elf->e_shnum;
}
else
{
Elf32_Ehdr* elf = (Elf32_Ehdr*)relf->image;
relf->segment_num = elf->e_phnum;
relf->section_num = elf->e_shnum;
}
TRACE("segment_num %u section_num %u\n", relf->segment_num, relf->section_num);
if (relf->segment_num)
relf->segments = (relf_segment_t*)calloc(relf->segment_num, sizeof(relf_segment_t));
if (relf->section_num)
relf->sections = (relf_section_t*)calloc(relf->section_num, sizeof(relf_section_t));
// load segment info
if (relf->type == RELF_64BIT)
{
Elf64_Ehdr* elf = (Elf64_Ehdr*)relf->image;
for (unsigned i = 0; i < relf->segment_num; i++)
{
const Elf64_Phdr* hdr = (const Elf64_Phdr*)
((uint8_t*)relf->image + elf->e_phoff + elf->e_phentsize * i);
relf_segment_t* segment = &relf->segments[i];
segment->type = hdr->p_type;
segment->flags = hdr->p_flags;
segment->f_offset = hdr->p_offset;
segment->f_size = hdr->p_filesz;
segment->v_addr = hdr->p_vaddr;
segment->v_size = hdr->p_memsz;
TRACE_SEGMENT(segment);
}
}
else
{
Elf32_Ehdr* elf = (Elf32_Ehdr*)relf->image;
for (unsigned i = 0; i < relf->segment_num; i++)
{
const Elf32_Phdr* hdr = (const Elf32_Phdr*)
((uint8_t*)relf->image + elf->e_phoff + elf->e_phentsize * i);
relf_segment_t* segment = &relf->segments[i];
segment->type = hdr->p_type;
segment->flags = hdr->p_flags;
segment->f_offset = hdr->p_offset;
segment->f_size = hdr->p_filesz;
segment->v_addr = hdr->p_vaddr;
segment->v_size = hdr->p_memsz;
TRACE_SEGMENT(segment);
}
}
// load section info
if (relf->type == RELF_64BIT)
{
Elf64_Ehdr* elf = (Elf64_Ehdr*)relf->image;
for (unsigned i = 0; i < relf->section_num; i++)
{
const Elf64_Shdr* hdr = (const Elf64_Shdr*)
((uint8_t*)relf->image + elf->e_shoff + elf->e_shentsize * i);
relf_section_t* section = &relf->sections[i];
section->type = hdr->sh_type;
section->flags = hdr->sh_flags;
// we will resolve names when string table is resolved
section->name = NULL;
section->f_offset = hdr->sh_offset;
section->f_size = hdr->sh_size;
section->v_addr = hdr->sh_addr;
section->link = hdr->sh_link;
section->info = hdr->sh_info;
section->entsize = hdr->sh_entsize;
TRACE_SECTION(section);
}
}
else
{
Elf32_Ehdr* elf = (Elf32_Ehdr*)relf->image;
for (unsigned i = 0; i < relf->section_num; i++)
{
const Elf32_Shdr* hdr = (const Elf32_Shdr*)
((uint8_t*)relf->image + elf->e_shoff + elf->e_shentsize * i);
relf_section_t* section = &relf->sections[i];
section->type = hdr->sh_type;
section->flags = hdr->sh_flags;
// we will resolve names when string table is resolved
section->name = NULL;
section->f_offset = hdr->sh_offset;
section->f_size = hdr->sh_size;
section->v_addr = hdr->sh_addr;
section->link = hdr->sh_link;
section->info = hdr->sh_info;
section->entsize = hdr->sh_entsize;
TRACE_SECTION(section);
}
}
return RELF_ERROR(RELF_OK); return RELF_ERROR(RELF_OK);
} }

View file

@ -2,12 +2,14 @@
#define __RELF_H #define __RELF_H
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
// composite error type // composite error type
typedef enum { typedef enum {
RELF_MMAP_FAILED = -4, // file memory mapping failed RELF_MMAP_FAILED = -5, // file memory mapping failed
RELF_UNSUPPORTED = -3, // big endian or not x86/x86-64 architecture RELF_UNSUPPORTED = -4, // big endian or not x86/x86-64 architecture
RELF_NOT_AN_ELF = -2, // wrong magic RELF_NOT_AN_ELF = -3, // wrong magic
RELF_TOO_BIG = -2, // file is over size_t limit
RELF_FAILED_OPEN = -1, // failed to stat or open file RELF_FAILED_OPEN = -1, // failed to stat or open file
RELF_OK = 0, RELF_OK = 0,
} relf_error_t; } relf_error_t;
@ -26,8 +28,8 @@ typedef union {
#define RELF_ERROR(e) ((relf_value_t) {.error = e}) #define RELF_ERROR(e) ((relf_value_t) {.error = e})
typedef enum { typedef enum {
RELF_64BIT, RELF_32BIT,
RELF_32BIT RELF_64BIT
} relf_type_t; } relf_type_t;
// we're using our own structures so parsing // we're using our own structures so parsing
@ -60,14 +62,29 @@ typedef struct {
uint64_t f_offset; uint64_t f_offset;
uint64_t f_size; uint64_t f_size;
uint64_t v_addr;
uint32_t link;
uint32_t info;
// for symbol table will tell size of symbol entry
uint64_t entsize;
} relf_section_t; } relf_section_t;
// relf instance // relf instance
typedef struct { typedef struct {
void* image; void* image;
size_t image_size;
// is it 64 or 32 bit mode // is it 64 or 32 bit mode
relf_type_t type; relf_type_t type;
unsigned segment_num;
unsigned section_num;
relf_segment_t* segments;
relf_section_t* sections;
} relf_t; } relf_t;
// opens ELF file, checks ELF magic and maps it into memory // opens ELF file, checks ELF magic and maps it into memory

28
src/relf/relf_debug.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef __RELF_DEBUG_H
#define __RELF_DEBUG_H
#include "blackjack/debug.h"
#ifdef DEBUG
#define TRACE_SEGMENT(segment) \
TRACE("segment type %u flags %u f_offset 0x%lx f_size %lu v_addr 0x%lx v_size %lu\n", \
segment->type, segment->flags, \
segment->f_offset, segment->f_size, \
segment->v_addr, segment->v_size \
)
#define TRACE_SECTION(section) \
TRACE("section type %x flags %x name %s f_offset 0x%lx f_size %lu v_addr 0x%lx link %x info %x entsize %lu\n", \
section->type, section->flags, \
section->name, \
section->f_offset, section->f_size, \
section->v_addr, \
section->link, section->info, \
section->entsize \
)
#else
#define TRACE_SEGMENT(segment)
#define TRACE_SECTION(section)
#endif
#endif