diff --git a/CMakeLists.txt b/CMakeLists.txt index cc7bcb5a..60ec6c68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -378,6 +378,12 @@ if(LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc) COMPONENT llvm-toolchain-llvmlibc-configs ) + install( + DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR}/llvmlibc-samples/src + DESTINATION samples + COMPONENT llvm-toolchain-llvmlibc-configs + ) # We aren't yet able to build C++ libraries to go with llvm-libc set(CXX_LIBS OFF) diff --git a/docs/llvmlibc.md b/docs/llvmlibc.md index f09ec028..d793b350 100644 --- a/docs/llvmlibc.md +++ b/docs/llvmlibc.md @@ -54,12 +54,21 @@ following command line options, in addition to `--target`, `-march` or `__llvm_libc_heap_limit` in addition to whatever other memory layout you want. +* LLVM libc does not define errno. If you are using a function that + sets errno then you must implement the function `int *__llvm_libc_errno()` + that returns the address of your definition of errno. + For example: ``` clang --config=llvmlibc.cfg --target=arm-none-eabi -march=armv7m -o hello hello.c -lsemihost -lcrt0 -Wl,--defsym=__stack=0x200000 ``` +## Samples + +The overlay package installs a llvmlibc directory in the samples/src +directory containing sample programs that use LLVM libc. + ## Limitations of LLVM libc in LLVM Embedded Toolchain for Arm At present, this toolchain does not build any C++ libraries to go with diff --git a/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/Makefile b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/Makefile new file mode 100644 index 00000000..2197d30a --- /dev/null +++ b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/Makefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2020-2024, Arm Limited and affiliates. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include ../../../Makefile.conf + +build: hello.elf + +hello.elf: *.c + ../$(BIN_PATH)/clang --config=llvmlibc.cfg $(MICROBIT_TARGET) -nostartfiles -lsemihost -g -fno-exceptions -fno-rtti -T microbit-llvmlibc.ld -o hello.elf $^ + +%.hex: %.elf + ../$(BIN_PATH)/llvm-objcopy -O ihex $< $@ + +run: hello.hex + qemu-system-arm -M microbit -semihosting -nographic -device loader,file=$< + +debug: hello.hex + qemu-system-arm -M microbit -semihosting -nographic -device loader,file=$< -s -S + +clean: + rm -f *.elf *.hex + +.PHONY: clean run debug diff --git a/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/crt0llvmlibc.c b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/crt0llvmlibc.c new file mode 100644 index 00000000..28c3fb01 --- /dev/null +++ b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/crt0llvmlibc.c @@ -0,0 +1,41 @@ +// Copyright (c) 2024, Arm Limited and affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +extern int main(int argc, char** argv); + +extern void _platform_init(); + +extern char __data_source[]; +extern char __data_start[]; +extern char __data_end[]; +extern char __data_size[]; +extern char __bss_start[]; +extern char __bss_end[]; +extern char __bss_size[]; +extern char __tls_base[]; +extern char __tdata_end[]; +extern char __tls_end[]; + +void _start(void) { + memcpy(__data_start, __data_source, (size_t) __data_size); + memset(__bss_start, 0, (size_t) __bss_size); + _platform_init(); + _Exit(main(0, NULL)); +} diff --git a/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/hello.c b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/hello.c new file mode 100644 index 00000000..65455d53 --- /dev/null +++ b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/hello.c @@ -0,0 +1,46 @@ +// Copyright (c) 2024, Arm Limited and affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +// Implementation of errno +int *__llvm_libc_errno() { + static int internal_err; + return &internal_err; +} + +// Example that uses heap, string and math library. + +int main(void) { + const char *hello_s = "hello "; + const char *world_s = "world"; + const size_t hello_s_len = strlen(hello_s); + const size_t world_s_len = strlen(world_s); + const size_t out_s_len = hello_s_len + world_s_len + 1; + char *out_s = (char*) malloc(out_s_len); + assert(out_s_len >= hello_s_len + 1); + strncpy(out_s, hello_s, hello_s_len + 1); + assert(out_s_len >= strlen(out_s) + world_s_len + 1); + strncat(out_s, world_s, world_s_len + 1); + // 2024-10-17 Embedded printf implementation does not currently + // support printing floating point numbers. + printf("%s %li\n", out_s, lround(400000 * atanf(1.0f))); + free(out_s); + return 0; +} diff --git a/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/microbit-llvmlibc.ld b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/microbit-llvmlibc.ld new file mode 100644 index 00000000..e74a19af --- /dev/null +++ b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/microbit-llvmlibc.ld @@ -0,0 +1,241 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2019 Keith Packard + * Copyright (c) 2024, Arm Limited and affiliates. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Linker script adapted from picolibc linker script + * llvm-libc uses different linker defined symbols for heap placement. + */ + +/* Hard-coded for Cortex-M0 */ +MEMORY +{ + boot_flash (rx!w) : + ORIGIN = 0x00000000, + LENGTH = 0x3000 + flash (rx!w) : + ORIGIN = 0x3000, + LENGTH = 0x3d000 + ram (w!rx) : + ORIGIN = 0x20000000, + LENGTH = 0x4000 +} + +PHDRS +{ + text_boot_flash PT_LOAD; + text PT_LOAD; + ram_init PT_LOAD; + ram PT_LOAD; + tls PT_TLS; +} + +SECTIONS +{ + PROVIDE(__stack = ORIGIN(ram) + LENGTH(ram)); + + .boot_flash : { + KEEP (*(.vectors)) + } >boot_flash AT>boot_flash :text_boot_flash + + .text : { + + /* code */ + *(.text.unlikely .text.unlikely.*) + *(.text.startup .text.startup.*) + *(.text .text.* .opd .opd.*) + PROVIDE (__start___lcxx_override = .); + *(__lcxx_override) + PROVIDE (__stop___lcxx_override = .); + *(.gnu.linkonce.t.*) + KEEP (*(.fini .fini.*)) + __text_end = .; + + PROVIDE (__etext = __text_end); + PROVIDE (_etext = __text_end); + PROVIDE (etext = __text_end); + + /* Need to pre-align so that the symbols come after padding */ + . = ALIGN(8); + + /* lists of constructors and destructors */ + PROVIDE_HIDDEN ( __preinit_array_start = . ); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN ( __preinit_array_end = . ); + + PROVIDE_HIDDEN ( __init_array_start = . ); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array .ctors)) + PROVIDE_HIDDEN ( __init_array_end = . ); + + PROVIDE_HIDDEN ( __fini_array_start = . ); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array .dtors)) + PROVIDE_HIDDEN ( __fini_array_end = . ); + + } >flash AT>flash :text + + .rodata : { + + /* read-only data */ + *(.rdata) + *(.rodata .rodata.*) + *(.gnu.linkonce.r.*) + + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + + } >flash AT>flash :text + + .data.rel.ro : { + + /* data that needs relocating */ + *(.data.rel.ro .data.rel.ro.*) + + } >flash AT>flash :text + + + /* + * Needs to be in its own segment with the PLT entries first + * so that the linker will compute the offsets to those + * entries correctly. + */ + .got : { + *(.got.plt) + *(.got) + } >flash AT>flash :text + + .except_ordered : { + *(.gcc_except_table *.gcc_except_table.*) + *(.ARM.extab* .gnu.linkonce.armextab.*) + } >flash AT>flash :text + + .except_unordered : { + . = ALIGN(8); + + PROVIDE(__exidx_start = .); + *(.ARM.exidx*) + PROVIDE(__exidx_end = .); + } >flash AT>flash :text + + + .data : /* For ld.bfd: ALIGN_WITH_INPUT */ { + *(.data .data.*) + *(.gnu.linkonce.d.*) + + /* Need to pre-align so that the symbols come after padding */ + . = ALIGN(8); + + PROVIDE( __global_pointer$ = . + 0x800 ); + PROVIDE( _gp = . + 0x8000); + *(.sdata .sdata.* .sdata2.*) + *(.gnu.linkonce.s.*) + } >ram AT>flash :ram_init + PROVIDE(__data_start = ADDR(.data)); + PROVIDE(__data_source = LOADADDR(.data)); + + /* Thread local initialized data. This gets + * space allocated as it is expected to be placed + * in ram to be used as a template for TLS data blocks + * allocated at runtime. We're slightly abusing that + * by placing the data in flash where it will be copied + * into the allocate ram addresses by the existing + * data initialization code in crt0. + * BFD includes .tbss alignment when computing .tdata + * alignment, but for ld.lld we have to explicitly pad + * as it only guarantees usage as a TLS template works + * rather than supporting this use case. + */ + .tdata : ALIGN(__tls_align) /* For ld.bfd: ALIGN_WITH_INPUT */ { + *(.tdata .tdata.* .gnu.linkonce.td.*) + PROVIDE(__data_end = .); + PROVIDE(__tdata_end = .); + } >ram AT>flash :tls :ram_init + PROVIDE( __tls_base = ADDR(.tdata)); + PROVIDE( __tdata_start = ADDR(.tdata)); + PROVIDE( __tdata_source = LOADADDR(.tdata) ); + PROVIDE( __tdata_source_end = LOADADDR(.tdata) + SIZEOF(.tdata) ); + PROVIDE( __data_source_end = __tdata_source_end ); + PROVIDE( __tdata_size = SIZEOF(.tdata) ); + + PROVIDE( __edata = __data_end ); + PROVIDE( _edata = __data_end ); + PROVIDE( edata = __data_end ); + PROVIDE( __data_size = __data_end - __data_start ); + PROVIDE( __data_source_size = __data_source_end - __data_source ); + + .tbss (NOLOAD) : { + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + PROVIDE( __tls_end = . ); + PROVIDE( __tbss_end = . ); + } >ram AT>ram :tls :ram + PROVIDE( __bss_start = ADDR(.tbss)); + PROVIDE( __tbss_start = ADDR(.tbss)); + PROVIDE( __tbss_offset = ADDR(.tbss) - ADDR(.tdata) ); + PROVIDE( __tbss_size = SIZEOF(.tbss) ); + PROVIDE( __tls_size = __tls_end - __tls_base ); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + PROVIDE( __arm64_tls_tcb_offset = MAX(16, __tls_align) ); + + .bss (NOLOAD) : { + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + + /* Align the heap */ + . = ALIGN(8); + __bss_end = .; + } >ram AT>ram :ram + PROVIDE( __non_tls_bss_start = ADDR(.bss) ); + PROVIDE( __end = __bss_end ); + _end = __bss_end; + PROVIDE( end = __bss_end ); + PROVIDE( __bss_size = __bss_end - __bss_start ); + + /* Make the rest of memory available for heap storage + * LLVM libc denotes heap with [__end, __llvm_libc_heap_limit) + */ + PROVIDE (__llvm_libc_heap_limit = __stack - (DEFINED(__stack_size) ? __stack_size : 4K)); +} + +ASSERT( __data_size == __data_source_size, "ERROR: .data/.tdata flash size does not match RAM size"); + diff --git a/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/vector.c b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/vector.c new file mode 100644 index 00000000..9ac444f6 --- /dev/null +++ b/llvmlibc-samples/src/llvmlibc/baremetal-semihosting/vector.c @@ -0,0 +1,56 @@ +// Copyright (c) 2024, Arm Limited and affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +/* Adapted from https://developer.arm.com/documentation/107565/0101/Use-case-examples/Generic-Information/What-is-inside-a-program-image-/Vector-table */ + +extern void __llvm_libc_exit(); + +extern uint8_t __stack[]; + +extern void _start(void); + + +void NMI_Handler() {} +void HardFault_Handler() { __llvm_libc_exit(); } +void MemManage_Handler() { __llvm_libc_exit(); } +void BusFault_Handler() { __llvm_libc_exit(); } +void UsageFault_Handler() { __llvm_libc_exit(); } +void SVC_Handler() {} +void DebugMon_Handler() {} +void PendSV_Handler() {} +void SysTick_Handler() {} +typedef void(*VECTOR_TABLE_Type)(void); +const VECTOR_TABLE_Type __VECTOR_TABLE[496] __attribute__((section(".vectors"))) __attribute__((aligned(128))) = { + (VECTOR_TABLE_Type)__stack, /* Initial Stack Pointer */ + _start, /* Reset Handler */ + NMI_Handler, /* NMI Handler */ + HardFault_Handler, /* Hard Fault Handler */ + MemManage_Handler, /* MPU Fault Handler */ + BusFault_Handler, /* Bus Fault Handler */ + UsageFault_Handler, /* Usage Fault Handler */ + 0, /* Reserved */ + 0, /* Reserved */ + 0, /* Reserved */ + 0, /* Reserved */ + SVC_Handler, /* SVC Handler */ + DebugMon_Handler, /* Debug Monitor Handler */ + 0, /* Reserved */ + PendSV_Handler, /* PendSV Handler */ + SysTick_Handler, /* SysTick Handler */ + /* Unused */ +};