Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
core.elf: Prepare for generic ELF I/O
Browse files Browse the repository at this point in the history
I.e., allowing to read non-native ELF files too. The byte order must
match the target's though.
  • Loading branch information
kinke committed Dec 8, 2018
1 parent ace7e9a commit 3baaece
Showing 1 changed file with 131 additions and 101 deletions.
232 changes: 131 additions & 101 deletions src/core/elf.d
Expand Up @@ -144,114 +144,165 @@ struct SharedObject
}
}

// -------------------------------
// File-based memory-mapped I/O:
// -------------------------------

struct ElfFile
/// File-based memory-mapped I/O
template ElfIO(alias Elf_Ehdr, alias Elf_Shdr, byte ELFCLASS)
{
@nogc nothrow:
static bool open(const(char)* path, out ElfFile file)
struct ElfFile
{
file = ElfFile(.open(path, O_RDONLY));
return file.isValid();
}
@nogc nothrow:
static bool open(const(char)* path, out ElfFile file)
{
file = ElfFile(.open(path, O_RDONLY));
return file.isValid();
}

this(int fd)
{
this.fd = fd;
if (fd != -1)
this(int fd)
{
// memory map header
this.ehdr = MMapRegion!Elf_Ehdr(fd, 0);
this.fd = fd;
if (fd != -1)
{
// memory map header
this.ehdr = MMapRegion!Elf_Ehdr(fd, 0);
}
}
}

@disable this(this);
@disable this(this);

~this()
{
if (fd != -1)
close(fd);
}
~this()
{
if (fd != -1)
close(fd);
}

private int fd = -1;
MMapRegion!Elf_Ehdr ehdr;
private int fd = -1;
MMapRegion!Elf_Ehdr ehdr;

bool isValid() const
{
return fd != -1 && isValidElfHeader(*ehdr);
}
bool isValid() const
{
enum EI_MAG0 = 0;
enum EI_MAG1 = 1;
enum EI_MAG2 = 2;
enum EI_MAG3 = 3;
enum EI_CLASS = 4;
enum EI_DATA = 5;

bool findSectionHeaderByName(const(char)[] sectionName, out ElfSectionHeader header) const
{
const index = findSectionIndexByName(sectionName);
if (index == -1)
return false;
header = ElfSectionHeader(this, index);
return true;
}
enum ELFMAG0 = 0x7f;
enum ELFMAG1 = 'E';
enum ELFMAG2 = 'L';
enum ELFMAG3 = 'F';

size_t findSectionIndexByName(const(char)[] sectionName) const
{
const stringSectionHeader = ElfSectionHeader(this, ehdr.e_shstrndx);
const stringSection = ElfSection(this, stringSectionHeader);
enum ELFCLASS32 = 1;
enum ELFCLASS64 = 2;

enum ELFDATA2LSB = 1;
enum ELFDATA2MSB = 2;

version (LittleEndian) alias ELFDATA = ELFDATA2LSB;
else version (BigEndian) alias ELFDATA = ELFDATA2MSB;
else static assert(0, "unsupported byte order");

if (fd == -1)
return false;

const ident = ehdr.e_ident;

foreach (i; 0 .. ehdr.e_shnum)
if (!(ident[EI_MAG0] == ELFMAG0 &&
ident[EI_MAG1] == ELFMAG1 &&
ident[EI_MAG2] == ELFMAG2 &&
ident[EI_MAG3] == ELFMAG3))
return false;

if (ident[EI_CLASS] != ELFCLASS)
return false;

// the file's byte order must correspond with the native one
if (ident[EI_DATA] != ELFDATA)
return false;

return true;
}

bool findSectionHeaderByName(const(char)[] sectionName, out ElfSectionHeader header) const
{
auto sectionHeader = ElfSectionHeader(this, i);
auto pCurrentName = cast(const(char)*) (stringSection.data.ptr + sectionHeader.sh_name);
auto currentName = pCurrentName[0 .. strlen(pCurrentName)];
if (sectionName == currentName)
return i;
const index = findSectionIndexByName(sectionName);
if (index == -1)
return false;
header = ElfSectionHeader(this, index);
return true;
}

// not found
return -1;
size_t findSectionIndexByName(const(char)[] sectionName) const
{
const stringSectionHeader = ElfSectionHeader(this, ehdr.e_shstrndx);
const stringSection = ElfSection(this, stringSectionHeader);

foreach (i; 0 .. ehdr.e_shnum)
{
auto sectionHeader = ElfSectionHeader(this, i);
auto pCurrentName = cast(const(char)*) (stringSection.data.ptr + sectionHeader.sh_name);
auto currentName = pCurrentName[0 .. strlen(pCurrentName)];
if (sectionName == currentName)
return i;
}

// not found
return -1;
}
}
}

