From d899a72babb4c2d80fe6e46a77e570864218b46f Mon Sep 17 00:00:00 2001 From: Tilman Sauerbeck Date: Sun, 16 Jun 2019 19:18:09 +0200 Subject: [PATCH] build: Add infrastructure for the bootloader program and libcommon. This builds, but does not produce a useful binary. --- .gitignore | 2 + SConscript.libcommon | 20 ++++ SConscript.target | 70 ++++++++++++ SConstruct | 38 +++++++ src/bootloader/bootloader.ld | 201 +++++++++++++++++++++++++++++++++++ src/bootloader/main.rs | 34 ++++++ src/bootloader/start.c | 33 ++++++ src/common/lib.rs | 38 +++++++ src/common/startup.c | 152 ++++++++++++++++++++++++++ thumbv6m-none-eabi.json | 15 +++ 10 files changed, 603 insertions(+) create mode 100644 .gitignore create mode 100644 SConscript.libcommon create mode 100644 SConscript.target create mode 100644 SConstruct create mode 100644 src/bootloader/bootloader.ld create mode 100644 src/bootloader/main.rs create mode 100644 src/bootloader/start.c create mode 100644 src/common/lib.rs create mode 100644 src/common/startup.c create mode 100644 thumbv6m-none-eabi.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe83278 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.sconsign.dblite +build-target diff --git a/SConscript.libcommon b/SConscript.libcommon new file mode 100644 index 0000000..902af89 --- /dev/null +++ b/SConscript.libcommon @@ -0,0 +1,20 @@ +Import('env') +env = env.Clone() + +source_files_rs = [ + 'src/common/lib.rs', # Must be listed first (see below). +] + +source_files_c = [ + 'src/common/startup.c', +] + +libcommon_rlib = env.Rustc('libcommon.rlib', source_files_rs[0]) + +for f in source_files_rs: + Depends(libcommon_rlib, f) + +libcommon_a = env.Library('libcommon.a', source_files_c) + +for f in source_files_c: + Depends(libcommon_a, f) diff --git a/SConscript.target b/SConscript.target new file mode 100644 index 0000000..6dd2f41 --- /dev/null +++ b/SConscript.target @@ -0,0 +1,70 @@ +import os + +Import('env') +env = env.Clone() + +host = os.environ.get('HOST', 'arm-none-eabi-') + +d = { + 'CC': 'gcc', + 'LD': 'ld', + 'AR': 'ar', + 'RANLIB': 'ranlib', + 'OBJCOPY': 'objcopy', +} + +for k, v in d.items(): + env[k] = host + v + +env.Append(CCFLAGS = [ + '-mcpu=cortex-m0plus', + '-mthumb', + '-ffunction-sections', + '-fdata-sections', + '-Os', +]) + +env.Append(LINKFLAGS = [ + '-mcpu=cortex-m0plus', + '-mthumb', + '-specs=nano.specs', + '-specs=nosys.specs', + '-nostdlib', + '-nostartfiles', +]) + +env.Append(RUSTCFLAGS = [ + '-C', 'opt-level=s', + '--target=thumbv6m-none-eabi', + '-L', '$LIBPATH', +]) + +env.Append(LINKFLAGS = [ + '-Wl,--gc-sections' +]) + +SConscript('SConscript.libcommon', exports='env', duplicate=0) + +libbootloader_source_files = [ + 'src/bootloader/main.rs', # Must be listed first (see below). +] + +libbootloader = env.Rustc('libbootloader.a', libbootloader_source_files[0]) + +for f in libbootloader_source_files: + Depends(libbootloader, f) + +Depends(libbootloader, 'libcommon.rlib') +Depends(libbootloader, 'libcommon.a') + +bootloader_env = env.Clone() + +bootloader_env.Append(LINKFLAGS = [ + '-Tsrc/bootloader/bootloader.ld', +]) + +bootloader_elf = bootloader_env.Program('gps-watch-bootloader.elf', 'src/bootloader/start.c', LIBS = ['bootloader', 'common']) + +bootloader_bin = bootloader_env.Objcopy(bootloader_elf) + +Default(bootloader_bin) diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..7f486b2 --- /dev/null +++ b/SConstruct @@ -0,0 +1,38 @@ +import SCons +import os + +root_env = Environment(ENV = {'PATH': os.environ['PATH']}) + +# Beautify output. +if ARGUMENTS.get('V', '0') != '1': + root_env['CCCOMSTR'] = ' CC $TARGET' + root_env['RUSTCCOMSTR'] = ' RUSTC $TARGET' + root_env['LINKCOMSTR'] = ' LINK $TARGET' + root_env['ARCOMSTR'] = ' AR $TARGET' + root_env['RANLIBCOMSTR'] = ' RANLIB $TARGET' + root_env['OBJCOPYCOMSTR'] = ' OBJCOPY $TARGET' + +# Make colored output of e.g. gcc work. +root_env['ENV']['TERM'] = os.environ.get('TERM') + +root_env.Append(CCFLAGS = [ + '-std=gnu99', + '-Wall', + '-Wextra', + '-Wwrite-strings', +]) + +root_env.Append(RUSTC = 'rustc') + +root_env.Append(BUILDERS = { 'Rustc': Builder( + action=Action('$RUSTC $RUSTCFLAGS -o $TARGET $SOURCE', '$RUSTCCOMSTR'))}) + +root_env.Append(BUILDERS = { 'Objcopy': Builder( + action=Action('$OBJCOPY -O binary $OBJCOPYFLAGS $SOURCES $TARGET', '$OBJCOPYCOMSTR'), + suffix='.bin', + src_suffix='.elf')}) + +env = root_env.Clone() +env['variant_dir'] = 'build-target' +env['LIBPATH'] = env['variant_dir'] +SConscript('SConscript.target', exports='env', variant_dir=env['variant_dir'], duplicate=0) diff --git a/src/bootloader/bootloader.ld b/src/bootloader/bootloader.ld new file mode 100644 index 0000000..5744429 --- /dev/null +++ b/src/bootloader/bootloader.ld @@ -0,0 +1,201 @@ +/* This is a copy of + * CMSIS/Device/ARM/ARMCM0plus/Source/GCC/gcc_arm.ld + * taken from + * git://github.com/ARM-software/CMSIS + */ + +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x40000 /* 256k */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x08000 /* 32k */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/src/bootloader/main.rs b/src/bootloader/main.rs new file mode 100644 index 0000000..198eb44 --- /dev/null +++ b/src/bootloader/main.rs @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Tilman Sauerbeck (tilman at code-monkey de) + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#![no_std] +#![crate_type="staticlib"] +#[link(name="libcommon.rlib")] + +extern crate common; + +#[no_mangle] +pub unsafe extern fn main() { + loop { + } +} diff --git a/src/bootloader/start.c b/src/bootloader/start.c new file mode 100644 index 0000000..5363ac0 --- /dev/null +++ b/src/bootloader/start.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Tilman Sauerbeck (tilman at code-monkey de) + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +extern void main(); + +__attribute__((noreturn)) void +_start (void) +{ + main(); + + for (;;) { + } +} diff --git a/src/common/lib.rs b/src/common/lib.rs new file mode 100644 index 0000000..3ed90e8 --- /dev/null +++ b/src/common/lib.rs @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Tilman Sauerbeck (tilman at code-monkey de) + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#![no_std] +#![crate_name="common"] +#![crate_type="rlib"] +#![feature(lang_items)] + +#[lang="eh_personality"] +extern fn eh_personality() { +} + +#[lang="panic_fmt"] +#[no_mangle] +pub fn rust_begin_unwind(_fmt: &core::fmt::Arguments, _file_line: &(&'static str, usize)) -> ! { + loop { + } +} diff --git a/src/common/startup.c b/src/common/startup.c new file mode 100644 index 0000000..daa8906 --- /dev/null +++ b/src/common/startup.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2019 Tilman Sauerbeck (tilman at code-monkey de) + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +void Reset_Handler (void); +void NMI_Handler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void HardFault_Handler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void SVC_Handler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void PendSV_Handler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void SysTick_Handler (void) __attribute__ ((weak, alias("Dummy_Handler"))); + +void DMA0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void DMA1_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void DMA2_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void DMA3_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void FTFA_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void PMC_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void LLWU_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void I2C0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void I2C1_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void SPI0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void SPI1_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void UART0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void UART1_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void UART2_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void ADC0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void CMP0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void TPM0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void TPM1_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void TPM2_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void RTC_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void RTC_Seconds_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void PIT_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void I2S0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void USB0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void DAC0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void TSI0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void MCG_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void LPTMR0_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void PORTA_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); +void PORTC_PORTD_IRQHandler (void) __attribute__ ((weak, alias("Dummy_Handler"))); + +extern void _start (void) __attribute__((noreturn)); + +extern uint32_t __etext; +extern uint32_t __data_start__; +extern uint32_t __data_end__; +extern uint32_t __bss_start__; +extern uint32_t __bss_end__; +extern uint32_t __StackTop; + +__attribute__ ((section(".vectors"))) +const void * __Vectors[48] = +{ + &__StackTop, /* Initial stack pointer */ + + (void *) Reset_Handler, + (void *) NMI_Handler, + (void *) HardFault_Handler, + (void *) (0UL), /* Reserved */ + (void *) (0UL), /* Reserved */ + (void *) (0UL), /* Reserved */ + (void *) (0UL), /* Reserved */ + (void *) (0UL), /* Reserved */ + (void *) (0UL), /* Reserved */ + (void *) (0UL), /* Reserved */ + (void *) SVC_Handler, + (void *) (0UL), /* Reserved */ + (void *) (0UL), /* Reserved */ + (void *) PendSV_Handler, + (void *) SysTick_Handler, + + (void *) DMA0_IRQHandler, + (void *) DMA1_IRQHandler, + (void *) DMA2_IRQHandler, + (void *) DMA3_IRQHandler, + (void *) (0UL), /* Reserved */ + (void *) FTFA_IRQHandler, + (void *) PMC_IRQHandler, + (void *) LLWU_IRQHandler, + (void *) I2C0_IRQHandler, + (void *) I2C1_IRQHandler, + (void *) SPI0_IRQHandler, + (void *) SPI1_IRQHandler, + (void *) UART0_IRQHandler, + (void *) UART1_IRQHandler, + (void *) UART2_IRQHandler, + (void *) ADC0_IRQHandler, + (void *) CMP0_IRQHandler, + (void *) TPM0_IRQHandler, + (void *) TPM1_IRQHandler, + (void *) TPM2_IRQHandler, + (void *) RTC_IRQHandler, + (void *) RTC_Seconds_IRQHandler, + (void *) PIT_IRQHandler, + (void *) I2S0_IRQHandler, + (void *) USB0_IRQHandler, + (void *) DAC0_IRQHandler, + (void *) TSI0_IRQHandler, + (void *) MCG_IRQHandler, + (void *) LPTMR0_IRQHandler, + (void *) (0UL), /* Reserved */ + (void *) PORTA_IRQHandler, + (void *) PORTC_PORTD_IRQHandler, +}; + +void +Reset_Handler (void) +{ + uint32_t *src, *dst; + + src = &__etext; + dst = &__data_start__; + + /* Initialize data. */ + while (dst < &__data_end__) + *dst++ = *src++; + + /* Clear bss. */ + for (dst = &__bss_start__; dst < &__bss_end__; dst++) + *dst = 0; + + _start(); +} + +void +Dummy_Handler (void) +{ + for (;;) { + } +} diff --git a/thumbv6m-none-eabi.json b/thumbv6m-none-eabi.json new file mode 100644 index 0000000..918798f --- /dev/null +++ b/thumbv6m-none-eabi.json @@ -0,0 +1,15 @@ +{ + "arch": "arm", + "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", + "executables": true, + "os": "none", + "pre-link-args": ["-Wl,-("], + "post-link-args": ["-Wl,-)"], + "target-endian": "little", + "target-pointer-width": "32", + "cpu": "cortex-m0", + "llvm-target": "thumbv6m-none-eabi", + "features": "+strict-align", + "relocation-model": "static", + "no-compiler-rt": true +} -- 2.30.2