From c09d293d2b34d15e8bf609466d55555c74587845 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Fri, 1 Jul 2016 22:57:09 +0200 Subject: o Adding elfinfo tool from the stm32 playground. --- apps/accel/CMakeLists.txt | 1 + cmake/elfinfo/.gitignore | 1 + cmake/elfinfo/CMakeLists.txt | 13 ++ cmake/elfinfo/elfinfo.cpp | 260 ++++++++++++++++++++++++++++++++ cmake/intel-quark-d2000.toolchain.cmake | 79 +++++++--- 5 files changed, 336 insertions(+), 18 deletions(-) create mode 100644 cmake/elfinfo/.gitignore create mode 100644 cmake/elfinfo/CMakeLists.txt create mode 100644 cmake/elfinfo/elfinfo.cpp diff --git a/apps/accel/CMakeLists.txt b/apps/accel/CMakeLists.txt index 91f1497..35948ae 100644 --- a/apps/accel/CMakeLists.txt +++ b/apps/accel/CMakeLists.txt @@ -1,3 +1,4 @@ add_executable(accel main.c) set_target_properties(accel PROPERTIES CHIP QUARK_D2000) toolchain_target(accel) +add_extra_commands(accel) diff --git a/cmake/elfinfo/.gitignore b/cmake/elfinfo/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/cmake/elfinfo/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/cmake/elfinfo/CMakeLists.txt b/cmake/elfinfo/CMakeLists.txt new file mode 100644 index 0000000..82efcdf --- /dev/null +++ b/cmake/elfinfo/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.2) + +project(elfinfo CXX ASM) + +add_executable(elfinfo elfinfo.cpp) +target_compile_options(elfinfo PUBLIC "--std=c++14") +target_link_libraries(elfinfo elf) + +INSTALL(TARGETS elfinfo + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/cmake/elfinfo/elfinfo.cpp b/cmake/elfinfo/elfinfo.cpp new file mode 100644 index 0000000..05a847a --- /dev/null +++ b/cmake/elfinfo/elfinfo.cpp @@ -0,0 +1,260 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::vector; + +enum class SectionType { + TEXT, DATA +}; + +const char *to_str(const SectionType &type) { + switch (type) { + case SectionType::TEXT: + return "text"; + case SectionType::DATA: + return "data"; + } +} + +struct Section { + SectionType type; + const Elf64_Addr start; + const Elf64_Addr end; + const Elf64_Xword size; + Elf64_Addr used; + + Section(SectionType type, Elf64_Addr start, Elf64_Xword size) : type(type), start(start), size(size), + end(start + size), used(0) { + } + + bool contains(Elf64_Addr address, Elf64_Xword size) const { + return contains(address) && contains(address + size); + } + + bool contains(Elf64_Addr address) const { + return start <= address && address < end; + } +}; + +vector
sections; + +char *filename = NULL; + +char *program; + +__attribute__((noreturn)) +void usage(const char *reason = NULL) { + if (reason != NULL) { + fprintf(stderr, "%s\n", reason); + } + fprintf(stderr, "usage: %s -f file [-t start:size] [-d start:size]\n", program); + fprintf(stderr, " -t/-d/-b: add text/data section\n"); + fprintf(stderr, "At least one section has to be specified\n"); + exit(EX_USAGE); +} + +void parse_start_size(char *input, Elf64_Addr &start, Elf64_Xword &size) { + char *str_size = strchr(input, ':'); + + if (str_size == NULL) { + usage("bad section specification, missing ':'"); + } + + *str_size = '\0'; + str_size++; + + if (sscanf(input, "%" SCNi64, &start) != 1) { + usage("bad section specification, could not parse start number"); + } + + size_t str_size_len = strlen(str_size); + + if (str_size_len < 1) { + usage("bad section specification"); + } + + char suffix = str_size[str_size_len - 1]; + int modifier; + + if (!isdigit(suffix)) { + switch (suffix) { + case 'k': + case 'K': + modifier = 1024; + break; + case 'm': + case 'M': + modifier = 1024 * 1024; + break; + default: + usage("bad size modifier, only 'k' and 'M' are allowed"); + } + } else { + modifier = 1; + } + + if (sscanf(str_size, "%" SCNi64, &size) != 1) { + usage("bad section specification, could not parse size number"); + } + size = size * modifier; +} + +bool debug = false; + +void parse_args(int argc, char **argv) { + int c; + + while ((c = getopt(argc, argv, "Df:t:d:")) != -1) { + switch (c) { + case 'D': + debug = true; + break; + case 't': + case 'd': { + Elf64_Addr start; + Elf64_Xword size; + parse_start_size(optarg, start, size); + SectionType type = c == 't' ? SectionType::TEXT : SectionType::DATA; + sections.push_back(Section(type, start, size)); + break; + } + case 'f': + filename = optarg; + break; + case '?': + if (optopt == 'c') + errx(EX_USAGE, "Option -%c requires an argument.\n", optopt); + else + errx(EX_USAGE, "Unknown option `-%c'.\n", optopt); + default: + abort(); + } + } + + if (filename == NULL || sections.empty()) { + usage(); + } +} + +void to_iso(Elf64_Addr i, char *buf) { + const char *suffix; + if (i > 1024 * 1024) { + i /= 1024 * 1024; + suffix = "M"; + } else if (i > 1024) { + i /= 1024; + suffix = "k"; + } else { + suffix = ""; + } + sprintf(buf, "%" PRIu64 "%s", i, suffix); +} + +int main(int argc, char **argv) { + program = argv[0]; + parse_args(argc, argv); + + if (elf_version(EV_CURRENT) == EV_NONE) + errx(EX_SOFTWARE, "ELF library initialization failed: %s", elf_errmsg(-1)); + + int fd; + if ((fd = open(filename, O_RDONLY, 0)) < 0) + err(EX_NOINPUT, "open \"%s\" failed", argv[1]); + + Elf *e; + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) + errx(EX_SOFTWARE, "elf_begin() failed: %s.", elf_errmsg(-1)); + if (elf_kind(e) != ELF_K_ELF) + errx(EX_DATAERR, "%s is not an ELF object.", argv[1]); + + size_t shstrndx; + if (elf_getshdrstrndx(e, &shstrndx) != 0) + errx(EX_SOFTWARE, "elf_getshdrstrndx() failed: %s.", elf_errmsg(-1)); + + size_t program_header_count; + if (elf_getphdrnum(e, &program_header_count) != 0) + errx(EX_DATAERR, "elf_getphdrnum() failed: %s.", elf_errmsg(-1)); + + size_t text_size = 0, data_size = 0, bss_size = 0; + for (int i = 0; i < program_header_count; i++) { + GElf_Phdr phdr; + + if (gelf_getphdr(e, i, &phdr) != &phdr) + errx(EX_SOFTWARE, "getphdr() failed: %s.", elf_errmsg(-1)); + + if (phdr.p_type == PT_LOAD) { + SectionType expectedType; + size_t *size; + + if (phdr.p_flags == (PF_X | PF_W | PF_R) || phdr.p_flags == (PF_X | PF_R)) { + if (debug) { + printf("Adding PH #%d as text\n", i); + } + + expectedType = SectionType::TEXT; + size = &text_size; + } else if (phdr.p_flags == (PF_R | PF_W)) { + expectedType = SectionType::DATA; + if (phdr.p_filesz > 0) { + if (debug) { + printf("Adding PH #%d as data\n", i); + } + size = &data_size; + } + else { + if (debug) { + printf("Adding PH #%d as bss\n", i); + } + size = &bss_size; + } + } else { + warnx("Unknown flag combination: 0x%02x", phdr.p_flags); + warnx("Unknown flag combination: 0x%02x", PF_X | PF_R); + continue; + } + + auto s = std::find_if(sections.begin(), sections.end(), [&](Section §ion) { + return section.type == expectedType && section.contains(phdr.p_vaddr, phdr.p_memsz); + }); + + if (s == sections.end()) { + fprintf(stderr, + "Could not find a section for elf header #%d of type %s, at address 0x%08" PRIx64 " with size %" PRId64 "\n", + i, to_str(expectedType), phdr.p_vaddr, phdr.p_memsz); + } + else { + (*s).used += phdr.p_memsz; + + *size += phdr.p_memsz; + } + } else { + // ignored + }; + } + + printf("Size by sections\n"); + printf("Type Start End Size Used\n"); + std::for_each(sections.begin(), sections.end(), [&](Section &s) { + char size[100]; + to_iso(s.size, size); + int used_pct = (int) (double(s.used) / double(s.size) * 100.0); + printf("%4s %08" PRIx64 " %08" PRIx64 " %5s %6" PRId64 " %3d%%\n", to_str(s.type), s.start, s.end, size, s.used, + used_pct); + }); + + printf("\n"); + printf("Size by type\n"); + printf("text=%zu, data=%zu, bss=%zu\n", text_size, data_size, bss_size); + return EXIT_SUCCESS; +} diff --git a/cmake/intel-quark-d2000.toolchain.cmake b/cmake/intel-quark-d2000.toolchain.cmake index 3e15792..16f12f5 100644 --- a/cmake/intel-quark-d2000.toolchain.cmake +++ b/cmake/intel-quark-d2000.toolchain.cmake @@ -1,6 +1,6 @@ if (${INTEL_QUARK_TOOLCHAIN_LOADED}) - return() -endif() + return() +endif () set(INTEL_QUARK_TOOLCHAIN_LOADED TRUE) include(CMakeForceCompiler) @@ -10,12 +10,14 @@ set(TRIPLE "i586-intel-elfiamcu") if (NOT IS_DIRECTORY "${ISSM_DIR}") message(FATAL_ERROR "ISSM_DIR has to be set to a directory:" ${ISSM_DIR}) set(ISSM_DIR CACHE PATH "The path to Intes ISSM") -endif() +endif () if (NOT INTEL_QUARK_CHIP) set(INTEL_QUARK_CHIP CACHE STRING "The Intel Quark chip to build for") message(FATAL_ERROR "INTEL_QUARK_CHIP has to be set before including the toolchain file") -endif() +endif () + +get_filename_component(toolchain_dir "${CMAKE_TOOLCHAIN_FILE}" DIRECTORY) set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR intel) @@ -26,22 +28,22 @@ set(BASE_FLAGS "-std=c90 -Wall -Wextra -Werror -Wno-unused-parameter") set(INCLUDES "") -if(IS_DIRECTORY "${QMSI_DIR}") +if (IS_DIRECTORY "${QMSI_DIR}") message("Using QMSI_DIR: ${QMSI_DIR}") -else() +else () message("Detecting QMSI_DIR..") find_path(QMSI_DIR qm_common.h - HINTS "${ISSM_DIR}/firmware/bsp/1.0/include" - NO_DEFAULT_PATH) + HINTS "${ISSM_DIR}/firmware/bsp/1.0/include" + NO_DEFAULT_PATH) - if(IS_DIRECTORY "${QMSI_DIR}") + if (IS_DIRECTORY "${QMSI_DIR}") message("Found QMSI_DIR: ${QMSI_DIR}") - else() + else () message(FATAL_ERROR "Could not find QMSI directory") - endif() + endif () -endif() +endif () # TODO: these directories should be validated list(APPEND includes "${ISSM_DIR}/firmware/bsp/1.0/include") @@ -52,9 +54,12 @@ list(APPEND includes "${ISSM_DIR}/firmware/bsp/1.0/drivers/bmc150") if (INTEL_QUARK_CHIP STREQUAL D2000) list(APPEND includes "${ISSM_DIR}/firmware/bsp/1.0/soc/quark_d2000/include") set(ld_file "${ISSM_DIR}/firmware/bsp/1.0/soc/quark_d2000/quark_d2000.ld") + + list(APPEND mcu_text_areas 0x00180000:20k) + list(APPEND mcu_data_areas 0x00280000:5k) elseif (INTEL_QUARK_CHIP STREQUAL SE) list(APPEND includes "${ISSM_DIR}/firmware/bsp/1.0/soc/quark_se/include") -endif() +endif () # QMSI Library file(GLOB_RECURSE qmsi_sources ${ISSM_DIR}/firmware/bsp/1.0/drivers/*.c) @@ -129,7 +134,7 @@ set(CMAKE_C_FLAGS "${BASE_FLAGS} ${TARGET_FLAGS} " CACHE STRING "c flags") set(CMAKE_CXX_FLAGS "${BASE_FLAGS} ${TARGET_FLAGS} -fno-exceptions -fno-rtti -felide-constructors -std=c++14" CACHE STRING "c++ flags") # ${CMAKE_C_FLAGS} is prepended to this string -set(LD_FILE ) +set(LD_FILE) set(linker_flags "") set(linker_flags "${linker_flags} -nostdlib") set(linker_flags "${linker_flags} -Xlinker -A") @@ -143,9 +148,9 @@ set(CMAKE_EXE_LINKER_FLAGS "${linker_flags}" CACHE STRING "linker flags" FORCE) set(GCC "${ISSM_DIR}/tools/compiler/bin/${TRIPLE}-gcc") -if(NOT EXISTS "${GCC}") +if (NOT EXISTS "${GCC}") message(FATAL_ERROR "Could not find ${TRIPLE}-gcc. Is $ISSM_DIR set correctly?") -endif() +endif () # No C++ support for D2000 # set(GXX "${ISSM_DIR}/tools/compiler/bin/${TRIPLE}-g++") @@ -156,13 +161,14 @@ endif() cmake_force_c_compiler("${GCC}" GNU) -# search for programs in the build host directories +# search for programs in the build elfinfo directories set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # for libraries and headers in the target directories set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) function(toolchain_target TARGET) + add_dependencies("${TARGET}" elfinfo) target_link_libraries("${TARGET}" intel_sys) target_link_libraries("${TARGET}" qmsi) target_link_libraries("${TARGET}" ipp) @@ -170,7 +176,44 @@ function(toolchain_target TARGET) target_link_libraries("${TARGET}" softfp) target_link_libraries("${TARGET}" c) target_link_libraries("${TARGET}" g) -# target_link_libraries("${TARGET}" gcc) + # target_link_libraries("${TARGET}" gcc) target_compile_definitions("${TARGET}" PUBLIC -D__IPP_ENABLED__) target_link_libraries("${TARGET}" "-Xlinker" "-T${ld_file}") endfunction() + +# elfinfo tools + +get_filename_component(ELFINFO_SOURCE_DIR "${toolchain_dir}/elfinfo" ABSOLUTE) +get_filename_component(ELFINFO_INSTALL_DIR "${CMAKE_BINARY_DIR}/elfinfo" ABSOLUTE) + +include(ExternalProject) +ExternalProject_Add(elfinfo + SOURCE_DIR "${ELFINFO_SOURCE_DIR}" + DOWNLOAD_COMMAND "" + UPDATE_COMMAND "" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${ELFINFO_INSTALL_DIR}) + +function(add_extra_commands target_name) + # add_custom_command(TARGET ${target_name} POST_BUILD + # COMMAND mkdir -p ${target_name}-info && arm-none-eabi-objdump -D ${target_name} > ${target_name}-info/${target_name}.asm) + # add_custom_command(TARGET ${target_name} POST_BUILD + # COMMAND mkdir -p ${target_name}-info && arm-none-eabi-nm -C ${target_name} > ${target_name}-info/${target_name}.nm) + # add_custom_command(TARGET ${target_name} POST_BUILD + # COMMAND mkdir -p ${target_name}-info && arm-none-eabi-size ${target_name} > ${target_name}-info/${target_name}.size) + # add_custom_command(TARGET ${target_name} POST_BUILD + # COMMAND mkdir -p ${target_name}-info && arm-none-eabi-readelf -a ${target_name} > ${target_name}-info/${target_name}.readelf) + # add_custom_command(TARGET ${target_name} POST_BUILD + # COMMAND mkdir -p ${target_name}-info && arm-none-eabi-objcopy -O ihex ${target_name} ${target_name}-info/${target_name}.hex) + # add_custom_command(TARGET ${target_name} POST_BUILD + # COMMAND mkdir -p ${target_name}-info && arm-none-eabi-objcopy -O binary ${target_name} ${target_name}-info/${target_name}.bin) + + foreach(area ${mcu_text_areas}) + set(text_segments "${text_segments}" "-t" "${area}") + endforeach() + foreach(area ${mcu_data_areas}) + set(data_segments "${data_segments}" "-d" "${area}") + endforeach() + + add_custom_command(TARGET ${target_name} DEPENDS elfinfo POST_BUILD + COMMAND "${ELFINFO_INSTALL_DIR}/bin/elfinfo" -f ${target_name} ${text_segments} ${data_segments}) +endfunction() -- cgit v1.2.3