From a607613e8d7817e51a1dbb36a87548ceef9cafab Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 6 Jun 2019 19:49:02 +0200 Subject: [PATCH] [fault] Use Build ID instead of CRC32 hash --- examples/nucleo_f103rb/hard_fault/main.cpp | 9 +++--- .../stm32f072_discovery/hard_fault/main.cpp | 7 +++-- .../stm32f469_discovery/hard_fault/main.cpp | 9 +++--- .../platform/fault/crashcatcher/fault.hpp | 5 ++-- .../fault/crashcatcher/fault_storage.cpp | 10 ------- .../fault/crashcatcher/fault_storage.hpp | 1 - .../platform/fault/crashcatcher/module.lb | 2 +- .../platform/fault/crashcatcher/module.md | 30 ++++++++++++------- .../scons/site_tools/artifact.py | 25 +++++++++------- .../scons/site_tools/postmortem_gdb.py | 1 + 10 files changed, 52 insertions(+), 47 deletions(-) diff --git a/examples/nucleo_f103rb/hard_fault/main.cpp b/examples/nucleo_f103rb/hard_fault/main.cpp index 72a6aced72..70966eb78c 100644 --- a/examples/nucleo_f103rb/hard_fault/main.cpp +++ b/examples/nucleo_f103rb/hard_fault/main.cpp @@ -54,12 +54,11 @@ main() if (FaultReporter::hasReport()) { MODM_LOG_ERROR << "\n\nHardFault! Copy the data into a 'coredump.txt' file, "; - MODM_LOG_ERROR << "then execute 'scons postmortem firmware="; - MODM_LOG_ERROR << modm::hex << FaultReporter::firmware() << "'.\n\n"; - for (const uint8_t data : FaultReporter()) - { + MODM_LOG_ERROR << "then execute\n\n\tscons postmortem firmware=" << modm::hex; + for (const auto data : FaultReporter::buildId()) MODM_LOG_ERROR << data; + MODM_LOG_ERROR << "\n\n"; + for (const auto data : FaultReporter()) MODM_LOG_ERROR << modm::hex << data << modm::flush; - } MODM_LOG_ERROR << "\n\n\n" << modm::flush; FaultReporter::clearAndReboot(); } diff --git a/examples/stm32f072_discovery/hard_fault/main.cpp b/examples/stm32f072_discovery/hard_fault/main.cpp index 0c964c40fd..f0f0b8b49e 100644 --- a/examples/stm32f072_discovery/hard_fault/main.cpp +++ b/examples/stm32f072_discovery/hard_fault/main.cpp @@ -40,9 +40,10 @@ main() if (FaultReporter::hasReport()) { MODM_LOG_ERROR << "\n\nHardFault! Copy the data into a 'coredump.txt' file, "; - MODM_LOG_ERROR << "then execute 'scons postmortem firmware="; - MODM_LOG_ERROR << modm::hex << FaultReporter::firmware() << "'.\n\n"; - for (const uint8_t data : FaultReporter()) + MODM_LOG_ERROR << "then execute\n\n\tscons postmortem firmware=" << modm::hex; + for (const auto data : FaultReporter::buildId()) MODM_LOG_ERROR << data; + MODM_LOG_ERROR << "\n\n"; + for (const auto data : FaultReporter()) MODM_LOG_ERROR << modm::hex << data << modm::flush; MODM_LOG_ERROR << "\n\n\n" << modm::flush; FaultReporter::clearAndReboot(); diff --git a/examples/stm32f469_discovery/hard_fault/main.cpp b/examples/stm32f469_discovery/hard_fault/main.cpp index 22e8d90ce5..cbf03af3f4 100644 --- a/examples/stm32f469_discovery/hard_fault/main.cpp +++ b/examples/stm32f469_discovery/hard_fault/main.cpp @@ -54,12 +54,11 @@ main() if (FaultReporter::hasReport()) { MODM_LOG_ERROR << "\n\nHardFault! Copy the data into a 'coredump.txt' file, "; - MODM_LOG_ERROR << "then execute 'scons postmortem firmware="; - MODM_LOG_ERROR << modm::hex << FaultReporter::firmware() << "'.\n\n"; - for (const uint8_t data : FaultReporter()) - { + MODM_LOG_ERROR << "then execute\n\n\tscons postmortem firmware=" << modm::hex; + for (const auto data : FaultReporter::buildId()) MODM_LOG_ERROR << data; + MODM_LOG_ERROR << "\n\n"; + for (const auto data : FaultReporter()) MODM_LOG_ERROR << modm::hex << data << modm::flush; - } MODM_LOG_ERROR << "\n\n\n" << modm::flush; FaultReporter::clearAndReboot(); } diff --git a/src/modm/platform/fault/crashcatcher/fault.hpp b/src/modm/platform/fault/crashcatcher/fault.hpp index ab916f2ee0..fc98f09b05 100644 --- a/src/modm/platform/fault/crashcatcher/fault.hpp +++ b/src/modm/platform/fault/crashcatcher/fault.hpp @@ -11,6 +11,7 @@ #pragma once #include "fault_storage.hpp" +#include /** * Called first after a HardFault occurred. @@ -44,8 +45,8 @@ class FaultReporter /// @returns report size > 0 static inline bool hasReport() { return begin() != end(); } - /// @returns a 32-bit hash of the firmware for identification - static inline uint32_t firmware() { return FaultStorage::firmwareHash(); } + /// @returns a 20-bytes SHA1 of the firmware for identification + static inline const std::array& buildId() { return modm::build_id(); } /// Clears the report static inline void clear() { FaultStorage::closeRead(); } /// Clears the report and reboots the device diff --git a/src/modm/platform/fault/crashcatcher/fault_storage.cpp b/src/modm/platform/fault/crashcatcher/fault_storage.cpp index e0e6e7e0b1..0b2c9a13ce 100644 --- a/src/modm/platform/fault/crashcatcher/fault_storage.cpp +++ b/src/modm/platform/fault/crashcatcher/fault_storage.cpp @@ -11,7 +11,6 @@ #include "fault_storage.hpp" #include -#include typedef struct { @@ -22,8 +21,6 @@ typedef struct extern "C" const table_pool_t __table_heap_start[]; extern "C" const table_pool_t __table_heap_end[]; -extern "C" const uint8_t __rom_start[]; -extern "C" const uint8_t __rom_end[]; static constexpr uint32_t magic_start = 0xBAADC0DE; static constexpr uint32_t magic_end = 0xC0FFEEEE; @@ -76,13 +73,6 @@ FaultStorage::closeRead() marker_end_ptr = nullptr; } -uint32_t -FaultStorage::firmwareHash() -{ - // Compute the CRC32 of the loaded binary image - return modm::math::crc32(__rom_start, __rom_end - __rom_start); -} - void FaultStorage::openWrite() { diff --git a/src/modm/platform/fault/crashcatcher/fault_storage.hpp b/src/modm/platform/fault/crashcatcher/fault_storage.hpp index dc2315e41c..2a20ac7d35 100644 --- a/src/modm/platform/fault/crashcatcher/fault_storage.hpp +++ b/src/modm/platform/fault/crashcatcher/fault_storage.hpp @@ -24,7 +24,6 @@ class FaultStorage public: static size_t openRead(); static void closeRead(); - static uint32_t firmwareHash(); class Iterator { diff --git a/src/modm/platform/fault/crashcatcher/module.lb b/src/modm/platform/fault/crashcatcher/module.lb index a122b4627a..c559866df2 100644 --- a/src/modm/platform/fault/crashcatcher/module.lb +++ b/src/modm/platform/fault/crashcatcher/module.lb @@ -25,7 +25,7 @@ def prepare(module, options): enumeration=["core", "core+stack", "core+stack+data"], default="core+stack+data")) - module.depends(":crashcatcher", ":cmsis:device", ":math:utils") + module.depends(":crashcatcher", ":cmsis:device", ":architecture:build_id") return True diff --git a/src/modm/platform/fault/crashcatcher/module.md b/src/modm/platform/fault/crashcatcher/module.md index 94c48ff148..9ecf66dd7b 100644 --- a/src/modm/platform/fault/crashcatcher/module.md +++ b/src/modm/platform/fault/crashcatcher/module.md @@ -73,8 +73,9 @@ int main() if (FaultReporter::hasReport()) // Check first after boot { Application::partialInitialize(); // Initialize only the necessary - uint32_t id = FaultReporter::firmware(); - reportBegin(id); // start your report with the firmware hash + reportBegin(); + for (const uint8_t data : FaultReporter::buildId()) + reportBuildId(data); // send each byte of Build ID for (const uint8_t data : FaultReporter()) reportData(data); // send each byte of data reportEnd(); // end the report @@ -108,7 +109,7 @@ int main() if (faultReport and applicationReady) { // Still valid AFTER clear, but BEFORE reboot - const uint32_t id = FaultReporter::firmware(); + const auto id = FaultReporter::buildId(); auto begin = FaultReporter::begin(); auto end = FaultReporter::end(); // @@ -133,8 +134,17 @@ arm-none-eabi-gdb -tui executable.elf -ex "set target-charset ASCII" \ -ex "target remote | CrashDebug --elf executable.elf --dump coredump.txt" ``` -Note that the firmware identifier is the CRC32 hash of the ROM section, this can -help you find the right ELF file. +Note that the `FaultReporter::buildId()` contains the GNU Build ID, which can +help you find the right ELF file: + +``` +arm-none-eabi-readelf -n executable.elf + +Displaying notes found in: .build_id + Owner Data size Description + GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) + Build ID: 59f08f7a37a7340799d9dba6b0c092bc3c9515c5 +``` ### Post-Mortem Debugging with SCons @@ -143,14 +153,14 @@ The `:build:scons` module provides a few helper methods for working with fault reports. You still need to copy the coredump data manually, however, the firmware selection is automated. -The SCons build system will automatically cache both the ELF and binary files -for the firmware ID for every firmware upload (using `scons artifact`). -When a fault is reported, you can tell SCons the firmware hash and it will use +The SCons build system will automatically cache the ELF file for the build id for +every firmware upload (using `scons artifact`). +When a fault is reported, you can tell SCons the firmware build id and it will use the corresponding ELF file automatically. ```sh # Copy data into coredump.txt touch coredump.txt -# Start postmortem debugging of {hash} -scons postmortem firmware={hash} +# Start postmortem debugging of executable with this build id +scons postmortem firmware=59f08f7a37a7340799d9dba6b0c092bc3c9515c5 ``` diff --git a/tools/build_script_generator/scons/site_tools/artifact.py b/tools/build_script_generator/scons/site_tools/artifact.py index 507377f0ae..24395ac8bc 100644 --- a/tools/build_script_generator/scons/site_tools/artifact.py +++ b/tools/build_script_generator/scons/site_tools/artifact.py @@ -14,7 +14,7 @@ import shutil from SCons.Script import * import subprocess -import binascii +from elftools.elf.elffile import ELFFile def run_store_artifact(target, source, env): artifactpath = os.path.join(env["CONFIG_BUILD_BASE"], "artifacts") @@ -22,15 +22,20 @@ def run_store_artifact(target, source, env): os.makedirs(artifactpath) except: pass - source = str(source[0]) - binary = os.path.splitext(source)[0]+".bin" - subprocess.call(env.subst("$OBJCOPY -O binary {} {}".format(source, binary)), shell=True) - with open(binary, "rb") as binfile: - firmware = binascii.crc32(binfile.read()) & 0xFFFFFFFF - - artifact = os.path.join(artifactpath, "{:08X}.elf".format(firmware)) - shutil.copy2(source, artifact) - shutil.copy2(binary, os.path.splitext(artifact)[0]+".bin") + + with open(source[0].path, "rb") as src: + elffile = ELFFile(src) + build_id = elffile.get_section_by_name(".build_id") + if build_id is not None: + for note in build_id.iter_notes(): + if note['n_type'] == "NT_GNU_BUILD_ID": + build_id = note['n_desc'] + + if build_id is not None: + artifact = os.path.join(artifactpath, "{}.elf".format(build_id.lower())) + shutil.copy2(source[0].path, artifact) + else: + print("Unable to find Build ID for '{}'!".format(source[0].path)) return 0 def store_artifact(env, source, alias="store_artifact"): diff --git a/tools/build_script_generator/scons/site_tools/postmortem_gdb.py b/tools/build_script_generator/scons/site_tools/postmortem_gdb.py index 87d02cdd9e..f81a07bbda 100644 --- a/tools/build_script_generator/scons/site_tools/postmortem_gdb.py +++ b/tools/build_script_generator/scons/site_tools/postmortem_gdb.py @@ -23,6 +23,7 @@ def run_post_mortem_gdb(target, source, env): print("\n> Using the newest firmware may be inaccurate!\n" "> Use 'firmware={hash}' argument to specify a specific firmware.\n") else: + artifact = artifact.lower() artifactpath = os.path.join(env["CONFIG_BUILD_BASE"], "artifacts", "{}.elf".format(artifact)) if os.path.isfile(artifactpath): source = artifactpath