[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: あく <alleteam@gmail.com>
This commit is contained in:
parent
92c1bb83bf
commit
645a7c5989
@ -679,7 +679,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_
|
|||||||
Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||||
Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t"
|
Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t"
|
||||||
Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool"
|
Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool"
|
||||||
Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*"
|
Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*"
|
||||||
|
Function,+,elf_symbolname_hash,uint32_t,const char*
|
||||||
Function,+,empty_screen_alloc,EmptyScreen*,
|
Function,+,empty_screen_alloc,EmptyScreen*,
|
||||||
Function,+,empty_screen_free,void,EmptyScreen*
|
Function,+,empty_screen_free,void,EmptyScreen*
|
||||||
Function,+,empty_screen_get_view,View*,EmptyScreen*
|
Function,+,empty_screen_get_view,View*,EmptyScreen*
|
||||||
|
|||||||
|
@ -808,7 +808,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_
|
|||||||
Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||||
Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t"
|
Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t"
|
||||||
Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool"
|
Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool"
|
||||||
Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*"
|
Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*"
|
||||||
|
Function,+,elf_symbolname_hash,uint32_t,const char*
|
||||||
Function,+,empty_screen_alloc,EmptyScreen*,
|
Function,+,empty_screen_alloc,EmptyScreen*,
|
||||||
Function,+,empty_screen_free,void,EmptyScreen*
|
Function,+,empty_screen_free,void,EmptyScreen*
|
||||||
Function,+,empty_screen_get_view,View*,EmptyScreen*
|
Function,+,empty_screen_get_view,View*,EmptyScreen*
|
||||||
|
|||||||
|
@ -7,27 +7,22 @@
|
|||||||
|
|
||||||
bool elf_resolve_from_hashtable(
|
bool elf_resolve_from_hashtable(
|
||||||
const ElfApiInterface* interface,
|
const ElfApiInterface* interface,
|
||||||
const char* name,
|
uint32_t hash,
|
||||||
Elf32_Addr* address) {
|
Elf32_Addr* address) {
|
||||||
|
bool result = false;
|
||||||
const HashtableApiInterface* hashtable_interface =
|
const HashtableApiInterface* hashtable_interface =
|
||||||
static_cast<const HashtableApiInterface*>(interface);
|
static_cast<const HashtableApiInterface*>(interface);
|
||||||
bool result = false;
|
|
||||||
uint32_t gnu_sym_hash = elf_gnu_hash(name);
|
|
||||||
|
|
||||||
sym_entry key = {
|
sym_entry key = {
|
||||||
.hash = gnu_sym_hash,
|
.hash = hash,
|
||||||
.address = 0,
|
.address = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
auto find_res =
|
auto find_res =
|
||||||
std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key);
|
std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key);
|
||||||
if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) {
|
if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) {
|
||||||
FURI_LOG_W(
|
FURI_LOG_W(
|
||||||
TAG,
|
TAG, "Can't find symbol with hash %lx @ %p!", hash, hashtable_interface->table_cbegin);
|
||||||
"Can't find symbol '%s' (hash %lx) @ %p!",
|
|
||||||
name,
|
|
||||||
gnu_sym_hash,
|
|
||||||
hashtable_interface->table_cbegin);
|
|
||||||
result = false;
|
result = false;
|
||||||
} else {
|
} else {
|
||||||
result = true;
|
result = true;
|
||||||
@ -36,3 +31,7 @@ bool elf_resolve_from_hashtable(
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t elf_symbolname_hash(const char* s) {
|
||||||
|
return elf_gnu_hash(s);
|
||||||
|
}
|
||||||
@ -19,15 +19,17 @@ struct sym_entry {
|
|||||||
/**
|
/**
|
||||||
* @brief Resolver for API entries using a pre-sorted table with hashes
|
* @brief Resolver for API entries using a pre-sorted table with hashes
|
||||||
* @param interface pointer to HashtableApiInterface
|
* @param interface pointer to HashtableApiInterface
|
||||||
* @param name function name
|
* @param hash gnu hash of function name
|
||||||
* @param address output for function address
|
* @param address output for function address
|
||||||
* @return true if the table contains a function
|
* @return true if the table contains a function
|
||||||
*/
|
*/
|
||||||
bool elf_resolve_from_hashtable(
|
bool elf_resolve_from_hashtable(
|
||||||
const ElfApiInterface* interface,
|
const ElfApiInterface* interface,
|
||||||
const char* name,
|
uint32_t hash,
|
||||||
Elf32_Addr* address);
|
Elf32_Addr* address);
|
||||||
|
|
||||||
|
uint32_t elf_symbolname_hash(const char* s);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +51,9 @@ struct HashtableApiInterface : public ElfApiInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define API_VARIABLE(x, var_type) \
|
#define API_VARIABLE(x, var_type) \
|
||||||
sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), }
|
sym_entry { \
|
||||||
|
.hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \
|
||||||
|
}
|
||||||
|
|
||||||
constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) {
|
constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) {
|
||||||
return k1.hash < k2.hash;
|
return k1.hash < k2.hash;
|
||||||
|
|||||||
@ -11,6 +11,6 @@ typedef struct ElfApiInterface {
|
|||||||
uint16_t api_version_minor;
|
uint16_t api_version_minor;
|
||||||
bool (*resolver_callback)(
|
bool (*resolver_callback)(
|
||||||
const struct ElfApiInterface* interface,
|
const struct ElfApiInterface* interface,
|
||||||
const char* name,
|
uint32_t hash,
|
||||||
Elf32_Addr* address);
|
Elf32_Addr* address);
|
||||||
} ElfApiInterface;
|
} ElfApiInterface;
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#include "elf_file.h"
|
#include "elf_file.h"
|
||||||
#include "elf_file_i.h"
|
#include "elf_file_i.h"
|
||||||
#include "elf_api_interface.h"
|
#include "elf_api_interface.h"
|
||||||
|
#include "../api_hashtable/api_hashtable.h"
|
||||||
|
|
||||||
#define TAG "elf"
|
#define TAG "elf"
|
||||||
|
|
||||||
@ -9,6 +10,7 @@
|
|||||||
#define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr))
|
#define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr))
|
||||||
#define IS_FLAGS_SET(v, m) (((v) & (m)) == (m))
|
#define IS_FLAGS_SET(v, m) (((v) & (m)) == (m))
|
||||||
#define RESOLVER_THREAD_YIELD_STEP 30
|
#define RESOLVER_THREAD_YIELD_STEP 30
|
||||||
|
#define FAST_RELOCATION_VERSION 1
|
||||||
|
|
||||||
// #define ELF_DEBUG_LOG 1
|
// #define ELF_DEBUG_LOG 1
|
||||||
|
|
||||||
@ -71,6 +73,7 @@ static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) {
|
|||||||
.size = 0,
|
.size = 0,
|
||||||
.rel_count = 0,
|
.rel_count = 0,
|
||||||
.rel_offset = 0,
|
.rel_offset = 0,
|
||||||
|
.fast_rel = NULL,
|
||||||
});
|
});
|
||||||
section_p = elf_file_get_section(elf, name);
|
section_p = elf_file_get_section(elf, name);
|
||||||
}
|
}
|
||||||
@ -168,7 +171,8 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) {
|
|||||||
static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) {
|
static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) {
|
||||||
if(sym->st_shndx == SHN_UNDEF) {
|
if(sym->st_shndx == SHN_UNDEF) {
|
||||||
Elf32_Addr addr = 0;
|
Elf32_Addr addr = 0;
|
||||||
if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) {
|
uint32_t hash = elf_symbolname_hash(sName);
|
||||||
|
if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) {
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -424,6 +428,7 @@ typedef enum {
|
|||||||
SectionTypeSymTab = 1 << 3,
|
SectionTypeSymTab = 1 << 3,
|
||||||
SectionTypeStrTab = 1 << 4,
|
SectionTypeStrTab = 1 << 4,
|
||||||
SectionTypeDebugLink = 1 << 5,
|
SectionTypeDebugLink = 1 << 5,
|
||||||
|
SectionTypeFastRelData = 1 << 6,
|
||||||
|
|
||||||
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab,
|
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab,
|
||||||
} SectionType;
|
} SectionType;
|
||||||
@ -505,7 +510,8 @@ static SectionType elf_preload_section(
|
|||||||
// TODO: how to do it not by name?
|
// TODO: how to do it not by name?
|
||||||
// .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER
|
// .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER
|
||||||
// .rel.ARM: type 0x9, flags SHT_REL
|
// .rel.ARM: type 0x9, flags SHT_REL
|
||||||
if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.")) {
|
if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") ||
|
||||||
|
str_prefix(name, ".fast.rel.ARM.")) {
|
||||||
FURI_LOG_D(TAG, "Ignoring ARM section");
|
FURI_LOG_D(TAG, "Ignoring ARM section");
|
||||||
return SectionTypeUnused;
|
return SectionTypeUnused;
|
||||||
}
|
}
|
||||||
@ -536,11 +542,31 @@ static SectionType elf_preload_section(
|
|||||||
|
|
||||||
// Load link info section
|
// Load link info section
|
||||||
if(section_header->sh_flags & SHF_INFO_LINK) {
|
if(section_header->sh_flags & SHF_INFO_LINK) {
|
||||||
|
if(str_prefix(name, ".rel")) {
|
||||||
name = name + strlen(".rel");
|
name = name + strlen(".rel");
|
||||||
ELFSection* section_p = elf_file_get_or_put_section(elf, name);
|
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_count = section_header->sh_size / sizeof(Elf32_Rel);
|
||||||
section_p->rel_offset = section_header->sh_offset;
|
section_p->rel_offset = section_header->sh_offset;
|
||||||
return SectionTypeRelData;
|
return SectionTypeRelData;
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(TAG, "Unknown link info section '%s'", name);
|
||||||
|
return SectionTypeERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load fast rel section
|
||||||
|
if(str_prefix(name, ".fast.rel")) {
|
||||||
|
name = name + strlen(".fast.rel");
|
||||||
|
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)) {
|
||||||
|
FURI_LOG_E(TAG, "Error loading section '%s'", name);
|
||||||
|
return SectionTypeERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name);
|
||||||
|
return SectionTypeFastRelData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load symbol table
|
// Load symbol table
|
||||||
@ -571,8 +597,90 @@ static SectionType elf_preload_section(
|
|||||||
return SectionTypeUnused;
|
return SectionTypeUnused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) {
|
||||||
|
Elf32_Addr addr = 0;
|
||||||
|
if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
return ELF_INVALID_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) {
|
||||||
|
UNUSED(elf);
|
||||||
|
const uint8_t* start = s->fast_rel->data;
|
||||||
|
const uint8_t version = *start;
|
||||||
|
|
||||||
|
if(version != FAST_RELOCATION_VERSION) {
|
||||||
|
FURI_LOG_E(TAG, "Unsupported fast relocation version %d", version);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
start += 1;
|
||||||
|
|
||||||
|
const uint32_t records_count = *((uint32_t*)start);
|
||||||
|
start += 4;
|
||||||
|
FURI_LOG_D(TAG, "Fast relocation records count: %ld", records_count);
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < records_count; i++) {
|
||||||
|
bool is_section = (*start & (0x1 << 7)) ? true : false;
|
||||||
|
uint8_t type = *start & 0x7F;
|
||||||
|
start += 1;
|
||||||
|
uint32_t hash_or_section_index = *((uint32_t*)start);
|
||||||
|
start += 4;
|
||||||
|
|
||||||
|
uint32_t section_value = ELF_INVALID_ADDRESS;
|
||||||
|
if(is_section) {
|
||||||
|
section_value = *((uint32_t*)start);
|
||||||
|
start += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t offsets_count = *((uint32_t*)start);
|
||||||
|
start += 4;
|
||||||
|
|
||||||
|
FURI_LOG_D(
|
||||||
|
TAG,
|
||||||
|
"Fast relocation record %ld: is_section=%d, type=%d, hash_or_section_index=%lX, offsets_count=%ld",
|
||||||
|
i,
|
||||||
|
is_section,
|
||||||
|
type,
|
||||||
|
hash_or_section_index,
|
||||||
|
offsets_count);
|
||||||
|
|
||||||
|
Elf32_Addr address = 0;
|
||||||
|
if(is_section) {
|
||||||
|
ELFSection* symSec = elf_section_of(elf, hash_or_section_index);
|
||||||
|
if(symSec) {
|
||||||
|
address = ((Elf32_Addr)symSec->data) + section_value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
address = elf_address_of_by_hash(elf, hash_or_section_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(address == ELF_INVALID_ADDRESS) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to resolve address for hash %lX", hash_or_section_index);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint32_t j = 0; j < offsets_count; j++) {
|
||||||
|
uint32_t offset = *((uint32_t*)start) & 0x00FFFFFF;
|
||||||
|
start += 3;
|
||||||
|
// FURI_LOG_I(TAG, " Fast relocation offset %ld: %ld", j, offset);
|
||||||
|
Elf32_Addr relAddr = ((Elf32_Addr)s->data) + offset;
|
||||||
|
elf_relocate_symbol(elf, relAddr, type, address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_free(s->fast_rel->data);
|
||||||
|
free(s->fast_rel);
|
||||||
|
s->fast_rel = NULL;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool elf_relocate_section(ELFFile* elf, ELFSection* section) {
|
static bool elf_relocate_section(ELFFile* elf, ELFSection* section) {
|
||||||
if(section->rel_count) {
|
if(section->fast_rel) {
|
||||||
|
FURI_LOG_D(TAG, "Fast relocating section");
|
||||||
|
return elf_relocate_fast(elf, section);
|
||||||
|
} else if(section->rel_count) {
|
||||||
FURI_LOG_D(TAG, "Relocating section");
|
FURI_LOG_D(TAG, "Relocating section");
|
||||||
return elf_relocate(elf, section);
|
return elf_relocate(elf, section);
|
||||||
} else {
|
} else {
|
||||||
@ -630,6 +738,10 @@ void elf_file_free(ELFFile* elf) {
|
|||||||
if(itref->value.data) {
|
if(itref->value.data) {
|
||||||
aligned_free(itref->value.data);
|
aligned_free(itref->value.data);
|
||||||
}
|
}
|
||||||
|
if(itref->value.fast_rel) {
|
||||||
|
aligned_free(itref->value.fast_rel->data);
|
||||||
|
free(itref->value.fast_rel);
|
||||||
|
}
|
||||||
free((void*)itref->key);
|
free((void*)itref->key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,14 +13,18 @@ DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST)
|
|||||||
*/
|
*/
|
||||||
typedef int32_t(entry_t)(void*);
|
typedef int32_t(entry_t)(void*);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct ELFSection ELFSection;
|
||||||
|
|
||||||
|
struct ELFSection {
|
||||||
void* data;
|
void* data;
|
||||||
uint16_t sec_idx;
|
|
||||||
Elf32_Word size;
|
Elf32_Word size;
|
||||||
|
|
||||||
size_t rel_count;
|
size_t rel_count;
|
||||||
Elf32_Off rel_offset;
|
Elf32_Off rel_offset;
|
||||||
} ELFSection;
|
ELFSection* fast_rel;
|
||||||
|
|
||||||
|
uint16_t sec_idx;
|
||||||
|
};
|
||||||
|
|
||||||
DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST)
|
DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST)
|
||||||
|
|
||||||
|
|||||||
@ -13,12 +13,12 @@ struct CompositeApiResolver {
|
|||||||
|
|
||||||
static bool composite_api_resolver_callback(
|
static bool composite_api_resolver_callback(
|
||||||
const ElfApiInterface* interface,
|
const ElfApiInterface* interface,
|
||||||
const char* name,
|
uint32_t hash,
|
||||||
Elf32_Addr* address) {
|
Elf32_Addr* address) {
|
||||||
CompositeApiResolver* resolver = (CompositeApiResolver*)interface;
|
CompositeApiResolver* resolver = (CompositeApiResolver*)interface;
|
||||||
for
|
for
|
||||||
M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) {
|
M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) {
|
||||||
if((*interface)->resolver_callback(*interface, name, address)) {
|
if((*interface)->resolver_callback(*interface, hash, address)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
scripts/distfap.py
Normal file → Executable file
0
scripts/distfap.py
Normal file → Executable file
169
scripts/fastfap.py
Executable file
169
scripts/fastfap.py
Executable file
@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from elftools.elf.elffile import ELFFile
|
||||||
|
from elftools.elf.relocation import RelocationSection
|
||||||
|
from elftools.elf.sections import SymbolTableSection
|
||||||
|
from fbt.sdk.hashes import gnu_sym_hash
|
||||||
|
from flipper.app import App
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RelData:
|
||||||
|
section: int
|
||||||
|
section_value: int
|
||||||
|
type: int
|
||||||
|
offset: int
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class UniqueRelData:
|
||||||
|
section: int
|
||||||
|
section_value: int
|
||||||
|
type: int
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RelSection:
|
||||||
|
name: str
|
||||||
|
oringinal_name: str
|
||||||
|
data: dict[UniqueRelData, list[int]]
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_relsection_data(data: dict[UniqueRelData, list[int]]) -> bytes:
|
||||||
|
result = struct.pack("<B", VERSION)
|
||||||
|
result += struct.pack("<I", len(data))
|
||||||
|
for unique, values in data.items():
|
||||||
|
if unique.section > 0:
|
||||||
|
result += struct.pack("<B", (1 << 7) | unique.type & 0x7F)
|
||||||
|
result += struct.pack("<I", unique.section)
|
||||||
|
result += struct.pack("<I", unique.section_value)
|
||||||
|
else:
|
||||||
|
result += struct.pack("<B", (0 << 7) | unique.type & 0x7F)
|
||||||
|
result += struct.pack("<I", gnu_sym_hash(unique.name))
|
||||||
|
|
||||||
|
result += struct.pack("<I", len(values))
|
||||||
|
for offset in values:
|
||||||
|
result += struct.pack(
|
||||||
|
"<BBB", offset & 0xFF, (offset >> 8) & 0xFF, (offset >> 16) & 0xFF
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class Main(App):
|
||||||
|
def init(self):
|
||||||
|
self.parser.add_argument("fap_src_path", help="App file to upload")
|
||||||
|
self.parser.add_argument("objcopy_path", help="Objcopy path")
|
||||||
|
self.parser.set_defaults(func=self.process)
|
||||||
|
|
||||||
|
def process(self):
|
||||||
|
fap_path = self.args.fap_src_path
|
||||||
|
objcopy_path = self.args.objcopy_path
|
||||||
|
|
||||||
|
sections: list[RelSection] = []
|
||||||
|
|
||||||
|
with open(fap_path, "rb") as f:
|
||||||
|
elf_file = ELFFile(f)
|
||||||
|
|
||||||
|
relocation_sections: list[RelocationSection] = []
|
||||||
|
symtab_section: SymbolTableSection | None = None
|
||||||
|
|
||||||
|
for section in elf_file.iter_sections():
|
||||||
|
if isinstance(section, RelocationSection):
|
||||||
|
relocation_sections.append(section)
|
||||||
|
|
||||||
|
if isinstance(section, SymbolTableSection):
|
||||||
|
symtab_section = section
|
||||||
|
|
||||||
|
if not symtab_section:
|
||||||
|
self.logger.error("No symbol table found")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if not relocation_sections:
|
||||||
|
self.logger.info("No relocation sections found")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
for section in relocation_sections:
|
||||||
|
section_relocations: list[RelData] = []
|
||||||
|
|
||||||
|
for relocation in section.iter_relocations():
|
||||||
|
symbol_id: int = relocation.entry["r_info_sym"]
|
||||||
|
offset: int = relocation.entry["r_offset"]
|
||||||
|
type: int = relocation.entry["r_info_type"]
|
||||||
|
symbol = symtab_section.get_symbol(symbol_id)
|
||||||
|
section_index: int = symbol["st_shndx"]
|
||||||
|
section_value: int = symbol["st_value"]
|
||||||
|
if section_index == "SHN_UNDEF":
|
||||||
|
section_index = 0
|
||||||
|
|
||||||
|
section_relocations.append(
|
||||||
|
RelData(section_index, section_value, type, offset, symbol.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
unique_relocations: dict[UniqueRelData, list[int]] = defaultdict(list)
|
||||||
|
for relocation in section_relocations:
|
||||||
|
unique = UniqueRelData(
|
||||||
|
relocation.section,
|
||||||
|
relocation.section_value,
|
||||||
|
relocation.type,
|
||||||
|
relocation.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
unique_relocations[unique].append(relocation.offset)
|
||||||
|
|
||||||
|
section_name = section.name
|
||||||
|
if section_name.startswith(".rel"):
|
||||||
|
section_name = ".fast.rel" + section_name[4:]
|
||||||
|
else:
|
||||||
|
self.logger.error(
|
||||||
|
"Unknown relocation section name: %s", section_name
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
sections.append(
|
||||||
|
RelSection(section_name, section.name, unique_relocations)
|
||||||
|
)
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
for section in sections:
|
||||||
|
data = serialize_relsection_data(section.data)
|
||||||
|
hash_name = hashlib.md5(section.name.encode()).hexdigest()
|
||||||
|
filename = f"{temp_dir}/{hash_name}.bin"
|
||||||
|
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
self.logger.error(f"File {filename} already exists")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
with open(filename, "wb") as f:
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
|
exit_code = subprocess.run(
|
||||||
|
[
|
||||||
|
objcopy_path,
|
||||||
|
"--add-section",
|
||||||
|
f"{section.name}={filename}",
|
||||||
|
fap_path,
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if exit_code.returncode != 0:
|
||||||
|
self.logger.error("objcopy failed")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
Main()()
|
||||||
@ -1,4 +1,5 @@
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
from .hashes import gnu_sym_hash
|
||||||
|
|
||||||
from cxxheaderparser.parser import CxxParser
|
from cxxheaderparser.parser import CxxParser
|
||||||
from . import (
|
from . import (
|
||||||
@ -72,13 +73,6 @@ class SymbolManager:
|
|||||||
self.api.headers.add(ApiHeader(header))
|
self.api.headers.add(ApiHeader(header))
|
||||||
|
|
||||||
|
|
||||||
def gnu_sym_hash(name: str):
|
|
||||||
h = 0x1505
|
|
||||||
for c in name:
|
|
||||||
h = (h << 5) + h + ord(c)
|
|
||||||
return str(hex(h))[-8:]
|
|
||||||
|
|
||||||
|
|
||||||
class SdkCollector:
|
class SdkCollector:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.symbol_manager = SymbolManager()
|
self.symbol_manager = SymbolManager()
|
||||||
|
|||||||
5
scripts/fbt/sdk/hashes.py
Normal file
5
scripts/fbt/sdk/hashes.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
def gnu_sym_hash(name: str) -> int:
|
||||||
|
h = 0x1505
|
||||||
|
for c in name:
|
||||||
|
h = ((h << 5) + h + ord(c)) & 0xFFFFFFFF
|
||||||
|
return h
|
||||||
@ -384,10 +384,16 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature):
|
|||||||
"${SOURCES} ${TARGET}"
|
"${SOURCES} ${TARGET}"
|
||||||
)
|
)
|
||||||
|
|
||||||
actions.append(
|
actions.extend(
|
||||||
|
(
|
||||||
Action(
|
Action(
|
||||||
objcopy_str,
|
objcopy_str,
|
||||||
"$APPMETAEMBED_COMSTR",
|
"$APPMETAEMBED_COMSTR",
|
||||||
|
),
|
||||||
|
Action(
|
||||||
|
"${PYTHON3} ${FBT_SCRIPT_DIR}/fastfap.py ${TARGET} ${OBJCOPY}",
|
||||||
|
"$FASTFAP_COMSTR",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -450,6 +456,7 @@ def generate(env, **kw):
|
|||||||
APPMETA_COMSTR="\tAPPMETA\t${TARGET}",
|
APPMETA_COMSTR="\tAPPMETA\t${TARGET}",
|
||||||
APPFILE_COMSTR="\tAPPFILE\t${TARGET}",
|
APPFILE_COMSTR="\tAPPFILE\t${TARGET}",
|
||||||
APPMETAEMBED_COMSTR="\tFAP\t${TARGET}",
|
APPMETAEMBED_COMSTR="\tFAP\t${TARGET}",
|
||||||
|
FASTFAP_COMSTR="\tFASTFAP\t${TARGET}",
|
||||||
APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}",
|
APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
0
scripts/fwsize.py
Normal file → Executable file
0
scripts/fwsize.py
Normal file → Executable file
0
scripts/get_env.py
Normal file → Executable file
0
scripts/get_env.py
Normal file → Executable file
0
scripts/runfap.py
Normal file → Executable file
0
scripts/runfap.py
Normal file → Executable file
0
scripts/sconsdist.py
Normal file → Executable file
0
scripts/sconsdist.py
Normal file → Executable file
0
scripts/selfupdate.py
Normal file → Executable file
0
scripts/selfupdate.py
Normal file → Executable file
0
scripts/slideshow.py
Normal file → Executable file
0
scripts/slideshow.py
Normal file → Executable file
@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] (
|
|||||||
exit /b 0
|
exit /b 0
|
||||||
)
|
)
|
||||||
|
|
||||||
set "FLIPPER_TOOLCHAIN_VERSION=21"
|
set "FLIPPER_TOOLCHAIN_VERSION=22"
|
||||||
|
|
||||||
if ["%FBT_TOOLCHAIN_PATH%"] == [""] (
|
if ["%FBT_TOOLCHAIN_PATH%"] == [""] (
|
||||||
set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%"
|
set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%"
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# public variables
|
# public variables
|
||||||
DEFAULT_SCRIPT_PATH="$(pwd -P)";
|
DEFAULT_SCRIPT_PATH="$(pwd -P)";
|
||||||
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}";
|
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"22"}";
|
||||||
|
|
||||||
if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then
|
if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then
|
||||||
FBT_TOOLCHAIN_PATH_WAS_SET=0;
|
FBT_TOOLCHAIN_PATH_WAS_SET=0;
|
||||||
|
|||||||
0
scripts/version.py
Normal file → Executable file
0
scripts/version.py
Normal file → Executable file
Loading…
x
Reference in New Issue
Block a user