Support for building and running external apps (#112)

This commit is contained in:
Ken Van Hoeylandt 2024-12-08 16:46:19 +01:00 committed by GitHub
parent 42e843b463
commit 415442e410
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
74 changed files with 2901 additions and 126 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
if (DEFINED ENV{ESP_IDF_VERSION}) if (DEFINED ENV{ESP_IDF_VERSION})
set(BOARD_COMPONENTS Tactility) set(BOARD_COMPONENTS Tactility TactilityC)
if("${IDF_TARGET}" STREQUAL "esp32") if("${IDF_TARGET}" STREQUAL "esp32")
list(APPEND BOARD_COMPONENTS list(APPEND BOARD_COMPONENTS
@ -28,6 +28,7 @@ else()
add_executable(AppSim ${SOURCES}) add_executable(AppSim ${SOURCES})
target_link_libraries(AppSim target_link_libraries(AppSim
PRIVATE Tactility PRIVATE Tactility
PRIVATE TactilityC
PRIVATE TactilityCore PRIVATE TactilityCore
PRIVATE TactilityHeadless PRIVATE TactilityHeadless
PRIVATE Simulator PRIVATE Simulator

View File

@ -2,6 +2,7 @@
// Apps // Apps
#include "Tactility.h" #include "Tactility.h"
#include "TactilityC/TactilityC.h"
namespace tt::service::wifi { namespace tt::service::wifi {
extern void wifi_task(void*); extern void wifi_task(void*);
@ -25,6 +26,8 @@ void app_main() {
.autoStartAppId = nullptr .autoStartAppId = nullptr
}; };
tt_init_tactility_c(); // ELF bindings for side-loading on ESP32
tt::run(config); tt::run(config);
} }

View File

@ -21,9 +21,13 @@ if (DEFINED ENV{ESP_IDF_VERSION})
"Boards" "Boards"
"App" "App"
"Tactility" "Tactility"
"TactilityC"
"TactilityCore"
"TactilityHeadless" "TactilityHeadless"
"Libraries/esp_lvgl_port" "Libraries/esp_lvgl_port"
"Libraries/elf_loader"
"Libraries/lvgl" "Libraries/lvgl"
"Libraries/lv_screenshot"
"Libraries/M5Unified" "Libraries/M5Unified"
"Libraries/M5GFX" "Libraries/M5GFX"
) )
@ -53,24 +57,24 @@ project(Tactility)
# Defined as regular project for PC and component for ESP # Defined as regular project for PC and component for ESP
if (NOT DEFINED ENV{ESP_IDF_VERSION}) if (NOT DEFINED ENV{ESP_IDF_VERSION})
add_subdirectory(Tactility) add_subdirectory(Tactility)
add_subdirectory(TactilityC)
add_subdirectory(TactilityCore)
add_subdirectory(TactilityHeadless) add_subdirectory(TactilityHeadless)
add_subdirectory(Boards/Simulator) add_subdirectory(Boards/Simulator)
endif() endif()
add_subdirectory(TactilityCore)
add_subdirectory(Libraries/lv_screenshot)
if (NOT DEFINED ENV{ESP_IDF_VERSION}) if (NOT DEFINED ENV{ESP_IDF_VERSION})
# FreeRTOS # FreeRTOS
set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Boards/Simulator/Source CACHE STRING "") set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Boards/Simulator/Source CACHE STRING "")
set(FREERTOS_PORT GCC_POSIX CACHE STRING "") set(FREERTOS_PORT GCC_POSIX CACHE STRING "")
add_subdirectory(Libraries/FreeRTOS-Kernel) add_subdirectory(Libraries/FreeRTOS-Kernel)
add_subdirectory(Libraries/lv_screenshot)
target_compile_definitions(freertos_kernel PUBLIC "projCOVERAGE_TEST=0") target_compile_definitions(freertos_kernel PUBLIC "projCOVERAGE_TEST=0")
target_include_directories(freertos_kernel target_include_directories(freertos_kernel
PUBLIC Boards/Simulator/Source # for FreeRTOSConfig.h PUBLIC Boards/Simulator/Source # for FreeRTOSConfig.h
) )
# EmbedTLS # EmbedTLS
set(ENABLE_TESTING OFF) set(ENABLE_TESTING OFF)
set(ENABLE_PROGRAMS OFF) set(ENABLE_PROGRAMS OFF)

