mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-25 22:10:21 +00:00
ELF-Loader: C++ plugin support, loader overhaul. (#1744)
* fap-loader: load all code and data sections * fap-loader: relocate all code and data sections * fap-loader: remove old elf loader * fap-loader: new jmp call relocation * openocd: resume on detach * fap-loader: trampoline for big jumps * fap-loader: rename cache * fap-loader: init_array support * fap-loader: untangled flipper_application into separate entities * fap-loader: fix debug * fap-loader: optimize section container * fap-loader: optimize key for section container * fap-loader: disable debug log * documentation * F7: bump api symbols version * Lib: cleanup elf_file.c Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
7e2008095e
commit
e6d22ed147
14 changed files with 1094 additions and 649 deletions
|
@ -25,7 +25,7 @@ static bool
|
|||
FlipperApplication* app = flipper_application_alloc(loader->storage, &hashtable_api_interface);
|
||||
|
||||
FlipperApplicationPreloadStatus preload_res =
|
||||
flipper_application_preload(app, string_get_cstr(path));
|
||||
flipper_application_preload_manifest(app, string_get_cstr(path));
|
||||
|
||||
bool load_success = false;
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ class AppState:
|
|||
|
||||
def is_loaded_in_gdb(self, gdb_app) -> bool:
|
||||
# Avoid constructing full app wrapper for comparison
|
||||
return self.entry_address == int(gdb_app["entry"])
|
||||
return self.entry_address == int(gdb_app["state"]["entry"])
|
||||
|
||||
@staticmethod
|
||||
def parse_debug_link_data(section_data: bytes) -> Tuple[str, int]:
|
||||
|
@ -78,13 +78,13 @@ class AppState:
|
|||
@staticmethod
|
||||
def from_gdb(gdb_app: "AppState") -> "AppState":
|
||||
state = AppState(str(gdb_app["manifest"]["name"].string()))
|
||||
state.entry_address = int(gdb_app["entry"])
|
||||
state.entry_address = int(gdb_app["state"]["entry"])
|
||||
|
||||
app_state = gdb_app["state"]
|
||||
if debug_link_size := int(app_state["debug_link_size"]):
|
||||
if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]):
|
||||
debug_link_data = (
|
||||
gdb.selected_inferior()
|
||||
.read_memory(int(app_state["debug_link"]), debug_link_size)
|
||||
.read_memory(int(app_state["debug_link_info"]["debug_link"]), debug_link_size)
|
||||
.tobytes()
|
||||
)
|
||||
state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data(
|
||||
|
|
|
@ -101,3 +101,7 @@ $_TARGETNAME configure -event trace-config {
|
|||
# assignment
|
||||
mmw 0xE0042004 0x00000020 0
|
||||
}
|
||||
|
||||
$_TARGETNAME configure -event gdb-detach {
|
||||
resume
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,1.9,,
|
||||
Version,+,1.10,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
|
@ -779,13 +779,13 @@ Function,-,fiprintf,int,"FILE*, const char*, ..."
|
|||
Function,-,fiscanf,int,"FILE*, const char*, ..."
|
||||
Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_free,void,FlipperApplication*
|
||||
Function,-,flipper_application_get_entry_address,const void*,FlipperApplication*
|
||||
Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication*
|
||||
Function,-,flipper_application_get_state,const FlipperApplicationState*,FlipperApplication*
|
||||
Function,-,flipper_application_get_thread,FuriThread*,FlipperApplication*
|
||||
Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus
|
||||
Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest*
|
||||
Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication*
|
||||
Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*"
|
||||
Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*"
|
||||
Function,-,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus
|
||||
Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*"
|
||||
Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage*
|
||||
|
|
|
21
lib/flipper_application/application_manifest.c
Normal file
21
lib/flipper_application/application_manifest.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include "application_manifest.h"
|
||||
|
||||
bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest) {
|
||||
if((manifest->base.manifest_magic != FAP_MANIFEST_MAGIC) ||
|
||||
(manifest->base.manifest_version != FAP_MANIFEST_SUPPORTED_VERSION)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flipper_application_manifest_is_compatible(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface) {
|
||||
if(manifest->base.api_version.major != api_interface->api_version_major /* ||
|
||||
manifest->base.api_version.minor > app->api_interface->api_version_minor */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,6 +1,12 @@
|
|||
/**
|
||||
* @file application_manifest.h
|
||||
* Flipper application manifest
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "elf/elf_api_interface.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -40,6 +46,25 @@ typedef FlipperApplicationManifestV1 FlipperApplicationManifest;
|
|||
|
||||
#pragma pack(pop)
|
||||
|
||||
/**
|
||||
* @brief Check if manifest is valid
|
||||
*
|
||||
* @param manifest
|
||||
* @return bool
|
||||
*/
|
||||
bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest);
|
||||
|
||||
/**
|
||||
* @brief Check if manifest is compatible with current ELF API interface
|
||||
*
|
||||
* @param manifest
|
||||
* @param api_interface
|
||||
* @return bool
|
||||
*/
|
||||
bool flipper_application_manifest_is_compatible(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <flipper_application/elf/elf.h>
|
||||
#include <elf.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define ELF_INVALID_ADDRESS 0xFFFFFFFF
|
||||
|
|
794
lib/flipper_application/elf/elf_file.c
Normal file
794
lib/flipper_application/elf/elf_file.c
Normal file
|
@ -0,0 +1,794 @@
|
|||
#include <elf.h>
|
||||
#include "elf_file.h"
|
||||
#include "elf_file_i.h"
|
||||
#include "elf_api_interface.h"
|
||||
|
||||
#define TAG "elf"
|
||||
|
||||
#define ELF_NAME_BUFFER_LEN 32
|
||||
#define SECTION_OFFSET(e, n) (e->section_table + n * sizeof(Elf32_Shdr))
|
||||
#define IS_FLAGS_SET(v, m) ((v & m) == m)
|
||||
#define RESOLVER_THREAD_YIELD_STEP 30
|
||||
|
||||
// #define ELF_DEBUG_LOG 1
|
||||
|
||||
#ifndef ELF_DEBUG_LOG
|
||||
#undef FURI_LOG_D
|
||||
#define FURI_LOG_D(...)
|
||||
#endif
|
||||
|
||||
#define TRAMPOLINE_CODE_SIZE 6
|
||||
|
||||
/**
|
||||
ldr r12, [pc, #2]
|
||||
bx r12
|
||||
*/
|
||||
const uint8_t trampoline_code_little_endian[TRAMPOLINE_CODE_SIZE] =
|
||||
{0xdf, 0xf8, 0x02, 0xc0, 0x60, 0x47};
|
||||
|
||||
typedef struct {
|
||||
uint8_t code[TRAMPOLINE_CODE_SIZE];
|
||||
uint32_t addr;
|
||||
} __attribute__((packed)) JMPTrampoline;
|
||||
|
||||
/**************************************************************************************************/
|
||||
/********************************************* Caches *********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
static bool address_cache_get(AddressCache_t cache, int symEntry, Elf32_Addr* symAddr) {
|
||||
Elf32_Addr* addr = AddressCache_get(cache, symEntry);
|
||||
if(addr) {
|
||||
*symAddr = *addr;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void address_cache_put(AddressCache_t cache, int symEntry, Elf32_Addr symAddr) {
|
||||
AddressCache_set_at(cache, symEntry, symAddr);
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/********************************************** ELF ***********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
static ELFSection* elf_file_get_section(ELFFile* elf, const char* name) {
|
||||
return ELFSectionDict_get(elf->sections, name);
|
||||
}
|
||||
|
||||
static void elf_file_put_section(ELFFile* elf, const char* name, ELFSection* section) {
|
||||
ELFSectionDict_set_at(elf->sections, strdup(name), *section);
|
||||
}
|
||||
|
||||
static bool elf_read_string_from_offset(ELFFile* elf, off_t offset, string_t name) {
|
||||
bool result = false;
|
||||
|
||||
off_t old = storage_file_tell(elf->fd);
|
||||
|
||||
do {
|
||||
if(!storage_file_seek(elf->fd, offset, true)) break;
|
||||
|
||||
char buffer[ELF_NAME_BUFFER_LEN + 1];
|
||||
buffer[ELF_NAME_BUFFER_LEN] = 0;
|
||||
|
||||
while(true) {
|
||||
uint16_t read = storage_file_read(elf->fd, buffer, ELF_NAME_BUFFER_LEN);
|
||||
string_cat_str(name, buffer);
|
||||
if(strlen(buffer) < ELF_NAME_BUFFER_LEN) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(storage_file_get_error(elf->fd) != FSE_OK || read == 0) break;
|
||||
}
|
||||
|
||||
} while(false);
|
||||
storage_file_seek(elf->fd, old, true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool elf_read_section_name(ELFFile* elf, off_t offset, string_t name) {
|
||||
return elf_read_string_from_offset(elf, elf->section_table_strings + offset, name);
|
||||
}
|
||||
|
||||
static bool elf_read_symbol_name(ELFFile* elf, off_t offset, string_t name) {
|
||||
return elf_read_string_from_offset(elf, elf->symbol_table_strings + offset, name);
|
||||
}
|
||||
|
||||
static bool elf_read_section_header(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header) {
|
||||
off_t offset = SECTION_OFFSET(elf, section_idx);
|
||||
return storage_file_seek(elf->fd, offset, true) &&
|
||||
storage_file_read(elf->fd, section_header, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr);
|
||||
}
|
||||
|
||||
static bool
|
||||
elf_read_section(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header, string_t name) {
|
||||
if(!elf_read_section_header(elf, section_idx, section_header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(section_header->sh_name && !elf_read_section_name(elf, section_header->sh_name, name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool elf_read_symbol(ELFFile* elf, int n, Elf32_Sym* sym, string_t name) {
|
||||
bool success = false;
|
||||
off_t old = storage_file_tell(elf->fd);
|
||||
off_t pos = elf->symbol_table + n * sizeof(Elf32_Sym);
|
||||
if(storage_file_seek(elf->fd, pos, true) &&
|
||||
storage_file_read(elf->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) {
|
||||
if(sym->st_name)
|
||||
success = elf_read_symbol_name(elf, sym->st_name, name);
|
||||
else {
|
||||
Elf32_Shdr shdr;
|
||||
success = elf_read_section(elf, sym->st_shndx, &shdr, name);
|
||||
}
|
||||
}
|
||||
storage_file_seek(elf->fd, old, true);
|
||||
return success;
|
||||
}
|
||||
|
||||
static ELFSection* elf_section_of(ELFFile* elf, int index) {
|
||||
ELFSectionDict_it_t it;
|
||||
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
|
||||
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
|
||||
if(itref->value.sec_idx == index) {
|
||||
return &itref->value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) {
|
||||
if(sym->st_shndx == SHN_UNDEF) {
|
||||
Elf32_Addr addr = 0;
|
||||
if(elf->api_interface->resolver_callback(sName, &addr)) {
|
||||
return addr;
|
||||
}
|
||||
} else {
|
||||
ELFSection* symSec = elf_section_of(elf, sym->st_shndx);
|
||||
if(symSec) {
|
||||
return ((Elf32_Addr)symSec->data) + sym->st_value;
|
||||
}
|
||||
}
|
||||
FURI_LOG_D(TAG, " Can not find address for symbol %s", sName);
|
||||
return ELF_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
__attribute__((unused)) static const char* elf_reloc_type_to_str(int symt) {
|
||||
#define STRCASE(name) \
|
||||
case name: \
|
||||
return #name;
|
||||
switch(symt) {
|
||||
STRCASE(R_ARM_NONE)
|
||||
STRCASE(R_ARM_TARGET1)
|
||||
STRCASE(R_ARM_ABS32)
|
||||
STRCASE(R_ARM_THM_PC22)
|
||||
STRCASE(R_ARM_THM_JUMP24)
|
||||
default:
|
||||
return "R_<unknow>";
|
||||
}
|
||||
#undef STRCASE
|
||||
}
|
||||
|
||||
static JMPTrampoline* elf_create_trampoline(Elf32_Addr addr) {
|
||||
JMPTrampoline* trampoline = malloc(sizeof(JMPTrampoline));
|
||||
memcpy(trampoline->code, trampoline_code_little_endian, TRAMPOLINE_CODE_SIZE);
|
||||
trampoline->addr = addr;
|
||||
return trampoline;
|
||||
}
|
||||
|
||||
static void elf_relocate_jmp_call(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
|
||||
int offset, hi, lo, s, j1, j2, i1, i2, imm10, imm11;
|
||||
int to_thumb, is_call, blx_bit = 1 << 12;
|
||||
|
||||
/* Get initial offset */
|
||||
hi = ((uint16_t*)relAddr)[0];
|
||||
lo = ((uint16_t*)relAddr)[1];
|
||||
s = (hi >> 10) & 1;
|
||||
j1 = (lo >> 13) & 1;
|
||||
j2 = (lo >> 11) & 1;
|
||||
i1 = (j1 ^ s) ^ 1;
|
||||
i2 = (j2 ^ s) ^ 1;
|
||||
imm10 = hi & 0x3ff;
|
||||
imm11 = lo & 0x7ff;
|
||||
offset = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
|
||||
if(offset & 0x01000000) offset -= 0x02000000;
|
||||
|
||||
to_thumb = symAddr & 1;
|
||||
is_call = (type == R_ARM_THM_PC22);
|
||||
|
||||
/* Store offset */
|
||||
int offset_copy = offset;
|
||||
|
||||
/* Compute final offset */
|
||||
offset += symAddr - relAddr;
|
||||
if(!to_thumb && is_call) {
|
||||
blx_bit = 0; /* bl -> blx */
|
||||
offset = (offset + 3) & -4; /* Compute offset from aligned PC */
|
||||
}
|
||||
|
||||
/* Check that relocation is possible
|
||||
* offset must not be out of range
|
||||
* if target is to be entered in arm mode:
|
||||
- bit 1 must not set
|
||||
- instruction must be a call (bl) or a jump to PLT */
|
||||
if(!to_thumb || offset >= 0x1000000 || offset < -0x1000000) {
|
||||
if(to_thumb || (symAddr & 2) || (!is_call)) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"can't relocate value at %x, %s, doing trampoline",
|
||||
relAddr,
|
||||
elf_reloc_type_to_str(type));
|
||||
|
||||
Elf32_Addr addr;
|
||||
if(!address_cache_get(elf->trampoline_cache, symAddr, &addr)) {
|
||||
addr = (Elf32_Addr)elf_create_trampoline(symAddr);
|
||||
address_cache_put(elf->trampoline_cache, symAddr, addr);
|
||||
}
|
||||
|
||||
offset = offset_copy;
|
||||
offset += (int)addr - relAddr;
|
||||
if(!to_thumb && is_call) {
|
||||
blx_bit = 0; /* bl -> blx */
|
||||
offset = (offset + 3) & -4; /* Compute offset from aligned PC */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute and store final offset */
|
||||
s = (offset >> 24) & 1;
|
||||
i1 = (offset >> 23) & 1;
|
||||
i2 = (offset >> 22) & 1;
|
||||
j1 = s ^ (i1 ^ 1);
|
||||
j2 = s ^ (i2 ^ 1);
|
||||
imm10 = (offset >> 12) & 0x3ff;
|
||||
imm11 = (offset >> 1) & 0x7ff;
|
||||
(*(uint16_t*)relAddr) = (uint16_t)((hi & 0xf800) | (s << 10) | imm10);
|
||||
(*(uint16_t*)(relAddr + 2)) =
|
||||
(uint16_t)((lo & 0xc000) | (j1 << 13) | blx_bit | (j2 << 11) | imm11);
|
||||
}
|
||||
|
||||
static bool elf_relocate_symbol(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
|
||||
switch(type) {
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_ABS32:
|
||||
*((uint32_t*)relAddr) += symAddr;
|
||||
FURI_LOG_D(TAG, " R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
|
||||
break;
|
||||
case R_ARM_THM_PC22:
|
||||
case R_ARM_THM_JUMP24:
|
||||
elf_relocate_jmp_call(elf, relAddr, type, symAddr);
|
||||
FURI_LOG_D(
|
||||
TAG, " R_ARM_THM_CALL/JMP relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, " Undefined relocation %d", type);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool elf_relocate(ELFFile* elf, Elf32_Shdr* h, ELFSection* s) {
|
||||
if(s->data) {
|
||||
Elf32_Rel rel;
|
||||
size_t relEntries = h->sh_size / sizeof(rel);
|
||||
size_t relCount;
|
||||
(void)storage_file_seek(elf->fd, h->sh_offset, true);
|
||||
FURI_LOG_D(TAG, " Offset Info Type Name");
|
||||
|
||||
int relocate_result = true;
|
||||
string_t symbol_name;
|
||||
string_init(symbol_name);
|
||||
|
||||
for(relCount = 0; relCount < relEntries; relCount++) {
|
||||
if(relCount % RESOLVER_THREAD_YIELD_STEP == 0) {
|
||||
FURI_LOG_D(TAG, " reloc YIELD");
|
||||
furi_delay_tick(1);
|
||||
}
|
||||
|
||||
if(storage_file_read(elf->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) {
|
||||
FURI_LOG_E(TAG, " reloc read fail");
|
||||
string_clear(symbol_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
Elf32_Addr symAddr;
|
||||
|
||||
int symEntry = ELF32_R_SYM(rel.r_info);
|
||||
int relType = ELF32_R_TYPE(rel.r_info);
|
||||
Elf32_Addr relAddr = ((Elf32_Addr)s->data) + rel.r_offset;
|
||||
|
||||
if(!address_cache_get(elf->relocation_cache, symEntry, &symAddr)) {
|
||||
Elf32_Sym sym;
|
||||
string_reset(symbol_name);
|
||||
if(!elf_read_symbol(elf, symEntry, &sym, symbol_name)) {
|
||||
FURI_LOG_E(TAG, " symbol read fail");
|
||||
string_clear(symbol_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
" %08X %08X %-16s %s",
|
||||
(unsigned int)rel.r_offset,
|
||||
(unsigned int)rel.r_info,
|
||||
elf_reloc_type_to_str(relType),
|
||||
string_get_cstr(symbol_name));
|
||||
|
||||
symAddr = elf_address_of(elf, &sym, string_get_cstr(symbol_name));
|
||||
address_cache_put(elf->relocation_cache, symEntry, symAddr);
|
||||
}
|
||||
|
||||
if(symAddr != ELF_INVALID_ADDRESS) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
" symAddr=%08X relAddr=%08X",
|
||||
(unsigned int)symAddr,
|
||||
(unsigned int)relAddr);
|
||||
if(!elf_relocate_symbol(elf, relAddr, relType, symAddr)) {
|
||||
relocate_result = false;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, " No symbol address of %s", string_get_cstr(symbol_name));
|
||||
relocate_result = false;
|
||||
}
|
||||
}
|
||||
string_clear(symbol_name);
|
||||
|
||||
return relocate_result;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Section not loaded");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/********************************************* MISC ***********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
static bool cstr_prefix(const char* prefix, const char* string) {
|
||||
return strncmp(prefix, string, strlen(prefix)) == 0;
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/************************************ Internal FAP interfaces *************************************/
|
||||
/**************************************************************************************************/
|
||||
typedef enum {
|
||||
SectionTypeERROR = 0,
|
||||
SectionTypeUnused = 1 << 0,
|
||||
SectionTypeData = 1 << 1,
|
||||
SectionTypeRelData = 1 << 2,
|
||||
SectionTypeSymTab = 1 << 3,
|
||||
SectionTypeStrTab = 1 << 4,
|
||||
SectionTypeManifest = 1 << 5,
|
||||
SectionTypeDebugLink = 1 << 6,
|
||||
|
||||
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab | SectionTypeManifest,
|
||||
} SectionType;
|
||||
|
||||
static bool elf_load_metadata(
|
||||
ELFFile* elf,
|
||||
Elf32_Shdr* section_header,
|
||||
FlipperApplicationManifest* manifest) {
|
||||
if(section_header->sh_size < sizeof(FlipperApplicationManifest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(manifest == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return storage_file_seek(elf->fd, section_header->sh_offset, true) &&
|
||||
storage_file_read(elf->fd, manifest, section_header->sh_size) ==
|
||||
section_header->sh_size;
|
||||
}
|
||||
|
||||
static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) {
|
||||
elf->debug_link_info.debug_link_size = section_header->sh_size;
|
||||
elf->debug_link_info.debug_link = malloc(section_header->sh_size);
|
||||
|
||||
return storage_file_seek(elf->fd, section_header->sh_offset, true) &&
|
||||
storage_file_read(elf->fd, elf->debug_link_info.debug_link, section_header->sh_size) ==
|
||||
section_header->sh_size;
|
||||
}
|
||||
|
||||
static SectionType elf_preload_section(
|
||||
ELFFile* elf,
|
||||
size_t section_idx,
|
||||
Elf32_Shdr* section_header,
|
||||
string_t name_string,
|
||||
FlipperApplicationManifest* manifest) {
|
||||
const char* name = string_get_cstr(name_string);
|
||||
|
||||
const struct {
|
||||
const char* prefix;
|
||||
SectionType type;
|
||||
} lookup_sections[] = {
|
||||
{".text", SectionTypeData},
|
||||
{".rodata", SectionTypeData},
|
||||
{".data", SectionTypeData},
|
||||
{".bss", SectionTypeData},
|
||||
{".preinit_array", SectionTypeData},
|
||||
{".init_array", SectionTypeData},
|
||||
{".fini_array", SectionTypeData},
|
||||
{".rel.text", SectionTypeRelData},
|
||||
{".rel.rodata", SectionTypeRelData},
|
||||
{".rel.data", SectionTypeRelData},
|
||||
{".rel.preinit_array", SectionTypeRelData},
|
||||
{".rel.init_array", SectionTypeRelData},
|
||||
{".rel.fini_array", SectionTypeRelData},
|
||||
};
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(lookup_sections); i++) {
|
||||
if(cstr_prefix(lookup_sections[i].prefix, name)) {
|
||||
FURI_LOG_D(TAG, "Found section %s", lookup_sections[i].prefix);
|
||||
|
||||
if(lookup_sections[i].type == SectionTypeRelData) {
|
||||
name = name + strlen(".rel");
|
||||
}
|
||||
|
||||
ELFSection* section_p = elf_file_get_section(elf, name);
|
||||
if(!section_p) {
|
||||
ELFSection section = {
|
||||
.data = NULL,
|
||||
.sec_idx = 0,
|
||||
.rel_sec_idx = 0,
|
||||
.size = 0,
|
||||
};
|
||||
|
||||
elf_file_put_section(elf, name, §ion);
|
||||
section_p = elf_file_get_section(elf, name);
|
||||
}
|
||||
|
||||
if(lookup_sections[i].type == SectionTypeRelData) {
|
||||
section_p->rel_sec_idx = section_idx;
|
||||
} else {
|
||||
section_p->sec_idx = section_idx;
|
||||
}
|
||||
|
||||
return lookup_sections[i].type;
|
||||
}
|
||||
}
|
||||
|
||||
if(strcmp(name, ".symtab") == 0) {
|
||||
FURI_LOG_D(TAG, "Found .symtab section");
|
||||
elf->symbol_table = section_header->sh_offset;
|
||||
elf->symbol_count = section_header->sh_size / sizeof(Elf32_Sym);
|
||||
return SectionTypeSymTab;
|
||||
} else if(strcmp(name, ".strtab") == 0) {
|
||||
FURI_LOG_D(TAG, "Found .strtab section");
|
||||
elf->symbol_table_strings = section_header->sh_offset;
|
||||
return SectionTypeStrTab;
|
||||
} else if(strcmp(name, ".fapmeta") == 0) {
|
||||
FURI_LOG_D(TAG, "Found .fapmeta section");
|
||||
if(elf_load_metadata(elf, section_header, manifest)) {
|
||||
return SectionTypeManifest;
|
||||
} else {
|
||||
return SectionTypeERROR;
|
||||
}
|
||||
} else if(strcmp(name, ".gnu_debuglink") == 0) {
|
||||
FURI_LOG_D(TAG, "Found .gnu_debuglink section");
|
||||
if(elf_load_debug_link(elf, section_header)) {
|
||||
return SectionTypeDebugLink;
|
||||
} else {
|
||||
return SectionTypeERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return SectionTypeUnused;
|
||||
}
|
||||
|
||||
static bool elf_load_section_data(ELFFile* elf, ELFSection* section) {
|
||||
Elf32_Shdr section_header;
|
||||
if(section->sec_idx == 0) {
|
||||
FURI_LOG_D(TAG, "Section is not present");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!elf_read_section_header(elf, section->sec_idx, §ion_header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(section_header.sh_size == 0) {
|
||||
FURI_LOG_D(TAG, "No data for section");
|
||||
return true;
|
||||
}
|
||||
|
||||
section->data = aligned_malloc(section_header.sh_size, section_header.sh_addralign);
|
||||
section->size = section_header.sh_size;
|
||||
|
||||
if(section_header.sh_type == SHT_NOBITS) {
|
||||
/* section is empty (.bss?) */
|
||||
/* no need to memset - allocator already did that */
|
||||
return true;
|
||||
}
|
||||
|
||||
if((!storage_file_seek(elf->fd, section_header.sh_offset, true)) ||
|
||||
(storage_file_read(elf->fd, section->data, section_header.sh_size) !=
|
||||
section_header.sh_size)) {
|
||||
FURI_LOG_E(TAG, " seek/read fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "0x%X", section->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool elf_relocate_section(ELFFile* elf, ELFSection* section) {
|
||||
Elf32_Shdr section_header;
|
||||
if(section->rel_sec_idx) {
|
||||
FURI_LOG_D(TAG, "Relocating section");
|
||||
if(elf_read_section_header(elf, section->rel_sec_idx, §ion_header))
|
||||
return elf_relocate(elf, §ion_header, section);
|
||||
else {
|
||||
FURI_LOG_E(TAG, "Error reading section header");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "No relocation index"); /* Not an error */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void elf_file_call_section_list(ELFFile* elf, const char* name, bool reverse_order) {
|
||||
ELFSection* section = elf_file_get_section(elf, name);
|
||||
|
||||
if(section && section->size) {
|
||||
const uint32_t* start = section->data;
|
||||
const uint32_t* end = section->data + section->size;
|
||||
|
||||
if(reverse_order) {
|
||||
while(end > start) {
|
||||
end--;
|
||||
((void (*)(void))(*end))();
|
||||
}
|
||||
} else {
|
||||
while(start < end) {
|
||||
((void (*)(void))(*start))();
|
||||
start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/********************************************* Public *********************************************/
|
||||
/**************************************************************************************************/
|
||||
|
||||
ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface) {
|
||||
ELFFile* elf = malloc(sizeof(ELFFile));
|
||||
elf->fd = storage_file_alloc(storage);
|
||||
elf->api_interface = api_interface;
|
||||
ELFSectionDict_init(elf->sections);
|
||||
AddressCache_init(elf->trampoline_cache);
|
||||
return elf;
|
||||
}
|
||||
|
||||
void elf_file_free(ELFFile* elf) {
|
||||
// free sections data
|
||||
{
|
||||
ELFSectionDict_it_t it;
|
||||
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
|
||||
ELFSectionDict_next(it)) {
|
||||
const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);
|
||||
if(itref->value.data) {
|
||||
aligned_free(itref->value.data);
|
||||
}
|
||||
free((void*)itref->key);
|
||||
}
|
||||
|
||||
ELFSectionDict_clear(elf->sections);
|
||||
}
|
||||
|
||||
// free trampoline data
|
||||
{
|
||||
AddressCache_it_t it;
|
||||
for(AddressCache_it(it, elf->trampoline_cache); !AddressCache_end_p(it);
|
||||
AddressCache_next(it)) {
|
||||
const AddressCache_itref_t* itref = AddressCache_cref(it);
|
||||
free((void*)itref->value);
|
||||
}
|
||||
|
||||
AddressCache_clear(elf->trampoline_cache);
|
||||
}
|
||||
|
||||
if(elf->debug_link_info.debug_link) {
|
||||
free(elf->debug_link_info.debug_link);
|
||||
}
|
||||
|
||||
storage_file_free(elf->fd);
|
||||
free(elf);
|
||||
}
|
||||
|
||||
bool elf_file_open(ELFFile* elf, const char* path) {
|
||||
Elf32_Ehdr h;
|
||||
Elf32_Shdr sH;
|
||||
|
||||
if(!storage_file_open(elf->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) ||
|
||||
!storage_file_seek(elf->fd, 0, true) ||
|
||||
storage_file_read(elf->fd, &h, sizeof(h)) != sizeof(h) ||
|
||||
!storage_file_seek(elf->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) ||
|
||||
storage_file_read(elf->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
elf->entry = h.e_entry;
|
||||
elf->sections_count = h.e_shnum;
|
||||
elf->section_table = h.e_shoff;
|
||||
elf->section_table_strings = sH.sh_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest) {
|
||||
bool result = false;
|
||||
string_t name;
|
||||
string_init(name);
|
||||
|
||||
FURI_LOG_D(TAG, "Looking for manifest section");
|
||||
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
|
||||
Elf32_Shdr section_header;
|
||||
|
||||
string_reset(name);
|
||||
if(!elf_read_section(elf, section_idx, §ion_header, name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(string_cmp(name, ".fapmeta") == 0) {
|
||||
if(elf_load_metadata(elf, §ion_header, manifest)) {
|
||||
FURI_LOG_D(TAG, "Load manifest done");
|
||||
result = true;
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manifest) {
|
||||
SectionType loaded_sections = SectionTypeERROR;
|
||||
string_t name;
|
||||
string_init(name);
|
||||
|
||||
FURI_LOG_D(TAG, "Scan ELF indexs...");
|
||||
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
|
||||
Elf32_Shdr section_header;
|
||||
|
||||
string_reset(name);
|
||||
if(!elf_read_section(elf, section_idx, §ion_header, name)) {
|
||||
loaded_sections = SectionTypeERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Preloading data for section #%d %s", section_idx, string_get_cstr(name));
|
||||
SectionType section_type =
|
||||
elf_preload_section(elf, section_idx, §ion_header, name, manifest);
|
||||
loaded_sections |= section_type;
|
||||
|
||||
if(section_type == SectionTypeERROR) {
|
||||
loaded_sections = SectionTypeERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(name);
|
||||
FURI_LOG_D(TAG, "Load symbols done");
|
||||
|
||||
return IS_FLAGS_SET(loaded_sections, SectionTypeValid);
|
||||
}
|
||||
|
||||
ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) {
|
||||
ELFFileLoadStatus status = ELFFileLoadStatusSuccess;
|
||||
ELFSectionDict_it_t it;
|
||||
|
||||
AddressCache_init(elf->relocation_cache);
|
||||
size_t start = furi_get_tick();
|
||||
|
||||
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
|
||||
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
|
||||
FURI_LOG_D(TAG, "Loading section '%s'", itref->key);
|
||||
if(!elf_load_section_data(elf, &itref->value)) {
|
||||
FURI_LOG_E(TAG, "Error loading section '%s'", itref->key);
|
||||
status = ELFFileLoadStatusUnspecifiedError;
|
||||
}
|
||||
}
|
||||
|
||||
if(status == ELFFileLoadStatusSuccess) {
|
||||
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
|
||||
ELFSectionDict_next(it)) {
|
||||
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
|
||||
FURI_LOG_D(TAG, "Relocating section '%s'", itref->key);
|
||||
if(!elf_relocate_section(elf, &itref->value)) {
|
||||
FURI_LOG_E(TAG, "Error relocating section '%s'", itref->key);
|
||||
status = ELFFileLoadStatusMissingImports;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fixing up entry point */
|
||||
if(status == ELFFileLoadStatusSuccess) {
|
||||
ELFSection* text_section = elf_file_get_section(elf, ".text");
|
||||
|
||||
if(text_section == NULL) {
|
||||
FURI_LOG_E(TAG, "No .text section found");
|
||||
status = ELFFileLoadStatusUnspecifiedError;
|
||||
} else {
|
||||
elf->entry += (uint32_t)text_section->data;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Relocation cache size: %u", AddressCache_size(elf->relocation_cache));
|
||||
FURI_LOG_D(TAG, "Trampoline cache size: %u", AddressCache_size(elf->trampoline_cache));
|
||||
AddressCache_clear(elf->relocation_cache);
|
||||
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void elf_file_pre_run(ELFFile* elf) {
|
||||
elf_file_call_section_list(elf, ".preinit_array", false);
|
||||
elf_file_call_section_list(elf, ".init_array", false);
|
||||
}
|
||||
|
||||
int32_t elf_file_run(ELFFile* elf, void* args) {
|
||||
int32_t result;
|
||||
result = ((int32_t(*)(void*))elf->entry)(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
void elf_file_post_run(ELFFile* elf) {
|
||||
elf_file_call_section_list(elf, ".fini_array", true);
|
||||
}
|
||||
|
||||
const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) {
|
||||
return elf_file->api_interface;
|
||||
}
|
||||
|
||||
void elf_file_init_debug_info(ELFFile* elf, ELFDebugInfo* debug_info) {
|
||||
// set entry
|
||||
debug_info->entry = elf->entry;
|
||||
|
||||
// copy debug info
|
||||
memcpy(&debug_info->debug_link_info, &elf->debug_link_info, sizeof(ELFDebugLinkInfo));
|
||||
|
||||
// init mmap
|
||||
debug_info->mmap_entry_count = ELFSectionDict_size(elf->sections);
|
||||
debug_info->mmap_entries = malloc(sizeof(ELFMemoryMapEntry) * debug_info->mmap_entry_count);
|
||||
uint32_t mmap_entry_idx = 0;
|
||||
|
||||
ELFSectionDict_it_t it;
|
||||
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
|
||||
const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);
|
||||
|
||||
const void* data_ptr = itref->value.data;
|
||||
if(data_ptr) {
|
||||
debug_info->mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr;
|
||||
debug_info->mmap_entries[mmap_entry_idx].name = itref->key;
|
||||
mmap_entry_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void elf_file_clear_debug_info(ELFDebugInfo* debug_info) {
|
||||
// clear debug info
|
||||
memset(&debug_info->debug_link_info, 0, sizeof(ELFDebugLinkInfo));
|
||||
|
||||
// clear mmap
|
||||
if(debug_info->mmap_entries) {
|
||||
free(debug_info->mmap_entries);
|
||||
debug_info->mmap_entries = NULL;
|
||||
}
|
||||
|
||||
debug_info->mmap_entry_count = 0;
|
||||
}
|
127
lib/flipper_application/elf/elf_file.h
Normal file
127
lib/flipper_application/elf/elf_file.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* @file elf_file.h
|
||||
* ELF file loader
|
||||
*/
|
||||
#pragma once
|
||||
#include <storage/storage.h>
|
||||
#include "../application_manifest.h"
|
||||
#include "elf_api_interface.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ELFFile ELFFile;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
uint32_t address;
|
||||
} ELFMemoryMapEntry;
|
||||
|
||||
typedef struct {
|
||||
uint32_t debug_link_size;
|
||||
uint8_t* debug_link;
|
||||
} ELFDebugLinkInfo;
|
||||
|
||||
typedef struct {
|
||||
uint32_t mmap_entry_count;
|
||||
ELFMemoryMapEntry* mmap_entries;
|
||||
ELFDebugLinkInfo debug_link_info;
|
||||
off_t entry;
|
||||
} ELFDebugInfo;
|
||||
|
||||
typedef enum {
|
||||
ELFFileLoadStatusSuccess = 0,
|
||||
ELFFileLoadStatusUnspecifiedError,
|
||||
ELFFileLoadStatusNoFreeMemory,
|
||||
ELFFileLoadStatusMissingImports,
|
||||
} ELFFileLoadStatus;
|
||||
|
||||
/**
|
||||
* @brief Allocate ELFFile instance
|
||||
* @param storage
|
||||
* @param api_interface
|
||||
* @return ELFFile*
|
||||
*/
|
||||
ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface);
|
||||
|
||||
/**
|
||||
* @brief Free ELFFile instance
|
||||
* @param elf_file
|
||||
*/
|
||||
void elf_file_free(ELFFile* elf_file);
|
||||
|
||||
/**
|
||||
* @brief Open ELF file
|
||||
* @param elf_file
|
||||
* @param path
|
||||
* @return bool
|
||||
*/
|
||||
bool elf_file_open(ELFFile* elf_file, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Load ELF file manifest
|
||||
* @param elf
|
||||
* @param manifest
|
||||
* @return bool
|
||||
*/
|
||||
bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest);
|
||||
|
||||
/**
|
||||
* @brief Load ELF file section table (load stage #1)
|
||||
* @param elf_file
|
||||
* @param manifest
|
||||
* @return bool
|
||||
*/
|
||||
bool elf_file_load_section_table(ELFFile* elf_file, FlipperApplicationManifest* manifest);
|
||||
|
||||
/**
|
||||
* @brief Load and relocate ELF file sections (load stage #2)
|
||||
* @param elf_file
|
||||
* @return ELFFileLoadStatus
|
||||
*/
|
||||
ELFFileLoadStatus elf_file_load_sections(ELFFile* elf_file);
|
||||
|
||||
/**
|
||||
* @brief Execute ELF file pre-run stage, call static constructors for example (load stage #3)
|
||||
* @param elf
|
||||
*/
|
||||
void elf_file_pre_run(ELFFile* elf);
|
||||
|
||||
/**
|
||||
* @brief Run ELF file (load stage #4)
|
||||
* @param elf_file
|
||||
* @param args
|
||||
* @return int32_t
|
||||
*/
|
||||
int32_t elf_file_run(ELFFile* elf_file, void* args);
|
||||
|
||||
/**
|
||||
* @brief Execute ELF file post-run stage, call static destructors for example (load stage #5)
|
||||
* @param elf
|
||||
*/
|
||||
void elf_file_post_run(ELFFile* elf);
|
||||
|
||||
/**
|
||||
* @brief Get ELF file API interface
|
||||
* @param elf_file
|
||||
* @return const ElfApiInterface*
|
||||
*/
|
||||
const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file);
|
||||
|
||||
/**
|
||||
* @brief Get ELF file debug info
|
||||
* @param elf_file
|
||||
* @param debug_info
|
||||
*/
|
||||
void elf_file_init_debug_info(ELFFile* elf_file, ELFDebugInfo* debug_info);
|
||||
|
||||
/**
|
||||
* @brief Clear ELF file debug info generated by elf_file_init_debug_info
|
||||
* @param debug_info
|
||||
*/
|
||||
void elf_file_clear_debug_info(ELFDebugInfo* debug_info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
46
lib/flipper_application/elf/elf_file_i.h
Normal file
46
lib/flipper_application/elf/elf_file_i.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
#include "elf_file.h"
|
||||
#include <m-dict.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST)
|
||||
|
||||
/**
|
||||
* Callable elf entry type
|
||||
*/
|
||||
typedef int32_t(entry_t)(void*);
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
uint16_t sec_idx;
|
||||
uint16_t rel_sec_idx;
|
||||
Elf32_Word size;
|
||||
} ELFSection;
|
||||
|
||||
DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST)
|
||||
|
||||
struct ELFFile {
|
||||
size_t sections_count;
|
||||
off_t section_table;
|
||||
off_t section_table_strings;
|
||||
|
||||
size_t symbol_count;
|
||||
off_t symbol_table;
|
||||
off_t symbol_table_strings;
|
||||
off_t entry;
|
||||
ELFSectionDict_t sections;
|
||||
|
||||
AddressCache_t relocation_cache;
|
||||
AddressCache_t trampoline_cache;
|
||||
|
||||
File* fd;
|
||||
const ElfApiInterface* api_interface;
|
||||
ELFDebugLinkInfo debug_link_info;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,477 +0,0 @@
|
|||
#include "flipper_application_i.h"
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "fapp-i"
|
||||
|
||||
#define RESOLVER_THREAD_YIELD_STEP 30
|
||||
|
||||
#define IS_FLAGS_SET(v, m) ((v & m) == m)
|
||||
#define SECTION_OFFSET(e, n) (e->section_table + n * sizeof(Elf32_Shdr))
|
||||
#define SYMBOL_OFFSET(e, n) (e->_table + n * sizeof(Elf32_Shdr))
|
||||
|
||||
bool flipper_application_load_elf_headers(FlipperApplication* e, const char* path) {
|
||||
Elf32_Ehdr h;
|
||||
Elf32_Shdr sH;
|
||||
|
||||
if(!storage_file_open(e->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) ||
|
||||
!storage_file_seek(e->fd, 0, true) ||
|
||||
storage_file_read(e->fd, &h, sizeof(h)) != sizeof(h) ||
|
||||
!storage_file_seek(e->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) ||
|
||||
storage_file_read(e->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
e->entry = h.e_entry;
|
||||
e->sections = h.e_shnum;
|
||||
e->section_table = h.e_shoff;
|
||||
e->section_table_strings = sH.sh_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool flipper_application_load_metadata(FlipperApplication* e, Elf32_Shdr* sh) {
|
||||
if(sh->sh_size < sizeof(e->manifest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return storage_file_seek(e->fd, sh->sh_offset, true) &&
|
||||
storage_file_read(e->fd, &e->manifest, sh->sh_size) == sh->sh_size;
|
||||
}
|
||||
|
||||
static bool flipper_application_load_debug_link(FlipperApplication* e, Elf32_Shdr* sh) {
|
||||
e->state.debug_link_size = sh->sh_size;
|
||||
e->state.debug_link = malloc(sh->sh_size);
|
||||
|
||||
return storage_file_seek(e->fd, sh->sh_offset, true) &&
|
||||
storage_file_read(e->fd, e->state.debug_link, sh->sh_size) == sh->sh_size;
|
||||
}
|
||||
|
||||
static FindFlags_t flipper_application_preload_section(
|
||||
FlipperApplication* e,
|
||||
Elf32_Shdr* sh,
|
||||
const char* name,
|
||||
int n) {
|
||||
FURI_LOG_D(TAG, "Processing: %s", name);
|
||||
|
||||
const struct {
|
||||
const char* name;
|
||||
uint16_t* ptr_section_idx;
|
||||
FindFlags_t flags;
|
||||
} lookup_sections[] = {
|
||||
{".text", &e->text.sec_idx, FoundText},
|
||||
{".rodata", &e->rodata.sec_idx, FoundRodata},
|
||||
{".data", &e->data.sec_idx, FoundData},
|
||||
{".bss", &e->bss.sec_idx, FoundBss},
|
||||
{".rel.text", &e->text.rel_sec_idx, FoundRelText},
|
||||
{".rel.rodata", &e->rodata.rel_sec_idx, FoundRelRodata},
|
||||
{".rel.data", &e->data.rel_sec_idx, FoundRelData},
|
||||
};
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(lookup_sections); i++) {
|
||||
if(strcmp(name, lookup_sections[i].name) == 0) {
|
||||
*lookup_sections[i].ptr_section_idx = n;
|
||||
return lookup_sections[i].flags;
|
||||
}
|
||||
}
|
||||
|
||||
if(strcmp(name, ".symtab") == 0) {
|
||||
e->symbol_table = sh->sh_offset;
|
||||
e->symbol_count = sh->sh_size / sizeof(Elf32_Sym);
|
||||
return FoundSymTab;
|
||||
} else if(strcmp(name, ".strtab") == 0) {
|
||||
e->symbol_table_strings = sh->sh_offset;
|
||||
return FoundStrTab;
|
||||
} else if(strcmp(name, ".fapmeta") == 0) {
|
||||
// Load metadata immediately
|
||||
if(flipper_application_load_metadata(e, sh)) {
|
||||
return FoundFappManifest;
|
||||
}
|
||||
} else if(strcmp(name, ".gnu_debuglink") == 0) {
|
||||
if(flipper_application_load_debug_link(e, sh)) {
|
||||
return FoundDebugLink;
|
||||
}
|
||||
}
|
||||
return FoundERROR;
|
||||
}
|
||||
|
||||
static bool
|
||||
read_string_from_offset(FlipperApplication* e, off_t offset, char* buffer, size_t buffer_size) {
|
||||
bool success = false;
|
||||
|
||||
off_t old = storage_file_tell(e->fd);
|
||||
if(storage_file_seek(e->fd, offset, true) &&
|
||||
(storage_file_read(e->fd, buffer, buffer_size) == buffer_size)) {
|
||||
success = true;
|
||||
}
|
||||
storage_file_seek(e->fd, old, true);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool read_section_name(FlipperApplication* e, off_t off, char* buf, size_t max) {
|
||||
return read_string_from_offset(e, e->section_table_strings + off, buf, max);
|
||||
}
|
||||
|
||||
static bool read_symbol_name(FlipperApplication* e, off_t off, char* buf, size_t max) {
|
||||
return read_string_from_offset(e, e->symbol_table_strings + off, buf, max);
|
||||
}
|
||||
|
||||
static bool read_section_header(FlipperApplication* e, int n, Elf32_Shdr* h) {
|
||||
off_t offset = SECTION_OFFSET(e, n);
|
||||
return storage_file_seek(e->fd, offset, true) &&
|
||||
storage_file_read(e->fd, h, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr);
|
||||
}
|
||||
|
||||
static bool read_section(FlipperApplication* e, int n, Elf32_Shdr* h, char* name, size_t nlen) {
|
||||
if(!read_section_header(e, n, h)) {
|
||||
return false;
|
||||
}
|
||||
if(!h->sh_name) {
|
||||
return true;
|
||||
}
|
||||
return read_section_name(e, h->sh_name, name, nlen);
|
||||
}
|
||||
|
||||
bool flipper_application_load_section_table(FlipperApplication* e) {
|
||||
furi_check(e->state.mmap_entry_count == 0);
|
||||
|
||||
size_t n;
|
||||
FindFlags_t found = FoundERROR;
|
||||
FURI_LOG_D(TAG, "Scan ELF indexs...");
|
||||
for(n = 1; n < e->sections; n++) {
|
||||
Elf32_Shdr section_header;
|
||||
char name[33] = {0};
|
||||
if(!read_section_header(e, n, §ion_header)) {
|
||||
return false;
|
||||
}
|
||||
if(section_header.sh_name &&
|
||||
!read_section_name(e, section_header.sh_name, name, sizeof(name))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_T(TAG, "Examining section %d %s", n, name);
|
||||
FindFlags_t section_flags =
|
||||
flipper_application_preload_section(e, §ion_header, name, n);
|
||||
found |= section_flags;
|
||||
if((section_flags & FoundGdbSection) != 0) {
|
||||
e->state.mmap_entry_count++;
|
||||
}
|
||||
if(IS_FLAGS_SET(found, FoundAll)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Load symbols done");
|
||||
return IS_FLAGS_SET(found, FoundValid);
|
||||
}
|
||||
|
||||
static const char* type_to_str(int symt) {
|
||||
#define STRCASE(name) \
|
||||
case name: \
|
||||
return #name;
|
||||
switch(symt) {
|
||||
STRCASE(R_ARM_NONE)
|
||||
STRCASE(R_ARM_ABS32)
|
||||
STRCASE(R_ARM_THM_PC22)
|
||||
STRCASE(R_ARM_THM_JUMP24)
|
||||
default:
|
||||
return "R_<unknow>";
|
||||
}
|
||||
#undef STRCASE
|
||||
}
|
||||
|
||||
static void relocate_jmp_call(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
|
||||
UNUSED(type);
|
||||
uint16_t upper_insn = ((uint16_t*)relAddr)[0];
|
||||
uint16_t lower_insn = ((uint16_t*)relAddr)[1];
|
||||
uint32_t S = (upper_insn >> 10) & 1;
|
||||
uint32_t J1 = (lower_insn >> 13) & 1;
|
||||
uint32_t J2 = (lower_insn >> 11) & 1;
|
||||
|
||||
int32_t offset = (S << 24) | /* S -> offset[24] */
|
||||
((~(J1 ^ S) & 1) << 23) | /* J1 -> offset[23] */
|
||||
((~(J2 ^ S) & 1) << 22) | /* J2 -> offset[22] */
|
||||
((upper_insn & 0x03ff) << 12) | /* imm10 -> offset[12:21] */
|
||||
((lower_insn & 0x07ff) << 1); /* imm11 -> offset[1:11] */
|
||||
if(offset & 0x01000000) offset -= 0x02000000;
|
||||
|
||||
offset += symAddr - relAddr;
|
||||
|
||||
S = (offset >> 24) & 1;
|
||||
J1 = S ^ (~(offset >> 23) & 1);
|
||||
J2 = S ^ (~(offset >> 22) & 1);
|
||||
|
||||
upper_insn = ((upper_insn & 0xf800) | (S << 10) | ((offset >> 12) & 0x03ff));
|
||||
((uint16_t*)relAddr)[0] = upper_insn;
|
||||
|
||||
lower_insn = ((lower_insn & 0xd000) | (J1 << 13) | (J2 << 11) | ((offset >> 1) & 0x07ff));
|
||||
((uint16_t*)relAddr)[1] = lower_insn;
|
||||
}
|
||||
|
||||
static bool relocate_symbol(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
|
||||
switch(type) {
|
||||
case R_ARM_ABS32:
|
||||
*((uint32_t*)relAddr) += symAddr;
|
||||
FURI_LOG_D(TAG, " R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
|
||||
break;
|
||||
case R_ARM_THM_PC22:
|
||||
case R_ARM_THM_JUMP24:
|
||||
relocate_jmp_call(relAddr, type, symAddr);
|
||||
FURI_LOG_D(
|
||||
TAG, " R_ARM_THM_CALL/JMP relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_D(TAG, " Undefined relocation %d", type);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static ELFSection_t* section_of(FlipperApplication* e, int index) {
|
||||
if(e->text.sec_idx == index) {
|
||||
return &e->text;
|
||||
} else if(e->data.sec_idx == index) {
|
||||
return &e->data;
|
||||
} else if(e->bss.sec_idx == index) {
|
||||
return &e->bss;
|
||||
} else if(e->rodata.sec_idx == index) {
|
||||
return &e->rodata;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Elf32_Addr address_of(FlipperApplication* e, Elf32_Sym* sym, const char* sName) {
|
||||
if(sym->st_shndx == SHN_UNDEF) {
|
||||
Elf32_Addr addr = 0;
|
||||
if(e->api_interface->resolver_callback(sName, &addr)) {
|
||||
return addr;
|
||||
}
|
||||
} else {
|
||||
ELFSection_t* symSec = section_of(e, sym->st_shndx);
|
||||
if(symSec) {
|
||||
return ((Elf32_Addr)symSec->data) + sym->st_value;
|
||||
}
|
||||
}
|
||||
FURI_LOG_D(TAG, " Can not find address for symbol %s", sName);
|
||||
return ELF_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
static bool read_symbol(FlipperApplication* e, int n, Elf32_Sym* sym, char* name, size_t nlen) {
|
||||
bool success = false;
|
||||
off_t old = storage_file_tell(e->fd);
|
||||
off_t pos = e->symbol_table + n * sizeof(Elf32_Sym);
|
||||
if(storage_file_seek(e->fd, pos, true) &&
|
||||
storage_file_read(e->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) {
|
||||
if(sym->st_name)
|
||||
success = read_symbol_name(e, sym->st_name, name, nlen);
|
||||
else {
|
||||
Elf32_Shdr shdr;
|
||||
success = read_section(e, sym->st_shndx, &shdr, name, nlen);
|
||||
}
|
||||
}
|
||||
storage_file_seek(e->fd, old, true);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool
|
||||
relocation_cache_get(RelocationAddressCache_t cache, int symEntry, Elf32_Addr* symAddr) {
|
||||
Elf32_Addr* addr = RelocationAddressCache_get(cache, symEntry);
|
||||
if(addr) {
|
||||
*symAddr = *addr;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
relocation_cache_put(RelocationAddressCache_t cache, int symEntry, Elf32_Addr symAddr) {
|
||||
RelocationAddressCache_set_at(cache, symEntry, symAddr);
|
||||
}
|
||||
|
||||
#define MAX_SYMBOL_NAME_LEN 128u
|
||||
|
||||
static bool relocate(FlipperApplication* e, Elf32_Shdr* h, ELFSection_t* s) {
|
||||
if(s->data) {
|
||||
Elf32_Rel rel;
|
||||
size_t relEntries = h->sh_size / sizeof(rel);
|
||||
size_t relCount;
|
||||
(void)storage_file_seek(e->fd, h->sh_offset, true);
|
||||
FURI_LOG_D(TAG, " Offset Info Type Name");
|
||||
|
||||
int relocate_result = true;
|
||||
char symbol_name[MAX_SYMBOL_NAME_LEN + 1] = {0};
|
||||
|
||||
for(relCount = 0; relCount < relEntries; relCount++) {
|
||||
if(relCount % RESOLVER_THREAD_YIELD_STEP == 0) {
|
||||
FURI_LOG_D(TAG, " reloc YIELD");
|
||||
furi_delay_tick(1);
|
||||
}
|
||||
|
||||
if(storage_file_read(e->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) {
|
||||
FURI_LOG_E(TAG, " reloc read fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
Elf32_Addr symAddr;
|
||||
|
||||
int symEntry = ELF32_R_SYM(rel.r_info);
|
||||
int relType = ELF32_R_TYPE(rel.r_info);
|
||||
Elf32_Addr relAddr = ((Elf32_Addr)s->data) + rel.r_offset;
|
||||
|
||||
if(!relocation_cache_get(e->relocation_cache, symEntry, &symAddr)) {
|
||||
Elf32_Sym sym;
|
||||
if(!read_symbol(e, symEntry, &sym, symbol_name, MAX_SYMBOL_NAME_LEN)) {
|
||||
FURI_LOG_E(TAG, " symbol read fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
" %08X %08X %-16s %s",
|
||||
(unsigned int)rel.r_offset,
|
||||
(unsigned int)rel.r_info,
|
||||
type_to_str(relType),
|
||||
symbol_name);
|
||||
|
||||
symAddr = address_of(e, &sym, symbol_name);
|
||||
relocation_cache_put(e->relocation_cache, symEntry, symAddr);
|
||||
}
|
||||
|
||||
if(symAddr != ELF_INVALID_ADDRESS) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
" symAddr=%08X relAddr=%08X",
|
||||
(unsigned int)symAddr,
|
||||
(unsigned int)relAddr);
|
||||
if(!relocate_symbol(relAddr, relType, symAddr)) {
|
||||
relocate_result = false;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, " No symbol address of %s", symbol_name);
|
||||
relocate_result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return relocate_result;
|
||||
} else
|
||||
FURI_LOG_I(TAG, "Section not loaded");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool flipper_application_load_section_data(FlipperApplication* e, ELFSection_t* s) {
|
||||
Elf32_Shdr section_header;
|
||||
if(s->sec_idx == 0) {
|
||||
FURI_LOG_I(TAG, "Section is not present");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!read_section_header(e, s->sec_idx, §ion_header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(section_header.sh_size == 0) {
|
||||
FURI_LOG_I(TAG, "No data for section");
|
||||
return true;
|
||||
}
|
||||
|
||||
s->data = aligned_malloc(section_header.sh_size, section_header.sh_addralign);
|
||||
// e->state.mmap_entry_count++;
|
||||
|
||||
if(section_header.sh_type == SHT_NOBITS) {
|
||||
/* section is empty (.bss?) */
|
||||
/* no need to memset - allocator already did that */
|
||||
/* memset(s->data, 0, h->sh_size); */
|
||||
FURI_LOG_D(TAG, "0x%X", s->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
if((!storage_file_seek(e->fd, section_header.sh_offset, true)) ||
|
||||
(storage_file_read(e->fd, s->data, section_header.sh_size) != section_header.sh_size)) {
|
||||
FURI_LOG_E(TAG, " seek/read fail");
|
||||
flipper_application_free_section(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "0x%X", s->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool flipper_application_relocate_section(FlipperApplication* e, ELFSection_t* s) {
|
||||
Elf32_Shdr section_header;
|
||||
if(s->rel_sec_idx) {
|
||||
FURI_LOG_D(TAG, "Relocating section");
|
||||
if(read_section_header(e, s->rel_sec_idx, §ion_header))
|
||||
return relocate(e, §ion_header, s);
|
||||
else {
|
||||
FURI_LOG_E(TAG, "Error reading section header");
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
FURI_LOG_D(TAG, "No relocation index"); /* Not an error */
|
||||
return true;
|
||||
}
|
||||
|
||||
FlipperApplicationLoadStatus flipper_application_load_sections(FlipperApplication* e) {
|
||||
FlipperApplicationLoadStatus status = FlipperApplicationLoadStatusSuccess;
|
||||
RelocationAddressCache_init(e->relocation_cache);
|
||||
size_t start = furi_get_tick();
|
||||
|
||||
struct {
|
||||
ELFSection_t* section;
|
||||
const char* name;
|
||||
} sections[] = {
|
||||
{&e->text, ".text"},
|
||||
{&e->rodata, ".rodata"},
|
||||
{&e->data, ".data"},
|
||||
{&e->bss, ".bss"},
|
||||
};
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(sections); i++) {
|
||||
if(!flipper_application_load_section_data(e, sections[i].section)) {
|
||||
FURI_LOG_E(TAG, "Error loading section '%s'", sections[i].name);
|
||||
status = FlipperApplicationLoadStatusUnspecifiedError;
|
||||
}
|
||||
}
|
||||
|
||||
if(status == FlipperApplicationLoadStatusSuccess) {
|
||||
for(size_t i = 0; i < COUNT_OF(sections); i++) {
|
||||
if(!flipper_application_relocate_section(e, sections[i].section)) {
|
||||
FURI_LOG_E(TAG, "Error relocating section '%s'", sections[i].name);
|
||||
status = FlipperApplicationLoadStatusMissingImports;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(status == FlipperApplicationLoadStatusSuccess) {
|
||||
e->state.mmap_entries =
|
||||
malloc(sizeof(FlipperApplicationMemoryMapEntry) * e->state.mmap_entry_count);
|
||||
uint32_t mmap_entry_idx = 0;
|
||||
for(size_t i = 0; i < COUNT_OF(sections); i++) {
|
||||
const void* data_ptr = sections[i].section->data;
|
||||
if(data_ptr) {
|
||||
FURI_LOG_I(TAG, "0x%X %s", (uint32_t)data_ptr, sections[i].name);
|
||||
e->state.mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr;
|
||||
e->state.mmap_entries[mmap_entry_idx].name = sections[i].name;
|
||||
mmap_entry_idx++;
|
||||
}
|
||||
}
|
||||
furi_check(mmap_entry_idx == e->state.mmap_entry_count);
|
||||
|
||||
/* Fixing up entry point */
|
||||
e->entry += (uint32_t)e->text.data;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Relocation cache size: %u", RelocationAddressCache_size(e->relocation_cache));
|
||||
RelocationAddressCache_clear(e->relocation_cache);
|
||||
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void flipper_application_free_section(ELFSection_t* s) {
|
||||
if(s->data) {
|
||||
aligned_free(s->data);
|
||||
}
|
||||
s->data = NULL;
|
||||
}
|
|
@ -1,16 +1,22 @@
|
|||
#include "flipper_application.h"
|
||||
#include "flipper_application_i.h"
|
||||
#include "elf/elf_file.h"
|
||||
|
||||
#define TAG "fapp"
|
||||
|
||||
struct FlipperApplication {
|
||||
ELFDebugInfo state;
|
||||
FlipperApplicationManifest manifest;
|
||||
ELFFile* elf;
|
||||
FuriThread* thread;
|
||||
};
|
||||
|
||||
/* For debugger access to app state */
|
||||
FlipperApplication* last_loaded_app = NULL;
|
||||
|
||||
FlipperApplication*
|
||||
flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
|
||||
FlipperApplication* app = malloc(sizeof(FlipperApplication));
|
||||
app->api_interface = api_interface;
|
||||
app->fd = storage_file_alloc(storage);
|
||||
app->elf = elf_file_alloc(storage, api_interface);
|
||||
app->thread = NULL;
|
||||
return app;
|
||||
}
|
||||
|
@ -25,56 +31,71 @@ void flipper_application_free(FlipperApplication* app) {
|
|||
|
||||
last_loaded_app = NULL;
|
||||
|
||||
if(app->state.debug_link_size) {
|
||||
free(app->state.debug_link);
|
||||
}
|
||||
|
||||
if(app->state.mmap_entries) {
|
||||
free(app->state.mmap_entries);
|
||||
}
|
||||
|
||||
ELFSection_t* sections[] = {&app->text, &app->rodata, &app->data, &app->bss};
|
||||
for(size_t i = 0; i < COUNT_OF(sections); i++) {
|
||||
flipper_application_free_section(sections[i]);
|
||||
}
|
||||
|
||||
storage_file_free(app->fd);
|
||||
|
||||
elf_file_clear_debug_info(&app->state);
|
||||
elf_file_free(app->elf);
|
||||
free(app);
|
||||
}
|
||||
|
||||
/* Parse headers, load manifest */
|
||||
FlipperApplicationPreloadStatus
|
||||
flipper_application_preload(FlipperApplication* app, const char* path) {
|
||||
if(!flipper_application_load_elf_headers(app, path) ||
|
||||
!flipper_application_load_section_table(app)) {
|
||||
return FlipperApplicationPreloadStatusInvalidFile;
|
||||
}
|
||||
|
||||
if((app->manifest.base.manifest_magic != FAP_MANIFEST_MAGIC) &&
|
||||
(app->manifest.base.manifest_version == FAP_MANIFEST_SUPPORTED_VERSION)) {
|
||||
static FlipperApplicationPreloadStatus
|
||||
flipper_application_validate_manifest(FlipperApplication* app) {
|
||||
if(!flipper_application_manifest_is_valid(&app->manifest)) {
|
||||
return FlipperApplicationPreloadStatusInvalidManifest;
|
||||
}
|
||||
|
||||
if(app->manifest.base.api_version.major != app->api_interface->api_version_major /* ||
|
||||
app->manifest.base.api_version.minor > app->api_interface->api_version_minor */) {
|
||||
if(!flipper_application_manifest_is_compatible(
|
||||
&app->manifest, elf_file_get_api_interface(app->elf))) {
|
||||
return FlipperApplicationPreloadStatusApiMismatch;
|
||||
}
|
||||
|
||||
return FlipperApplicationPreloadStatusSuccess;
|
||||
}
|
||||
|
||||
/* Parse headers, load manifest */
|
||||
FlipperApplicationPreloadStatus
|
||||
flipper_application_preload_manifest(FlipperApplication* app, const char* path) {
|
||||
if(!elf_file_open(app->elf, path) || !elf_file_load_manifest(app->elf, &app->manifest)) {
|
||||
return FlipperApplicationPreloadStatusInvalidFile;
|
||||
}
|
||||
|
||||
return flipper_application_validate_manifest(app);
|
||||
}
|
||||
|
||||
/* Parse headers, load full file */
|
||||
FlipperApplicationPreloadStatus
|
||||
flipper_application_preload(FlipperApplication* app, const char* path) {
|
||||
if(!elf_file_open(app->elf, path) || !elf_file_load_section_table(app->elf, &app->manifest)) {
|
||||
return FlipperApplicationPreloadStatusInvalidFile;
|
||||
}
|
||||
|
||||
return flipper_application_validate_manifest(app);
|
||||
}
|
||||
|
||||
const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {
|
||||
return &app->manifest;
|
||||
}
|
||||
|
||||
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
|
||||
last_loaded_app = app;
|
||||
return flipper_application_load_sections(app);
|
||||
ELFFileLoadStatus status = elf_file_load_sections(app->elf);
|
||||
|
||||
switch(status) {
|
||||
case ELFFileLoadStatusSuccess:
|
||||
elf_file_init_debug_info(app->elf, &app->state);
|
||||
return FlipperApplicationLoadStatusSuccess;
|
||||
case ELFFileLoadStatusNoFreeMemory:
|
||||
return FlipperApplicationLoadStatusNoFreeMemory;
|
||||
case ELFFileLoadStatusMissingImports:
|
||||
return FlipperApplicationLoadStatusMissingImports;
|
||||
default:
|
||||
return FlipperApplicationLoadStatusUnspecifiedError;
|
||||
}
|
||||
}
|
||||
|
||||
const FlipperApplicationState* flipper_application_get_state(FlipperApplication* app) {
|
||||
return &app->state;
|
||||
static int32_t flipper_application_thread(void* context) {
|
||||
elf_file_pre_run(last_loaded_app->elf);
|
||||
int32_t result = elf_file_run(last_loaded_app->elf, context);
|
||||
elf_file_post_run(last_loaded_app->elf);
|
||||
return result;
|
||||
}
|
||||
|
||||
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
|
||||
|
@ -86,20 +107,12 @@ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
|
|||
app->thread = furi_thread_alloc();
|
||||
furi_thread_set_stack_size(app->thread, manifest->stack_size);
|
||||
furi_thread_set_name(app->thread, manifest->name);
|
||||
furi_thread_set_callback(app->thread, (entry_t*)app->entry);
|
||||
furi_thread_set_callback(app->thread, flipper_application_thread);
|
||||
furi_thread_set_context(app->thread, args);
|
||||
|
||||
return app->thread;
|
||||
}
|
||||
|
||||
FuriThread* flipper_application_get_thread(FlipperApplication* app) {
|
||||
return app->thread;
|
||||
}
|
||||
|
||||
void const* flipper_application_get_entry_address(FlipperApplication* app) {
|
||||
return (void*)app->entry;
|
||||
}
|
||||
|
||||
static const char* preload_status_strings[] = {
|
||||
[FlipperApplicationPreloadStatusSuccess] = "Success",
|
||||
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* @file flipper_application.h
|
||||
* Flipper application
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "application_manifest.h"
|
||||
|
@ -79,6 +83,14 @@ void flipper_application_free(FlipperApplication* app);
|
|||
FlipperApplicationPreloadStatus
|
||||
flipper_application_preload(FlipperApplication* app, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Validate elf file and load application manifest
|
||||
* @param app Application pointer
|
||||
* @return Preload result code
|
||||
*/
|
||||
FlipperApplicationPreloadStatus
|
||||
flipper_application_preload_manifest(FlipperApplication* app, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Get pointer to application manifest for preloaded application
|
||||
* @param app Application pointer
|
||||
|
@ -93,13 +105,6 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic
|
|||
*/
|
||||
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app);
|
||||
|
||||
/**
|
||||
* @brief Get state object for loaded application
|
||||
* @param app Application pointer
|
||||
* @return Pointer to state object
|
||||
*/
|
||||
const FlipperApplicationState* flipper_application_get_state(FlipperApplication* app);
|
||||
|
||||
/**
|
||||
* @brief Create application thread at entry point address, using app name and
|
||||
* stack size from metadata. Returned thread isn't started yet.
|
||||
|
@ -110,20 +115,6 @@ const FlipperApplicationState* flipper_application_get_state(FlipperApplication*
|
|||
*/
|
||||
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args);
|
||||
|
||||
/**
|
||||
* @brief Get previously spawned thread
|
||||
* @param app Application pointer
|
||||
* @return Created thread
|
||||
*/
|
||||
FuriThread* flipper_application_get_thread(FlipperApplication* app);
|
||||
|
||||
/**
|
||||
* @brief Return relocated and valid address of app's entry point
|
||||
* @param app Application pointer
|
||||
* @return Address of app's entry point
|
||||
*/
|
||||
void const* flipper_application_get_entry_address(FlipperApplication* app);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,99 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "elf.h"
|
||||
#include "flipper_application.h"
|
||||
#include <m-dict.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DICT_DEF2(RelocationAddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST)
|
||||
|
||||
/**
|
||||
* Callable elf entry type
|
||||
*/
|
||||
typedef int32_t(entry_t)(void*);
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
uint16_t sec_idx;
|
||||
uint16_t rel_sec_idx;
|
||||
} ELFSection_t;
|
||||
|
||||
struct FlipperApplication {
|
||||
const ElfApiInterface* api_interface;
|
||||
File* fd;
|
||||
FlipperApplicationState state;
|
||||
FlipperApplicationManifest manifest;
|
||||
|
||||
size_t sections;
|
||||
off_t section_table;
|
||||
off_t section_table_strings;
|
||||
|
||||
size_t symbol_count;
|
||||
off_t symbol_table;
|
||||
off_t symbol_table_strings;
|
||||
off_t entry;
|
||||
|
||||
ELFSection_t text;
|
||||
ELFSection_t rodata;
|
||||
ELFSection_t data;
|
||||
ELFSection_t bss;
|
||||
|
||||
FuriThread* thread;
|
||||
RelocationAddressCache_t relocation_cache;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
FoundERROR = 0,
|
||||
FoundSymTab = (1 << 0),
|
||||
FoundStrTab = (1 << 2),
|
||||
FoundText = (1 << 3),
|
||||
FoundRodata = (1 << 4),
|
||||
FoundData = (1 << 5),
|
||||
FoundBss = (1 << 6),
|
||||
FoundRelText = (1 << 7),
|
||||
FoundRelRodata = (1 << 8),
|
||||
FoundRelData = (1 << 9),
|
||||
FoundRelBss = (1 << 10),
|
||||
FoundFappManifest = (1 << 11),
|
||||
FoundDebugLink = (1 << 12),
|
||||
FoundValid = FoundSymTab | FoundStrTab | FoundFappManifest,
|
||||
FoundExec = FoundValid | FoundText,
|
||||
FoundGdbSection = FoundText | FoundRodata | FoundData | FoundBss,
|
||||
FoundAll = FoundSymTab | FoundStrTab | FoundText | FoundRodata | FoundData | FoundBss |
|
||||
FoundRelText | FoundRelRodata | FoundRelData | FoundRelBss | FoundDebugLink,
|
||||
} FindFlags_t;
|
||||
|
||||
/**
|
||||
* @brief Load and validate basic ELF file headers
|
||||
* @param e Application instance
|
||||
* @param path FS path to application file
|
||||
* @return true if ELF file is valid
|
||||
*/
|
||||
bool flipper_application_load_elf_headers(FlipperApplication* e, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Iterate over all sections and save related indexes
|
||||
* @param e Application instance
|
||||
* @return true if all required sections are found
|
||||
*/
|
||||
bool flipper_application_load_section_table(FlipperApplication* e);
|
||||
|
||||
/**
|
||||
* @brief Load section data to memory and process relocations
|
||||
* @param e Application instance
|
||||
* @return Status code
|
||||
*/
|
||||
FlipperApplicationLoadStatus flipper_application_load_sections(FlipperApplication* e);
|
||||
|
||||
/**
|
||||
* @brief Release section data
|
||||
* @param s section pointer
|
||||
*/
|
||||
void flipper_application_free_section(ELFSection_t* s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Reference in a new issue