Skip to content

Commit 645a7c5

Browse files
DrZlo13skotopes
andauthored
[FL-3386] Fast FAP Loader (#2790)
* FBT: build and add FastFAP(tm) sections * Elf file: fast loading fap files. Really fast, like x15 times faster. * fastfap.py: cleanup unused imports * Toolchain: 23 version * Elf File: remove log messages * Scripts: fix file permissions * FBT: explicit interpreter for fastfap invocation Co-authored-by: あく <[email protected]>
1 parent 92c1bb8 commit 645a7c5

File tree

22 files changed

+338
-42
lines changed

22 files changed

+338
-42
lines changed

firmware/targets/f18/api_symbols.csv

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_
679679
Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
680680
Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t"
681681
Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool"
682-
Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*"
682+
Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*"
683+
Function,+,elf_symbolname_hash,uint32_t,const char*
683684
Function,+,empty_screen_alloc,EmptyScreen*,
684685
Function,+,empty_screen_free,void,EmptyScreen*
685686
Function,+,empty_screen_get_view,View*,EmptyScreen*

firmware/targets/f7/api_symbols.csv

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_
808808
Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
809809
Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t"
810810
Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool"
811-
Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*"
811+
Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*"
812+
Function,+,elf_symbolname_hash,uint32_t,const char*
812813
Function,+,empty_screen_alloc,EmptyScreen*,
813814
Function,+,empty_screen_free,void,EmptyScreen*
814815
Function,+,empty_screen_get_view,View*,EmptyScreen*

lib/flipper_application/api_hashtable/api_hashtable.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,22 @@
77

88
bool elf_resolve_from_hashtable(
99
const ElfApiInterface* interface,
10-
const char* name,
10+
uint32_t hash,
1111
Elf32_Addr* address) {
12+
bool result = false;
1213
const HashtableApiInterface* hashtable_interface =
1314
static_cast<const HashtableApiInterface*>(interface);
14-
bool result = false;
15-
uint32_t gnu_sym_hash = elf_gnu_hash(name);
1615

1716
sym_entry key = {
18-
.hash = gnu_sym_hash,
17+
.hash = hash,
1918
.address = 0,
2019
};
2120

2221
auto find_res =
2322
std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key);
24-
if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) {
23+
if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) {
2524
FURI_LOG_W(
26-
TAG,
27-
"Can't find symbol '%s' (hash %lx) @ %p!",
28-
name,
29-
gnu_sym_hash,
30-
hashtable_interface->table_cbegin);
25+
TAG, "Can't find symbol with hash %lx @ %p!", hash, hashtable_interface->table_cbegin);
3126
result = false;
3227
} else {
3328
result = true;
@@ -36,3 +31,7 @@ bool elf_resolve_from_hashtable(
3631

3732
return result;
3833
}
34+
35+
uint32_t elf_symbolname_hash(const char* s) {
36+
return elf_gnu_hash(s);
37+
}

lib/flipper_application/api_hashtable/api_hashtable.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ struct sym_entry {
1919
/**
2020
* @brief Resolver for API entries using a pre-sorted table with hashes
2121
* @param interface pointer to HashtableApiInterface
22-
* @param name function name
22+
* @param hash gnu hash of function name
2323
* @param address output for function address
2424
* @return true if the table contains a function
2525
*/
2626
bool elf_resolve_from_hashtable(
2727
const ElfApiInterface* interface,
28-
const char* name,
28+
uint32_t hash,
2929
Elf32_Addr* address);
3030

31+
uint32_t elf_symbolname_hash(const char* s);
32+
3133
#ifdef __cplusplus
3234
}
3335

@@ -48,8 +50,10 @@ struct HashtableApiInterface : public ElfApiInterface {
4850
.hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast<ret_type(*) args_type>(x)) \
4951
}
5052

51-
#define API_VARIABLE(x, var_type) \
52-
sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), }
53+
#define API_VARIABLE(x, var_type) \
54+
sym_entry { \
55+
.hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \
56+
}
5357

