Embed assets in elf file (#2466)
* FBT: file_assets generator * Elf file: process manifest section externally * FBT, file_assets generator: add assets signature * Storage: assets path alias * Flipper application: assets unpacker * Apps, Storage: use '/data' alias for apps data * Storage: copy file to file * Assets: log flag, fixes * Update f18 api * Assets: asserts * Assets: fix signature_data check * App assets: example * Example assets: fix folder structure in readme * Assets: fix error handling * Assets builder: use ansii instead of utf-8, use .fapassets section instead of .fapfiles, add assets path to signature * Elf file: comment strange places * Storage: totaly optimized storage_file_copy_to_file
This commit is contained in:
361
lib/flipper_application/application_assets.c
Normal file
361
lib/flipper_application/application_assets.c
Normal file
@@ -0,0 +1,361 @@
|
||||
#include "application_assets.h"
|
||||
#include <toolbox/path.h>
|
||||
#include <storage/storage_i.h>
|
||||
|
||||
// #define ELF_ASSETS_DEBUG_LOG 1
|
||||
|
||||
#ifndef ELF_ASSETS_DEBUG_LOG
|
||||
#undef FURI_LOG_D
|
||||
#define FURI_LOG_D(...)
|
||||
#undef FURI_LOG_E
|
||||
#define FURI_LOG_E(...)
|
||||
#endif
|
||||
|
||||
#define FLIPPER_APPLICATION_ASSETS_MAGIC 0x4F4C5A44
|
||||
#define FLIPPER_APPLICATION_ASSETS_VERSION 1
|
||||
#define FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME ".assets.signature"
|
||||
|
||||
#define BUFFER_SIZE 512
|
||||
|
||||
#define TAG "fap_assets"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t dirs_count;
|
||||
uint32_t files_count;
|
||||
} FlipperApplicationAssetsHeader;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef enum {
|
||||
AssetsSignatureResultEqual,
|
||||
AssetsSignatureResultNotEqual,
|
||||
AssetsSignatureResultError,
|
||||
} AssetsSignatureResult;
|
||||
|
||||
static FuriString* flipper_application_assets_alloc_app_full_path(FuriString* app_name) {
|
||||
furi_assert(app_name);
|
||||
FuriString* full_path = furi_string_alloc_set(APPS_ASSETS_PATH "/");
|
||||
furi_string_cat(full_path, app_name);
|
||||
return full_path;
|
||||
}
|
||||
|
||||
static FuriString* flipper_application_assets_alloc_signature_file_path(FuriString* app_name) {
|
||||
furi_assert(app_name);
|
||||
FuriString* signature_file_path = flipper_application_assets_alloc_app_full_path(app_name);
|
||||
furi_string_cat(signature_file_path, "/" FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME);
|
||||
|
||||
return signature_file_path;
|
||||
}
|
||||
|
||||
static uint8_t* flipper_application_assets_alloc_and_load_data(File* file, size_t* size) {
|
||||
furi_assert(file);
|
||||
|
||||
uint8_t* data = NULL;
|
||||
uint32_t length = 0;
|
||||
|
||||
// read data length
|
||||
if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = malloc(length);
|
||||
|
||||
// read data
|
||||
if(storage_file_read(file, (void*)data, length) != length) {
|
||||
free((void*)data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(size != NULL) {
|
||||
*size = length;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static bool flipper_application_assets_process_files(
|
||||
Storage* storage,
|
||||
File* file,
|
||||
FuriString* app_name,
|
||||
uint32_t files_count) {
|
||||
furi_assert(storage);
|
||||
furi_assert(file);
|
||||
furi_assert(app_name);
|
||||
|
||||
UNUSED(storage);
|
||||
|
||||
bool success = false;
|
||||
uint32_t length = 0;
|
||||
char* path = NULL;
|
||||
FuriString* file_path = furi_string_alloc();
|
||||
File* destination = storage_file_alloc(storage);
|
||||
|
||||
FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);
|
||||
|
||||
for(uint32_t i = 0; i < files_count; i++) {
|
||||
path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL);
|
||||
|
||||
if(path == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
// read file size
|
||||
if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) {
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_set(file_path, full_path);
|
||||
furi_string_cat(file_path, "/");
|
||||
furi_string_cat(file_path, path);
|
||||
|
||||
if(!storage_file_open(
|
||||
destination, furi_string_get_cstr(file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
FURI_LOG_E(TAG, "Can't create file: %s", furi_string_get_cstr(file_path));
|
||||
break;
|
||||
}
|
||||
|
||||
// copy data to file
|
||||
if(!storage_file_copy_to_file(file, destination, length)) {
|
||||
FURI_LOG_E(TAG, "Can't copy data to file: %s", furi_string_get_cstr(file_path));
|
||||
break;
|
||||
}
|
||||
|
||||
storage_file_close(destination);
|
||||
|
||||
free(path);
|
||||
path = NULL;
|
||||
|
||||
if(i == files_count - 1) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(path != NULL) {
|
||||
free(path);
|
||||
}
|
||||
|
||||
storage_file_free(destination);
|
||||
furi_string_free(file_path);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool flipper_application_assets_process_dirs(
|
||||
Storage* storage,
|
||||
File* file,
|
||||
FuriString* app_name,
|
||||
uint32_t dirs_count) {
|
||||
furi_assert(storage);
|
||||
furi_assert(file);
|
||||
furi_assert(app_name);
|
||||
|
||||
bool success = false;
|
||||
FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);
|
||||
|
||||
do {
|
||||
if(!storage_simply_mkdir(storage, APPS_ASSETS_PATH)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!storage_simply_mkdir(storage, furi_string_get_cstr(full_path))) {
|
||||
break;
|
||||
}
|
||||
|
||||
FuriString* dir_path = furi_string_alloc();
|
||||
char* path = NULL;
|
||||
|
||||
for(uint32_t i = 0; i < dirs_count; i++) {
|
||||
path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL);
|
||||
|
||||
if(path == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_set(dir_path, full_path);
|
||||
furi_string_cat(dir_path, "/");
|
||||
furi_string_cat(dir_path, path);
|
||||
|
||||
if(!storage_simply_mkdir(storage, furi_string_get_cstr(dir_path))) {
|
||||
FURI_LOG_E(TAG, "Can't create directory: %s", furi_string_get_cstr(dir_path));
|
||||
break;
|
||||
}
|
||||
|
||||
free(path);
|
||||
path = NULL;
|
||||
|
||||
if(i == dirs_count - 1) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(path != NULL) {
|
||||
free(path);
|
||||
}
|
||||
|
||||
furi_string_free(dir_path);
|
||||
} while(false);
|
||||
|
||||
furi_string_free(full_path);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static AssetsSignatureResult flipper_application_assets_process_signature(
|
||||
Storage* storage,
|
||||
File* file,
|
||||
FuriString* app_name,
|
||||
uint8_t** signature_data,
|
||||
size_t* signature_data_size) {
|
||||
furi_assert(storage);
|
||||
furi_assert(file);
|
||||
furi_assert(app_name);
|
||||
furi_assert(signature_data);
|
||||
furi_assert(signature_data_size);
|
||||
|
||||
AssetsSignatureResult result = AssetsSignatureResultError;
|
||||
File* signature_file = storage_file_alloc(storage);
|
||||
FuriString* signature_file_path =
|
||||
flipper_application_assets_alloc_signature_file_path(app_name);
|
||||
|
||||
do {
|
||||
// read signature
|
||||
*signature_data =
|
||||
flipper_application_assets_alloc_and_load_data(file, signature_data_size);
|
||||
|
||||
if(*signature_data == NULL) { //-V547
|
||||
FURI_LOG_E(TAG, "Can't read signature");
|
||||
break;
|
||||
}
|
||||
|
||||
result = AssetsSignatureResultNotEqual;
|
||||
|
||||
if(!storage_file_open(
|
||||
signature_file,
|
||||
furi_string_get_cstr(signature_file_path),
|
||||
FSAM_READ_WRITE,
|
||||
FSOM_OPEN_EXISTING)) {
|
||||
FURI_LOG_E(TAG, "Can't open signature file");
|
||||
break;
|
||||
}
|
||||
|
||||
size_t signature_size = storage_file_size(signature_file);
|
||||
uint8_t* signature_file_data = malloc(signature_size);
|
||||
if(storage_file_read(signature_file, signature_file_data, signature_size) !=
|
||||
signature_size) {
|
||||
FURI_LOG_E(TAG, "Can't read signature file");
|
||||
free(signature_file_data);
|
||||
break;
|
||||
}
|
||||
|
||||
if(memcmp(*signature_data, signature_file_data, signature_size) == 0) {
|
||||
FURI_LOG_D(TAG, "Assets signature is equal");
|
||||
result = AssetsSignatureResultEqual;
|
||||
}
|
||||
|
||||
free(signature_file_data);
|
||||
} while(0);
|
||||
|
||||
storage_file_free(signature_file);
|
||||
furi_string_free(signature_file_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool flipper_application_assets_load(File* file, const char* elf_path, size_t offset, size_t size) {
|
||||
UNUSED(size);
|
||||
furi_assert(file);
|
||||
furi_assert(elf_path);
|
||||
FlipperApplicationAssetsHeader header;
|
||||
bool result = false;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
uint8_t* signature_data = NULL;
|
||||
size_t signature_data_size = 0;
|
||||
FuriString* app_name = furi_string_alloc();
|
||||
path_extract_filename_no_ext(elf_path, app_name);
|
||||
|
||||
FURI_LOG_D(TAG, "Loading assets for %s", furi_string_get_cstr(app_name));
|
||||
|
||||
do {
|
||||
if(!storage_file_seek(file, offset, true)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// read header
|
||||
if(storage_file_read(file, &header, sizeof(header)) != sizeof(header)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(header.magic != FLIPPER_APPLICATION_ASSETS_MAGIC) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(header.version != FLIPPER_APPLICATION_ASSETS_VERSION) {
|
||||
break;
|
||||
}
|
||||
|
||||
// process signature
|
||||
AssetsSignatureResult signature_result = flipper_application_assets_process_signature(
|
||||
storage, file, app_name, &signature_data, &signature_data_size);
|
||||
|
||||
if(signature_result == AssetsSignatureResultError) {
|
||||
FURI_LOG_E(TAG, "Assets signature error");
|
||||
break;
|
||||
} else if(signature_result == AssetsSignatureResultEqual) {
|
||||
FURI_LOG_D(TAG, "Assets signature equal, skip loading");
|
||||
result = true;
|
||||
break;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Assets signature not equal, loading");
|
||||
|
||||
// remove old assets
|
||||
FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);
|
||||
storage_simply_remove_recursive(storage, furi_string_get_cstr(full_path));
|
||||
furi_string_free(full_path);
|
||||
|
||||
FURI_LOG_D(TAG, "Assets removed");
|
||||
}
|
||||
|
||||
// process directories
|
||||
if(!flipper_application_assets_process_dirs(storage, file, app_name, header.dirs_count)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// process files
|
||||
if(!flipper_application_assets_process_files(storage, file, app_name, header.files_count)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// write signature
|
||||
FuriString* signature_file_path =
|
||||
flipper_application_assets_alloc_signature_file_path(app_name);
|
||||
File* signature_file = storage_file_alloc(storage);
|
||||
|
||||
if(storage_file_open(
|
||||
signature_file,
|
||||
furi_string_get_cstr(signature_file_path),
|
||||
FSAM_WRITE,
|
||||
FSOM_CREATE_ALWAYS)) {
|
||||
storage_file_write(signature_file, signature_data, signature_data_size);
|
||||
}
|
||||
|
||||
storage_file_free(signature_file);
|
||||
furi_string_free(signature_file_path);
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
if(signature_data != NULL) {
|
||||
free(signature_data);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_string_free(app_name);
|
||||
|
||||
FURI_LOG_D(TAG, "Assets loading %s", result ? "success" : "failed");
|
||||
|
||||
return result;
|
||||
}
|
17
lib/flipper_application/application_assets.h
Normal file
17
lib/flipper_application/application_assets.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file application_assets.h
|
||||
* Flipper application assets
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <storage/storage.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool flipper_application_assets_load(File* file, const char* elf_path, size_t offset, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -241,7 +241,7 @@ static void elf_relocate_jmp_call(ELFFile* elf, Elf32_Addr relAddr, int type, El
|
||||
if(to_thumb || (symAddr & 2) || (!is_call)) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"can't relocate value at %x, %s, doing trampoline",
|
||||
"can't relocate value at %lx, %s, doing trampoline",
|
||||
relAddr,
|
||||
elf_reloc_type_to_str(type));
|
||||
|
||||
@@ -421,29 +421,11 @@ typedef enum {
|
||||
SectionTypeRelData = 1 << 2,
|
||||
SectionTypeSymTab = 1 << 3,
|
||||
SectionTypeStrTab = 1 << 4,
|
||||
SectionTypeManifest = 1 << 5,
|
||||
SectionTypeDebugLink = 1 << 6,
|
||||
SectionTypeDebugLink = 1 << 5,
|
||||
|
||||
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab | SectionTypeManifest,
|
||||
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab,
|
||||
} 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);
|
||||
@@ -478,7 +460,7 @@ static bool elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr*
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "0x%X", section->data);
|
||||
FURI_LOG_D(TAG, "0x%p", section->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -486,8 +468,7 @@ static SectionType elf_preload_section(
|
||||
ELFFile* elf,
|
||||
size_t section_idx,
|
||||
Elf32_Shdr* section_header,
|
||||
FuriString* name_string,
|
||||
FlipperApplicationManifest* manifest) {
|
||||
FuriString* name_string) {
|
||||
const char* name = furi_string_get_cstr(name_string);
|
||||
|
||||
#ifdef ELF_DEBUG_LOG
|
||||
@@ -572,16 +553,6 @@ static SectionType elf_preload_section(
|
||||
return SectionTypeStrTab;
|
||||
}
|
||||
|
||||
// Load manifest section
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Load debug link section
|
||||
if(strcmp(name, ".gnu_debuglink") == 0) {
|
||||
FURI_LOG_D(TAG, "Found .gnu_debuglink section");
|
||||
@@ -692,41 +663,12 @@ bool elf_file_open(ELFFile* elf, const char* path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest) {
|
||||
bool result = false;
|
||||
FuriString* name;
|
||||
name = furi_string_alloc();
|
||||
|
||||
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;
|
||||
|
||||
furi_string_reset(name);
|
||||
if(!elf_read_section(elf, section_idx, §ion_header, name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_free(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manifest) {
|
||||
bool elf_file_load_section_table(ELFFile* elf) {
|
||||
SectionType loaded_sections = SectionTypeERROR;
|
||||
FuriString* name;
|
||||
name = furi_string_alloc();
|
||||
FuriString* name = furi_string_alloc();
|
||||
|
||||
FURI_LOG_D(TAG, "Scan ELF indexs...");
|
||||
// TODO: why we start from 1?
|
||||
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
|
||||
Elf32_Shdr section_header;
|
||||
|
||||
@@ -738,8 +680,7 @@ bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manif
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG, "Preloading data for section #%d %s", section_idx, furi_string_get_cstr(name));
|
||||
SectionType section_type =
|
||||
elf_preload_section(elf, section_idx, §ion_header, name, manifest);
|
||||
SectionType section_type = elf_preload_section(elf, section_idx, §ion_header, name);
|
||||
loaded_sections |= section_type;
|
||||
|
||||
if(section_type == SectionTypeERROR) {
|
||||
@@ -753,14 +694,49 @@ bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manif
|
||||
return IS_FLAGS_SET(loaded_sections, SectionTypeValid);
|
||||
}
|
||||
|
||||
ElfProcessSectionResult elf_process_section(
|
||||
ELFFile* elf,
|
||||
const char* name,
|
||||
ElfProcessSection* process_section,
|
||||
void* context) {
|
||||
ElfProcessSectionResult result = ElfProcessSectionResultNotFound;
|
||||
FuriString* section_name = furi_string_alloc();
|
||||
Elf32_Shdr section_header;
|
||||
|
||||
// find section
|
||||
// TODO: why we start from 1?
|
||||
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
|
||||
furi_string_reset(section_name);
|
||||
if(!elf_read_section(elf, section_idx, §ion_header, section_name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_cmp(section_name, name) == 0) {
|
||||
result = ElfProcessSectionResultCannotProcess;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(result != ElfProcessSectionResultNotFound) { //-V547
|
||||
if(process_section(elf->fd, section_header.sh_offset, section_header.sh_size, context)) {
|
||||
result = ElfProcessSectionResultSuccess;
|
||||
} else {
|
||||
result = ElfProcessSectionResultCannotProcess;
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_free(section_name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) {
|
||||
ELFFileLoadStatus status = ELFFileLoadStatusSuccess;
|
||||
ELFSectionDict_it_t it;
|
||||
|
||||
AddressCache_init(elf->relocation_cache);
|
||||
|
||||
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
|
||||
ELFSectionDict_next(it)) {
|
||||
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)) {
|
||||
|
@@ -37,6 +37,14 @@ typedef enum {
|
||||
ELFFileLoadStatusMissingImports,
|
||||
} ELFFileLoadStatus;
|
||||
|
||||
typedef enum {
|
||||
ElfProcessSectionResultNotFound,
|
||||
ElfProcessSectionResultCannotProcess,
|
||||
ElfProcessSectionResultSuccess,
|
||||
} ElfProcessSectionResult;
|
||||
|
||||
typedef bool(ElfProcessSection)(File* file, size_t offset, size_t size, void* context);
|
||||
|
||||
/**
|
||||
* @brief Allocate ELFFile instance
|
||||
* @param storage
|
||||
@@ -59,21 +67,12 @@ void elf_file_free(ELFFile* elf_file);
|
||||
*/
|
||||
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);
|
||||
bool elf_file_load_section_table(ELFFile* elf_file);
|
||||
|
||||
/**
|
||||
* @brief Load and relocate ELF file sections (load stage #2)
|
||||
@@ -122,6 +121,21 @@ void elf_file_init_debug_info(ELFFile* elf_file, ELFDebugInfo* debug_info);
|
||||
*/
|
||||
void elf_file_clear_debug_info(ELFDebugInfo* debug_info);
|
||||
|
||||
/**
|
||||
* @brief Process ELF file section
|
||||
*
|
||||
* @param elf_file
|
||||
* @param name
|
||||
* @param process_section
|
||||
* @param context
|
||||
* @return ElfProcessSectionResult
|
||||
*/
|
||||
ElfProcessSectionResult elf_process_section(
|
||||
ELFFile* elf_file,
|
||||
const char* name,
|
||||
ElfProcessSection* process_section,
|
||||
void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -1,6 +1,7 @@
|
||||
#include "flipper_application.h"
|
||||
#include "elf/elf_file.h"
|
||||
#include <notification/notification_messages.h>
|
||||
#include "application_assets.h"
|
||||
|
||||
#define TAG "fapp"
|
||||
|
||||
@@ -55,24 +56,83 @@ static FlipperApplicationPreloadStatus
|
||||
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)) {
|
||||
static bool flipper_application_process_manifest_section(
|
||||
File* file,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
void* context) {
|
||||
FlipperApplicationManifest* manifest = context;
|
||||
|
||||
if(size < sizeof(FlipperApplicationManifest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(manifest == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return storage_file_seek(file, offset, true) &&
|
||||
storage_file_read(file, manifest, size) == size;
|
||||
}
|
||||
|
||||
// we can't use const char* as context because we will lose the const qualifier
|
||||
typedef struct {
|
||||
const char* path;
|
||||
} FlipperApplicationPreloadAssetsContext;
|
||||
|
||||
static bool flipper_application_process_assets_section(
|
||||
File* file,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
void* context) {
|
||||
FlipperApplicationPreloadAssetsContext* preload_context = context;
|
||||
return flipper_application_assets_load(file, preload_context->path, offset, size);
|
||||
}
|
||||
|
||||
static FlipperApplicationPreloadStatus
|
||||
flipper_application_load(FlipperApplication* app, const char* path, bool load_full) {
|
||||
if(!elf_file_open(app->elf, path)) {
|
||||
return FlipperApplicationPreloadStatusInvalidFile;
|
||||
}
|
||||
|
||||
// if we are loading full file
|
||||
if(load_full) {
|
||||
// load section table
|
||||
if(!elf_file_load_section_table(app->elf)) {
|
||||
return FlipperApplicationPreloadStatusInvalidFile;
|
||||
}
|
||||
|
||||
// load assets section
|
||||
FlipperApplicationPreloadAssetsContext preload_context = {.path = path};
|
||||
if(elf_process_section(
|
||||
app->elf,
|
||||
".fapassets",
|
||||
flipper_application_process_assets_section,
|
||||
&preload_context) == ElfProcessSectionResultCannotProcess) {
|
||||
return FlipperApplicationPreloadStatusInvalidFile;
|
||||
}
|
||||
}
|
||||
|
||||
// load manifest section
|
||||
if(elf_process_section(
|
||||
app->elf, ".fapmeta", flipper_application_process_manifest_section, &app->manifest) !=
|
||||
ElfProcessSectionResultSuccess) {
|
||||
return FlipperApplicationPreloadStatusInvalidFile;
|
||||
}
|
||||
|
||||
return flipper_application_validate_manifest(app);
|
||||
}
|
||||
|
||||
/* Parse headers, load manifest */
|
||||
FlipperApplicationPreloadStatus
|
||||
flipper_application_preload_manifest(FlipperApplication* app, const char* path) {
|
||||
return flipper_application_load(app, path, false);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
return flipper_application_load(app, path, true);
|
||||
}
|
||||
|
||||
const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {
|
||||
|
Reference in New Issue
Block a user