mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
ELF, Flipper application: do not crash on "out of memory" (#3664)
* ELF, Flipper application: do not crash on "out of memory" * loader: better error messages * typo * fix position * Loader: QR code for common errors * NFC: error message * Loader: error descriptions
This commit is contained in:
@@ -435,7 +435,6 @@ static bool elf_relocate(ELFFile* elf, ELFSection* s) {
|
||||
/************************************ Internal FAP interfaces *************************************/
|
||||
/**************************************************************************************************/
|
||||
typedef enum {
|
||||
SectionTypeERROR = 0,
|
||||
SectionTypeUnused = 1 << 0,
|
||||
SectionTypeData = 1 << 1,
|
||||
SectionTypeRelData = 1 << 2,
|
||||
@@ -443,8 +442,6 @@ typedef enum {
|
||||
SectionTypeStrTab = 1 << 4,
|
||||
SectionTypeDebugLink = 1 << 5,
|
||||
SectionTypeFastRelData = 1 << 6,
|
||||
|
||||
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab,
|
||||
} SectionType;
|
||||
|
||||
static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) {
|
||||
@@ -460,37 +457,62 @@ static bool str_prefix(const char* str, const char* prefix) {
|
||||
return strncmp(prefix, str, strlen(prefix)) == 0;
|
||||
}
|
||||
|
||||
static bool elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* section_header) {
|
||||
typedef enum {
|
||||
ELFLoadSectionResultSuccess,
|
||||
ELFLoadSectionResultNoMemory,
|
||||
ELFLoadSectionResultError,
|
||||
} ELFLoadSectionResult;
|
||||
|
||||
typedef struct {
|
||||
SectionType type;
|
||||
ELFLoadSectionResult result;
|
||||
} SectionTypeInfo;
|
||||
|
||||
static ELFLoadSectionResult
|
||||
elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* section_header) {
|
||||
if(section_header->sh_size == 0) {
|
||||
FURI_LOG_D(TAG, "No data for section");
|
||||
return true;
|
||||
return ELFLoadSectionResultSuccess;
|
||||
}
|
||||
|
||||
size_t safe_size = section_header->sh_size + 1024;
|
||||
|
||||
furi_kernel_lock();
|
||||
|
||||
if(memmgr_heap_get_max_free_block() < safe_size) {
|
||||
furi_kernel_unlock();
|
||||
FURI_LOG_E(TAG, "Not enough memory to load section data");
|
||||
return ELFLoadSectionResultNoMemory;
|
||||
}
|
||||
|
||||
section->data = aligned_malloc(section_header->sh_size, section_header->sh_addralign);
|
||||
section->size = section_header->sh_size;
|
||||
|
||||
furi_kernel_unlock();
|
||||
|
||||
if(section_header->sh_type == SHT_NOBITS) {
|
||||
// BSS section, no data to load
|
||||
return true;
|
||||
return ELFLoadSectionResultSuccess;
|
||||
}
|
||||
|
||||
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;
|
||||
return ELFLoadSectionResultError;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "0x%p", section->data);
|
||||
return true;
|
||||
return ELFLoadSectionResultSuccess;
|
||||
}
|
||||
|
||||
static SectionType elf_preload_section(
|
||||
static SectionTypeInfo elf_preload_section(
|
||||
ELFFile* elf,
|
||||
size_t section_idx,
|
||||
Elf32_Shdr* section_header,
|
||||
FuriString* name_string) {
|
||||
const char* name = furi_string_get_cstr(name_string);
|
||||
SectionTypeInfo info;
|
||||
|
||||
#ifdef ELF_DEBUG_LOG
|
||||
// log section name, type and flags
|
||||
@@ -527,7 +549,10 @@ static SectionType elf_preload_section(
|
||||
if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") ||
|
||||
str_prefix(name, ".fast.rel.ARM.")) {
|
||||
FURI_LOG_D(TAG, "Ignoring ARM section");
|
||||
return SectionTypeUnused;
|
||||
|
||||
info.type = SectionTypeUnused;
|
||||
info.result = ELFLoadSectionResultSuccess;
|
||||
return info;
|
||||
}
|
||||
|
||||
// Load allocable section
|
||||
@@ -546,26 +571,32 @@ static SectionType elf_preload_section(
|
||||
elf->fini_array = section_p;
|
||||
}
|
||||
|
||||
if(!elf_load_section_data(elf, section_p, section_header)) {
|
||||
info.type = SectionTypeData;
|
||||
info.result = elf_load_section_data(elf, section_p, section_header);
|
||||
|
||||
if(info.result != ELFLoadSectionResultSuccess) {
|
||||
FURI_LOG_E(TAG, "Error loading section '%s'", name);
|
||||
return SectionTypeERROR;
|
||||
} else {
|
||||
return SectionTypeData;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
// Load link info section
|
||||
if(section_header->sh_flags & SHF_INFO_LINK) {
|
||||
info.type = SectionTypeRelData;
|
||||
|
||||
if(str_prefix(name, ".rel")) {
|
||||
name = name + strlen(".rel");
|
||||
ELFSection* section_p = elf_file_get_or_put_section(elf, name);
|
||||
section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel);
|
||||
section_p->rel_offset = section_header->sh_offset;
|
||||
return SectionTypeRelData;
|
||||
info.result = ELFLoadSectionResultSuccess;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unknown link info section '%s'", name);
|
||||
return SectionTypeERROR;
|
||||
info.result = ELFLoadSectionResultError;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
// Load fast rel section
|
||||
@@ -574,13 +605,16 @@ static SectionType elf_preload_section(
|
||||
ELFSection* section_p = elf_file_get_or_put_section(elf, name);
|
||||
section_p->fast_rel = malloc(sizeof(ELFSection));
|
||||
|
||||
if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) {
|
||||
info.type = SectionTypeFastRelData;
|
||||
info.result = elf_load_section_data(elf, section_p->fast_rel, section_header);
|
||||
|
||||
if(info.result != ELFLoadSectionResultSuccess) {
|
||||
FURI_LOG_E(TAG, "Error loading section '%s'", name);
|
||||
return SectionTypeERROR;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name);
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name);
|
||||
return SectionTypeFastRelData;
|
||||
return info;
|
||||
}
|
||||
|
||||
// Load symbol table
|
||||
@@ -588,27 +622,39 @@ static SectionType elf_preload_section(
|
||||
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;
|
||||
|
||||
info.type = SectionTypeSymTab;
|
||||
info.result = ELFLoadSectionResultSuccess;
|
||||
return info;
|
||||
}
|
||||
|
||||
// Load string table
|
||||
if(strcmp(name, ".strtab") == 0) {
|
||||
FURI_LOG_D(TAG, "Found .strtab section");
|
||||
elf->symbol_table_strings = section_header->sh_offset;
|
||||
return SectionTypeStrTab;
|
||||
|
||||
info.type = SectionTypeStrTab;
|
||||
info.result = ELFLoadSectionResultSuccess;
|
||||
return info;
|
||||
}
|
||||
|
||||
// Load debug link section
|
||||
if(strcmp(name, ".gnu_debuglink") == 0) {
|
||||
FURI_LOG_D(TAG, "Found .gnu_debuglink section");
|
||||
info.type = SectionTypeDebugLink;
|
||||
|
||||
if(elf_load_debug_link(elf, section_header)) {
|
||||
return SectionTypeDebugLink;
|
||||
info.result = ELFLoadSectionResultSuccess;
|
||||
return info;
|
||||
} else {
|
||||
return SectionTypeERROR;
|
||||
info.result = ELFLoadSectionResultError;
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
return SectionTypeUnused;
|
||||
info.type = SectionTypeUnused;
|
||||
info.result = ELFLoadSectionResultSuccess;
|
||||
return info;
|
||||
}
|
||||
|
||||
static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) {
|
||||
@@ -836,35 +882,57 @@ bool elf_file_open(ELFFile* elf, const char* path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool elf_file_load_section_table(ELFFile* elf) {
|
||||
SectionType loaded_sections = SectionTypeERROR;
|
||||
ElfLoadSectionTableResult elf_file_load_section_table(ELFFile* elf) {
|
||||
SectionType loaded_sections = 0;
|
||||
FuriString* name = furi_string_alloc();
|
||||
ElfLoadSectionTableResult result = ElfLoadSectionTableResultSuccess;
|
||||
|
||||
FURI_LOG_D(TAG, "Scan ELF indexs...");
|
||||
// TODO FL-3526: why we start from 1?
|
||||
|
||||
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)) {
|
||||
loaded_sections = SectionTypeERROR;
|
||||
loaded_sections = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
loaded_sections |= section_type;
|
||||
SectionTypeInfo section_type_info =
|
||||
elf_preload_section(elf, section_idx, §ion_header, name);
|
||||
loaded_sections |= section_type_info.type;
|
||||
|
||||
if(section_type == SectionTypeERROR) {
|
||||
loaded_sections = SectionTypeERROR;
|
||||
if(section_type_info.result != ELFLoadSectionResultSuccess) {
|
||||
if(section_type_info.result == ELFLoadSectionResultNoMemory) {
|
||||
FURI_LOG_E(TAG, "Not enough memory");
|
||||
result = ElfLoadSectionTableResultNoMemory;
|
||||
} else if(section_type_info.result == ELFLoadSectionResultError) {
|
||||
FURI_LOG_E(TAG, "Error loading section");
|
||||
result = ElfLoadSectionTableResultError;
|
||||
}
|
||||
|
||||
loaded_sections = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_free(name);
|
||||
|
||||
return IS_FLAGS_SET(loaded_sections, SectionTypeValid);
|
||||
if(result != ElfLoadSectionTableResultSuccess) {
|
||||
return result;
|
||||
} else {
|
||||
bool sections_valid =
|
||||
IS_FLAGS_SET(loaded_sections, SectionTypeSymTab | SectionTypeStrTab) |
|
||||
IS_FLAGS_SET(loaded_sections, SectionTypeFastRelData);
|
||||
if(sections_valid) {
|
||||
return ElfLoadSectionTableResultSuccess;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "No valid sections found");
|
||||
return ElfLoadSectionTableResultError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ElfProcessSectionResult elf_process_section(
|
||||
@@ -877,7 +945,6 @@ ElfProcessSectionResult elf_process_section(
|
||||
Elf32_Shdr section_header;
|
||||
|
||||
// find section
|
||||
// TODO FL-3526: 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)) {
|
||||
|
||||
@@ -33,7 +33,6 @@ typedef struct {
|
||||
typedef enum {
|
||||
ELFFileLoadStatusSuccess = 0,
|
||||
ELFFileLoadStatusUnspecifiedError,
|
||||
ELFFileLoadStatusNoFreeMemory,
|
||||
ELFFileLoadStatusMissingImports,
|
||||
} ELFFileLoadStatus;
|
||||
|
||||
@@ -43,6 +42,12 @@ typedef enum {
|
||||
ElfProcessSectionResultSuccess,
|
||||
} ElfProcessSectionResult;
|
||||
|
||||
typedef enum {
|
||||
ElfLoadSectionTableResultError,
|
||||
ElfLoadSectionTableResultNoMemory,
|
||||
ElfLoadSectionTableResultSuccess,
|
||||
} ElfLoadSectionTableResult;
|
||||
|
||||
typedef bool(ElfProcessSection)(File* file, size_t offset, size_t size, void* context);
|
||||
|
||||
/**
|
||||
@@ -70,9 +75,9 @@ bool elf_file_open(ELFFile* elf_file, const char* path);
|
||||
/**
|
||||
* @brief Load ELF file section table (load stage #1)
|
||||
* @param elf_file
|
||||
* @return bool
|
||||
* @return ElfLoadSectionTableResult
|
||||
*/
|
||||
bool elf_file_load_section_table(ELFFile* elf_file);
|
||||
ElfLoadSectionTableResult elf_file_load_section_table(ELFFile* elf_file);
|
||||
|
||||
/**
|
||||
* @brief Load and relocate ELF file sections (load stage #2)
|
||||
|
||||
@@ -161,8 +161,11 @@ static FlipperApplicationPreloadStatus
|
||||
// if we are loading full file
|
||||
if(load_full) {
|
||||
// load section table
|
||||
if(!elf_file_load_section_table(app->elf)) {
|
||||
ElfLoadSectionTableResult load_result = elf_file_load_section_table(app->elf);
|
||||
if(load_result == ElfLoadSectionTableResultError) {
|
||||
return FlipperApplicationPreloadStatusInvalidFile;
|
||||
} else if(load_result == ElfLoadSectionTableResultNoMemory) {
|
||||
return FlipperApplicationPreloadStatusNotEnoughMemory;
|
||||
}
|
||||
|
||||
// load assets section
|
||||
@@ -219,8 +222,6 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio
|
||||
elf_file_init_debug_info(app->elf, &app->state);
|
||||
flipper_application_list_add_app(app);
|
||||
return FlipperApplicationLoadStatusSuccess;
|
||||
case ELFFileLoadStatusNoFreeMemory:
|
||||
return FlipperApplicationLoadStatusNoFreeMemory;
|
||||
case ELFFileLoadStatusMissingImports:
|
||||
return FlipperApplicationLoadStatusMissingImports;
|
||||
default:
|
||||
@@ -272,38 +273,38 @@ FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char
|
||||
return app->thread;
|
||||
}
|
||||
|
||||
static const char* preload_status_strings[] = {
|
||||
[FlipperApplicationPreloadStatusSuccess] = "Success",
|
||||
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
|
||||
[FlipperApplicationPreloadStatusInvalidFile] = "Invalid file",
|
||||
[FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest",
|
||||
[FlipperApplicationPreloadStatusApiTooOld] =
|
||||
"Update Application to use with this Firmware (ApiTooOld)",
|
||||
[FlipperApplicationPreloadStatusApiTooNew] =
|
||||
"Update Firmware to use with this Application (ApiTooNew)",
|
||||
[FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch",
|
||||
};
|
||||
|
||||
static const char* load_status_strings[] = {
|
||||
[FlipperApplicationLoadStatusSuccess] = "Success",
|
||||
[FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error",
|
||||
[FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory",
|
||||
[FlipperApplicationLoadStatusMissingImports] =
|
||||
"Update Firmware to use with this Application (MissingImports)",
|
||||
};
|
||||
|
||||
const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) {
|
||||
if(status >= COUNT_OF(preload_status_strings) || preload_status_strings[status] == NULL) {
|
||||
return "Unknown error";
|
||||
switch(status) {
|
||||
case FlipperApplicationPreloadStatusSuccess:
|
||||
return "Success";
|
||||
case FlipperApplicationPreloadStatusInvalidFile:
|
||||
return "Invalid file";
|
||||
case FlipperApplicationPreloadStatusNotEnoughMemory:
|
||||
return "Not enough memory";
|
||||
case FlipperApplicationPreloadStatusInvalidManifest:
|
||||
return "Invalid file manifest";
|
||||
case FlipperApplicationPreloadStatusApiTooOld:
|
||||
return "Update Application to use with this Firmware (ApiTooOld)";
|
||||
case FlipperApplicationPreloadStatusApiTooNew:
|
||||
return "Update Firmware to use with this Application (ApiTooNew)";
|
||||
case FlipperApplicationPreloadStatusTargetMismatch:
|
||||
return "Hardware target mismatch";
|
||||
}
|
||||
return preload_status_strings[status];
|
||||
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
const char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status) {
|
||||
if(status >= COUNT_OF(load_status_strings) || load_status_strings[status] == NULL) {
|
||||
switch(status) {
|
||||
case FlipperApplicationLoadStatusSuccess:
|
||||
return "Success";
|
||||
case FlipperApplicationLoadStatusUnspecifiedError:
|
||||
return "Unknown error";
|
||||
case FlipperApplicationLoadStatusMissingImports:
|
||||
return "Update Firmware to use with this Application (MissingImports)";
|
||||
}
|
||||
return load_status_strings[status];
|
||||
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
const FlipperAppPluginDescriptor*
|
||||
|
||||
@@ -18,8 +18,8 @@ extern "C" {
|
||||
|
||||
typedef enum {
|
||||
FlipperApplicationPreloadStatusSuccess = 0,
|
||||
FlipperApplicationPreloadStatusUnspecifiedError,
|
||||
FlipperApplicationPreloadStatusInvalidFile,
|
||||
FlipperApplicationPreloadStatusNotEnoughMemory,
|
||||
FlipperApplicationPreloadStatusInvalidManifest,
|
||||
FlipperApplicationPreloadStatusApiTooOld,
|
||||
FlipperApplicationPreloadStatusApiTooNew,
|
||||
@@ -29,7 +29,6 @@ typedef enum {
|
||||
typedef enum {
|
||||
FlipperApplicationLoadStatusSuccess = 0,
|
||||
FlipperApplicationLoadStatusUnspecifiedError,
|
||||
FlipperApplicationLoadStatusNoFreeMemory,
|
||||
FlipperApplicationLoadStatusMissingImports,
|
||||
} FlipperApplicationLoadStatus;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user