Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GNU Build ID to identify firmware #219

Merged
merged 2 commits into from
Jun 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions examples/nucleo_f103rb/hard_fault/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
7 changes: 4 additions & 3 deletions examples/stm32f072_discovery/hard_fault/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
9 changes: 4 additions & 5 deletions examples/stm32f469_discovery/hard_fault/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
29 changes: 29 additions & 0 deletions src/modm/architecture/interface/build_id.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2019, Niklas Hauser
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#pragma once

#include <cstdint>
#include <array>

namespace modm
{

/**
* Return the GNU Build ID as a 160-bit SHA1 array.
*
* @ingroup modm_architecture_build_id
*/
[[nodiscard]]
const std::array<uint8_t, 20>&
build_id();

}
14 changes: 14 additions & 0 deletions src/modm/architecture/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ class BlockDevice(Module):
env.copy("interface/block_device.hpp")
# -----------------------------------------------------------------------------

class BuildId(Module):
def init(self, module):
module.name = "build_id"
module.description = "GNU Build ID"

def prepare(self, module, options):
return True

def build(self, env):
env.outbasepath = "modm/src/modm/architecture"
env.copy("interface/build_id.hpp")
# -----------------------------------------------------------------------------

class Can(Module):
def init(self, module):
module.name = "can"
Expand Down Expand Up @@ -348,6 +361,7 @@ def prepare(module, options):
module.add_submodule(Assert())
module.add_submodule(Atomic())
module.add_submodule(BlockDevice())
module.add_submodule(BuildId())
module.add_submodule(Can())
module.add_submodule(Clock())
module.add_submodule(Delay())
Expand Down
33 changes: 33 additions & 0 deletions src/modm/platform/core/cortex/build_id.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2019, Niklas Hauser
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#include <modm/architecture/interface/build_id.hpp>

typedef struct {
uint32_t namesz;
uint32_t descsz;
uint32_t type;
uint8_t data[];
} ElfNoteSection_t;
extern "C" const ElfNoteSection_t __build_id[];

namespace modm
{

const std::array<uint8_t, 20>&
build_id()
{
const uint8_t *const sha1 = &__build_id->data[__build_id->namesz];
auto *const hash = reinterpret_cast<const std::array<uint8_t, 20> *>(sha1);
return *hash;
}

}
6 changes: 6 additions & 0 deletions src/modm/platform/core/cortex/linker.macros
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ TOTAL_STACK_SIZE = MAIN_STACK_SIZE + PROCESS_STACK_SIZE;
KEEP(*(.assertion))
__assertion_table_end = .;
} >{{memory}}

.build_id :
{
__build_id = .;
KEEP(*(.note.gnu.build-id))
} > {{memory}}
%% endmacro


Expand Down
4 changes: 4 additions & 0 deletions src/modm/platform/core/cortex/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def prepare(module, options):
":architecture:interrupt",
":architecture:memory",
":architecture:unaligned",
":architecture:build_id",
":platform:clock",
":cmsis:device")

Expand Down Expand Up @@ -274,6 +275,9 @@ def build(env):
env.template("delay.cpp.in")
env.copy("delay.hpp")

# GNU Build ID
env.copy("build_id.cpp")

# modm-test implements the clock methods itself
if not env.has_module(":test:architecture"):
env.copy("clock.cpp")
Expand Down
5 changes: 3 additions & 2 deletions src/modm/platform/fault/crashcatcher/fault.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#pragma once
#include "fault_storage.hpp"
#include <modm/architecture/interface/build_id.hpp>

/**
* Called first after a HardFault occurred.
Expand Down Expand Up @@ -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<uint8_t, 20>& buildId() { return modm::build_id(); }
/// Clears the report
static inline void clear() { FaultStorage::closeRead(); }
/// Clears the report and reboots the device
Expand Down
10 changes: 0 additions & 10 deletions src/modm/platform/fault/crashcatcher/fault_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include "fault_storage.hpp"
#include <modm/architecture/utils.hpp>
#include <modm/math/utils/crc32.hpp>

typedef struct
{
Expand All @@ -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;
Expand Down Expand Up @@ -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()
{
Expand Down
1 change: 0 additions & 1 deletion src/modm/platform/fault/crashcatcher/fault_storage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class FaultStorage
public:
static size_t openRead();
static void closeRead();
static uint32_t firmwareHash();

class Iterator
{
Expand Down
2 changes: 1 addition & 1 deletion src/modm/platform/fault/crashcatcher/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
30 changes: 20 additions & 10 deletions src/modm/platform/fault/crashcatcher/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
//
Expand All @@ -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
Expand All @@ -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
```
3 changes: 2 additions & 1 deletion tools/build_script_generator/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ def common_compiler_flags(compiler, target):
"-Wl,--fatal-warnings",
"-Wl,--gc-sections",
"-Wl,--relax",
"-Wl,-Map,{target_base}.map,--cref",
"-Wl,--build-id=sha1",
# "-Wl,-Map,{target_base}.map,--cref",
]
# C Preprocessor defines
flags["cppdefines"] = []
Expand Down
25 changes: 15 additions & 10 deletions tools/build_script_generator/scons/site_tools/artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,28 @@
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")
try:
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"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down