Skip to content

Commit

Permalink
feat(ram): Add flat ELF loading capabilities (#485)
Browse files Browse the repository at this point in the history
This adds the ELF format as an additional pre-loadable file format.
  • Loading branch information
zarubaf authored Oct 29, 2024
1 parent 2047043 commit c1a64ae
Show file tree
Hide file tree
Showing 4 changed files with 324 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ else
SIM_LDFLAGS += -lzstd
endif

# Elf image support
IMAGE_ELF ?= 1
ifeq ($(IMAGE_ELF),0)
SIM_CXXFLAGS += -DNO_IMAGE_ELF
endif

# spike-dasm plugin
WITH_SPIKE_DASM ?= 1
ifeq ($(WITH_SPIKE_DASM),1)
Expand Down
124 changes: 124 additions & 0 deletions src/test/csrc/common/elfloader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/***************************************************************************************
* Copyright (c) 2024 Axelera AI
*
* DiffTest is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "elfloader.h"

void ElfBinary::load() {
assert(size >= sizeof(Elf64_Ehdr));
eh64 = (const Elf64_Ehdr *)raw;
assert(IS_ELF32(*eh64) || IS_ELF64(*eh64));

if (IS_ELF32(*eh64))
parse(data32);
else
parse(data64);
}

template <typename ehdr_t, typename phdr_t, typename shdr_t, typename sym_t>
void ElfBinary::parse(ElfBinaryData<ehdr_t, phdr_t, shdr_t, sym_t> &data) {
data.eh = (const ehdr_t *)raw;
data.ph = (const phdr_t *)(raw + data.eh->e_phoff);
entry = data.eh->e_entry;
assert(size >= data.eh->e_phoff + data.eh->e_phnum * sizeof(*data.ph));
for (unsigned i = 0; i < data.eh->e_phnum; i++) {
if (data.ph[i].p_type == PT_LOAD && data.ph[i].p_memsz) {
if (data.ph[i].p_filesz) {
assert(size >= data.ph[i].p_offset + data.ph[i].p_filesz);
sections.push_back({
.data_src = (const uint8_t *)raw + data.ph[i].p_offset,
.data_dst = data.ph[i].p_paddr,
.data_len = data.ph[i].p_filesz,
.zero_dst = data.ph[i].p_paddr + data.ph[i].p_filesz,
.zero_len = data.ph[i].p_memsz - data.ph[i].p_filesz,
});
}
}
}
}

ElfBinaryFile::ElfBinaryFile(const char *filename) : filename(filename) {
int fd = open(filename, O_RDONLY);
struct stat s;
assert(fd != -1);
assert(fstat(fd, &s) >= 0);
size = s.st_size;

raw = (uint8_t *)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
assert(raw != MAP_FAILED);
close(fd);

load();
}

ElfBinaryFile::~ElfBinaryFile() {
if (raw)
munmap((void *)raw, size);
}

bool isElfFile(const char *filename) {
int fd = -1;

#ifdef NO_IMAGE_ELF
return false;
#endif

fd = open(filename, O_RDONLY);
assert(fd);

uint8_t buf[4];

size_t sz = read(fd, buf, 4);
if (!IS_ELF(*((const Elf64_Ehdr *)buf))) {
close(fd);
return false;
}

close(fd);
return true;
}

long readFromElf(void *ptr, const char *file_name, long buf_size) {
auto elf_file = ElfBinaryFile(file_name);

if (elf_file.sections.size() < 1) {
printf("The requested elf '%s' contains zero sections\n", file_name);
return -1;
}

uint64_t len_written = 0;
auto base_addr = elf_file.sections[0].data_dst;

if (base_addr != PMEM_BASE) {
printf(
"The first address in the elf does not match the base of the physical memory. It is likely that execution "
"leads to unexpected behaviour.");
}

for (auto section: elf_file.sections) {
auto len = section.data_len + section.zero_len;
auto offset = section.data_dst - base_addr;

if (offset + len > buf_size) {
printf("The size (%ld bytes) of the section at address 0x%lx is larger than buf_size!\n", len, section.data_dst);
return -1;
}

printf("Loading %ld bytes at address 0x%lx at offset 0x%lx\n", len, section.data_dst, offset);
std::memset((uint8_t *)ptr + offset, 0, len);
std::memcpy((uint8_t *)ptr + offset, section.data_src, section.data_len);
len_written += len;
}
return len_written;
}
189 changes: 189 additions & 0 deletions src/test/csrc/common/elfloader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/***************************************************************************************
* Copyright (c) 2024 Axelera AI
*
* DiffTest is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#ifndef __ELFLOADER_H
#define __ELFLOADER_H

#include "config.h"
#include <assert.h>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <map>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <vector>

#define IS_ELF(hdr) \
((hdr).e_ident[0] == 0x7f && (hdr).e_ident[1] == 'E' && (hdr).e_ident[2] == 'L' && (hdr).e_ident[3] == 'F')

#define IS_ELF32(hdr) (IS_ELF(hdr) && (hdr).e_ident[4] == 1)
#define IS_ELF64(hdr) (IS_ELF(hdr) && (hdr).e_ident[4] == 2)

#define PT_LOAD 1
#define SHT_NOBITS 8
#define SHT_PROGBITS 0x1
#define SHT_GROUP 0x11

typedef struct {
uint8_t e_ident[16];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry;
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} Elf32_Ehdr;

typedef struct {
uint32_t sh_name;
uint32_t sh_type;
uint32_t sh_flags;
uint32_t sh_addr;
uint32_t sh_offset;
uint32_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint32_t sh_addralign;
uint32_t sh_entsize;
} Elf32_Shdr;

typedef struct {
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} Elf32_Phdr;

typedef struct {
uint32_t st_name;
uint32_t st_value;
uint32_t st_size;
uint8_t st_info;
uint8_t st_other;
uint16_t st_shndx;
} Elf32_Sym;

typedef struct {
uint8_t e_ident[16];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint64_t e_entry;
uint64_t e_phoff;
uint64_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} Elf64_Ehdr;

typedef struct {
uint32_t sh_name;
uint32_t sh_type;
uint64_t sh_flags;
uint64_t sh_addr;
uint64_t sh_offset;
uint64_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint64_t sh_addralign;
uint64_t sh_entsize;
} Elf64_Shdr;

typedef struct {
uint32_t p_type;
uint32_t p_flags;
uint64_t p_offset;
uint64_t p_vaddr;
uint64_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
} Elf64_Phdr;

typedef struct {
uint32_t st_name;
uint8_t st_info;
uint8_t st_other;
uint16_t st_shndx;
uint64_t st_value;
uint64_t st_size;
} Elf64_Sym;

template <typename ehdr_t, typename phdr_t, typename shdr_t, typename sym_t> struct ElfBinaryData {
const ehdr_t *eh = nullptr;
const phdr_t *ph = nullptr;
};

struct ElfSection {
const uint8_t *data_src;
uint64_t data_dst;
size_t data_len;
uint64_t zero_dst;
size_t zero_len;
};

struct ElfBinary {
const uint8_t *raw = nullptr;
size_t size = 0;

const Elf64_Ehdr *eh64 = nullptr;
ElfBinaryData<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym> data32;
ElfBinaryData<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Sym> data64;

uint64_t entry;
std::vector<ElfSection> sections;

void load();

template <typename ehdr_t, typename phdr_t, typename shdr_t, typename sym_t>
void parse(ElfBinaryData<ehdr_t, phdr_t, shdr_t, sym_t> &data);
};

struct ElfBinaryFile : public ElfBinary {
const std::string filename;

ElfBinaryFile(const char *filename);

~ElfBinaryFile();
};

// Is the file at the given path an Elf file
bool isElfFile(const char *filename);
// load binary content at `file_name` into ptr. Returns the number of bytes
// written.
long readFromElf(void *ptr, const char *file_name, long buf_size);

#endif // __ELFLOADER_H
5 changes: 5 additions & 0 deletions src/test/csrc/common/ram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "ram.h"
#include "common.h"
#include "compress.h"
#include "elfloader.h"
#include <iostream>
#include <sys/mman.h>
#ifdef CONFIG_DIFFTEST_PERFCNT
Expand Down Expand Up @@ -284,6 +285,10 @@ MmapMemory::MmapMemory(const char *image, uint64_t n_bytes) : SimMemory(n_bytes)
Info("Zstd file detected and loading image from extracted zstd file\n");
img_size = readFromZstd(ram, image, memory_size, LOAD_RAM);
assert(img_size >= 0);
} else if (isElfFile(image)) {
Info("ELF file detected and loading image from extracted elf file\n");
img_size = readFromElf(ram, image, memory_size);
assert(img_size >= 0);
} else {
InputReader *reader = createInputReader(image);
img_size = reader->read_all(ram, memory_size);
Expand Down

0 comments on commit c1a64ae

Please sign in to comment.