5458
constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) {
5559
return k1.hash < k2.hash;

lib/flipper_application/elf/elf_api_interface.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ typedef struct ElfApiInterface {
1111
uint16_t api_version_minor;
1212
bool (*resolver_callback)(
1313
const struct ElfApiInterface* interface,
14-
const char* name,
14+
uint32_t hash,
1515
Elf32_Addr* address);
1616
} ElfApiInterface;

lib/flipper_application/elf/elf_file.c

Lines changed: 119 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
#include "elf_file.h"
33
#include "elf_file_i.h"
44
#include "elf_api_interface.h"
5+
#include "../api_hashtable/api_hashtable.h"
56

67
#define TAG "elf"
78

89
#define ELF_NAME_BUFFER_LEN 32
910
#define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr))
1011
#define IS_FLAGS_SET(v, m) (((v) & (m)) == (m))
1112
#define RESOLVER_THREAD_YIELD_STEP 30
13+
#define FAST_RELOCATION_VERSION 1
1214

1315
// #define ELF_DEBUG_LOG 1
1416

@@ -71,6 +73,7 @@ static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) {
7173
.size = 0,
7274
.rel_count = 0,
7375
.rel_offset = 0,
76+
.fast_rel = NULL,
7477
});
7578
section_p = elf_file_get_section(elf, name);
7679
}
@@ -168,7 +171,8 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) {
168171
static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) {
169172
if(sym->st_shndx == SHN_UNDEF) {
170173
Elf32_Addr addr = 0;
171-
if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) {
174+
uint32_t hash = elf_symbolname_hash(sName);
175+
if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) {
172176
return addr;
173177
}
174178
} else {
@@ -424,6 +428,7 @@ typedef enum {
424428
SectionTypeSymTab = 1 << 3,
425429
SectionTypeStrTab = 1 << 4,
426430
SectionTypeDebugLink = 1 << 5,
431+
SectionTypeFastRelData = 1 << 6,
427432

428433
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab,
429434
} SectionType;
@@ -505,7 +510,8 @@ static SectionType elf_preload_section(
505510
// TODO: how to do it not by name?
506511
// .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER
507512
// .rel.ARM: type 0x9, flags SHT_REL
508-
if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.")) {
513+
if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") ||
514+
str_prefix(name, ".fast.rel.ARM.")) {
509515
FURI_LOG_D(TAG, "Ignoring ARM section");
510516
return SectionTypeUnused;
511517
}
@@ -536,11 +542,31 @@ static SectionType elf_preload_section(
536542

537543
// Load link info section
538544
if(section_header->sh_flags & SHF_INFO_LINK) {
539-
name = name + strlen(".rel");
545+
if(str_prefix(name, ".rel")) {
546+
name = name + strlen(".rel");
547+
ELFSection* section_p = elf_file_get_or_put_section(elf, name);
548+
section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel);
549+
section_p->rel_offset = section_header->sh_offset;
550+
return SectionTypeRelData;
551+
} else {
552+
FURI_LOG_E(TAG, "Unknown link info section '%s'", name);
553+
return SectionTypeERROR;
554+
}
555+
}
556+
557+
// Load fast rel section
558+
if(str_prefix(name, ".fast.rel")) {
559+
name = name + strlen(".fast.rel");
540560
ELFSection* section_p = elf_file_get_or_put_section(elf, name);
541-
section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel);
542-
section_p->rel_offset = section_header->sh_offset;
543-
return SectionTypeRelData;
561+
section_p->fast_rel = malloc(sizeof(ELFSection));
562+
563+
if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) {
564+
FURI_LOG_E(TAG, "Error loading section '%s'", name);
565+
return SectionTypeERROR;
566+
}
567+
568+
FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name);
569+
return SectionTypeFastRelData;
544570
}
545571

546572
// Load symbol table
@@ -571,8 +597,90 @@ static SectionType elf_preload_section(
571597
return SectionTypeUnused;
572598
}
573599