struct ElfSectionHeader
{
@nogc nothrow:
this(ref const ElfFile file, size_t index)
struct ElfSectionHeader
{
assert(Elf_Shdr.sizeof == file.ehdr.e_shentsize);
shdr = MMapRegion!Elf_Shdr(
file.fd,
file.ehdr.e_shoff + index * Elf_Shdr.sizeof
);
}
@nogc nothrow:
this(ref const ElfFile file, size_t index)
{
assert(Elf_Shdr.sizeof == file.ehdr.e_shentsize);
shdr = MMapRegion!Elf_Shdr(
file.fd,
file.ehdr.e_shoff + index * Elf_Shdr.sizeof
);
}

@disable this(this);
@disable this(this);

alias shdr this;
MMapRegion!Elf_Shdr shdr;
}
alias shdr this;
MMapRegion!Elf_Shdr shdr;
}

struct ElfSection
{
@nogc nothrow:
this(ref const ElfFile file, ref const ElfSectionHeader shdr)
struct ElfSection
{
mappedRegion = MMapRegion!void(file.fd, shdr.sh_offset, shdr.sh_size);
size = shdr.sh_size;
}
@nogc nothrow:
this(ref const ElfFile file, ref const ElfSectionHeader shdr)
{
mappedRegion = MMapRegion!void(file.fd, shdr.sh_offset, shdr.sh_size);
size = shdr.sh_size;
}

@disable this(this);
@disable this(this);

const(void)[] data() const
{
return mappedRegion.data[0 .. size];
}
const(void)[] data() const
{
return mappedRegion.data[0 .. size];
}

alias data this;
alias data this;

private:
MMapRegion!void mappedRegion;
size_t size;
private:
MMapRegion!void mappedRegion;
size_t size;
}
}

enum ELFCLASS32 = 1;
enum ELFCLASS64 = 2;

version (D_LP64) alias ELFCLASS = ELFCLASS64;
else alias ELFCLASS = ELFCLASS32;

// convenience aliases for native ELF files
alias ElfFile = ElfIO!(Elf_Ehdr, Elf_Shdr, ELFCLASS).ElfFile;
alias ElfSectionHeader = ElfIO!(Elf_Ehdr, Elf_Shdr, ELFCLASS).ElfSectionHeader;
alias ElfSection = ElfIO!(Elf_Ehdr, Elf_Shdr, ELFCLASS).ElfSection;

private @nogc nothrow:

version (linux)
Expand All @@ -267,27 +318,6 @@ else
extern(C) const(char)* getprogname();
}

bool isValidElfHeader(ref const Elf_Ehdr ehdr)
{
version (D_LP64) alias ELFCLASS = ELFCLASS64;
else alias ELFCLASS = ELFCLASS32;

version (LittleEndian) alias ELFDATA = ELFDATA2LSB;
else version (BigEndian) alias ELFDATA = ELFDATA2MSB;
else static assert(0, "unsupported byte order");

if (ehdr.e_ident[EI_MAG0] != ELFMAG0) return false;
if (ehdr.e_ident[EI_MAG1] != ELFMAG1) return false;
if (ehdr.e_ident[EI_MAG2] != ELFMAG2) return false;
if (ehdr.e_ident[EI_MAG3] != ELFMAG3) return false;

// elf class and data encoding should match target's config
if (ehdr.e_ident[EI_CLASS] != ELFCLASS) return false;
if (ehdr.e_ident[EI_DATA] != ELFDATA ) return false;

return true;
}

struct MMapRegion(T)
{
@nogc nothrow:
Expand Down

0 comments on commit 3baaece

Please sign in to comment.