View 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.

View File

@ -1,4 +1,5 @@
# TODOs # TODOs
- Loader: Use Timer instead of Thread, and move API to `tt::app::`
- Gpio: Use Timer instead of Thread - Gpio: Use Timer instead of Thread
- I2cScannerThread: Use Timer instead of Thread - I2cScannerThread: Use Timer instead of Thread
- Bug: I2C Scanner is on M5Stack devices is broken - 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 - 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. - 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 - 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 # Core Ideas
- Support for displays with different DPI. Consider the layer-based system like on Android. - Support for displays with different DPI. Consider the layer-based system like on Android.
- If present, use LED to show boot status - If present, use LED to show boot status
- 2 wire speaker support - 2 wire speaker support
- tt::app::start() and similar functions as proxies for Loader app start/stop/etc. - 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 # App Ideas
- System logger - System logger

View 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)

View File

@ -0,0 +1,4 @@
rm sdkconfig
cp ../../sdkconfig sdkconfig
cat sdkconfig.override >> sdkconfig
idf.py build

View 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)

View 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;
}

View File

@ -0,0 +1 @@
CONFIG_PARTITION_TABLE_SINGLE_APP=y

View File

@ -0,0 +1 @@
ec9b5b8881cf5c38113ed7eab28a71ed7bfc9f477d6faef851f7f59f7a07b536

View File

@ -0,0 +1,5 @@
# ChangeLog
## v0.1.0 - 2023-08-14
* Add basic ELF loader component

View 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()

View 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

View File

@ -0,0 +1,83 @@
## Description
[![Component Registry](https://components.espressif.com/components/espressif/elf_loader/badge.svg)](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)

View 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()

View File

@ -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)

View File

@ -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.

View File

@ -0,0 +1 @@
idf_component_register(SRCS "main.c")

View File

@ -0,0 +1,5 @@
dependencies:
elf_loader:
version: "0.*"
override_path: "../../../../components/elf_loader"

View File

@ -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;
}

View File

@ -0,0 +1 @@
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=n

View File

@ -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)

View 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
```

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "elf_loader_example_main.c"
INCLUDE_DIRS ""
EMBED_TXTFILES "test.elf")

View File

@ -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

View File

@ -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;
}

View File

@ -0,0 +1,5 @@
dependencies:
elf_loader:
version: "0.*"
override_path: "../../../../components/elf_loader"

View File

@ -0,0 +1,2 @@
CONFIG_SPIRAM=y
CONFIG_ELF_LOADER_LOAD_PSRAM=y

View File

@ -0,0 +1 @@
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n

View File

@ -0,0 +1 @@
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n

View 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

View 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

View 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

View 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

View 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

View 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.

View File

@ -0,0 +1 @@
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH})

View 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;
}

View 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);
}

View 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

View 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;
}

View 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();
}

View File

@ -4,26 +4,32 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) 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}) if (DEFINED ENV{ESP_IDF_VERSION})
target_link_libraries(lv_screenshot file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
PUBLIC idf::lvgl
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "src/"
REQUIRES lvgl
) )
add_definitions(-DESP_PLATFORM)
else() 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 target_link_libraries(lv_screenshot
PUBLIC lvgl PUBLIC lvgl
) )

View File

@ -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. 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: Noteworthy features:
- Touch UI capabilities (via LVGL) with support for input devices such as on-device trackball or keyboard. - Touch UI capabilities (via LVGL) with support for input devices such as keyboard, trackable and more.
- An application platform that can run apps and services. - Run apps and services.
- Basic applications to boost productivity, such as a Wi-Fi connectivity and I2C apps. - 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. - Includes a PC simulator build target to speed up development.
Requirements: Requirements:
@ -122,6 +123,7 @@ Directories explained:
- `App`: The application/firmware example project - `App`: The application/firmware example project
- `Boards`: Contains board configuration projects with drivers (and simulator) - `Boards`: Contains board configuration projects with drivers (and simulator)
- `Tactility`: The main application platform code - `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. - `TactilityHeadless`: Service framework and default services.
- `TactilityCore`: Core functionality regarding threads, stdlib, etc. - `TactilityCore`: Core functionality regarding threads, stdlib, etc.
- `Libraries`: Contains a mix of regular libraries and ESP modules - `Libraries`: Contains a mix of regular libraries and ESP modules

