From d8ef0991fbc3a31b9a64ae45df5fe03459ac5921 Mon Sep 17 00:00:00 2001 From: Sergei Gavrilov Date: Tue, 18 Jun 2024 01:40:47 +1000 Subject: [PATCH] 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 --- applications/main/nfc/nfc_app.c | 9 +- applications/services/loader/loader.c | 216 ++++++++++++++---- applications/services/loader/loader_i.h | 12 + assets/icons/Loader/err_01.png | Bin 0 -> 394 bytes assets/icons/Loader/err_02.png | Bin 0 -> 397 bytes assets/icons/Loader/err_03.png | Bin 0 -> 385 bytes assets/icons/Loader/err_04.png | Bin 0 -> 385 bytes assets/icons/Loader/err_05.png | Bin 0 -> 393 bytes assets/icons/Loader/err_06.png | Bin 0 -> 398 bytes assets/icons/Loader/err_07.png | Bin 0 -> 401 bytes assets/icons/Loader/err_09.png | Bin 0 -> 394 bytes lib/flipper_application/elf/elf_file.c | 137 ++++++++--- lib/flipper_application/elf/elf_file.h | 11 +- lib/flipper_application/flipper_application.c | 57 ++--- lib/flipper_application/flipper_application.h | 3 +- 15 files changed, 324 insertions(+), 121 deletions(-) create mode 100644 assets/icons/Loader/err_01.png create mode 100644 assets/icons/Loader/err_02.png create mode 100644 assets/icons/Loader/err_03.png create mode 100644 assets/icons/Loader/err_04.png create mode 100644 assets/icons/Loader/err_05.png create mode 100644 assets/icons/Loader/err_06.png create mode 100644 assets/icons/Loader/err_07.png create mode 100644 assets/icons/Loader/err_09.png diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index d0557de2a..62a1206f1 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -464,13 +464,10 @@ static bool nfc_is_hal_ready(void) { // No connection to the chip, show an error screen DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Error: NFC Chip Failed", 64, 0, AlignCenter, AlignTop); dialog_message_set_text( - message, - "Error!\nNFC chip failed to start\n\n\nSend a photo of this to:\nsupport@flipperzero.one", - 0, - 0, - AlignLeft, - AlignTop); + message, "Send error photo via\nsupport.flipper.net", 0, 63, AlignLeft, AlignBottom); + dialog_message_set_icon(message, &I_err_09, 128 - 25, 64 - 25); dialog_message_show(dialogs, message); dialog_message_free(message); furi_record_close(RECORD_DIALOGS); diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index d4a4d581a..de013381f 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -27,11 +27,11 @@ static const char* loader_find_external_application_by_name(const char* app_name // API -LoaderStatus - loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) { - furi_check(loader); - furi_check(name); - +static LoaderMessageLoaderStatusResult loader_start_internal( + Loader* loader, + const char* name, + const char* args, + FuriString* error_message) { LoaderMessage message; LoaderMessageLoaderStatusResult result; @@ -44,43 +44,126 @@ LoaderStatus furi_message_queue_put(loader->queue, &message, FuriWaitForever); api_lock_wait_unlock_and_free(message.api_lock); - return result.value; + return result; } -static void - loader_show_gui_error(LoaderStatus status, const char* name, FuriString* error_message) { - if(status == LoaderStatusErrorUnknownApp && +typedef struct { + const char* error; + const char* description; + const char* url; + const Icon* icon; +} LoaderError; + +static const LoaderError err_app_not_found = + {"App Not Found", "Update firmware or app", "err_01", &I_err_01}; +static const LoaderError err_invalid_flie = {"Invalid File", "Update the app", "err_02", &I_err_02}; +static const LoaderError err_invalid_manifest = + {"Invalid Manifest", "Update firmware or app", "err_03", &I_err_03}; +static const LoaderError err_missing_imports = + {"Missing Imports", "Update firmware", "err_04", &I_err_04}; +static const LoaderError err_hw_target_mismatch = + {"HW Target\nMismatch", "App not supported", "err_05", &I_err_05}; +static const LoaderError err_outdated_app = {"Outdated App", "Update the app", "err_06", &I_err_06}; +static const LoaderError err_outdated_firmware = + {"Outdated\nFirmware", "Update firmware", "err_07", &I_err_07}; + +static void loader_dialog_prepare_and_show(DialogsApp* dialogs, const LoaderError* err) { + FuriString* header = furi_string_alloc_printf("Error: %s", err->error); + FuriString* text = + furi_string_alloc_printf("%s\nLearn more:\nr.flipper.net/%s", err->description, err->url); + DialogMessage* message = dialog_message_alloc(); + + dialog_message_set_header(message, furi_string_get_cstr(header), 64, 0, AlignCenter, AlignTop); + dialog_message_set_text(message, furi_string_get_cstr(text), 0, 63, AlignLeft, AlignBottom); + dialog_message_set_icon(message, err->icon, 128 - 25, 64 - 25); + dialog_message_show(dialogs, message); + + dialog_message_free(message); + furi_string_free(header); + furi_string_free(text); +} + +static void loader_show_gui_error( + LoaderMessageLoaderStatusResult status, + const char* name, + FuriString* error_message) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + + if(status.value == LoaderStatusErrorUnknownApp && loader_find_external_application_by_name(name) != NULL) { // Special case for external apps - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogMessage* message = dialog_message_alloc(); dialog_message_set_header(message, "Update needed", 64, 3, AlignCenter, AlignTop); - dialog_message_set_buttons(message, NULL, NULL, NULL); dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22); dialog_message_set_text( message, "Update firmware\nto run this app", 3, 26, AlignLeft, AlignTop); dialog_message_show(dialogs, message); - dialog_message_free(message); - furi_record_close(RECORD_DIALOGS); - } else if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) { + } else if(status.value == LoaderStatusErrorUnknownApp) { + loader_dialog_prepare_and_show(dialogs, &err_app_not_found); + } else if(status.value == LoaderStatusErrorInternal) { // TODO FL-3522: we have many places where we can emit a double start, ex: desktop, menu // so i prefer to not show LoaderStatusErrorAppStarted error message for now - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(message, NULL, NULL, NULL); + switch(status.error) { + case LoaderStatusErrorInvalidFile: + loader_dialog_prepare_and_show(dialogs, &err_invalid_flie); + break; + case LoaderStatusErrorInvalidManifest: + loader_dialog_prepare_and_show(dialogs, &err_invalid_manifest); + break; + case LoaderStatusErrorMissingImports: + loader_dialog_prepare_and_show(dialogs, &err_missing_imports); + break; + case LoaderStatusErrorHWMismatch: + loader_dialog_prepare_and_show(dialogs, &err_hw_target_mismatch); + break; + case LoaderStatusErrorOutdatedApp: + loader_dialog_prepare_and_show(dialogs, &err_outdated_app); + break; + case LoaderStatusErrorOutdatedFirmware: + loader_dialog_prepare_and_show(dialogs, &err_outdated_firmware); + break; + case LoaderStatusErrorOutOfMemory: + dialog_message_set_header( + message, "Error: Out of Memory", 64, 0, AlignCenter, AlignTop); + dialog_message_set_text( + message, + "Not enough RAM to run the\napp. Please reboot the device", + 64, + 13, + AlignCenter, + AlignTop); + dialog_message_set_buttons(message, NULL, NULL, "Reboot"); + if(dialog_message_show(dialogs, message) == DialogMessageButtonRight) { + furi_hal_power_reset(); + } + break; + default: + // Generic error + dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); - furi_string_replace(error_message, "/ext/apps/", ""); - furi_string_replace(error_message, ", ", "\n"); - furi_string_replace(error_message, ": ", "\n"); + furi_string_replace(error_message, "/ext/apps/", ""); + furi_string_replace(error_message, ", ", "\n"); + furi_string_replace(error_message, ": ", "\n"); - dialog_message_set_text( - message, furi_string_get_cstr(error_message), 64, 35, AlignCenter, AlignCenter); - - dialog_message_show(dialogs, message); - dialog_message_free(message); - furi_record_close(RECORD_DIALOGS); + dialog_message_set_text( + message, furi_string_get_cstr(error_message), 64, 35, AlignCenter, AlignCenter); + dialog_message_show(dialogs, message); + break; + } } + + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); +} + +LoaderStatus + loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) { + furi_check(loader); + furi_check(name); + + LoaderMessageLoaderStatusResult result = + loader_start_internal(loader, name, args, error_message); + return result.value; } LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) { @@ -88,10 +171,11 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const furi_check(name); FuriString* error_message = furi_string_alloc(); - LoaderStatus status = loader_start(loader, name, args, error_message); - loader_show_gui_error(status, name, error_message); + LoaderMessageLoaderStatusResult result = + loader_start_internal(loader, name, args, error_message); + loader_show_gui_error(result, name, error_message); furi_string_free(error_message); - return status; + return result.value; } void loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args) { @@ -316,13 +400,46 @@ static LoaderStatus loader_make_success_status(FuriString* error_message) { return LoaderStatusOk; } -static LoaderStatus loader_start_external_app( +static LoaderStatusError + loader_status_error_from_preload_status(FlipperApplicationPreloadStatus status) { + switch(status) { + case FlipperApplicationPreloadStatusInvalidFile: + return LoaderStatusErrorInvalidFile; + case FlipperApplicationPreloadStatusNotEnoughMemory: + return LoaderStatusErrorOutOfMemory; + case FlipperApplicationPreloadStatusInvalidManifest: + return LoaderStatusErrorInvalidManifest; + case FlipperApplicationPreloadStatusApiTooOld: + return LoaderStatusErrorOutdatedApp; + case FlipperApplicationPreloadStatusApiTooNew: + return LoaderStatusErrorOutdatedFirmware; + case FlipperApplicationPreloadStatusTargetMismatch: + return LoaderStatusErrorHWMismatch; + default: + return LoaderStatusErrorUnknown; + } +} + +static LoaderStatusError + loader_status_error_from_load_status(FlipperApplicationLoadStatus status) { + switch(status) { + case FlipperApplicationLoadStatusMissingImports: + return LoaderStatusErrorMissingImports; + default: + return LoaderStatusErrorUnknown; + } +} + +static LoaderMessageLoaderStatusResult loader_start_external_app( Loader* loader, Storage* storage, const char* path, const char* args, FuriString* error_message) { - LoaderStatus status = loader_make_success_status(error_message); + LoaderMessageLoaderStatusResult result; + result.value = loader_make_success_status(error_message); + result.error = LoaderStatusErrorUnknown; + LoaderEvent event; event.type = LoaderEventTypeApplicationBeforeLoad; furi_pubsub_publish(loader->pubsub, &event); @@ -337,8 +454,9 @@ static LoaderStatus loader_start_external_app( flipper_application_preload(loader->app.fap, path); if(preload_res != FlipperApplicationPreloadStatusSuccess) { const char* err_msg = flipper_application_preload_status_to_string(preload_res); - status = loader_make_status_error( + result.value = loader_make_status_error( LoaderStatusErrorInternal, error_message, "Preload failed, %s: %s", path, err_msg); + result.error = loader_status_error_from_preload_status(preload_res); break; } @@ -346,15 +464,16 @@ static LoaderStatus loader_start_external_app( flipper_application_map_to_memory(loader->app.fap); if(load_status != FlipperApplicationLoadStatusSuccess) { const char* err_msg = flipper_application_load_status_to_string(load_status); - status = loader_make_status_error( + result.value = loader_make_status_error( LoaderStatusErrorInternal, error_message, "Load failed, %s: %s", path, err_msg); + result.error = loader_status_error_from_load_status(load_status); break; } FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start)); if(flipper_application_is_plugin(loader->app.fap)) { - status = loader_make_status_error( + result.value = loader_make_status_error( LoaderStatusErrorInternal, error_message, "Plugin %s is not runnable", path); break; } @@ -376,14 +495,14 @@ static LoaderStatus loader_start_external_app( loader_start_app_thread(loader, FlipperInternalApplicationFlagDefault); } while(0); - if(status != LoaderStatusOk) { + if(result.value != LoaderStatusOk) { flipper_application_free(loader->app.fap); loader->app.fap = NULL; event.type = LoaderEventTypeApplicationLoadFailed; furi_pubsub_publish(loader->pubsub, &event); } - return status; + return result; } // process messages @@ -419,23 +538,26 @@ static bool loader_do_is_locked(Loader* loader) { return loader->app.thread != NULL; } -static LoaderStatus loader_do_start_by_name( +static LoaderMessageLoaderStatusResult loader_do_start_by_name( Loader* loader, const char* name, const char* args, FuriString* error_message) { - LoaderStatus status; + LoaderMessageLoaderStatusResult status; + status.value = loader_make_success_status(error_message); + status.error = LoaderStatusErrorUnknown; + do { // check lock if(loader_do_is_locked(loader)) { if(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE) { - status = loader_make_status_error( + status.value = loader_make_status_error( LoaderStatusErrorAppStarted, error_message, "Loader is locked"); } else { const char* current_thread_name = furi_thread_get_name(furi_thread_get_id(loader->app.thread)); - status = loader_make_status_error( + status.value = loader_make_status_error( LoaderStatusErrorAppStarted, error_message, "Loader is locked, please close the \"%s\" first", @@ -449,7 +571,7 @@ static LoaderStatus loader_do_start_by_name( const FlipperInternalApplication* app = loader_find_application_by_name(name); if(app) { loader_start_internal_app(loader, app, args); - status = loader_make_success_status(error_message); + status.value = loader_make_success_status(error_message); break; } } @@ -457,7 +579,7 @@ static LoaderStatus loader_do_start_by_name( // check Applications if(strcmp(name, LOADER_APPLICATIONS_NAME) == 0) { loader_do_applications_show(loader); - status = loader_make_success_status(error_message); + status.value = loader_make_success_status(error_message); break; } @@ -480,7 +602,7 @@ static LoaderStatus loader_do_start_by_name( furi_record_close(RECORD_STORAGE); } - status = loader_make_status_error( + status.value = loader_make_status_error( LoaderStatusErrorUnknownApp, error_message, "Application \"%s\" not found", name); } while(false); @@ -555,13 +677,13 @@ int32_t loader_srv(void* p) { if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) { switch(message.type) { case LoaderMessageTypeStartByName: - message.status_value->value = loader_do_start_by_name( + *(message.status_value) = loader_do_start_by_name( loader, message.start.name, message.start.args, message.start.error_message); api_lock_unlock(message.api_lock); break; case LoaderMessageTypeStartByNameDetachedWithGuiError: { FuriString* error_message = furi_string_alloc(); - LoaderStatus status = loader_do_start_by_name( + LoaderMessageLoaderStatusResult status = loader_do_start_by_name( loader, message.start.name, message.start.args, error_message); loader_show_gui_error(status, message.start.name, error_message); if(message.start.name) free((void*)message.start.name); diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index daa5484d3..95c0c0006 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -39,8 +39,20 @@ typedef struct { FuriString* error_message; } LoaderMessageStartByName; +typedef enum { + LoaderStatusErrorUnknown, + LoaderStatusErrorInvalidFile, + LoaderStatusErrorInvalidManifest, + LoaderStatusErrorMissingImports, + LoaderStatusErrorHWMismatch, + LoaderStatusErrorOutdatedApp, + LoaderStatusErrorOutOfMemory, + LoaderStatusErrorOutdatedFirmware, +} LoaderStatusError; + typedef struct { LoaderStatus value; + LoaderStatusError error; } LoaderMessageLoaderStatusResult; typedef struct { diff --git a/assets/icons/Loader/err_01.png b/assets/icons/Loader/err_01.png new file mode 100644 index 0000000000000000000000000000000000000000..7ffdf761da64afe452664bd98020b59c06b81c32 GIT binary patch literal 394 zcmV;50d@X~P)P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPz6*u0w3i%DJ+3sXE2} zaCwts8N!O#C7Qd&hb6O@kqI$KTynB9<;&N5vXz oxl;IYj>%kGMwd!Zm54L>0_ZZS)%egVs{jB107*qoM6N<$f(eYTbpQYW literal 0 HcmV?d00001 diff --git a/assets/icons/Loader/err_02.png b/assets/icons/Loader/err_02.png new file mode 100644 index 0000000000000000000000000000000000000000..e00b1cb3b813c7db4a956eb60055a6a23cdbaa5a GIT binary patch literal 397 zcmV;80doF{P)P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPnVsr6kDZP_XZ!5wHr~f>NL&AyfAhJIk>Xi} z(-9Pz*?ts?z>L4yB4q*;O$ixb(QRl^MLhy30TB>|p+yX*M*v0k5mov`#ne`5^dT7BDwG!{tZUse~DtD4f%(>Ch{&KLK_=+7-g1+Qu$ z2+a6yf&bu0?J7$UoW-Rf7!#m0&H9#GaS66xs=b(*>sVqar5-MGcPU1dZ7cn&v4c3T zk^?gl*O$9YP?rD9QdM8&%@_gW904;glsBWVQNyTt6_;)WWx{9M{`B>Gk{=2cGgR!+ ryHu~jgD4a;?^W1VWLNe_GFvNO&F{9HrD6Vh00000NkvXXu0mjfdFh}U literal 0 HcmV?d00001 diff --git a/assets/icons/Loader/err_03.png b/assets/icons/Loader/err_03.png new file mode 100644 index 0000000000000000000000000000000000000000..bb28c29abef21a7debcf62a87a8ec97c6352a4e3 GIT binary patch literal 385 zcmV-{0e=38P)P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPnnvJ5|Lv<&ijar*Hi8oc*;ZV? zQgAtk(Anuc<~-yX=P#9&?M4;UckK$z?W_N#PB2s#k%Glh%E*dW39bygqRf4CJmI~1 z6l!?o{c}{`tu=lbYn5P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPe*f@S0@_S)|676o-3 zPBJKOR`Y0-fz|#Q52Z5TT;bkcT#zxvdR)Ru*O}1@}9}zb{;A=P$z)~ zrzse*)}Caq*_cvR*;Mt%H~=n)7qDjJ>Fndk#Z#M%ZT7A0eD_XMk9A9rKA17UPhOF&aofva`)4bXw|ma f#n}^4vxAa1fFPzbmwU<;00000NkvXXu0mjfV1J-q literal 0 HcmV?d00001 diff --git a/assets/icons/Loader/err_05.png b/assets/icons/Loader/err_05.png new file mode 100644 index 0000000000000000000000000000000000000000..c606f9a331a9a65719d80e01bdee913be9bfad14 GIT binary patch literal 393 zcmV;40e1e0P)P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPh=bKZ4IyuKe*~ghvfQNbRxa8JuJW&enP)E@GQf9|uR#eWo zc7vE_TTv|LJhN2&jL%=v<(i@MwCYP000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yP7R8|b_7W-k-Wh)Y~0Z#OMw!B0+Nh7ayUx?oG6HbI-7*Ernv5Q zNC&n!h1|H4AFf}>v!yY=5^v7wIISID#vf$`Wtdd1T;($*lk>XQrv# zuQIi4wa3c5i!*neuC3aioYYVekh;6N)sAluJ{_jJg|uP+_{PtIq-sp?Nq!0>LqU%n zaTie4(eWo-6(kY$9BWysY2(Z22wD1xL1)I7vBxA;&!=-4A1#uny5;QJ_R`D9#B*ol s^jqhom(DQFrJKigd>Ip&tJP000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yP*U?OdwPuD;}xVwf5v<_`MD~o zZidqdN@2IJMhWcXTim3TfsD3{3@}X_BdVw?P-Z{@X^as$oUQ#IS&o1FdimP0o-B&G zlZBpn@4TV|jK;}-A_-|>Ql-o&vl^8*qub}vPYgO!-i(=H^}MP}d1=x3DuvXWy4%-p vRd5P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPR~~+6TDJ_kD+IyV!7f=D z@MyrX;@U_p*>A7X&3lpE3PnikKm*qI>f)4NH_$fn=`s+zI#z3QYY;3vbH;9}Zed2J zS1($=+F5;az^Yd5%b1;6z#JHTL+@%s1?@nYEInH4PlM_>JU| literal 0 HcmV?d00001 diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 398f25209..d0c4f52fb 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -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)) { diff --git a/lib/flipper_application/elf/elf_file.h b/lib/flipper_application/elf/elf_file.h index 921b506bd..caeeb3848 100644 --- a/lib/flipper_application/elf/elf_file.h +++ b/lib/flipper_application/elf/elf_file.h @@ -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) diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index a3509896f..376e9c9ea 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -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* diff --git a/lib/flipper_application/flipper_application.h b/lib/flipper_application/flipper_application.h index 3daca5bc0..71627bd4b 100644 --- a/lib/flipper_application/flipper_application.h +++ b/lib/flipper_application/flipper_application.h @@ -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;