Support for building and running external apps (#112)
This commit is contained in:
parent
42e843b463
commit
415442e410
@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
set(BOARD_COMPONENTS Tactility)
|
||||
set(BOARD_COMPONENTS Tactility TactilityC)
|
||||
|
||||
if("${IDF_TARGET}" STREQUAL "esp32")
|
||||
list(APPEND BOARD_COMPONENTS
|
||||
@ -28,6 +28,7 @@ else()
|
||||
add_executable(AppSim ${SOURCES})
|
||||
target_link_libraries(AppSim
|
||||
PRIVATE Tactility
|
||||
PRIVATE TactilityC
|
||||
PRIVATE TactilityCore
|
||||
PRIVATE TactilityHeadless
|
||||
PRIVATE Simulator
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
// Apps
|
||||
#include "Tactility.h"
|
||||
#include "TactilityC/TactilityC.h"
|
||||
|
||||
namespace tt::service::wifi {
|
||||
extern void wifi_task(void*);
|
||||
@ -25,6 +26,8 @@ void app_main() {
|
||||
.autoStartAppId = nullptr
|
||||
};
|
||||
|
||||
tt_init_tactility_c(); // ELF bindings for side-loading on ESP32
|
||||
|
||||
tt::run(config);
|
||||
}
|
||||
|
||||
|
||||
@ -21,9 +21,13 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
"Boards"
|
||||
"App"
|
||||
"Tactility"
|
||||
"TactilityC"
|
||||
"TactilityCore"
|
||||
"TactilityHeadless"
|
||||
"Libraries/esp_lvgl_port"
|
||||
"Libraries/elf_loader"
|
||||
"Libraries/lvgl"
|
||||
"Libraries/lv_screenshot"
|
||||
"Libraries/M5Unified"
|
||||
"Libraries/M5GFX"
|
||||
)
|
||||
@ -53,24 +57,24 @@ project(Tactility)
|
||||
# Defined as regular project for PC and component for ESP
|
||||
if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
||||
add_subdirectory(Tactility)
|
||||
add_subdirectory(TactilityC)
|
||||
add_subdirectory(TactilityCore)
|
||||
add_subdirectory(TactilityHeadless)
|
||||
add_subdirectory(Boards/Simulator)
|
||||
endif()
|
||||
|
||||
add_subdirectory(TactilityCore)
|
||||
|
||||
add_subdirectory(Libraries/lv_screenshot)
|
||||
|
||||
if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
||||
# FreeRTOS
|
||||
set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Boards/Simulator/Source CACHE STRING "")
|
||||
set(FREERTOS_PORT GCC_POSIX CACHE STRING "")
|
||||
add_subdirectory(Libraries/FreeRTOS-Kernel)
|
||||
add_subdirectory(Libraries/lv_screenshot)
|
||||
target_compile_definitions(freertos_kernel PUBLIC "projCOVERAGE_TEST=0")
|
||||
target_include_directories(freertos_kernel
|
||||
PUBLIC Boards/Simulator/Source # for FreeRTOSConfig.h
|
||||
)
|
||||
|
||||
|
||||
# EmbedTLS
|
||||
set(ENABLE_TESTING OFF)
|
||||
set(ENABLE_PROGRAMS OFF)
|
||||
|
||||
18
Documentation/external-apps.md
Normal file
18
Documentation/external-apps.md
Normal file
@ -0,0 +1,18 @@
|
||||
# External Apps
|
||||
|
||||
## Building
|
||||
|
||||
Steps to build:
|
||||
|
||||
- Build the main project with `idf.py build` in the root folder of the project
|
||||
- Open a terminal at `ExternalApps/${AppName}`
|
||||
- The first time, run `./build.sh` (during consecutive runs, you can run `idf.py build`)
|
||||
- Find the executable `build/${AppName}.app.elf` and copy it to an SD card.
|
||||
|
||||
## Developing apps
|
||||
|
||||
Currently, only C is supported. The `TactilityC` project converts Tactility's C++ code into C.
|
||||
|
||||
Methods need to be manually exposed to the ELF apps, by editing `TactilityC/Source/TactilityC.cpp`
|
||||
|
||||
You can put apps in `Data/config` or `Data/assets` during development, because putting it on an SD card takes more effort.
|
||||
@ -1,4 +1,5 @@
|
||||
# TODOs
|
||||
- Loader: Use Timer instead of Thread, and move API to `tt::app::`
|
||||
- Gpio: Use Timer instead of Thread
|
||||
- I2cScannerThread: Use Timer instead of Thread
|
||||
- Bug: I2C Scanner is on M5Stack devices is broken
|
||||
@ -21,12 +22,14 @@
|
||||
- Commit fix to esp_lvgl_port to have `esp_lvgl_port_disp.c` user driver_data instead of user_data
|
||||
- Wifi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes activate again.
|
||||
- T-Deck Plus: Create separate board config
|
||||
- External app loading: Check version of Tactility and check ESP target hardware, to check for compatibility.
|
||||
|
||||
# Core Ideas
|
||||
- Support for displays with different DPI. Consider the layer-based system like on Android.
|
||||
- If present, use LED to show boot status
|
||||
- 2 wire speaker support
|
||||
- tt::app::start() and similar functions as proxies for Loader app start/stop/etc.
|
||||
- USB implementation to make device act as mass storage device.
|
||||
|
||||
# App Ideas
|
||||
- System logger
|
||||
|
||||
17
ExternalApps/HelloWorld/CMakeLists.txt
Normal file
17
ExternalApps/HelloWorld/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(TACTILITY_SKIP_SPIFFS 1)
|
||||
add_definitions(-DESP_TARGET)
|
||||
add_compile_definitions(ESP_TARGET)
|
||||
|
||||
add_definitions(-DLV_CONF_PATH=../../../Boards/Simulator/Source/lv_conf.h)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../../Libraries/elf_loader")
|
||||
|
||||
project(HelloWorld)
|
||||
|
||||
include(elf_loader)
|
||||
project_elf(HelloWorld)
|
||||
|
||||
4
ExternalApps/HelloWorld/build.sh
Executable file
4
ExternalApps/HelloWorld/build.sh
Executable file
@ -0,0 +1,4 @@
|
||||
rm sdkconfig
|
||||
cp ../../sdkconfig sdkconfig
|
||||
cat sdkconfig.override >> sdkconfig
|
||||
idf.py build
|
||||
16
ExternalApps/HelloWorld/main/CMakeLists.txt
Normal file
16
ExternalApps/HelloWorld/main/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
)
|
||||
|
||||
add_prebuilt_library(Tactility ../../../build/esp-idf/Tactility/libTactility.a)
|
||||
add_prebuilt_library(TactilityC ../../../build/esp-idf/TactilityC/libTactilityC.a)
|
||||
add_prebuilt_library(TactilityCore ../../../build/esp-idf/TactilityCore/libTactilityCore.a)
|
||||
add_prebuilt_library(TactilityHeadless ../../../build/esp-idf/TactilityHeadless/libTactilityHeadless.a)
|
||||
add_prebuilt_library(lvgl ../../../build/esp-idf/lvgl/liblvgl.a)
|
||||
|
||||
include_directories("../../../TactilityC/Source")
|
||||
include_directories("../../../Libraries/lvgl/src")
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE TactilityC Tactility TactilityCore TactilityHeadless lvgl)
|
||||
29
ExternalApps/HelloWorld/main/Source/main.c
Normal file
29
ExternalApps/HelloWorld/main/Source/main.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include <stddef.h>
|
||||
#include "TactilityC/app/App.h"
|
||||
#include "TactilityC/lvgl/Toolbar.h"
|
||||
|
||||
/**
|
||||
* Note: LVGL and Tactility methods need to be exposed manually from TactilityC/Source/TactilityC.cpp
|
||||
* Only C is supported for now (C++ symbols fail to link)
|
||||
*/
|
||||
static void onShow(AppContextHandle context, lv_obj_t* parent) {
|
||||
lv_obj_t* toolbar = tt_lvgl_toolbar_create(parent, context);
|
||||
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
|
||||
|
||||
lv_obj_t* label = lv_label_create(parent);
|
||||
lv_label_set_text(label, "Hello, world!");
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
tt_set_app_manifest(
|
||||
"Hello World",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
onShow,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
1
ExternalApps/HelloWorld/sdkconfig.override
Normal file
1
ExternalApps/HelloWorld/sdkconfig.override
Normal file
@ -0,0 +1 @@
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP=y
|
||||
1
Libraries/elf_loader/.component_hash
Normal file
1
Libraries/elf_loader/.component_hash
Normal file
@ -0,0 +1 @@
|
||||
ec9b5b8881cf5c38113ed7eab28a71ed7bfc9f477d6faef851f7f59f7a07b536
|
||||
5
Libraries/elf_loader/CHANGELOG.md
Normal file
5
Libraries/elf_loader/CHANGELOG.md
Normal file
@ -0,0 +1,5 @@
|
||||
# ChangeLog
|
||||
|
||||
## v0.1.0 - 2023-08-14
|
||||
|
||||
* Add basic ELF loader component
|
||||
26
Libraries/elf_loader/CMakeLists.txt
Normal file
26
Libraries/elf_loader/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
if(CONFIG_ELF_LOADER)
|
||||
set(srcs "src/esp_elf_symbol.c"
|
||||
"src/esp_elf.c"
|
||||
"src/esp_elf_adapter.c")
|
||||
|
||||
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
list(APPEND srcs "src/arch/esp_elf_xtensa.c")
|
||||
|
||||
# ESP32-S2 need to set MMU to run ELF
|
||||
if(CONFIG_IDF_TARGET_ESP32S2 AND (CONFIG_ELF_LOADER_LOAD_PSRAM))
|
||||
list(APPEND srcs "src/soc/esp_elf_esp32s2.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(include_dirs "include")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS ${include_dirs}
|
||||
PRIV_REQUIRES spi_flash)
|
||||
|
||||
include(package_manager)
|
||||
if(CONFIG_ELF_LOADER)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
endif()
|
||||
48
Libraries/elf_loader/Kconfig
Normal file
48
Libraries/elf_loader/Kconfig
Normal file
@ -0,0 +1,48 @@
|
||||
menu "Espressif ELF Loader Configuration"
|
||||
visible if (IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3)
|
||||
|
||||
config ELF_LOADER
|
||||
bool "Enable Espressif ELF Loader"
|
||||
default y
|
||||
depends on (IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3)
|
||||
help
|
||||
Select this option to enable ELF Loader and show the submenu with ELF Loader configuration choices.
|
||||
|
||||
if ELF_LOADER
|
||||
config ELF_LOADER_CACHE_OFFSET
|
||||
bool
|
||||
default n
|
||||
help
|
||||
Select this option if D-cache and I-cache has different offset to access the same physical address.
|
||||
|
||||
config ELF_LOADER_SET_MMU
|
||||
bool
|
||||
default n
|
||||
help
|
||||
Select this option if D-cache and I-cache is not symmetry。
|
||||
|
||||
config ELF_LOADER_LOAD_PSRAM
|
||||
bool "Load ELF to PSRAM"
|
||||
default y
|
||||
depends on (IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3) && SPIRAM
|
||||
select ELF_LOADER_CACHE_OFFSET if (IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3)
|
||||
select ELF_LOADER_SET_MMU if IDF_TARGET_ESP32S2
|
||||
help
|
||||
Load ELF file into PSRAM instead of internal SRAM.
|
||||
|
||||
menu "ELF Symbols Table"
|
||||
|
||||
config ELF_LOADER_LIBC_SYMBOLS
|
||||
bool "Libc Symbols Table"
|
||||
default y
|
||||
|
||||
config ELF_LOADER_ESPIDF_SYMBOLS
|
||||
bool "ESP-IDF Symbols Table"
|
||||
default y
|
||||
|
||||
config ELF_LOADER_CUSTOMER_SYMBOLS
|
||||
bool "Customer Symbols Table"
|
||||
default n
|
||||
endmenu
|
||||
endif
|
||||
endmenu
|
||||
83
Libraries/elf_loader/README.md
Normal file
83
Libraries/elf_loader/README.md
Normal file
@ -0,0 +1,83 @@
|
||||
## Description
|
||||
|
||||
[](https://components.espressif.com/components/espressif/elf_loader)
|
||||
|
||||
Espressif ELF(Executable and Linkable Format) loader is a software development kit that is developed based on the ESP-IDF, mainly used to load ELF file compiled based on ESP32 series SoCs to the executable memory area, then link and execute it.
|
||||
|
||||
In this way, the application does not need compile into the whole firmware in advance. It is like running the compiled program through terminal input `./main.o` on the Ubuntu platform automatically, which realizes the separation of application and kernel.
|
||||
|
||||
This ELF loader supports following SoCs:
|
||||
|
||||
- ESP32
|
||||
- ESP32-S2, support running ELF in PSRAM
|
||||
- ESP32-S3, support running ELF in PSRAM
|
||||
|
||||
### Usage
|
||||
|
||||
#### Firmware
|
||||
|
||||
Add a dependency on this component in your component or project's idf_component.yml file.
|
||||
|
||||
```yml
|
||||
dependencies:
|
||||
espressif/elf_loader: "0.*"
|
||||
```
|
||||
|
||||
Enable ELF loader in the menuconfig:
|
||||
|
||||
```
|
||||
Component config --->
|
||||
ESP-ELFLoader Configuration --->
|
||||
[*] Enable Espressif ELF Loader
|
||||
```
|
||||
|
||||
Add API calls in your project as follows:
|
||||
|
||||
```c
|
||||
#include "esp_elf.h"
|
||||
|
||||
esp_elf_t elf;
|
||||
|
||||
// Your Code
|
||||
|
||||
esp_elf_init(&elf);
|
||||
esp_elf_relocate(&elf, elf_file_data_bytes);
|
||||
esp_elf_request(&elf, 0, argc, argv);
|
||||
esp_elf_deinit(&elf);
|
||||
```
|
||||
|
||||
#### ELF APP
|
||||
|
||||
To use this feature to compile ELF file, including the required CMake file in your project's CMakeLists.txt file after the line project(XXXX).
|
||||
|
||||
```cmake
|
||||
project(XXXX)
|
||||
|
||||
# Add
|
||||
include(elf_loader)
|
||||
project_elf(XXXX)
|
||||
```
|
||||
|
||||
Build the project as an ordinary ESP-IDF project, and then the ELF file named `XXXX.app.elf` is in the build directory.
|
||||
|
||||
### Adding the Component to Your Project
|
||||
|
||||
Please use the component manager command add-dependency to add the elf_loader component as a dependency in your project. During the CMake step, the component will be downloaded automatically.
|
||||
|
||||
```
|
||||
idf.py add-dependency "espressif/elf_loader=*"
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
Please use the component manager command create-project-from-example to create a project from the example template.
|
||||
|
||||
```
|
||||
idf.py create-project-from-example "espressif/elf_loader=*:elf_loader_example"
|
||||
```
|
||||
|
||||
This command will download the example elf_loader_example into the current folder. You can navigate into it to build and flash the example.
|
||||
|
||||
Alternatively, you can download examples from the esp-iot-solution repository:
|
||||
1. [build_elf_file_example](https://github.com/espressif/esp-iot-solution/tree/master/examples/elf_loader/build_elf_file_example)
|
||||
2. [elf_loader_example](https://github.com/espressif/esp-iot-solution/tree/master/examples/elf_loader/elf_loader_example)
|
||||
62
Libraries/elf_loader/elf_loader.cmake
Normal file
62
Libraries/elf_loader/elf_loader.cmake
Normal file
@ -0,0 +1,62 @@
|
||||
# The script is to generate ELF for application
|
||||
|
||||
# Trick to temporarily redefine project(). When functions are overriden in CMake, the originals can still be accessed
|
||||
# using an underscore prefixed function of the same name. The following lines make sure that project calls
|
||||
# the original project(). See https://cmake.org/pipermail/cmake/2015-October/061751.html.
|
||||
function(project_elf)
|
||||
endfunction()
|
||||
|
||||
function(_project_elf)
|
||||
endfunction()
|
||||
|
||||
macro(project_elf project_name)
|
||||
# Enable these options to remove unused symbols and reduce linked objects
|
||||
set(cflags -nostartfiles
|
||||
-nostdlib
|
||||
-fPIC
|
||||
-shared
|
||||
-e app_main
|
||||
-fdata-sections
|
||||
-ffunction-sections
|
||||
-Wl,--gc-sections
|
||||
-fvisibility=hidden)
|
||||
|
||||
# Enable this options to remove unnecessary sections in
|
||||
list(APPEND cflags -Wl,--strip-all
|
||||
-Wl,--strip-debug
|
||||
-Wl,--strip-discarded)
|
||||
|
||||
list(APPEND cflags -Dmain=app_main)
|
||||
|
||||
idf_build_set_property(COMPILE_OPTIONS "${cflags}" APPEND)
|
||||
|
||||
set(elf_app "${CMAKE_PROJECT_NAME}.app.elf")
|
||||
|
||||
# Remove more unused sections
|
||||
string(REPLACE "-elf-gcc" "-elf-strip" ${CMAKE_STRIP} ${CMAKE_C_COMPILER})
|
||||
set(strip_flags --strip-unneeded
|
||||
--remove-section=.xt.lit
|
||||
--remove-section=.xt.prop
|
||||
--remove-section=.comment
|
||||
--remove-section=.xtensa.info
|
||||
--remove-section=.got.loc
|
||||
--remove-section=.got)
|
||||
|
||||
# Link input list of libraries to ELF
|
||||
if(ELF_COMPONENTS)
|
||||
foreach(c "${ELF_COMPONENTS}")
|
||||
set(elf_libs "${elf_libs}" "esp-idf/${c}/lib${c}.a")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set(elf_libs ${elf_libs} "${ELF_LIBS}" "esp-idf/main/libmain.a")
|
||||
spaces2list(elf_libs)
|
||||
|
||||
add_custom_command(OUTPUT elf_app
|
||||
COMMAND ${CMAKE_C_COMPILER} ${cflags} ${elf_libs} -o ${elf_app}
|
||||
COMMAND ${CMAKE_STRIP} ${strip_flags} ${elf_app}
|
||||
DEPENDS idf::main
|
||||
COMMENT "Build ELF: ${elf_app}"
|
||||
)
|
||||
add_custom_target(elf ALL DEPENDS elf_app)
|
||||
endmacro()
|
||||
@ -0,0 +1,11 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(hello_world)
|
||||
|
||||
include(elf_loader)
|
||||
project_elf(hello_world)
|
||||
@ -0,0 +1,13 @@
|
||||
## Build ELF file Example
|
||||
|
||||
This example shows how to build ELF file.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
* Note: Only ESP32, ESP32-S2 and ESP32-S3 are supported
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py build` to build project, and when compiling is done you can see `hello_world.app.elf` in `build` directory.
|
||||
@ -0,0 +1 @@
|
||||
idf_component_register(SRCS "main.c")
|
||||
@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
elf_loader:
|
||||
version: "0.*"
|
||||
override_path: "../../../../components/elf_loader"
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
char buffer[] = "hello world %d\n";
|
||||
static int s_cnt;
|
||||
|
||||
int try_test(void)
|
||||
{
|
||||
for (int i = 0; i < 10; i++) {
|
||||
printf(buffer, s_cnt++);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
try_test();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=n
|
||||
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(elf-loader)
|
||||
50
Libraries/elf_loader/examples/elf_loader_example/README.md
Normal file
50
Libraries/elf_loader/examples/elf_loader_example/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
## ELF Loader Example
|
||||
|
||||
This example shows how to use ELF loader to run ELF file.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
* Note: Only ESP32, ESP32-S2 and ESP32-S3 are supported
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board based on espressif ESP32/ESP32-S2/ESP32-S3 SoC
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`).
|
||||
|
||||
In the `Example Configuration` menu:
|
||||
|
||||
* Enable ELF input arguments in the `Support arguments for ELF main` option if necessary.
|
||||
* Set the arguments in the `Arguments of ELF main` option.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
## Example Output
|
||||
|
||||
As you run the example, you will see the following log:
|
||||
|
||||
```
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (382) elf_loader: Start to relocate ELF
|
||||
I (392) elf_loader: Start to run ELF
|
||||
hello world 0
|
||||
hello world 1
|
||||
hello world 2
|
||||
hello world 3
|
||||
hello world 4
|
||||
hello world 5
|
||||
hello world 6
|
||||
hello world 7
|
||||
hello world 8
|
||||
hello world 9
|
||||
I (10392) elf_loader: Success to exit from APP of ELF
|
||||
```
|
||||
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "elf_loader_example_main.c"
|
||||
INCLUDE_DIRS ""
|
||||
EMBED_TXTFILES "test.elf")
|
||||
@ -0,0 +1,12 @@
|
||||
menu "ELF Loader Example Configuration"
|
||||
|
||||
config ELF_LOADER_MAIN_ARGS
|
||||
bool "Support arguments for ELF main"
|
||||
default n
|
||||
|
||||
config ELF_LOADER_MAIN_ARGS_STRING
|
||||
string "Arguments of ELF main"
|
||||
default "main"
|
||||
depends on ELF_LOADER_MAIN_ARGS
|
||||
|
||||
endmenu
|
||||
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_elf.h"
|
||||
|
||||
static const char *TAG = "elf_loader";
|
||||
|
||||
extern const uint8_t test_elf_start[] asm("_binary_test_elf_start");
|
||||
extern const uint8_t test_elf_end[] asm("_binary_test_elf_end");
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_MAIN_ARGS
|
||||
static int elf_args_decode(const char *str, int *argc, char ***argv)
|
||||
{
|
||||
int n;
|
||||
char **argv_buf;
|
||||
char *s;
|
||||
char *pbuf;
|
||||
int len;
|
||||
|
||||
s = (char *)str;
|
||||
n = 1;
|
||||
len = 1;
|
||||
while (*s) {
|
||||
if (*s++ == ' ') {
|
||||
n++;
|
||||
}
|
||||
|
||||
len++;
|
||||
}
|
||||
|
||||
pbuf = malloc(n * sizeof(char *) + len);
|
||||
if (!pbuf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
argv_buf = (char **)pbuf;
|
||||
s = pbuf + n * sizeof(char *);
|
||||
memcpy(s, str, len);
|
||||
for (int i = 0; i < n; i++) {
|
||||
argv_buf[i] = s;
|
||||
|
||||
while (*s != ' ' || *s == 0) {
|
||||
s++;
|
||||
}
|
||||
|
||||
*s++ = 0;
|
||||
}
|
||||
|
||||
*argc = n;
|
||||
*argv = argv_buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int app_main(void)
|
||||
{
|
||||
int ret;
|
||||
int argc;
|
||||
char **argv;
|
||||
esp_elf_t elf;
|
||||
const uint8_t *buffer = test_elf_start;
|
||||
|
||||
ESP_LOGI(TAG, "Start to relocate ELF file");
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_MAIN_ARGS
|
||||
if (strlen(CONFIG_ELF_LOADER_MAIN_ARGS_STRING) <= 0) {
|
||||
ESP_LOGE(TAG, "Failed to check arguments %s",
|
||||
CONFIG_ELF_LOADER_MAIN_ARGS_STRING);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = elf_args_decode(CONFIG_ELF_LOADER_MAIN_ARGS_STRING, &argc, &argv);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "Failed to decode arguments %s errno=%d",
|
||||
CONFIG_ELF_LOADER_MAIN_ARGS_STRING, ret);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
argc = 0;
|
||||
argv = NULL;
|
||||
#endif
|
||||
|
||||
ret = esp_elf_init(&elf);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "Failed to initialize ELF file errno=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_elf_relocate(&elf, buffer);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "Failed to relocate ELF file errno=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Start to run ELF file");
|
||||
|
||||
esp_elf_request(&elf, 0, argc, argv);
|
||||
|
||||
ESP_LOGI(TAG, "Success to exit from ELF file");
|
||||
|
||||
esp_elf_deinit(&elf);
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_MAIN_ARGS
|
||||
if (argv) {
|
||||
free(argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
elf_loader:
|
||||
version: "0.*"
|
||||
override_path: "../../../../components/elf_loader"
|
||||
|
||||
BIN
Libraries/elf_loader/examples/elf_loader_example/main/test.elf
Normal file
BIN
Libraries/elf_loader/examples/elf_loader_example/main/test.elf
Normal file
Binary file not shown.
@ -0,0 +1,2 @@
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_ELF_LOADER_LOAD_PSRAM=y
|
||||
@ -0,0 +1 @@
|
||||
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
|
||||
@ -0,0 +1 @@
|
||||
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
|
||||
8
Libraries/elf_loader/idf_component.yml
Normal file
8
Libraries/elf_loader/idf_component.yml
Normal file
@ -0,0 +1,8 @@
|
||||
dependencies:
|
||||
espressif/cmake_utilities:
|
||||
version: 0.*
|
||||
idf:
|
||||
version: '>=4.4.3'
|
||||
description: Espressif ELF(Executable and Linkable Format) Loader
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/components/elf_loader
|
||||
version: 0.1.0
|
||||
41
Libraries/elf_loader/include/elf_symbol.h
Normal file
41
Libraries/elf_loader/include/elf_symbol.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "private/elf_types.h"
|
||||
#include "elf_symbol.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ESP_ELFSYM_EXPORT(_sym) { #_sym, (void*)&_sym }
|
||||
#define ESP_ELFSYM_END { NULL, NULL }
|
||||
|
||||
/** @brief Function symbol description */
|
||||
|
||||
struct esp_elfsym {
|
||||
const char *name; /*!< Function name */
|
||||
const void *sym; /*!< Function pointer */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Find symbol address by name.
|
||||
*
|
||||
* @param sym_name - Symbol name
|
||||
*
|
||||
* @return Symbol address if success or 0 if failed.
|
||||
*/
|
||||
uintptr_t elf_find_sym(const char *sym_name);
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS
|
||||
void elf_set_custom_symbols(const struct esp_elfsym* symbols);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
94
Libraries/elf_loader/include/esp_elf.h
Normal file
94
Libraries/elf_loader/include/esp_elf.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "private/elf_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Map symbol's address of ELF to physic space.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param sym - ELF symbol address
|
||||
*
|
||||
* @return Mapped physic address.
|
||||
*/
|
||||
uintptr_t esp_elf_map_sym(esp_elf_t *elf, uintptr_t sym);
|
||||
|
||||
/**
|
||||
* @brief Initialize ELF object.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
*
|
||||
* @return ESP_OK if sucess or other if failed.
|
||||
*/
|
||||
int esp_elf_init(esp_elf_t *elf);
|
||||
|
||||
/**
|
||||
* @brief Decode and relocate ELF data.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param pbuf - ELF data buffer
|
||||
*
|
||||
* @return ESP_OK if sucess or other if failed.
|
||||
*/
|
||||
int esp_elf_relocate(esp_elf_t *elf, const uint8_t *pbuf);
|
||||
|
||||
/**
|
||||
* @brief Request running relocated ELF function.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param opt - Request options
|
||||
* @param argc - Arguments number
|
||||
* @param argv - Arguments value array
|
||||
*
|
||||
* @return ESP_OK if sucess or other if failed.
|
||||
*/
|
||||
int esp_elf_request(esp_elf_t *elf, int opt, int argc, char *argv[]);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize ELF object.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_deinit(esp_elf_t *elf);
|
||||
|
||||
/**
|
||||
* @brief Print header description information of ELF.
|
||||
*
|
||||
* @param pbuf - ELF data buffer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_print_hdr(const uint8_t *pbuf);
|
||||
|
||||
/**
|
||||
* @brief Print section header description information of ELF.
|
||||
*
|
||||
* @param pbuf - ELF data buffer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_print_shdr(const uint8_t *pbuf);
|
||||
|
||||
/**
|
||||
* @brief Print section information of ELF.
|
||||
*
|
||||
* @param pbuf - ELF data buffer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_print_sec(esp_elf_t *elf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
101
Libraries/elf_loader/include/private/elf_platform.h
Normal file
101
Libraries/elf_loader/include/private/elf_platform.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "private/elf_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef ELF_ALIGN_SIZE
|
||||
#define ELF_ALIGN_SIZE 4
|
||||
#endif
|
||||
|
||||
#define ELF_ALIGN(_a) (((_a) + (ELF_ALIGN_SIZE - 1)) & \
|
||||
(~(ELF_ALIGN_SIZE - 1)))
|
||||
|
||||
/**
|
||||
* @brief Allocate block of memory.
|
||||
*
|
||||
* @param n - Memory size in byte
|
||||
* @param exec - True: memory can run executable code; False: memory can R/W data
|
||||
*
|
||||
* @return Memory pointer if success or NULL if failed.
|
||||
*/
|
||||
void *esp_elf_malloc(uint32_t n, bool exec);
|
||||
|
||||
/**
|
||||
* @brief Free block of memory.
|
||||
*
|
||||
* @param ptr - memory block pointer allocated by "esp_elf_malloc"
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_free(void *ptr);
|
||||
|
||||
/**
|
||||
* @brief Relocates target architecture symbol of ELF
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param rela - Relocated symbol data
|
||||
* @param sym - ELF symbol table
|
||||
* @param addr - Jumping target address
|
||||
*
|
||||
* @return ESP_OK if sucess or other if failed.
|
||||
*/
|
||||
int esp_elf_arch_relocate(esp_elf_t *elf, const elf32_rela_t *rela,
|
||||
const elf32_sym_t *sym, uint32_t addr);
|
||||
|
||||
/**
|
||||
* @brief Remap symbol from ".data" to ".text" section.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param sym - ELF symbol table
|
||||
*
|
||||
* @return Remapped symbol value
|
||||
*/
|
||||
#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET
|
||||
uintptr_t elf_remap_text(esp_elf_t *elf, uintptr_t sym);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Flush data from cache to external RAM.
|
||||
*
|
||||
* @param None
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM
|
||||
void esp_elf_arch_flush(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize MMU hardware remapping function.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
*
|
||||
* @return 0 if success or a negative value if failed.
|
||||
*/
|
||||
#ifdef CONFIG_ELF_LOADER_SET_MMU
|
||||
int esp_elf_arch_init_mmu(esp_elf_t *elf);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief De-initialize MMU hardware remapping function.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
#ifdef CONFIG_ELF_LOADER_SET_MMU
|
||||
void esp_elf_arch_deinit_mmu(esp_elf_t *elf);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
205
Libraries/elf_loader/include/private/elf_types.h
Normal file
205
Libraries/elf_loader/include/private/elf_types.h
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define EI_NIDENT 16 /*!< Magic number and other information length */
|
||||
|
||||
/** @brief Section Type */
|
||||
|
||||
#define SHT_NULL 0 /*!< invalid section header */
|
||||
#define SHT_PROGBITS 1 /*!< some useful section like .text, .data .got, .plt .rodata .interp and so on */
|
||||
#define SHT_SYMTAB 2 /*!< symbol table */
|
||||
#define SHT_STRTAB 3 /*!< string table */
|
||||
#define SHT_RELA 4 /*!< relocation table */
|
||||
#define SHT_HASH 5 /*!< HASH table */
|
||||
#define SHT_SYNAMIC 6 /*!< dynamic symbol table */
|
||||
#define SHT_NOTE 7 /*!< note information */
|
||||
#define SHT_NOBITS 8 /*!< .bss */
|
||||
#define SHT_REL 9 /*!< relocation table */
|
||||
#define SHT_SHKIB 10 /*!< reserved but has unspecified semantics. */
|
||||
#define SHT_SYNSYM 11 /*!< dynamic symbol */
|
||||
#define SHT_LOPROC 0x70000000 /*!< reserved for processor-specific semantics */
|
||||
#define SHT_LOUSER 0x7fffffff /*!< lower bound of the range of indexes reserved for application programs */
|
||||
#define SHT_HIUSER 0xffffffff /*!< upper bound of the range of indexes reserved for application programs. */
|
||||
|
||||
/** @brief Section Attribute Flags */
|
||||
|
||||
#define SHF_WRITE 1 /*!< writable when task runs */
|
||||
#define SHF_ALLOC 2 /*!< allocated when task runs */
|
||||
#define SHF_EXECINSTR 4 /*!< machine code */
|
||||
#define SHF_MASKPROG 0xf0000000 /*!< reserved for processor-specific semantics */
|
||||
|
||||
/** @brief Symbol Types */
|
||||
|
||||
#define STT_NOTYPE 0 /*!< symbol type is unspecified */
|
||||
#define STT_OBJECT 1 /*!< data object */
|
||||
#define STT_FUNC 2 /*!< code object */
|
||||
#define STT_SECTION 3 /*!< symbol identifies an ELF section */
|
||||
#define STT_FILE 4 /*!< symbol's name is file name */
|
||||
#define STT_COMMON 5 /*!< common data object */
|
||||
#define STT_TLS 6 /*!< thread-local data object */
|
||||
#define STT_NUM 7 /*!< defined types in generic range */
|
||||
#define STT_LOOS 10 /*!< Low OS specific range */
|
||||
#define STT_HIOS 12 /*!< High OS specific range */
|
||||
#define STT_LOPROC 13 /*!< processor specific range */
|
||||
#define STT_HIPROC 15 /*!< processor specific link range */
|
||||
|
||||
/** @brief Section names */
|
||||
|
||||
#define ELF_BSS ".bss" /*!< uninitialized data */
|
||||
#define ELF_DATA ".data" /*!< initialized data */
|
||||
#define ELF_DEBUG ".debug" /*!< debug */
|
||||
#define ELF_DYNAMIC ".dynamic" /*!< dynamic linking information */
|
||||
#define ELF_DYNSTR ".dynstr" /*!< dynamic string table */
|
||||
#define ELF_DYNSYM ".dynsym" /*!< dynamic symbol table */
|
||||
#define ELF_FINI ".fini" /*!< termination code */
|
||||
#define ELF_GOT ".got" /*!< global offset table */
|
||||
#define ELF_HASH ".hash" /*!< symbol hash table */
|
||||
#define ELF_INIT ".init" /*!< initialization code */
|
||||
#define ELF_REL_DATA ".rel.data" /*!< relocation data */
|
||||
#define ELF_REL_FINI ".rel.fini" /*!< relocation termination code */
|
||||
#define ELF_REL_INIT ".rel.init" /*!< relocation initialization code */
|
||||
#define ELF_REL_DYN ".rel.dyn" /*!< relocaltion dynamic link info */
|
||||
#define ELF_REL_RODATA ".rel.rodata" /*!< relocation read-only data */
|
||||
#define ELF_REL_TEXT ".rel.text" /*!< relocation code */
|
||||
#define ELF_RODATA ".rodata" /*!< read-only data */
|
||||
#define ELF_SHSTRTAB ".shstrtab" /*!< section header string table */
|
||||
#define ELF_STRTAB ".strtab" /*!< string table */
|
||||
#define ELF_SYMTAB ".symtab" /*!< symbol table */
|
||||
#define ELF_TEXT ".text" /*!< code */
|
||||
#define ELF_DATA_REL_RO ".data.rel.ro" /*!< dynamic read-only data */
|
||||
|
||||
/** @brief ELF section and symbol operation */
|
||||
|
||||
#define ELF_SEC_TEXT 0
|
||||
#define ELF_SEC_BSS 1
|
||||
#define ELF_SEC_DATA 2
|
||||
#define ELF_SEC_RODATA 3
|
||||
#define ELF_SEC_DRLRO 4
|
||||
#define ELF_SECS 5
|
||||
|
||||
#define ELF_ST_BIND(_i) ((_i) >> 4)
|
||||
#define ELF_ST_TYPE(_i) ((_i) & 0xf)
|
||||
#define ELF_ST_INFO(_b, _t) (((_b)<<4) + ((_t) & 0xf))
|
||||
|
||||
#define ELF_R_SYM(_i) ((_i) >> 8)
|
||||
#define ELF_R_TYPE(_i) ((unsigned char)(_i))
|
||||
#define ELF_R_INFO(_s, _t) (((_s) << 8) + (unsigned char)(_t))
|
||||
|
||||
#define ELF_SEC_MAP(_elf, _sec, _addr) \
|
||||
((_elf)->sec[(_sec)].addr - \
|
||||
(_elf)->sec[(_sec)].v_addr + \
|
||||
(_addr))
|
||||
|
||||
typedef unsigned int Elf32_Addr;
|
||||
typedef unsigned int Elf32_Off;
|
||||
typedef unsigned int Elf32_Word;
|
||||
typedef unsigned short Elf32_Half;
|
||||
typedef int Elf32_Sword;
|
||||
|
||||
/** @brief ELF Header */
|
||||
|
||||
typedef struct elf32_hdr {
|
||||
unsigned char ident[EI_NIDENT]; /*!< ELF Identification */
|
||||
Elf32_Half type; /*!< object file type */
|
||||
Elf32_Half machine; /*!< machine */
|
||||
Elf32_Word version; /*!< object file version */
|
||||
Elf32_Addr entry; /*!< virtual entry point */
|
||||
Elf32_Off phoff; /*!< program header table offset */
|
||||
Elf32_Off shoff; /*!< section header table offset */
|
||||
Elf32_Word flags; /*!< processor-specific flags */
|
||||
Elf32_Half ehsize; /*!< ELF header size */
|
||||
Elf32_Half phentsize; /*!< program header entry size */
|
||||
Elf32_Half phnum; /*!< number of program header entries */
|
||||
Elf32_Half shentsize; /*!< section header entry size */
|
||||
Elf32_Half shnum; /*!< number of section header entries */
|
||||
Elf32_Half shstrndx; /*!< section header table's "section header string table" entry offset */
|
||||
} elf32_hdr_t;
|
||||
|
||||
/** @brief Section Header */
|
||||
|
||||
typedef struct elf32_shdr {
|
||||
Elf32_Word name; /*!< section index and it is offset in section .shstrtab */
|
||||
Elf32_Word type; /*!< type */
|
||||
Elf32_Word flags; /*!< flags */
|
||||
Elf32_Addr addr; /*!< start address when map to task space */
|
||||
Elf32_Off offset; /*!< offset address from file start address */
|
||||
Elf32_Word size; /*!< size */
|
||||
Elf32_Word link; /*!< link to another section */
|
||||
Elf32_Word info; /*!< additional section information */
|
||||
Elf32_Word addralign; /*!< address align */
|
||||
Elf32_Word entsize; /*!< index table size */
|
||||
} elf32_shdr_t;
|
||||
|
||||
/** @brief Symbol Table Entry */
|
||||
|
||||
typedef struct elf32_sym {
|
||||
Elf32_Word name; /*!< name - index into string table */
|
||||
Elf32_Addr value; /*!< symbol value */
|
||||
Elf32_Word size; /*!< symbol size */
|
||||
unsigned char info; /*!< type and binding */
|
||||
unsigned char other; /*!< 0 - no defined meaning */
|
||||
Elf32_Half shndx; /*!< section header index */
|
||||
} elf32_sym_t;
|
||||
|
||||
/** @brief Relocation entry with implicit addend */
|
||||
|
||||
typedef struct elf32_rel {
|
||||
Elf32_Addr offset; /*!< offset of relocation */
|
||||
Elf32_Word info; /*!< symbol table index and type */
|
||||
} elf32_rel_t;
|
||||
|
||||
/** @brief Relocation entry with explicit addend */
|
||||
|
||||
typedef struct elf32_rela {
|
||||
Elf32_Addr offset; /*!< offset of relocation */
|
||||
Elf32_Word info; /*!< symbol table index and type */
|
||||
Elf32_Sword addend; /*!< Added information */
|
||||
} elf32_rela_t;
|
||||
|
||||
/** @brief ELF section object */
|
||||
|
||||
typedef struct esp_elf_sec {
|
||||
uintptr_t v_addr; /*!< symbol virtual address */
|
||||
off_t offset; /*!< offset in ELF */
|
||||
|
||||
uintptr_t addr; /*!< section physic address in memory */
|
||||
size_t size; /*!< section size */
|
||||
} esp_elf_sec_t;
|
||||
|
||||
/** @brief ELF object */
|
||||
|
||||
typedef struct esp_elf {
|
||||
unsigned char *ptext; /*!< instruction buffer pointer */
|
||||
|
||||
unsigned char *pdata; /*!< data buffer pointer */
|
||||
|
||||
esp_elf_sec_t sec[ELF_SECS]; /*!< ".bss", "data", "rodata", ".text" */
|
||||
|
||||
int (*entry)(int argc, char *argv[]); /*!< Entry pointer of ELF */
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_SET_MMU
|
||||
uint32_t text_off; /* .text symbol offset */
|
||||
|
||||
uint32_t mmu_off; /* MMU unit offset */
|
||||
uint32_t mmu_num; /* MMU unit total number */
|
||||
#endif
|
||||
} esp_elf_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
202
Libraries/elf_loader/license.txt
Normal file
202
Libraries/elf_loader/license.txt
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
1
Libraries/elf_loader/project_include.cmake
Normal file
1
Libraries/elf_loader/project_include.cmake
Normal file
@ -0,0 +1 @@
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH})
|
||||
112
Libraries/elf_loader/src/arch/esp_elf_xtensa.c
Normal file
112
Libraries/elf_loader/src/arch/esp_elf_xtensa.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/errno.h>
|
||||
#include "esp_elf.h"
|
||||
#include "esp_log.h"
|
||||
#include "private/elf_platform.h"
|
||||
|
||||
/** @brief Xtensa relocations defined by the ABIs */
|
||||
|
||||
#define R_XTENSA_NONE 0
|
||||
#define R_XTENSA_32 1
|
||||
#define R_XTENSA_RTLD 2
|
||||
#define R_XTENSA_GLOB_DAT 3
|
||||
#define R_XTENSA_JMP_SLOT 4
|
||||
#define R_XTENSA_RELATIVE 5
|
||||
#define R_XTENSA_PLT 6
|
||||
#define R_XTENSA_OP0 8
|
||||
#define R_XTENSA_OP1 9
|
||||
#define R_XTENSA_OP2 10
|
||||
#define R_XTENSA_ASM_EXPAND 11
|
||||
#define R_XTENSA_ASM_SIMPLIFY 12
|
||||
#define R_XTENSA_GNU_VTINHERIT 15
|
||||
#define R_XTENSA_GNU_VTENTRY 16
|
||||
#define R_XTENSA_DIFF8 17
|
||||
#define R_XTENSA_DIFF16 18
|
||||
#define R_XTENSA_DIFF32 19
|
||||
#define R_XTENSA_SLOT0_OP 20
|
||||
#define R_XTENSA_SLOT1_OP 21
|
||||
#define R_XTENSA_SLOT2_OP 22
|
||||
#define R_XTENSA_SLOT3_OP 23
|
||||
#define R_XTENSA_SLOT4_OP 24
|
||||
#define R_XTENSA_SLOT5_OP 25
|
||||
#define R_XTENSA_SLOT6_OP 26
|
||||
#define R_XTENSA_SLOT7_OP 27
|
||||
#define R_XTENSA_SLOT8_OP 28
|
||||
#define R_XTENSA_SLOT9_OP 29
|
||||
#define R_XTENSA_SLOT10_OP 30
|
||||
#define R_XTENSA_SLOT11_OP 31
|
||||
#define R_XTENSA_SLOT12_OP 32
|
||||
#define R_XTENSA_SLOT13_OP 33
|
||||
#define R_XTENSA_SLOT14_OP 34
|
||||
#define R_XTENSA_SLOT0_ALT 35
|
||||
#define R_XTENSA_SLOT1_ALT 36
|
||||
#define R_XTENSA_SLOT2_ALT 37
|
||||
#define R_XTENSA_SLOT3_ALT 38
|
||||
#define R_XTENSA_SLOT4_ALT 39
|
||||
#define R_XTENSA_SLOT5_ALT 40
|
||||
#define R_XTENSA_SLOT6_ALT 41
|
||||
#define R_XTENSA_SLOT7_ALT 42
|
||||
#define R_XTENSA_SLOT8_ALT 43
|
||||
#define R_XTENSA_SLOT9_ALT 44
|
||||
#define R_XTENSA_SLOT10_ALT 45
|
||||
#define R_XTENSA_SLOT11_ALT 46
|
||||
#define R_XTENSA_SLOT12_ALT 47
|
||||
#define R_XTENSA_SLOT13_ALT 48
|
||||
#define R_XTENSA_SLOT14_ALT 49
|
||||
|
||||
static const char *TAG = "elf_arch";
|
||||
|
||||
/**
|
||||
* @brief Relocates target architecture symbol of ELF
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param rela - Relocated symbol data
|
||||
* @param sym - ELF symbol table
|
||||
* @param addr - Jumping target address
|
||||
*
|
||||
* @return ESP_OK if sucess or other if failed.
|
||||
*/
|
||||
int esp_elf_arch_relocate(esp_elf_t *elf, const elf32_rela_t *rela,
|
||||
const elf32_sym_t *sym, uint32_t addr)
|
||||
{
|
||||
uint32_t val;
|
||||
uint32_t *where;
|
||||
|
||||
assert(elf && rela);
|
||||
|
||||
where = (uint32_t *)esp_elf_map_sym(elf, rela->offset);
|
||||
|
||||
ESP_LOGD(TAG, "where=%p addr=%x offset=%x\n", where, (int)addr, (int)rela->offset);
|
||||
|
||||
switch (ELF_R_TYPE(rela->info)) {
|
||||
case R_XTENSA_RELATIVE:
|
||||
val = esp_elf_map_sym(elf, *where);
|
||||
#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET
|
||||
*where = elf_remap_text(elf, val);
|
||||
#else
|
||||
*where = val;
|
||||
#endif
|
||||
break;
|
||||
case R_XTENSA_RTLD:
|
||||
break;
|
||||
case R_XTENSA_GLOB_DAT:
|
||||
case R_XTENSA_JMP_SLOT:
|
||||
#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET
|
||||
*where = elf_remap_text(elf, addr);
|
||||
#else
|
||||
*where = addr;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "info=%d is not supported", ELF_R_TYPE(rela->info));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
459
Libraries/elf_loader/src/esp_elf.c
Normal file
459
Libraries/elf_loader/src/esp_elf.c
Normal file
@ -0,0 +1,459 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "elf_symbol.h"
|
||||
#include "private/elf_platform.h"
|
||||
|
||||
#define stype(_s, _t) ((_s)->type == (_t))
|
||||
#define sflags(_s, _f) (((_s)->flags & (_f)) == (_f))
|
||||
|
||||
static const char *TAG = "ELF";
|
||||
|
||||
/**
|
||||
* @brief Map symbol's address of ELF to physic space.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param sym - ELF symbol address
|
||||
*
|
||||
* @return ESP_OK if sucess or other if failed.
|
||||
*/
|
||||
uintptr_t esp_elf_map_sym(esp_elf_t *elf, uintptr_t sym)
|
||||
{
|
||||
for (int i = 0; i < ELF_SECS; i++) {
|
||||
if ((sym >= elf->sec[i].v_addr) &&
|
||||
(sym < (elf->sec[i].v_addr + elf->sec[i].size))) {
|
||||
return sym - elf->sec[i].v_addr + elf->sec[i].addr;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize ELF object.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
*
|
||||
* @return ESP_OK if sucess or other if failed.
|
||||
*/
|
||||
int esp_elf_init(esp_elf_t *elf)
|
||||
{
|
||||
ESP_LOGI(TAG, "ELF loader version: %d.%d.%d", ELF_LOADER_VER_MAJOR, ELF_LOADER_VER_MINOR, ELF_LOADER_VER_PATCH);
|
||||
|
||||
if (!elf) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(elf, 0, sizeof(esp_elf_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode and relocate ELF data.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param pbuf - ELF data buffer
|
||||
*
|
||||
* @return ESP_OK if sucess or other if failed.
|
||||
*/
|
||||
int esp_elf_relocate(esp_elf_t *elf, const uint8_t *pbuf)
|
||||
{
|
||||
uint32_t entry;
|
||||
uint32_t size;
|
||||
|
||||
const elf32_hdr_t *phdr;
|
||||
const elf32_shdr_t *pshdr;
|
||||
const char *shstrab;
|
||||
|
||||
if (!elf || !pbuf) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phdr = (const elf32_hdr_t *)pbuf;
|
||||
pshdr = (const elf32_shdr_t *)(pbuf + phdr->shoff);
|
||||
shstrab = (const char *)pbuf + pshdr[phdr->shstrndx].offset;
|
||||
|
||||
/* Calculate ELF image size */
|
||||
|
||||
for (uint32_t i = 0; i < phdr->shnum; i++) {
|
||||
const char *name = shstrab + pshdr[i].name;
|
||||
|
||||
if (stype(&pshdr[i], SHT_PROGBITS) && sflags(&pshdr[i], SHF_ALLOC)) {
|
||||
if (sflags(&pshdr[i], SHF_EXECINSTR) && !strcmp(ELF_TEXT, name)) {
|
||||
ESP_LOGD(TAG, ".text sec addr=0x%08x size=0x%08x offset=0x%08x",
|
||||
pshdr[i].addr, pshdr[i].size, pshdr[i].offset);
|
||||
|
||||
elf->sec[ELF_SEC_TEXT].v_addr = pshdr[i].addr;
|
||||
elf->sec[ELF_SEC_TEXT].size = ELF_ALIGN(pshdr[i].size);
|
||||
elf->sec[ELF_SEC_TEXT].offset = pshdr[i].offset;
|
||||
|
||||
ESP_LOGD(TAG, ".text offset is 0x%lx size is 0x%x",
|
||||
elf->sec[ELF_SEC_TEXT].offset,
|
||||
elf->sec[ELF_SEC_TEXT].size);
|
||||
} else if (sflags(&pshdr[i], SHF_WRITE) && !strcmp(ELF_DATA, name)) {
|
||||
ESP_LOGD(TAG, ".data sec addr=0x%08x size=0x%08x offset=0x%08x",
|
||||
pshdr[i].addr, pshdr[i].size, pshdr[i].offset);
|
||||
|
||||
elf->sec[ELF_SEC_DATA].v_addr = pshdr[i].addr;
|
||||
elf->sec[ELF_SEC_DATA].size = pshdr[i].size;
|
||||
elf->sec[ELF_SEC_DATA].offset = pshdr[i].offset;
|
||||
|
||||
ESP_LOGD(TAG, ".data offset is 0x%lx size is 0x%x",
|
||||
elf->sec[ELF_SEC_DATA].offset,
|
||||
elf->sec[ELF_SEC_DATA].size);
|
||||
} else if (!strcmp(ELF_RODATA, name)) {
|
||||
ESP_LOGD(TAG, ".rodata sec addr=0x%08x size=0x%08x offset=0x%08x",
|
||||
pshdr[i].addr, pshdr[i].size, pshdr[i].offset);
|
||||
|
||||
elf->sec[ELF_SEC_RODATA].v_addr = pshdr[i].addr;
|
||||
elf->sec[ELF_SEC_RODATA].size = pshdr[i].size;
|
||||
elf->sec[ELF_SEC_RODATA].offset = pshdr[i].offset;
|
||||
|
||||
ESP_LOGD(TAG, ".rodata offset is 0x%lx size is 0x%x",
|
||||
elf->sec[ELF_SEC_RODATA].offset,
|
||||
elf->sec[ELF_SEC_RODATA].size);
|
||||
} else if (!strcmp(ELF_DATA_REL_RO, name)) {
|
||||
ESP_LOGD(TAG, ".data.rel.ro sec addr=0x%08x size=0x%08x offset=0x%08x",
|
||||
pshdr[i].addr, pshdr[i].size, pshdr[i].offset);
|
||||
|
||||
elf->sec[ELF_SEC_DRLRO].v_addr = pshdr[i].addr;
|
||||
elf->sec[ELF_SEC_DRLRO].size = pshdr[i].size;
|
||||
elf->sec[ELF_SEC_DRLRO].offset = pshdr[i].offset;
|
||||
|
||||
ESP_LOGD(TAG, ".data.rel.ro offset is 0x%lx size is 0x%x",
|
||||
elf->sec[ELF_SEC_DRLRO].offset,
|
||||
elf->sec[ELF_SEC_DRLRO].size);
|
||||
}
|
||||
} else if (stype(&pshdr[i], SHT_NOBITS) &&
|
||||
sflags(&pshdr[i], SHF_ALLOC | SHF_WRITE) &&
|
||||
!strcmp(ELF_BSS, name)) {
|
||||
ESP_LOGD(TAG, ".bss sec addr=0x%08x size=0x%08x offset=0x%08x",
|
||||
pshdr[i].addr, pshdr[i].size, pshdr[i].offset);
|
||||
|
||||
elf->sec[ELF_SEC_BSS].v_addr = pshdr[i].addr;
|
||||
elf->sec[ELF_SEC_BSS].size = pshdr[i].size;
|
||||
elf->sec[ELF_SEC_BSS].offset = pshdr[i].offset;
|
||||
|
||||
ESP_LOGD(TAG, ".bss offset is 0x%lx size is 0x%x",
|
||||
elf->sec[ELF_SEC_BSS].offset,
|
||||
elf->sec[ELF_SEC_BSS].size);
|
||||
}
|
||||
}
|
||||
|
||||
/* No .text on image */
|
||||
|
||||
if (!elf->sec[ELF_SEC_TEXT].size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
elf->ptext = esp_elf_malloc(elf->sec[ELF_SEC_TEXT].size, true);
|
||||
if (!elf->ptext) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
size = elf->sec[ELF_SEC_DATA].size +
|
||||
elf->sec[ELF_SEC_RODATA].size +
|
||||
elf->sec[ELF_SEC_BSS].size +
|
||||
elf->sec[ELF_SEC_DRLRO].size;
|
||||
if (size) {
|
||||
elf->pdata = esp_elf_malloc(size, false);
|
||||
if (!elf->pdata) {
|
||||
esp_elf_free(elf->ptext);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump ".text" from ELF to excutable space memory */
|
||||
|
||||
elf->sec[ELF_SEC_TEXT].addr = (Elf32_Addr)elf->ptext;
|
||||
memcpy(elf->ptext, pbuf + elf->sec[ELF_SEC_TEXT].offset,
|
||||
elf->sec[ELF_SEC_TEXT].size);
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_SET_MMU
|
||||
if (esp_elf_arch_init_mmu(elf)) {
|
||||
esp_elf_free(elf->ptext);
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Dump ".data", ".rodata" and ".bss" from ELF to R/W space memory.
|
||||
*
|
||||
* Todo: Dump ".rodata" to rodata section by MMU/MPU.
|
||||
*/
|
||||
|
||||
if (size) {
|
||||
uint8_t *pdata = elf->pdata;
|
||||
|
||||
if (elf->sec[ELF_SEC_DATA].size) {
|
||||
elf->sec[ELF_SEC_DATA].addr = (uint32_t)pdata;
|
||||
|
||||
memcpy(pdata, pbuf + elf->sec[ELF_SEC_DATA].offset,
|
||||
elf->sec[ELF_SEC_DATA].size);
|
||||
|
||||
pdata += elf->sec[ELF_SEC_DATA].size;
|
||||
}
|
||||
|
||||
if (elf->sec[ELF_SEC_RODATA].size) {
|
||||
elf->sec[ELF_SEC_RODATA].addr = (uint32_t)pdata;
|
||||
|
||||
memcpy(pdata, pbuf + elf->sec[ELF_SEC_RODATA].offset,
|
||||
elf->sec[ELF_SEC_RODATA].size);
|
||||
|
||||
pdata += elf->sec[ELF_SEC_RODATA].size;
|
||||
}
|
||||
|
||||
if (elf->sec[ELF_SEC_DRLRO].size) {
|
||||
elf->sec[ELF_SEC_DRLRO].addr = (uint32_t)pdata;
|
||||
|
||||
memcpy(pdata, pbuf + elf->sec[ELF_SEC_DRLRO].offset,
|
||||
elf->sec[ELF_SEC_DRLRO].size);
|
||||
|
||||
pdata += elf->sec[ELF_SEC_DRLRO].size;
|
||||
}
|
||||
|
||||
if (elf->sec[ELF_SEC_BSS].size) {
|
||||
elf->sec[ELF_SEC_BSS].addr = (uint32_t)pdata;
|
||||
memset(pdata, 0, elf->sec[ELF_SEC_BSS].size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set ELF entry */
|
||||
|
||||
entry = phdr->entry + elf->sec[ELF_SEC_TEXT].addr -
|
||||
elf->sec[ELF_SEC_TEXT].v_addr;
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET
|
||||
elf->entry = (void *)elf_remap_text(elf, (uintptr_t)entry);
|
||||
#else
|
||||
elf->entry = (void *)entry;
|
||||
#endif
|
||||
|
||||
/* Relocation section data */
|
||||
|
||||
for (uint32_t i = 0; i < phdr->shnum; i++) {
|
||||
if (stype(&pshdr[i], SHT_RELA)) {
|
||||
uint32_t nr_reloc;
|
||||
const elf32_rela_t *rela;
|
||||
const elf32_sym_t *symtab;
|
||||
const char *strtab;
|
||||
|
||||
nr_reloc = pshdr[i].size / sizeof(elf32_rela_t);
|
||||
rela = (const elf32_rela_t *)(pbuf + pshdr[i].offset);
|
||||
symtab = (const elf32_sym_t *)(pbuf + pshdr[pshdr[i].link].offset);
|
||||
strtab = (const char *)(pbuf + pshdr[pshdr[pshdr[i].link].link].offset);
|
||||
|
||||
ESP_LOGD(TAG, "Section %s has %d symbol tables", shstrab + pshdr[i].name, (int)nr_reloc);
|
||||
|
||||
for (int i = 0; i < nr_reloc; i++) {
|
||||
int type;
|
||||
uintptr_t addr = 0;
|
||||
elf32_rela_t rela_buf;
|
||||
|
||||
memcpy(&rela_buf, &rela[i], sizeof(elf32_rela_t));
|
||||
|
||||
const elf32_sym_t *sym = &symtab[ELF_R_SYM(rela_buf.info)];
|
||||
|
||||
type = ELF_R_TYPE(rela->info);
|
||||
if (type == STT_COMMON) {
|
||||
const char *comm_name = strtab + sym->name;
|
||||
|
||||
if (comm_name[0]) {
|
||||
addr = elf_find_sym(comm_name);
|
||||
|
||||
if (!addr) {
|
||||
ESP_LOGE(TAG, "Can't find common %s", strtab + sym->name);
|
||||
esp_elf_free(elf->pdata);
|
||||
esp_elf_free(elf->ptext);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Find common %s addr=%x", comm_name, addr);
|
||||
}
|
||||
} else if (type == STT_FILE) {
|
||||
const char *func_name = strtab + sym->name;
|
||||
|
||||
if (sym->value) {
|
||||
addr = esp_elf_map_sym(elf, sym->value);
|
||||
} else {
|
||||
addr = elf_find_sym(func_name);
|
||||
}
|
||||
|
||||
if (!addr) {
|
||||
ESP_LOGE(TAG, "Can't find symbol %s", func_name);
|
||||
esp_elf_free(elf->pdata);
|
||||
esp_elf_free(elf->ptext);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Find function %s addr=%x", func_name, addr);
|
||||
}
|
||||
|
||||
esp_elf_arch_relocate(elf, &rela_buf, sym, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM
|
||||
esp_elf_arch_flush();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request running relocated ELF function.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param opt - Request options
|
||||
* @param argc - Arguments number
|
||||
* @param argv - Arguments value array
|
||||
*
|
||||
* @return ESP_OK if sucess or other if failed.
|
||||
*/
|
||||
int esp_elf_request(esp_elf_t *elf, int opt, int argc, char *argv[])
|
||||
{
|
||||
elf->entry(argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinitialize ELF object.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_deinit(esp_elf_t *elf)
|
||||
{
|
||||
if (elf->pdata) {
|
||||
esp_elf_free(elf->pdata);
|
||||
elf->pdata = NULL;
|
||||
}
|
||||
|
||||
if (elf->ptext) {
|
||||
esp_elf_free(elf->ptext);
|
||||
elf->ptext = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_SET_MMU
|
||||
esp_elf_arch_deinit_mmu(elf);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print header description information of ELF.
|
||||
*
|
||||
* @param pbuf - ELF data buffer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_print_hdr(const uint8_t *pbuf)
|
||||
{
|
||||
const char *s_bits, *s_endian;
|
||||
const elf32_hdr_t *hdr = (const elf32_hdr_t *)pbuf;
|
||||
|
||||
switch (hdr->ident[4]) {
|
||||
case 1:
|
||||
s_bits = "32-bit";
|
||||
break;
|
||||
case 2:
|
||||
s_bits = "64-bit";
|
||||
break;
|
||||
default:
|
||||
s_bits = "invalid bits";
|
||||
break;
|
||||
}
|
||||
|
||||
switch (hdr->ident[5]) {
|
||||
case 1:
|
||||
s_endian = "little-endian";
|
||||
break;
|
||||
case 2:
|
||||
s_endian = "big-endian";
|
||||
break;
|
||||
default:
|
||||
s_endian = "invalid endian";
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdr->ident[0] == 0x7f) {
|
||||
ESP_LOGI(TAG, "%-40s %c%c%c", "Class:", hdr->ident[1], hdr->ident[2], hdr->ident[3]);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "%-40s %s, %s", "Format:", s_bits, s_endian);
|
||||
ESP_LOGI(TAG, "%-40s %x", "Version(current):", hdr->ident[6]);
|
||||
|
||||
ESP_LOGI(TAG, "%-40s %d", "Type:", hdr->type);
|
||||
ESP_LOGI(TAG, "%-40s %d", "Machine:", hdr->machine);
|
||||
ESP_LOGI(TAG, "%-40s %x", "Version:", hdr->version);
|
||||
ESP_LOGI(TAG, "%-40s %x", "Entry point address:", hdr->entry);
|
||||
ESP_LOGI(TAG, "%-40s %x", "Start of program headers:", hdr->phoff);
|
||||
ESP_LOGI(TAG, "%-40s %d", "Start of section headers:", hdr->shoff);
|
||||
ESP_LOGI(TAG, "%-40s 0x%x", "Flags:", hdr->flags);
|
||||
ESP_LOGI(TAG, "%-40s %d", "Size of this header(bytes):", hdr->ehsize);
|
||||
ESP_LOGI(TAG, "%-40s %d", "Size of program headers(bytes):", hdr->phentsize);
|
||||
ESP_LOGI(TAG, "%-40s %d", "Number of program headers:", hdr->phnum);
|
||||
ESP_LOGI(TAG, "%-40s %d", "Size of section headers(bytes):", hdr->shentsize);
|
||||
ESP_LOGI(TAG, "%-40s %d", "Number of section headers:", hdr->shnum);
|
||||
ESP_LOGI(TAG, "%-40s %d", "Section header string table i:", hdr->shstrndx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print section header description information of ELF.
|
||||
*
|
||||
* @param pbuf - ELF data buffer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_print_shdr(const uint8_t *pbuf)
|
||||
{
|
||||
const elf32_hdr_t *phdr = (const elf32_hdr_t *)pbuf;
|
||||
const elf32_shdr_t *pshdr = (const elf32_shdr_t *)((size_t)pbuf + phdr->shoff);
|
||||
|
||||
for (int i = 0; i < phdr->shnum; i++) {
|
||||
ESP_LOGI(TAG, "%-40s %d", "name:", pshdr->name);
|
||||
ESP_LOGI(TAG, "%-40s %d", "type:", pshdr->type);
|
||||
ESP_LOGI(TAG, "%-40s 0x%x", "flags:", pshdr->flags);
|
||||
ESP_LOGI(TAG, "%-40s %x", "addr", pshdr->addr);
|
||||
ESP_LOGI(TAG, "%-40s %x", "offset:", pshdr->offset);
|
||||
ESP_LOGI(TAG, "%-40s %d", "size", pshdr->size);
|
||||
ESP_LOGI(TAG, "%-40s 0x%x", "link", pshdr->link);
|
||||
ESP_LOGI(TAG, "%-40s %d", "addralign", pshdr->addralign);
|
||||
ESP_LOGI(TAG, "%-40s %d", "entsize", pshdr->entsize);
|
||||
|
||||
pshdr = (const elf32_shdr_t *)((size_t)pshdr + sizeof(elf32_shdr_t));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print section information of ELF.
|
||||
*
|
||||
* @param pbuf - ELF data buffer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_print_sec(esp_elf_t *elf)
|
||||
{
|
||||
const char *sec_names[ELF_SECS] = {
|
||||
"text", "bss", "data", "rodata"
|
||||
};
|
||||
|
||||
for (int i = 0; i < ELF_SECS; i++) {
|
||||
ESP_LOGI(TAG, "%s: 0x%08x size 0x%08x", sec_names[i], elf->sec[i].addr, elf->sec[i].size);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "entry: %p", elf->entry);
|
||||
}
|
||||
108
Libraries/elf_loader/src/esp_elf_adapter.c
Normal file
108
Libraries/elf_loader/src/esp_elf_adapter.c
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/errno.h>
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "soc/soc.h"
|
||||
#include "private/elf_platform.h"
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||
#define OFFSET_TEXT_VALUE (SOC_IROM_LOW - SOC_DROM_LOW)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Allocate block of memory.
|
||||
*
|
||||
* @param n - Memory size in byte
|
||||
* @param exec - True: memory can run executable code; False: memory can R/W data
|
||||
*
|
||||
* @return Memory pointer if success or NULL if failed.
|
||||
*/
|
||||
void *esp_elf_malloc(uint32_t n, bool exec)
|
||||
{
|
||||
#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM
|
||||
uint32_t caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
|
||||
#else
|
||||
uint32_t caps = exec ? MALLOC_CAP_EXEC : MALLOC_CAP_8BIT;
|
||||
#endif
|
||||
|
||||
return heap_caps_malloc(n, caps);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free block of memory.
|
||||
*
|
||||
* @param ptr - memory block pointer allocated by "esp_elf_malloc"
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void esp_elf_free(void *ptr)
|
||||
{
|
||||
heap_caps_free(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remap symbol from ".data" to ".text" section.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
* @param sym - ELF symbol table
|
||||
*
|
||||
* @return Remapped symbol value
|
||||
*/
|
||||
#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET
|
||||
uintptr_t elf_remap_text(esp_elf_t *elf, uintptr_t sym)
|
||||
{
|
||||
uintptr_t mapped_sym;
|
||||
esp_elf_sec_t *sec = &elf->sec[ELF_SEC_TEXT];
|
||||
|
||||
if ((sym >= sec->addr) &&
|
||||
(sym < (sec->addr + sec->size))) {
|
||||
#ifdef CONFIG_ELF_LOADER_SET_MMU
|
||||
mapped_sym = sym + elf->text_off;
|
||||
#else
|
||||
mapped_sym = sym + OFFSET_TEXT_VALUE;
|
||||
#endif
|
||||
} else {
|
||||
mapped_sym = sym;
|
||||
}
|
||||
|
||||
return mapped_sym;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Flush data from cache to external RAM.
|
||||
*
|
||||
* @param None
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM
|
||||
void IRAM_ATTR esp_elf_arch_flush(void)
|
||||
{
|
||||
extern void spi_flash_disable_interrupts_caches_and_other_cpu(void);
|
||||
extern void spi_flash_enable_interrupts_caches_and_other_cpu(void);
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
extern void Cache_WriteBack_All(void);
|
||||
|
||||
Cache_WriteBack_All();
|
||||
#else
|
||||
void esp_spiram_writeback_cache(void);
|
||||
|
||||
esp_spiram_writeback_cache();
|
||||
#endif
|
||||
|
||||
spi_flash_disable_interrupts_caches_and_other_cpu();
|
||||
spi_flash_enable_interrupts_caches_and_other_cpu();
|
||||
}
|
||||
#endif
|
||||
|
||||
216
Libraries/elf_loader/src/esp_elf_symbol.c
Normal file
216
Libraries/elf_loader/src/esp_elf_symbol.c
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <reent.h>
|
||||
#include <pthread.h>
|
||||
#include <setjmp.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
#include "elf_symbol.h"
|
||||
|
||||
|
||||
extern int __ltdf2 (double a, double b);
|
||||
extern unsigned int __fixunsdfsi (double a);
|
||||
extern int __gtdf2 (double a, double b);
|
||||
extern double __floatunsidf (unsigned int i);
|
||||
extern double __divdf3 (double a, double b);
|
||||
|
||||
/** @brief Libc public functions symbols look-up table */
|
||||
|
||||
static const struct esp_elfsym g_esp_libc_elfsyms[] = {
|
||||
|
||||
/* string.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(strerror),
|
||||
ESP_ELFSYM_EXPORT(memset),
|
||||
ESP_ELFSYM_EXPORT(memcpy),
|
||||
ESP_ELFSYM_EXPORT(strlen),
|
||||
ESP_ELFSYM_EXPORT(strtod),
|
||||
ESP_ELFSYM_EXPORT(strrchr),
|
||||
ESP_ELFSYM_EXPORT(strchr),
|
||||
ESP_ELFSYM_EXPORT(strcmp),
|
||||
ESP_ELFSYM_EXPORT(strtol),
|
||||
ESP_ELFSYM_EXPORT(strcspn),
|
||||
ESP_ELFSYM_EXPORT(strncat),
|
||||
|
||||
/* stdio.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(puts),
|
||||
ESP_ELFSYM_EXPORT(putchar),
|
||||
ESP_ELFSYM_EXPORT(fputc),
|
||||
ESP_ELFSYM_EXPORT(fputs),
|
||||
ESP_ELFSYM_EXPORT(printf),
|
||||
ESP_ELFSYM_EXPORT(vfprintf),
|
||||
ESP_ELFSYM_EXPORT(fprintf),
|
||||
ESP_ELFSYM_EXPORT(fwrite),
|
||||
|
||||
/* unistd.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(usleep),
|
||||
ESP_ELFSYM_EXPORT(sleep),
|
||||
ESP_ELFSYM_EXPORT(exit),
|
||||
ESP_ELFSYM_EXPORT(close),
|
||||
|
||||
/* stdlib.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(malloc),
|
||||
ESP_ELFSYM_EXPORT(calloc),
|
||||
ESP_ELFSYM_EXPORT(realloc),
|
||||
ESP_ELFSYM_EXPORT(free),
|
||||
|
||||
/* time.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(clock_gettime),
|
||||
ESP_ELFSYM_EXPORT(strftime),
|
||||
|
||||
/* pthread.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(pthread_create),
|
||||
ESP_ELFSYM_EXPORT(pthread_attr_init),
|
||||
ESP_ELFSYM_EXPORT(pthread_attr_setstacksize),
|
||||
ESP_ELFSYM_EXPORT(pthread_detach),
|
||||
ESP_ELFSYM_EXPORT(pthread_join),
|
||||
ESP_ELFSYM_EXPORT(pthread_exit),
|
||||
|
||||
/* newlib */
|
||||
|
||||
ESP_ELFSYM_EXPORT(__errno),
|
||||
ESP_ELFSYM_EXPORT(__getreent),
|
||||
#ifdef __HAVE_LOCALE_INFO__
|
||||
ESP_ELFSYM_EXPORT(__locale_ctype_ptr),
|
||||
#else
|
||||
ESP_ELFSYM_EXPORT(_ctype_),
|
||||
#endif
|
||||
|
||||
/* math */
|
||||
|
||||
ESP_ELFSYM_EXPORT(__ltdf2),
|
||||
ESP_ELFSYM_EXPORT(__fixunsdfsi),
|
||||
ESP_ELFSYM_EXPORT(__gtdf2),
|
||||
ESP_ELFSYM_EXPORT(__floatunsidf),
|
||||
ESP_ELFSYM_EXPORT(__divdf3),
|
||||
|
||||
/* getopt.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(getopt_long),
|
||||
ESP_ELFSYM_EXPORT(optind),
|
||||
ESP_ELFSYM_EXPORT(opterr),
|
||||
ESP_ELFSYM_EXPORT(optarg),
|
||||
ESP_ELFSYM_EXPORT(optopt),
|
||||
|
||||
/* setjmp.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(longjmp),
|
||||
ESP_ELFSYM_EXPORT(setjmp),
|
||||
|
||||
ESP_ELFSYM_END
|
||||
};
|
||||
|
||||
/** @brief ESP-IDF public functions symbols look-up table */
|
||||
|
||||
static const struct esp_elfsym g_esp_espidf_elfsyms[] = {
|
||||
|
||||
/* sys/socket.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(lwip_bind),
|
||||
ESP_ELFSYM_EXPORT(lwip_setsockopt),
|
||||
ESP_ELFSYM_EXPORT(lwip_socket),
|
||||
ESP_ELFSYM_EXPORT(lwip_listen),
|
||||
ESP_ELFSYM_EXPORT(lwip_accept),
|
||||
ESP_ELFSYM_EXPORT(lwip_recv),
|
||||
ESP_ELFSYM_EXPORT(lwip_recvfrom),
|
||||
ESP_ELFSYM_EXPORT(lwip_send),
|
||||
ESP_ELFSYM_EXPORT(lwip_sendto),
|
||||
ESP_ELFSYM_EXPORT(lwip_connect),
|
||||
|
||||
/* arpa/inet.h */
|
||||
|
||||
ESP_ELFSYM_EXPORT(ipaddr_addr),
|
||||
ESP_ELFSYM_EXPORT(lwip_htons),
|
||||
ESP_ELFSYM_EXPORT(lwip_htonl),
|
||||
ESP_ELFSYM_EXPORT(ip4addr_ntoa),
|
||||
|
||||
/* ROM functions */
|
||||
|
||||
ESP_ELFSYM_EXPORT(ets_printf),
|
||||
|
||||
ESP_ELFSYM_END
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS
|
||||
static const struct esp_elfsym* custom_symbols = NULL;
|
||||
void elf_set_custom_symbols(const struct esp_elfsym* symbols) {
|
||||
custom_symbols = symbols;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Find symbol address by name.
|
||||
*
|
||||
* @param sym_name - Symbol name
|
||||
*
|
||||
* @return Symbol address if success or 0 if failed.
|
||||
*/
|
||||
uintptr_t elf_find_sym(const char *sym_name)
|
||||
{
|
||||
const struct esp_elfsym *syms;
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_LIBC_SYMBOLS
|
||||
syms = g_esp_libc_elfsyms;
|
||||
while (syms->name) {
|
||||
if (!strcmp(syms->name, sym_name)) {
|
||||
return (uintptr_t)syms->sym;
|
||||
}
|
||||
|
||||
syms++;
|
||||
}
|
||||
#else
|
||||
syms = g_esp_libc_elfsyms;
|
||||
(void)syms;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_ESPIDF_SYMBOLS
|
||||
syms = g_esp_espidf_elfsyms;
|
||||
while (syms->name) {
|
||||
if (!strcmp(syms->name, sym_name)) {
|
||||
return (uintptr_t)syms->sym;
|
||||
}
|
||||
|
||||
syms++;
|
||||
}
|
||||
#else
|
||||
syms = g_esp_espidf_elfsyms;
|
||||
(void)syms;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS
|
||||
syms = custom_symbols;
|
||||
if (syms != NULL) {
|
||||
while (syms->name) {
|
||||
if (!strcmp(syms->name, sym_name)) {
|
||||
return (uintptr_t)syms->sym;
|
||||
}
|
||||
|
||||
syms++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
127
Libraries/elf_loader/src/soc/esp_elf_esp32s2.c
Normal file
127
Libraries/elf_loader/src/soc/esp_elf_esp32s2.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/errno.h>
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp32s2/rom/cache.h"
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "esp32s2/spiram.h"
|
||||
#endif
|
||||
#include "soc/mmu.h"
|
||||
#include "private/elf_platform.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define PSRAM_VADDR_START 0x3f800000
|
||||
#else
|
||||
#define PSRAM_VADDR_START (DRAM0_CACHE_ADDRESS_HIGH - esp_spiram_get_size())
|
||||
#endif
|
||||
|
||||
#define MMU_INVALID BIT(14)
|
||||
#define MMU_UNIT_SIZE 0x10000
|
||||
#define MMU_REG ((volatile uint32_t *)DR_REG_MMU_TABLE)
|
||||
|
||||
#define ESP_MMU_IBUS_BASE IRAM0_ADDRESS_LOW
|
||||
#define ESP_MMU_IBUS_MAX ((IRAM0_ADDRESS_HIGH - IRAM0_ADDRESS_LOW) / \
|
||||
MMU_UNIT_SIZE)
|
||||
#define ESP_MMU_IBUS_OFF (8)
|
||||
|
||||
#define DADDR_2_SECS(v) ((v) / MMU_UNIT_SIZE)
|
||||
#define IADDR_2_SECS(v) ((v) / MMU_UNIT_SIZE)
|
||||
|
||||
#define PSRAM_OFF(v) ((v) - PSRAM_VADDR_START)
|
||||
#define PSRAM_SECS(v) DADDR_2_SECS(PSRAM_OFF((uintptr_t)(v)))
|
||||
#define PSRAM_ALIGN(v) ((uintptr_t)(v) & (~(MMU_UNIT_SIZE - 1)))
|
||||
|
||||
#define ICACHE_ADDR(s) (ESP_MMU_IBUS_BASE + (s) * MMU_UNIT_SIZE)
|
||||
|
||||
extern void spi_flash_disable_interrupts_caches_and_other_cpu(void);
|
||||
extern void spi_flash_enable_interrupts_caches_and_other_cpu(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize MMU hardware remapping function.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
*
|
||||
* @return 0 if success or a negative value if failed.
|
||||
*/
|
||||
int IRAM_ATTR esp_elf_arch_init_mmu(esp_elf_t *elf)
|
||||
{
|
||||
int ret;
|
||||
int off;
|
||||
uint32_t ibus_secs;
|
||||
volatile uint32_t dbus_secs;
|
||||
esp_elf_sec_t *sec = &elf->sec[ELF_SEC_TEXT];
|
||||
volatile uint32_t *p = MMU_REG;
|
||||
|
||||
if (sec->size % MMU_UNIT_SIZE) {
|
||||
ibus_secs = IADDR_2_SECS(sec->size) + 1;
|
||||
} else {
|
||||
ibus_secs = IADDR_2_SECS(sec->size);
|
||||
}
|
||||
|
||||
dbus_secs = PSRAM_SECS(elf->ptext);
|
||||
|
||||
spi_flash_disable_interrupts_caches_and_other_cpu();
|
||||
|
||||
for (off = ESP_MMU_IBUS_OFF; off < ESP_MMU_IBUS_MAX; off++) {
|
||||
if (p[off] == MMU_INVALID) {
|
||||
int j;
|
||||
|
||||
for (j = 1; j < ibus_secs; j++) {
|
||||
if (p[off + j] != MMU_INVALID) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j >= ibus_secs) {
|
||||
for (int k = 0; k < ibus_secs; k++) {
|
||||
p [off + k] = MMU_ACCESS_SPIRAM | (dbus_secs + k);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spi_flash_enable_interrupts_caches_and_other_cpu();
|
||||
|
||||
if (off >= ESP_MMU_IBUS_MAX) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
elf->mmu_off = off;
|
||||
elf->mmu_num = ibus_secs;
|
||||
elf->text_off = ICACHE_ADDR(off) - PSRAM_ALIGN(elf->ptext);
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief De-initialize MMU hardware remapping function.
|
||||
*
|
||||
* @param elf - ELF object pointer
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void IRAM_ATTR esp_elf_arch_deinit_mmu(esp_elf_t *elf)
|
||||
{
|
||||
volatile int num = elf->mmu_num;
|
||||
volatile int off = elf->mmu_off;
|
||||
volatile uint32_t *p = MMU_REG;
|
||||
|
||||
spi_flash_disable_interrupts_caches_and_other_cpu();
|
||||
|
||||
for (int i = 0; i < num; i++) {
|
||||
p [off + i] = MMU_INVALID;
|
||||
}
|
||||
|
||||
spi_flash_enable_interrupts_caches_and_other_cpu();
|
||||
}
|
||||
@ -4,26 +4,32 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
file(GLOB SOURCES "src/*.c*")
|
||||
file(GLOB HEADERS "src/*.h*")
|
||||
|
||||
add_library(lv_screenshot STATIC)
|
||||
|
||||
target_sources(lv_screenshot
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(lv_screenshot
|
||||
PRIVATE private
|
||||
PUBLIC src
|
||||
)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
target_link_libraries(lv_screenshot
|
||||
PUBLIC idf::lvgl
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "src/"
|
||||
REQUIRES lvgl
|
||||
)
|
||||
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
else()
|
||||
file(GLOB SOURCES "src/*.c*")
|
||||
file(GLOB HEADERS "src/*.h*")
|
||||
|
||||
add_library(lv_screenshot STATIC)
|
||||
|
||||
target_sources(lv_screenshot
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(lv_screenshot
|
||||
PRIVATE private
|
||||
PUBLIC src
|
||||
)
|
||||
|
||||
target_link_libraries(lv_screenshot
|
||||
PUBLIC lvgl
|
||||
)
|
||||
|
||||
@ -19,9 +19,10 @@ There are also built-in apps:
|
||||
Play with the built-in apps or build your own! Use one of the supported devices or set up the drivers for your own hardware platform.
|
||||
|
||||
Noteworthy features:
|
||||
- Touch UI capabilities (via LVGL) with support for input devices such as on-device trackball or keyboard.
|
||||
- An application platform that can run apps and services.
|
||||
- Basic applications to boost productivity, such as a Wi-Fi connectivity and I2C apps.
|
||||
- Touch UI capabilities (via LVGL) with support for input devices such as keyboard, trackable and more.
|
||||
- Run apps and services.
|
||||
- Run external apps from SD card. (limited APIs available, for now)
|
||||
- Basic applications to boost productivity, such as a Wi-Fi connectivity and I2C.
|
||||
- Includes a PC simulator build target to speed up development.
|
||||
|
||||
Requirements:
|
||||
@ -122,6 +123,7 @@ Directories explained:
|
||||
- `App`: The application/firmware example project
|
||||
- `Boards`: Contains board configuration projects with drivers (and simulator)
|
||||
- `Tactility`: The main application platform code
|
||||
- `TactilityC`: C wrappers for making side-loaded apps (C++ is not supported yet)
|
||||
- `TactilityHeadless`: Service framework and default services.
|
||||
- `TactilityCore`: Core functionality regarding threads, stdlib, etc.
|
||||
- `Libraries`: Contains a mix of regular libraries and ESP modules
|
||||
|
||||
@ -10,11 +10,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source/"
|
||||
PRIV_INCLUDE_DIRS "Private/"
|
||||
REQUIRES TactilityHeadless lvgl driver
|
||||
)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB}
|
||||
PUBLIC lv_screenshot
|
||||
REQUIRES TactilityHeadless lvgl driver elf_loader lv_screenshot
|
||||
)
|
||||
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
|
||||
@ -53,8 +53,11 @@ namespace app {
|
||||
namespace wifiapsettings { extern const AppManifest manifest; }
|
||||
namespace wificonnect { extern const AppManifest manifest; }
|
||||
namespace wifimanage { extern const AppManifest manifest; }
|
||||
|
||||
extern const AppManifest elfWrapperManifest;
|
||||
}
|
||||
|
||||
|
||||
#ifndef ESP_PLATFORM
|
||||
extern const app::AppManifest screenshot_app;
|
||||
#endif
|
||||
@ -77,6 +80,8 @@ static const std::vector<const app::AppManifest*> system_apps = {
|
||||
&app::wifimanage::manifest,
|
||||
#ifndef ESP_PLATFORM
|
||||
&app::screenshot::manifest, // Screenshots don't work yet on ESP32
|
||||
#else
|
||||
&app::elfWrapperManifest, // For hot-loading ELF apps
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@ -57,32 +57,32 @@ struct AppManifest {
|
||||
/**
|
||||
* App type affects launch behaviour.
|
||||
*/
|
||||
const Type type = TypeUser;
|
||||
Type type = TypeUser;
|
||||
|
||||
/**
|
||||
* Non-blocking method to call when app is started.
|
||||
*/
|
||||
const AppOnStart onStart = nullptr;
|
||||
AppOnStart onStart = nullptr;
|
||||
|
||||
/**
|
||||
* Non-blocking method to call when app is stopped.
|
||||
*/
|
||||
const AppOnStop _Nullable onStop = nullptr;
|
||||
AppOnStop _Nullable onStop = nullptr;
|
||||
|
||||
/**
|
||||
* Non-blocking method to create the GUI
|
||||
*/
|
||||
const AppOnShow _Nullable onShow = nullptr;
|
||||
AppOnShow _Nullable onShow = nullptr;
|
||||
|
||||
/**
|
||||
* Non-blocking method, called before gui is destroyed
|
||||
*/
|
||||
const AppOnHide _Nullable onHide = nullptr;
|
||||
AppOnHide _Nullable onHide = nullptr;
|
||||
|
||||
/**
|
||||
* Handle the result for apps that are launched
|
||||
*/
|
||||
const AppOnResult _Nullable onResult = nullptr;
|
||||
AppOnResult _Nullable onResult = nullptr;
|
||||
};
|
||||
|
||||
struct {
|
||||
|
||||
126
Tactility/Source/app/ElfApp.cpp
Normal file
126
Tactility/Source/app/ElfApp.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include "file/File.h"
|
||||
#include "ElfApp.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "esp_elf.h"
|
||||
|
||||
#include "service/loader/Loader.h"
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
#define TAG "elf_app"
|
||||
#define ELF_WRAPPER_APP_ID "ElfWrapper"
|
||||
|
||||
static size_t elfManifestSetCount = 0;
|
||||
std::unique_ptr<uint8_t[]> elfFileData;
|
||||
esp_elf_t elf;
|
||||
|
||||
bool startElfApp(const char* filePath) {
|
||||
TT_LOG_I(TAG, "Starting ELF %s", filePath);
|
||||
|
||||
assert(elfFileData == nullptr);
|
||||
|
||||
size_t size = 0;
|
||||
elfFileData = file::readBinary(filePath, size);
|
||||
if (elfFileData == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_elf_init(&elf) < 0) {
|
||||
TT_LOG_E(TAG, "Failed to initialize");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_elf_relocate(&elf, elfFileData.get()) < 0) {
|
||||
TT_LOG_E(TAG, "Failed to load executable");
|
||||
return false;
|
||||
}
|
||||
|
||||
int argc = 0;
|
||||
char* argv[] = {};
|
||||
|
||||
size_t manifest_set_count = elfManifestSetCount;
|
||||
if (esp_elf_request(&elf, 0, argc, argv) < 0) {
|
||||
TT_LOG_W(TAG, "Executable returned error code");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elfManifestSetCount > manifest_set_count) {
|
||||
service::loader::startApp(ELF_WRAPPER_APP_ID);
|
||||
} else {
|
||||
TT_LOG_W(TAG, "App did not set manifest to run - cleaning up ELF");
|
||||
esp_elf_deinit(&elf);
|
||||
elfFileData = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void onStart(AppContext& app) {}
|
||||
static void onStop(AppContext& app) {}
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {}
|
||||
static void onHide(AppContext& app) {}
|
||||
static void onResult(AppContext& app, Result result, const Bundle& resultBundle) {}
|
||||
|
||||
AppManifest elfManifest = {
|
||||
.id = "",
|
||||
.name = "",
|
||||
.type = TypeHidden,
|
||||
.onStart = onStart,
|
||||
.onStop = onStop,
|
||||
.onShow = onShow,
|
||||
.onHide = onHide,
|
||||
.onResult = onResult
|
||||
};
|
||||
|
||||
static void onStartWrapper(AppContext& app) {
|
||||
elfManifest.onStart(app);
|
||||
}
|
||||
|
||||
static void onStopWrapper(AppContext& app) {
|
||||
elfManifest.onStop(app);
|
||||
TT_LOG_I(TAG, "Cleaning up ELF");
|
||||
esp_elf_deinit(&elf);
|
||||
elfFileData = nullptr;
|
||||
}
|
||||
|
||||
static void onShowWrapper(AppContext& app, lv_obj_t* parent) {
|
||||
elfManifest.onShow(app, parent);
|
||||
}
|
||||
|
||||
static void onHideWrapper(AppContext& app) {
|
||||
elfManifest.onHide(app);
|
||||
}
|
||||
|
||||
static void onResultWrapper(AppContext& app, Result result, const Bundle& bundle) {
|
||||
elfManifest.onResult(app, result, bundle);
|
||||
}
|
||||
|
||||
AppManifest elfWrapperManifest = {
|
||||
.id = ELF_WRAPPER_APP_ID,
|
||||
.name = "ELF Wrapper",
|
||||
.type = TypeHidden,
|
||||
.onStart = onStartWrapper,
|
||||
.onStop = onStopWrapper,
|
||||
.onShow = onShowWrapper,
|
||||
.onHide = onHideWrapper,
|
||||
.onResult = onResultWrapper
|
||||
};
|
||||
|
||||
void setElfAppManifest(const AppManifest& manifest) {
|
||||
elfManifest.id = manifest.id;
|
||||
elfManifest.name = manifest.name;
|
||||
elfWrapperManifest.name = manifest.name;
|
||||
elfManifest.onStart = manifest.onStart;
|
||||
elfManifest.onStop = manifest.onStop;
|
||||
elfManifest.onShow = manifest.onShow;
|
||||
elfManifest.onHide = manifest.onHide;
|
||||
elfManifest.onResult = manifest.onResult;
|
||||
|
||||
elfManifestSetCount++;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // ESP_PLATFORM
|
||||
16
Tactility/Source/app/ElfApp.h
Normal file
16
Tactility/Source/app/ElfApp.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppManifest.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
bool startElfApp(const char* filePath);
|
||||
|
||||
void setElfAppManifest(const AppManifest& manifest);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ESP_PLATFORM
|
||||
@ -1,15 +1,17 @@
|
||||
#include "FilesData.h"
|
||||
#include "Tactility.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "Tactility.h"
|
||||
#include "Assets.h"
|
||||
#include "Check.h"
|
||||
#include "FileUtils.h"
|
||||
#include "StringUtils.h"
|
||||
#include "app/ElfApp.h"
|
||||
#include "app/imageviewer/ImageViewer.h"
|
||||
#include "app/textviewer/TextViewer.h"
|
||||
#include "lvgl.h"
|
||||
#include "service/loader/Loader.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <memory>
|
||||
@ -52,6 +54,15 @@ static bool hasFileExtension(const char* path, const char* extension) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isSupportedExecutableFile(const char* filename) {
|
||||
#ifdef ESP_PLATFORM
|
||||
// Currently only the PNG library is built into Tactility
|
||||
return hasFileExtension(filename, ".elf");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool isSupportedImageFile(const char* filename) {
|
||||
// Currently only the PNG library is built into Tactility
|
||||
return hasFileExtension(filename, ".png");
|
||||
@ -120,7 +131,11 @@ static void viewFile(const char* path, const char* filename) {
|
||||
|
||||
TT_LOG_I(TAG, "Clicked %s", filepath);
|
||||
|
||||
if (isSupportedImageFile(filename)) {
|
||||
if (isSupportedExecutableFile(filename)) {
|
||||
#ifdef ESP_PLATFORM
|
||||
app::startElfApp(processed_filepath);
|
||||
#endif
|
||||
} else if (isSupportedImageFile(filename)) {
|
||||
auto bundle = std::make_shared<Bundle>();
|
||||
bundle->putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath);
|
||||
service::loader::startApp("ImageViewer", false, bundle);
|
||||
|
||||
@ -109,3 +109,15 @@ extern const AppManifest manifest = {
|
||||
};
|
||||
|
||||
}
|
||||
extern "C" {
|
||||
|
||||
extern void tt_app_selectiondialog_start2(const char* title, int argc, const char* argv[]) {
|
||||
std::vector<std::string> list;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const char* item = argv[i];
|
||||
list.push_back(item);
|
||||
}
|
||||
tt::app::selectiondialog::start(title, list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,4 +19,4 @@ namespace tt::app::selectiondialog {
|
||||
/** App result data */
|
||||
|
||||
int32_t getResultIndex(const Bundle& bundle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,65 +1,13 @@
|
||||
#include "LabelUtils.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "file/File.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
#define TAG "tt_lv_label"
|
||||
|
||||
static long file_get_size(FILE* file) {
|
||||
long original_offset = ftell(file);
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
TT_LOG_E(TAG, "fseek failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
long file_size = ftell(file);
|
||||
if (file_size == -1) {
|
||||
TT_LOG_E(TAG, "Could not get file length");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(file, original_offset, SEEK_SET) != 0) {
|
||||
TT_LOG_E(TAG, "fseek Failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return file_size;
|
||||
}
|
||||
|
||||
static char* str_alloc_from_file(const char* filepath) {
|
||||
FILE* file = fopen(filepath, "rb");
|
||||
|
||||
if (file == nullptr) {
|
||||
TT_LOG_E(TAG, "Failed to open %s", filepath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
long content_length = file_get_size(file);
|
||||
|
||||
auto* text_buffer = static_cast<char*>(malloc(content_length + 1));
|
||||
if (text_buffer == nullptr) {
|
||||
TT_LOG_E(TAG, "Insufficient memory. Failed to allocate %ldl bytes.", content_length);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int buffer;
|
||||
uint32_t buffer_offset = 0;
|
||||
text_buffer[0] = 0;
|
||||
while ((buffer = fgetc(file)) != EOF && buffer_offset < content_length) {
|
||||
text_buffer[buffer_offset] = (char)buffer;
|
||||
buffer_offset++;
|
||||
}
|
||||
text_buffer[buffer_offset] = 0;
|
||||
|
||||
fclose(file);
|
||||
return text_buffer;
|
||||
}
|
||||
|
||||
void label_set_text_file(lv_obj_t* label, const char* filepath) {
|
||||
char* text = str_alloc_from_file(filepath);
|
||||
lv_label_set_text(label, text);
|
||||
free(text);
|
||||
auto text = file::readString(filepath);
|
||||
lv_label_set_text(label, (const char*)text.get());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
40
TactilityC/CMakeLists.txt
Normal file
40
TactilityC/CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source/"
|
||||
REQUIRES Tactility TactilityCore TactilityHeadless lvgl elf_loader
|
||||
)
|
||||
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
else()
|
||||
file(GLOB_RECURSE SOURCES "Source/*.c**")
|
||||
file(GLOB_RECURSE HEADERS "Source/*.h**")
|
||||
|
||||
add_library(TactilityC OBJECT)
|
||||
|
||||
target_sources(TactilityC
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(TactilityC
|
||||
PUBLIC Source/
|
||||
)
|
||||
|
||||
add_definitions(-D_Nullable=)
|
||||
add_definitions(-D_Nonnull=)
|
||||
target_link_libraries(TactilityC
|
||||
PRIVATE Tactility
|
||||
PRIVATE TactilityCore
|
||||
PRIVATE TactilityHeadless
|
||||
PRIVATE lvgl
|
||||
)
|
||||
endif()
|
||||
|
||||
50
TactilityC/Source/TactilityC/TactilityC.cpp
Normal file
50
TactilityC/Source/TactilityC/TactilityC.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include "elf_symbol.h"
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/SelectionDialog.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
const struct esp_elfsym elf_symbols[] {
|
||||
// Tactility
|
||||
ESP_ELFSYM_EXPORT(tt_app_selectiondialog_start),
|
||||
ESP_ELFSYM_EXPORT(tt_set_app_manifest),
|
||||
ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create),
|
||||
ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create_simple),
|
||||
// lv_obj
|
||||
ESP_ELFSYM_EXPORT(lv_obj_add_event_cb),
|
||||
ESP_ELFSYM_EXPORT(lv_obj_align),
|
||||
ESP_ELFSYM_EXPORT(lv_obj_align_to),
|
||||
ESP_ELFSYM_EXPORT(lv_obj_get_user_data),
|
||||
ESP_ELFSYM_EXPORT(lv_obj_set_user_data),
|
||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_margin_all),
|
||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_pad_all),
|
||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_border_width),
|
||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_border_color),
|
||||
// lv_button
|
||||
ESP_ELFSYM_EXPORT(lv_button_create),
|
||||
// lv_label
|
||||
ESP_ELFSYM_EXPORT(lv_label_create),
|
||||
ESP_ELFSYM_EXPORT(lv_label_set_text),
|
||||
ESP_ELFSYM_EXPORT(lv_label_set_text_fmt),
|
||||
ESP_ELFSYM_END
|
||||
};
|
||||
|
||||
void tt_init_tactility_c() {
|
||||
elf_set_custom_symbols(elf_symbols);
|
||||
}
|
||||
|
||||
#else // PC
|
||||
|
||||
void tt_init_tactility_c() {
|
||||
}
|
||||
|
||||
#endif // ESP_PLATFORM
|
||||
|
||||
}
|
||||
|
||||
11
TactilityC/Source/TactilityC/TactilityC.h
Normal file
11
TactilityC/Source/TactilityC/TactilityC.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void tt_init_tactility_c();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
102
TactilityC/Source/TactilityC/app/App.cpp
Normal file
102
TactilityC/Source/TactilityC/app/App.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
#include "App.h"
|
||||
#include "Log.h"
|
||||
#include "app/ElfApp.h"
|
||||
|
||||
#define TAG "tactilityc_app"
|
||||
|
||||
AppOnStart elfOnStart = nullptr;
|
||||
AppOnStop elfOnStop = nullptr;
|
||||
AppOnShow elfOnShow = nullptr;
|
||||
AppOnHide elfOnHide = nullptr;
|
||||
AppOnResult elfOnResult = nullptr;
|
||||
|
||||
static void onStartWrapper(tt::app::AppContext& context) {
|
||||
if (elfOnStart != nullptr) {
|
||||
TT_LOG_I(TAG, "onStartWrapper");
|
||||
elfOnStart(&context);
|
||||
} else {
|
||||
TT_LOG_W(TAG, "onStartWrapper not set");
|
||||
}
|
||||
}
|
||||
|
||||
static void onStopWrapper(tt::app::AppContext& context) {
|
||||
if (elfOnStop != nullptr) {
|
||||
TT_LOG_I(TAG, "onStopWrapper");
|
||||
elfOnStop(&context);
|
||||
} else {
|
||||
TT_LOG_W(TAG, "onStopWrapper not set");
|
||||
}
|
||||
}
|
||||
|
||||
static void onShowWrapper(tt::app::AppContext& context, lv_obj_t* parent) {
|
||||
if (elfOnShow != nullptr) {
|
||||
TT_LOG_I(TAG, "onShowWrapper");
|
||||
elfOnShow(&context, parent);
|
||||
} else {
|
||||
TT_LOG_W(TAG, "onShowWrapper not set");
|
||||
}
|
||||
}
|
||||
|
||||
static void onHideWrapper(tt::app::AppContext& context) {
|
||||
if (elfOnHide != nullptr) {
|
||||
TT_LOG_I(TAG, "onHideWrapper");
|
||||
elfOnHide(&context);
|
||||
} else {
|
||||
TT_LOG_W(TAG, "onHideWrapper not set");
|
||||
}
|
||||
}
|
||||
|
||||
static void onResultWrapper(tt::app::AppContext& context, tt::app::Result result, const tt::Bundle& resultData) {
|
||||
if (elfOnResult != nullptr) {
|
||||
TT_LOG_I(TAG, "onResultWrapper");
|
||||
// Result convertedResult = AppResultError;
|
||||
// switch (result) {
|
||||
// case tt::app::ResultOk:
|
||||
// convertedResult = AppResultOk;
|
||||
// break;
|
||||
// case tt::app::ResultCancelled:
|
||||
// convertedResult = AppResultCancelled;
|
||||
// break;
|
||||
// case tt::app::ResultError:
|
||||
// convertedResult = AppResultError;
|
||||
// break;
|
||||
// }
|
||||
// elfOnResult(&context, convertedResult, (BundleHandle)&resultData);
|
||||
} else {
|
||||
TT_LOG_W(TAG, "onResultWrapper not set");
|
||||
}
|
||||
}
|
||||
|
||||
tt::app::AppManifest manifest = {
|
||||
.id = "ElfWrapperInTactilityC",
|
||||
.name = "",
|
||||
.icon = "",
|
||||
.onStart = onStartWrapper,
|
||||
.onStop = onStopWrapper,
|
||||
.onShow = onShowWrapper,
|
||||
.onHide = onHideWrapper,
|
||||
.onResult = onResultWrapper
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
void tt_set_app_manifest(
|
||||
const char* name,
|
||||
const char* _Nullable icon,
|
||||
AppOnStart onStart,
|
||||
AppOnStop _Nullable onStop,
|
||||
AppOnShow _Nullable onShow,
|
||||
AppOnHide _Nullable onHide,
|
||||
AppOnResult _Nullable onResult
|
||||
) {
|
||||
manifest.name = name;
|
||||
manifest.icon = icon ? icon : "";
|
||||
elfOnStart = onStart;
|
||||
elfOnStop = onStop;
|
||||
elfOnShow = onShow;
|
||||
elfOnHide = onHide;
|
||||
elfOnResult = onResult;
|
||||
tt::app::setElfAppManifest(manifest);
|
||||
}
|
||||
|
||||
}
|
||||
36
TactilityC/Source/TactilityC/app/App.h
Normal file
36
TactilityC/Source/TactilityC/app/App.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void* AppContextHandle;
|
||||
typedef void* BundleHandle;
|
||||
|
||||
typedef enum {
|
||||
AppResultOk,
|
||||
AppResultCancelled,
|
||||
AppResultError
|
||||
} Result;
|
||||
|
||||
typedef void (*AppOnStart)(AppContextHandle app);
|
||||
typedef void (*AppOnStop)(AppContextHandle app);
|
||||
typedef void (*AppOnShow)(AppContextHandle app, lv_obj_t* parent);
|
||||
typedef void (*AppOnHide)(AppContextHandle app);
|
||||
typedef void (*AppOnResult)(AppContextHandle app, Result result, BundleHandle resultData);
|
||||
|
||||
void tt_set_app_manifest(
|
||||
const char* name,
|
||||
const char* _Nullable icon,
|
||||
AppOnStart onStart,
|
||||
AppOnStop _Nullable onStop,
|
||||
AppOnShow _Nullable onShow,
|
||||
AppOnHide _Nullable onHide,
|
||||
AppOnResult _Nullable onResult
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
14
TactilityC/Source/TactilityC/app/SelectionDialog.cpp
Normal file
14
TactilityC/Source/TactilityC/app/SelectionDialog.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "app/selectiondialog/SelectionDialog.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void tt_app_selectiondialog_start(const char* title, int argc, const char* argv[]) {
|
||||
std::vector<std::string> list;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const char* item = argv[i];
|
||||
list.push_back(item);
|
||||
}
|
||||
tt::app::selectiondialog::start(title, list);
|
||||
}
|
||||
|
||||
}
|
||||
11
TactilityC/Source/TactilityC/app/SelectionDialog.h
Normal file
11
TactilityC/Source/TactilityC/app/SelectionDialog.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void tt_app_selectiondialog_start(const char* title, int argc, const char* argv[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
14
TactilityC/Source/TactilityC/lvgl/Toolbar.cpp
Normal file
14
TactilityC/Source/TactilityC/lvgl/Toolbar.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "Toolbar.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
lv_obj_t* tt_lvgl_toolbar_create(lv_obj_t* parent, AppContextHandle context) {
|
||||
return tt::lvgl::toolbar_create(parent, *(tt::app::AppContext*)context);
|
||||
}
|
||||
|
||||
lv_obj_t* tt_lvgl_toolbar_create_simple(lv_obj_t* parent, const char* title) {
|
||||
return tt::lvgl::toolbar_create(parent, title);
|
||||
}
|
||||
|
||||
}
|
||||
15
TactilityC/Source/TactilityC/lvgl/Toolbar.h
Normal file
15
TactilityC/Source/TactilityC/lvgl/Toolbar.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "TactilityC/app/App.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
lv_obj_t* tt_lvgl_toolbar_create(lv_obj_t* parent, AppContextHandle context);
|
||||
lv_obj_t* tt_lvgl_toolbar_create_simple(lv_obj_t* parent, const char* title);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -3,31 +3,35 @@ cmake_minimum_required(VERSION 3.16)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
file(GLOB_RECURSE SOURCES "Source/*.c*")
|
||||
file(GLOB_RECURSE HEADERS "Source/*.h*")
|
||||
|
||||
add_library(TactilityCore OBJECT)
|
||||
|
||||
target_sources(TactilityCore
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(TactilityCore SYSTEM
|
||||
PUBLIC Source/
|
||||
)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
target_link_libraries(TactilityCore
|
||||
PUBLIC idf::mbedtls
|
||||
PRIVATE idf::nvs_flash # ESP-IDF // for secure.c
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source/"
|
||||
REQUIRES mbedtls nvs_flash
|
||||
)
|
||||
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
else()
|
||||
file(GLOB_RECURSE SOURCES "Source/*.c*")
|
||||
file(GLOB_RECURSE HEADERS "Source/*.h*")
|
||||
|
||||
add_library(TactilityCore OBJECT)
|
||||
|
||||
target_sources(TactilityCore
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(TactilityCore SYSTEM
|
||||
PUBLIC Source/
|
||||
)
|
||||
|
||||
add_definitions(-D_Nullable=)
|
||||
add_definitions(-D_Nonnull=)
|
||||
target_link_libraries(TactilityCore
|
||||
PUBLIC mbedtls
|
||||
PUBLIC freertos_kernel
|
||||
PUBLIC mbedtls
|
||||
PUBLIC freertos_kernel
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@ -112,7 +112,7 @@ ThreadId Mutex::getOwner() const {
|
||||
|
||||
|
||||
std::unique_ptr<ScopedMutexUsage> Mutex::scoped() const {
|
||||
return std::move(std::make_unique<ScopedMutexUsage>(*this));
|
||||
return std::make_unique<ScopedMutexUsage>(*this);
|
||||
}
|
||||
|
||||
Mutex* tt_mutex_alloc(Mutex::Type type) {
|
||||
|
||||
82
TactilityCore/Source/file/File.cpp
Normal file
82
TactilityCore/Source/file/File.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include "File.h"
|
||||
|
||||
namespace tt::file {
|
||||
|
||||
#define TAG "file"
|
||||
|
||||
long getSize(FILE* file) {
|
||||
long original_offset = ftell(file);
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
TT_LOG_E(TAG, "fseek failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
long file_size = ftell(file);
|
||||
if (file_size == -1) {
|
||||
TT_LOG_E(TAG, "Could not get file length");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(file, original_offset, SEEK_SET) != 0) {
|
||||
TT_LOG_E(TAG, "fseek Failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return file_size;
|
||||
}
|
||||
|
||||
static std::unique_ptr<uint8_t[]> readBinaryInternal(const char* filepath, size_t& outSize, size_t sizePadding = 0) {
|
||||
FILE* file = fopen(filepath, "rb");
|
||||
|
||||
if (file == nullptr) {
|
||||
TT_LOG_E(TAG, "Failed to open %s", filepath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
long content_length = getSize(file);
|
||||
if (content_length == -1) {
|
||||
TT_LOG_E(TAG, "Failed to determine content length for %s", filepath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto data = std::make_unique<uint8_t[]>(content_length + sizePadding);
|
||||
if (data == nullptr) {
|
||||
TT_LOG_E(TAG, "Insufficient memory. Failed to allocate %ldl bytes.", content_length);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t buffer_offset = 0;
|
||||
while (buffer_offset < content_length) {
|
||||
size_t bytes_read = fread(&data.get()[buffer_offset], 1, content_length - buffer_offset, file);
|
||||
TT_LOG_I(TAG, "Read %d bytes", bytes_read);
|
||||
if (bytes_read > 0) {
|
||||
buffer_offset += bytes_read;
|
||||
} else { // Something went wrong?
|
||||
data = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outSize = buffer_offset;
|
||||
|
||||
fclose(file);
|
||||
return data;
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> readBinary(const char* filepath, size_t& outSize) {
|
||||
return readBinaryInternal(filepath, outSize);
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> readString(const char* filepath) {
|
||||
size_t size = 0;
|
||||
auto data = readBinaryInternal(filepath, size, 1);
|
||||
if (size > 0) {
|
||||
data.get()[size] = 0; // Append null terminator
|
||||
return data;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
15
TactilityCore/Source/file/File.h
Normal file
15
TactilityCore/Source/file/File.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "TactilityCore.h"
|
||||
#include <cstdio>
|
||||
|
||||
namespace tt::file {
|
||||
|
||||
#define TAG "file"
|
||||
|
||||
long getSize(FILE* file);
|
||||
|
||||
std::unique_ptr<uint8_t[]> readBinary(const char* filepath, size_t& outSize);
|
||||
std::unique_ptr<uint8_t[]> readString(const char* filepath);
|
||||
|
||||
}
|
||||
@ -10,18 +10,15 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source/"
|
||||
PRIV_INCLUDE_DIRS "Private/"
|
||||
REQUIRES esp_wifi nvs_flash spiffs driver
|
||||
REQUIRES TactilityCore esp_wifi nvs_flash spiffs driver
|
||||
)
|
||||
|
||||
set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/assets")
|
||||
spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT)
|
||||
|
||||
set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/config")
|
||||
spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB}
|
||||
PUBLIC TactilityCore
|
||||
)
|
||||
if (NOT DEFINED TACTILITY_SKIP_SPIFFS)
|
||||
set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/assets")
|
||||
spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT)
|
||||
set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/config")
|
||||
spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT)
|
||||
endif()
|
||||
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
else()
|
||||
|
||||
@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
|
||||
|
||||
# Hardware: Main
|
||||
CONFIG_TT_BOARD_LILYGO_TDECK=y
|
||||
|
||||
@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
|
||||
|
||||
# Hardware: Main
|
||||
CONFIG_TT_BOARD_M5STACK_CORE2=y
|
||||
|
||||
@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
|
||||
|
||||
# Hardware: Main
|
||||
CONFIG_TT_BOARD_M5STACK_CORES3=y
|
||||
|
||||
@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
|
||||
|
||||
# Hardware: Main
|
||||
CONFIG_TT_BOARD_YELLOW_BOARD_24_CAP=y
|
||||
|
||||
@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
|
||||
|
||||
# Hardware defaults
|
||||
CONFIG_TT_BOARD_CUSTOM=y
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user