View File

@ -10,11 +10,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source/" INCLUDE_DIRS "Source/"
PRIV_INCLUDE_DIRS "Private/" PRIV_INCLUDE_DIRS "Private/"
REQUIRES TactilityHeadless lvgl driver REQUIRES TactilityHeadless lvgl driver elf_loader lv_screenshot
)
target_link_libraries(${COMPONENT_LIB}
PUBLIC lv_screenshot
) )
add_definitions(-DESP_PLATFORM) add_definitions(-DESP_PLATFORM)

View File

@ -53,8 +53,11 @@ namespace app {
namespace wifiapsettings { extern const AppManifest manifest; } namespace wifiapsettings { extern const AppManifest manifest; }
namespace wificonnect { extern const AppManifest manifest; } namespace wificonnect { extern const AppManifest manifest; }
namespace wifimanage { extern const AppManifest manifest; } namespace wifimanage { extern const AppManifest manifest; }
extern const AppManifest elfWrapperManifest;
} }
#ifndef ESP_PLATFORM #ifndef ESP_PLATFORM
extern const app::AppManifest screenshot_app; extern const app::AppManifest screenshot_app;
#endif #endif
@ -77,6 +80,8 @@ static const std::vector<const app::AppManifest*> system_apps = {
&app::wifimanage::manifest, &app::wifimanage::manifest,
#ifndef ESP_PLATFORM #ifndef ESP_PLATFORM
&app::screenshot::manifest, // Screenshots don't work yet on ESP32 &app::screenshot::manifest, // Screenshots don't work yet on ESP32
#else
&app::elfWrapperManifest, // For hot-loading ELF apps
#endif #endif
}; };

View File

@ -57,32 +57,32 @@ struct AppManifest {
/** /**
* App type affects launch behaviour. * App type affects launch behaviour.
*/ */
const Type type = TypeUser; Type type = TypeUser;
/** /**
* Non-blocking method to call when app is started. * 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. * 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 * Non-blocking method to create the GUI
*/ */
const AppOnShow _Nullable onShow = nullptr; AppOnShow _Nullable onShow = nullptr;
/** /**
* Non-blocking method, called before gui is destroyed * 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 * Handle the result for apps that are launched
*/ */
const AppOnResult _Nullable onResult = nullptr; AppOnResult _Nullable onResult = nullptr;
}; };
struct { struct {

View 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

View 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

View File

@ -1,15 +1,17 @@
#include "FilesData.h" #include "FilesData.h"
#include "Tactility.h"
#include "app/AppContext.h" #include "app/AppContext.h"
#include "Tactility.h"
#include "Assets.h" #include "Assets.h"
#include "Check.h" #include "Check.h"
#include "FileUtils.h" #include "FileUtils.h"
#include "StringUtils.h" #include "StringUtils.h"
#include "app/ElfApp.h"
#include "app/imageviewer/ImageViewer.h" #include "app/imageviewer/ImageViewer.h"
#include "app/textviewer/TextViewer.h" #include "app/textviewer/TextViewer.h"
#include "lvgl.h" #include "lvgl.h"
#include "service/loader/Loader.h" #include "service/loader/Loader.h"
#include "lvgl/Toolbar.h" #include "lvgl/Toolbar.h"
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include <memory> #include <memory>
@ -52,6 +54,15 @@ static bool hasFileExtension(const char* path, const char* extension) {
return true; 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) { static bool isSupportedImageFile(const char* filename) {
// Currently only the PNG library is built into Tactility // Currently only the PNG library is built into Tactility
return hasFileExtension(filename, ".png"); return hasFileExtension(filename, ".png");
@ -120,7 +131,11 @@ static void viewFile(const char* path, const char* filename) {
TT_LOG_I(TAG, "Clicked %s", filepath); 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>(); auto bundle = std::make_shared<Bundle>();
bundle->putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath); bundle->putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath);
service::loader::startApp("ImageViewer", false, bundle); service::loader::startApp("ImageViewer", false, bundle);

View File

@ -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);
}
}

