From abff044ae3bf279e05c6bc68dfcd3da120c4dbfd Mon Sep 17 00:00:00 2001 From: Dmytro Meleshko Date: Wed, 20 Dec 2023 22:19:52 +0100 Subject: [PATCH] add an a linker plugin experiment, move tools to a subdirectory --- .gitignore | 2 +- CMakeLists.txt | 18 +- Makefile | 4 +- tools/CMakeLists.txt | 24 +++ encode_image.py => tools/encode_image.py | 0 encode_video.py => tools/encode_video.py | 0 .../generate_console_font.py | 0 render_fonts.py => tools/render_fonts.py | 0 tools/test_linker_plugin.cc | 161 ++++++++++++++++++ 9 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 tools/CMakeLists.txt rename encode_image.py => tools/encode_image.py (100%) rename encode_video.py => tools/encode_video.py (100%) rename generate_console_font.py => tools/generate_console_font.py (100%) rename render_fonts.py => tools/render_fonts.py (100%) create mode 100644 tools/test_linker_plugin.cc diff --git a/.gitignore b/.gitignore index 6c88597..6d7e67a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .pio/ -.cache/clangd/ +**/.cache/clangd/ local.mk .ccls compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index d2d0542..065a0b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.17) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/arm_gcc_toolchain.cmake") @@ -21,6 +21,7 @@ option(FETCHCONTENT_QUIET "" OFF) # Output download progress and other logs option(FETCHCONTENT_UPDATES_DISCONNECTED "" ON) # Don't check for updates every time include(FetchContent) +include(ExternalProject) set(DEPENDENCIES_DIR "" CACHE FILEPATH "The directory in which the dependencies will be downloaded and unpacked") if(DEPENDENCIES_DIR STREQUAL "") @@ -74,6 +75,16 @@ FetchContent_Declare(stm32_mw_usb_host DOWNLOAD_NAME "stm32_mw_usb_host_v3.3.4.tar.gz" ) +# TODO comments +ExternalProject_Add(tools + PREFIX "${CMAKE_BINARY_DIR}/_deps/tools-prefix" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/tools" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tools" + DOWNLOAD_COMMAND "" + INSTALL_COMMAND "" # An empty string suppresses this step since at least CMake 3.10 + BUILD_ALWAYS YES # TODO +) + get_property(project_languages GLOBAL PROPERTY ENABLED_LANGUAGES) foreach(lang IN LISTS project_languages) foreach(config IN ITEMS "DEBUG" "MINSIZEREL" "RELEASE" "RELWITHDEBINFO") @@ -295,6 +306,11 @@ set(linker_map_file $.map) target_link_options(firmware PRIVATE LINKER:--cref,-Map=${linker_map_file}) set_property(TARGET firmware APPEND PROPERTY ADDITIONAL_CLEAN_FILES "${linker_map_file}") +add_dependencies(firmware tools) +set(linker_plugin_file "${CMAKE_CURRENT_BINARY_DIR}/tools/libtest_linker_plugin.so") +target_link_options(firmware PRIVATE LINKER:-plugin,${linker_plugin_file}) +set_property(TARGET firmware APPEND PROPERTY LINK_DEPENDS "${linker_plugin_file}") + include(CheckCCompilerFlag) # A replacement for the CheckLinkerFlag module, added in CMake v3.18, which # only needs CMake v3.14. diff --git a/Makefile b/Makefile index 1a6baaa..a519f44 100644 --- a/Makefile +++ b/Makefile @@ -95,14 +95,14 @@ cmake freshcmake $(BUILD_DIR_STAMP): # This rule simply checks that the build system has been generated. ensurecmake: $(BUILD_DIR_STAMP) -.PHONY: all firmware upload clean +.PHONY: all firmware tools upload clean # The targets from the actual build system that will be exposed to the user. # $(BUILD_DIR_STAMP) is an order-only dependency here: Make will ensure that it # is made the first time, but won't care about file modification times later. # Afterwards, it is CMake's responsibility to regenerate the files when it # detects a change in any `CMakeLists.txt`. The plus sign before the command # tells Make to execute it even in the dry-run mode. -all firmware upload clean: | $(BUILD_DIR_STAMP) +all firmware tools upload clean: | $(BUILD_DIR_STAMP) +$(BUILD_TOOL) -C '$(BUILD_DIR)' $@ .PHONY: distclean diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..812eced --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.5) +project(stmes_tools C CXX) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_EXTENSIONS YES) +set(CMAKE_C_STANDARD_REQUIRED YES) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_EXTENSIONS YES) +set(CMAKE_CXX_STANDARD_REQUIRED YES) + +add_library(test_linker_plugin SHARED test_linker_plugin.cc) + +# TODO: Share this with the top-level CMakeLists.txt somehow +target_compile_options(test_linker_plugin PRIVATE + -Wall -Wextra -Wpedantic -pedantic-errors + -Werror=return-type + -Wdouble-promotion + -Wmissing-declarations + -Werror=float-conversion + $<$:-Werror=strict-prototypes> +) diff --git a/encode_image.py b/tools/encode_image.py similarity index 100% rename from encode_image.py rename to tools/encode_image.py diff --git a/encode_video.py b/tools/encode_video.py similarity index 100% rename from encode_video.py rename to tools/encode_video.py diff --git a/generate_console_font.py b/tools/generate_console_font.py similarity index 100% rename from generate_console_font.py rename to tools/generate_console_font.py diff --git a/render_fonts.py b/tools/render_fonts.py similarity index 100% rename from render_fonts.py rename to tools/render_fonts.py diff --git a/tools/test_linker_plugin.cc b/tools/test_linker_plugin.cc new file mode 100644 index 0000000..7c518f0 --- /dev/null +++ b/tools/test_linker_plugin.cc @@ -0,0 +1,161 @@ +// TODO comments + +#include +#include +#include +#include + +#define HAVE_STDINT_H 1 +#define HAVE_INTTYPES_H 1 +#include + +#define PACKAGE +#define PACKAGE_VERSION +#include + +// TODO check status + +static const struct { + ld_plugin_tag num; + const char* name; +} LD_PLUGIN_TAG_NAMES[] = { + { LDPT_NULL, "LDPT_NULL" }, + { LDPT_NULL, "LDPT_NULL" }, + { LDPT_API_VERSION, "LDPT_API_VERSION" }, + { LDPT_GOLD_VERSION, "LDPT_GOLD_VERSION" }, + { LDPT_LINKER_OUTPUT, "LDPT_LINKER_OUTPUT" }, + { LDPT_OPTION, "LDPT_OPTION" }, + { LDPT_REGISTER_CLAIM_FILE_HOOK, "LDPT_REGISTER_CLAIM_FILE_HOOK" }, + { LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK, "LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK" }, + { LDPT_REGISTER_CLEANUP_HOOK, "LDPT_REGISTER_CLEANUP_HOOK" }, + { LDPT_ADD_SYMBOLS, "LDPT_ADD_SYMBOLS" }, + { LDPT_GET_SYMBOLS, "LDPT_GET_SYMBOLS" }, + { LDPT_ADD_INPUT_FILE, "LDPT_ADD_INPUT_FILE" }, + { LDPT_MESSAGE, "LDPT_MESSAGE" }, + { LDPT_GET_INPUT_FILE, "LDPT_GET_INPUT_FILE" }, + { LDPT_RELEASE_INPUT_FILE, "LDPT_RELEASE_INPUT_FILE" }, + { LDPT_ADD_INPUT_LIBRARY, "LDPT_ADD_INPUT_LIBRARY" }, + { LDPT_OUTPUT_NAME, "LDPT_OUTPUT_NAME" }, + { LDPT_SET_EXTRA_LIBRARY_PATH, "LDPT_SET_EXTRA_LIBRARY_PATH" }, + { LDPT_GNU_LD_VERSION, "LDPT_GNU_LD_VERSION" }, + { LDPT_GET_VIEW, "LDPT_GET_VIEW" }, + { LDPT_GET_INPUT_SECTION_COUNT, "LDPT_GET_INPUT_SECTION_COUNT" }, + { LDPT_GET_INPUT_SECTION_TYPE, "LDPT_GET_INPUT_SECTION_TYPE" }, + { LDPT_GET_INPUT_SECTION_NAME, "LDPT_GET_INPUT_SECTION_NAME" }, + { LDPT_GET_INPUT_SECTION_CONTENTS, "LDPT_GET_INPUT_SECTION_CONTENTS" }, + { LDPT_UPDATE_SECTION_ORDER, "LDPT_UPDATE_SECTION_ORDER" }, + { LDPT_ALLOW_SECTION_ORDERING, "LDPT_ALLOW_SECTION_ORDERING" }, + { LDPT_GET_SYMBOLS_V2, "LDPT_GET_SYMBOLS_V2" }, + { LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS, "LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS" }, + { LDPT_UNIQUE_SEGMENT_FOR_SECTIONS, "LDPT_UNIQUE_SEGMENT_FOR_SECTIONS" }, + { LDPT_GET_SYMBOLS_V3, "LDPT_GET_SYMBOLS_V3" }, + { LDPT_GET_INPUT_SECTION_ALIGNMENT, "LDPT_GET_INPUT_SECTION_ALIGNMENT" }, + { LDPT_GET_INPUT_SECTION_SIZE, "LDPT_GET_INPUT_SECTION_SIZE" }, + { LDPT_REGISTER_NEW_INPUT_HOOK, "LDPT_REGISTER_NEW_INPUT_HOOK" }, + { LDPT_GET_WRAP_SYMBOLS, "LDPT_GET_WRAP_SYMBOLS" }, + { LDPT_ADD_SYMBOLS_V2, "LDPT_ADD_SYMBOLS_V2" }, + { LDPT_GET_API_VERSION, "LDPT_GET_API_VERSION" }, + { LDPT_REGISTER_CLAIM_FILE_HOOK_V2, "LDPT_REGISTER_CLAIM_FILE_HOOK_V2" }, +}; + +static struct { + ld_plugin_message message; + ld_plugin_register_claim_file register_claim_file; + ld_plugin_register_all_symbols_read register_all_symbols_read; + ld_plugin_register_cleanup register_cleanup; + ld_plugin_get_view get_view; +} api = {}; + +#define LOG(...) (api.message(LDPL_INFO, __VA_ARGS__)) + +// struct LinkedFile { +// std::string name; +// int fd; +// off_t offset; +// off_t size; +// void* handle; +// LinkedFile() {} +// explicit LinkedFile(const ld_plugin_input_file& raw) +// : name(raw.name), fd(raw.fd), offset(raw.offset), size(raw.filesize), handle(raw.handle) {} +// }; + +// static std::list linked_files; + +extern "C" ld_plugin_status onload(ld_plugin_tv* tv); +static ld_plugin_status claim_file_hook(const ld_plugin_input_file* file, int* claimed); +static ld_plugin_status all_symbols_read_hook(); +static ld_plugin_status cleanup_hook(); + +ld_plugin_status onload(ld_plugin_tv* tv) { + ld_plugin_tv* first_tv = tv; + + for (; tv->tv_tag != LDPT_NULL; tv++) { + switch (tv->tv_tag) { + case LDPT_MESSAGE: api.message = tv->tv_u.tv_message; break; + case LDPT_REGISTER_CLAIM_FILE_HOOK: + api.register_claim_file = tv->tv_u.tv_register_claim_file; + break; + case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: + api.register_all_symbols_read = tv->tv_u.tv_register_all_symbols_read; + break; + case LDPT_REGISTER_CLEANUP_HOOK: api.register_cleanup = tv->tv_u.tv_register_cleanup; break; + case LDPT_GET_VIEW: api.get_view = tv->tv_u.tv_get_view; break; + default: break; + } + } + + LOG("hi!"); + + for (tv = first_tv; tv->tv_tag != 0; tv++) { + for (const auto& tag : LD_PLUGIN_TAG_NAMES) { + if (tag.num == tv->tv_tag) { + switch (tv->tv_tag) { + case LDPT_NULL: + case LDPT_API_VERSION: + case LDPT_GNU_LD_VERSION: + case LDPT_LINKER_OUTPUT: LOG("%s(%d)", tag.name, tv->tv_u.tv_val); break; + case LDPT_OPTION: + case LDPT_OUTPUT_NAME: LOG("%s(%s)", tag.name, tv->tv_u.tv_string); break; + default: LOG("%s(%p)", tag.name, (void*)tv->tv_u.tv_string); break; + } + break; + } + } + } + + api.register_cleanup(&cleanup_hook); + api.register_all_symbols_read(&all_symbols_read_hook); + api.register_claim_file(&claim_file_hook); + + return LDPS_OK; +} + +ld_plugin_status claim_file_hook(const ld_plugin_input_file* file, int* claimed) { + *claimed = false; + // linked_files.push_back(LinkedFile(*file)); + // LOG("%s %d %d", file->name, file->offset, *claimed); + LOG("%s", file->name); + std::unique_ptr alloced_contents = nullptr; + const void* view = nullptr; + if (api.get_view) { + api.get_view(file->handle, &view); + } else { + alloced_contents.reset(new char[file->filesize]); + pread(file->fd, alloced_contents.get(), file->filesize, file->offset); + view = alloced_contents.get(); + } + return LDPS_OK; +} + +ld_plugin_status all_symbols_read_hook() { + // for (const auto& file : linked_files) { + // LOG("%s %p", file.name.c_str(), file.handle); + // // api.get_symbols(file.handle, 0, nullptr); + // } + return LDPS_OK; +} + +ld_plugin_status cleanup_hook() { + // linked_files.clear(); + return LDPS_OK; +}