600+
static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) {
601+
Elf32_Addr addr = 0;
602+
if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) {
603+
return addr;
604+
}
605+
return ELF_INVALID_ADDRESS;
606+
}
607+
608+
static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) {
609+
UNUSED(elf);
610+
const uint8_t* start = s->fast_rel->data;
611+
const uint8_t version = *start;
612+
613+
if(version != FAST_RELOCATION_VERSION) {
614+
FURI_LOG_E(TAG, "Unsupported fast relocation version %d", version);
615+
return false;
616+
}
617+
start += 1;
618+
619+
const uint32_t records_count = *((uint32_t*)start);
620+
start += 4;
621+
FURI_LOG_D(TAG, "Fast relocation records count: %ld", records_count);
622+
623+
for(uint32_t i = 0; i < records_count; i++) {
624+
bool is_section = (*start & (0x1 << 7)) ? true : false;
625+
uint8_t type = *start & 0x7F;
626+
start += 1;
627+
uint32_t hash_or_section_index = *((uint32_t*)start);
628+
start += 4;
629+
630+
uint32_t section_value = ELF_INVALID_ADDRESS;
631+
if(is_section) {
632+
section_value = *((uint32_t*)start);
633+
start += 4;
634+
}
635+
636+
const uint32_t offsets_count = *((uint32_t*)start);
637+
start += 4;
638+
639+
FURI_LOG_D(
640+
TAG,
641+
"Fast relocation record %ld: is_section=%d, type=%d, hash_or_section_index=%lX, offsets_count=%ld",
642+
i,
643+
is_section,
644+
type,
645+
hash_or_section_index,
646+
offsets_count);
647+
648+
Elf32_Addr address = 0;
649+
if(is_section) {
650+
ELFSection* symSec = elf_section_of(elf, hash_or_section_index);
651+
if(symSec) {
652+
address = ((Elf32_Addr)symSec->data) + section_value;
653+
}
654+
} else {
655+
address = elf_address_of_by_hash(elf, hash_or_section_index);
656+
}
657+
658+
if(address == ELF_INVALID_ADDRESS) {
659+
FURI_LOG_E(TAG, "Failed to resolve address for hash %lX", hash_or_section_index);
660+
return false;
661+
}
662+
663+
for(uint32_t j = 0; j < offsets_count; j++) {
664+
uint32_t offset = *((uint32_t*)start) & 0x00FFFFFF;
665+
start += 3;
666+
// FURI_LOG_I(TAG, " Fast relocation offset %ld: %ld", j, offset);
667+
Elf32_Addr relAddr = ((Elf32_Addr)s->data) + offset;
668+
elf_relocate_symbol(elf, relAddr, type, address);
669+
}
670+
}
671+
672+
aligned_free(s->fast_rel->data);
673+
free(s->fast_rel);
674+
s->fast_rel = NULL;
675+
676+
return true;
677+
}
678+
574679
static bool elf_relocate_section(ELFFile* elf, ELFSection* section) {
575-
if(section->rel_count) {
680+
if(section->fast_rel) {
681+
FURI_LOG_D(TAG, "Fast relocating section");
682+
return elf_relocate_fast(elf, section);
683+
} else if(section->rel_count) {
576684
FURI_LOG_D(TAG, "Relocating section");
577685
return elf_relocate(elf, section);
578686
} else {
@@ -630,6 +738,10 @@ void elf_file_free(ELFFile* elf) {
630738
if(itref->value.data) {
631739
aligned_free(itref->value.data);
632740
}
741+
if(itref->value.fast_rel) {
742+
aligned_free(itref->value.fast_rel->data);
743+
free(itref->value.fast_rel);
744+
}
633745
free((void*)itref->key);
634746
}
635747

lib/flipper_application/elf/elf_file_i.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST)
1313
*/
1414
typedef int32_t(entry_t)(void*);
1515

16-
typedef struct {
16+
typedef struct ELFSection ELFSection;
17+
18+
struct ELFSection {
1719
void* data;
18-
uint16_t sec_idx;
1920
Elf32_Word size;
2021

2122
size_t rel_count;
2223
Elf32_Off rel_offset;
23-
} ELFSection;
24+
ELFSection* fast_rel;
25+
26+
uint16_t sec_idx;
27+
};
2428

2529
DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST)
2630

lib/flipper_application/plugins/composite_resolver.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ struct CompositeApiResolver {
1313

1414
static bool composite_api_resolver_callback(
1515
const ElfApiInterface* interface,
16-
const char* name,
16+
uint32_t hash,
1717
Elf32_Addr* address) {
1818
CompositeApiResolver* resolver = (CompositeApiResolver*)interface;
1919
for
2020
M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) {
21-
if((*interface)->resolver_callback(*interface, name, address)) {
21+
if((*interface)->resolver_callback(*interface, hash, address)) {
2222
return true;
2323
}
2424
}

scripts/distfap.py

100644100755
File mode changed.

0 commit comments

Comments
 (0)