View File

@ -19,4 +19,4 @@ namespace tt::app::selectiondialog {
/** App result data */ /** App result data */
int32_t getResultIndex(const Bundle& bundle); int32_t getResultIndex(const Bundle& bundle);
} }

View File

@ -1,65 +1,13 @@
#include "LabelUtils.h" #include "LabelUtils.h"
#include "TactilityCore.h" #include "file/File.h"
namespace tt::lvgl { namespace tt::lvgl {
#define TAG "tt_lv_label" #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) { void label_set_text_file(lv_obj_t* label, const char* filepath) {
char* text = str_alloc_from_file(filepath); auto text = file::readString(filepath);
lv_label_set_text(label, text); lv_label_set_text(label, (const char*)text.get());
free(text);
} }
} // namespace } // namespace

40
TactilityC/CMakeLists.txt Normal file
View 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()

View 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
}

View File

@ -0,0 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void tt_init_tactility_c();
#ifdef __cplusplus
}
#endif

View 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);
}
}

View 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

View 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);
}
}

View 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

View 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);
}
}

View 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

View File

@ -3,31 +3,35 @@ cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) 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}) if (DEFINED ENV{ESP_IDF_VERSION})
add_definitions(-DESP_PLATFORM) file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
target_link_libraries(TactilityCore
PUBLIC idf::mbedtls idf_component_register(
PRIVATE idf::nvs_flash # ESP-IDF // for secure.c SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source/"
REQUIRES mbedtls nvs_flash
) )
add_definitions(-DESP_PLATFORM)
else() 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_Nullable=)
add_definitions(-D_Nonnull=) add_definitions(-D_Nonnull=)
target_link_libraries(TactilityCore target_link_libraries(TactilityCore
PUBLIC mbedtls PUBLIC mbedtls
PUBLIC freertos_kernel PUBLIC freertos_kernel
) )
endif() endif()

View File

@ -112,7 +112,7 @@ ThreadId Mutex::getOwner() const {
std::unique_ptr<ScopedMutexUsage> Mutex::scoped() 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) { Mutex* tt_mutex_alloc(Mutex::Type type) {

View 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;
}
}
}

View 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);
}

View File

@ -10,18 +10,15 @@ if (DEFINED ENV{ESP_IDF_VERSION})
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source/" INCLUDE_DIRS "Source/"
PRIV_INCLUDE_DIRS "Private/" 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") if (NOT DEFINED TACTILITY_SKIP_SPIFFS)
spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT) 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") set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/config")
spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT) spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT)
endif()
target_link_libraries(${COMPONENT_LIB}
PUBLIC TactilityCore
)
add_definitions(-DESP_PLATFORM) add_definitions(-DESP_PLATFORM)
else() else()

View File

@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
# Hardware: Main # Hardware: Main
CONFIG_TT_BOARD_LILYGO_TDECK=y CONFIG_TT_BOARD_LILYGO_TDECK=y

View File

@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
# Hardware: Main # Hardware: Main
CONFIG_TT_BOARD_M5STACK_CORE2=y CONFIG_TT_BOARD_M5STACK_CORE2=y

View File

@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
# Hardware: Main # Hardware: Main
CONFIG_TT_BOARD_M5STACK_CORES3=y CONFIG_TT_BOARD_M5STACK_CORES3=y

View File

@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
# Hardware: Main # Hardware: Main
CONFIG_TT_BOARD_YELLOW_BOARD_24_CAP=y CONFIG_TT_BOARD_YELLOW_BOARD_24_CAP=y

View File

@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y
# Hardware defaults # Hardware defaults
CONFIG_TT_BOARD_CUSTOM=y CONFIG_TT_BOARD_CUSTOM=y