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
@ -25,7 +25,7 @@ static bool
|
|||||||
FlipperApplication* app = flipper_application_alloc(loader->storage, &hashtable_api_interface);
|
FlipperApplication* app = flipper_application_alloc(loader->storage, &hashtable_api_interface);
|
||||||
|
|
||||||
FlipperApplicationPreloadStatus preload_res =
|
FlipperApplicationPreloadStatus preload_res =
|
||||||
flipper_application_preload(app, string_get_cstr(path));
|
flipper_application_preload_manifest(app, string_get_cstr(path));
|
||||||
|
|
||||||
bool load_success = false;
|
bool load_success = false;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ class AppState:
|
|||||||
|
|
||||||
def is_loaded_in_gdb(self, gdb_app) -> bool:
|
def is_loaded_in_gdb(self, gdb_app) -> bool:
|
||||||
# Avoid constructing full app wrapper for comparison
|
# 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
|
@staticmethod
|
||||||
def parse_debug_link_data(section_data: bytes) -> Tuple[str, int]:
|
def parse_debug_link_data(section_data: bytes) -> Tuple[str, int]:
|
||||||
@ -78,13 +78,13 @@ class AppState:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def from_gdb(gdb_app: "AppState") -> "AppState":
|
def from_gdb(gdb_app: "AppState") -> "AppState":
|
||||||
state = AppState(str(gdb_app["manifest"]["name"].string()))
|
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"]
|
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 = (
|
debug_link_data = (
|
||||||
gdb.selected_inferior()
|
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()
|
.tobytes()
|
||||||
)
|
)
|
||||||
state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data(
|
state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data(
|
||||||
|
@ -101,3 +101,7 @@ $_TARGETNAME configure -event trace-config {
|
|||||||
# assignment
|
# assignment
|
||||||
mmw 0xE0042004 0x00000020 0
|
mmw 0xE0042004 0x00000020 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event gdb-detach {
|
||||||
|
resume
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,1.9,,
|
Version,+,1.10,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.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,-,fiscanf,int,"FILE*, const char*, ..."
|
||||||
Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*"
|
Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*"
|
||||||
Function,+,flipper_application_free,void,FlipperApplication*
|
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_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_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_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication*
|
||||||
Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*"
|
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_preload_status_to_string,const char*,FlipperApplicationPreloadStatus
|
||||||
Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*"
|
Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*"
|
||||||
Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage*
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "elf/elf_api_interface.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -40,6 +46,25 @@ typedef FlipperApplicationManifestV1 FlipperApplicationManifest;
|
|||||||
|
|
||||||
#pragma pack(pop)
|
#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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <flipper_application/elf/elf.h>
|
#include <elf.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define ELF_INVALID_ADDRESS 0xFFFFFFFF
|
#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.h"
|
||||||
#include "flipper_application_i.h"
|
#include "elf/elf_file.h"
|
||||||
|
|
||||||
#define TAG "fapp"
|
#define TAG "fapp"
|
||||||
|
|
||||||
|
struct FlipperApplication {
|
||||||
|
ELFDebugInfo state;
|
||||||
|
FlipperApplicationManifest manifest;
|
||||||
|
ELFFile* elf;
|
||||||
|
FuriThread* thread;
|
||||||
|
};
|
||||||
|
|
||||||
/* For debugger access to app state */
|
/* For debugger access to app state */
|
||||||
FlipperApplication* last_loaded_app = NULL;
|
FlipperApplication* last_loaded_app = NULL;
|
||||||
|
|
||||||
FlipperApplication*
|
FlipperApplication*
|
||||||
flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
|
flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
|
||||||
FlipperApplication* app = malloc(sizeof(FlipperApplication));
|
FlipperApplication* app = malloc(sizeof(FlipperApplication));
|
||||||
app->api_interface = api_interface;
|
app->elf = elf_file_alloc(storage, api_interface);
|
||||||
app->fd = storage_file_alloc(storage);
|
|
||||||
app->thread = NULL;
|
app->thread = NULL;
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
@ -25,56 +31,71 @@ void flipper_application_free(FlipperApplication* app) {
|
|||||||
|
|
||||||
last_loaded_app = NULL;
|
last_loaded_app = NULL;
|
||||||
|
|
||||||
if(app->state.debug_link_size) {
|
elf_file_clear_debug_info(&app->state);
|
||||||
free(app->state.debug_link);
|
elf_file_free(app->elf);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
free(app);
|
free(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse headers, load manifest */
|
static FlipperApplicationPreloadStatus
|
||||||
FlipperApplicationPreloadStatus
|
flipper_application_validate_manifest(FlipperApplication* app) {
|
||||||
flipper_application_preload(FlipperApplication* app, const char* path) {
|
if(!flipper_application_manifest_is_valid(&app->manifest)) {
|
||||||
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)) {
|
|
||||||
return FlipperApplicationPreloadStatusInvalidManifest;
|
return FlipperApplicationPreloadStatusInvalidManifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(app->manifest.base.api_version.major != app->api_interface->api_version_major /* ||
|
if(!flipper_application_manifest_is_compatible(
|
||||||
app->manifest.base.api_version.minor > app->api_interface->api_version_minor */) {
|
&app->manifest, elf_file_get_api_interface(app->elf))) {
|
||||||
return FlipperApplicationPreloadStatusApiMismatch;
|
return FlipperApplicationPreloadStatusApiMismatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FlipperApplicationPreloadStatusSuccess;
|
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) {
|
const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {
|
||||||
return &app->manifest;
|
return &app->manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
|
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
|
||||||
last_loaded_app = 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) {
|
static int32_t flipper_application_thread(void* context) {
|
||||||
return &app->state;
|
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) {
|
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();
|
app->thread = furi_thread_alloc();
|
||||||
furi_thread_set_stack_size(app->thread, manifest->stack_size);
|
furi_thread_set_stack_size(app->thread, manifest->stack_size);
|
||||||
furi_thread_set_name(app->thread, manifest->name);
|
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);
|
furi_thread_set_context(app->thread, args);
|
||||||
|
|
||||||
return app->thread;
|
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[] = {
|
static const char* preload_status_strings[] = {
|
||||||
[FlipperApplicationPreloadStatusSuccess] = "Success",
|
[FlipperApplicationPreloadStatusSuccess] = "Success",
|
||||||
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
|
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* @file flipper_application.h
|
||||||
|
* Flipper application
|
||||||
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "application_manifest.h"
|
#include "application_manifest.h"
|
||||||
@ -79,6 +83,14 @@ void flipper_application_free(FlipperApplication* app);
|
|||||||
FlipperApplicationPreloadStatus
|
FlipperApplicationPreloadStatus
|
||||||
flipper_application_preload(FlipperApplication* app, const char* path);
|
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
|
* @brief Get pointer to application manifest for preloaded application
|
||||||
* @param app Application pointer
|
* @param app Application pointer
|
||||||
@ -93,13 +105,6 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic
|
|||||||
*/
|
*/
|
||||||
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app);
|
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
|
* @brief Create application thread at entry point address, using app name and
|
||||||
* stack size from metadata. Returned thread isn't started yet.
|
* 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);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#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 New Issue
Block a user