diff --git a/CMakeLists.txt b/CMakeLists.txt index 73cd4246..dd5c96c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,7 +140,7 @@ set(LLVM_TOOLCHAIN_C_LIBRARY "Which C library to use." ) set_property(CACHE LLVM_TOOLCHAIN_C_LIBRARY - PROPERTY STRINGS picolibc newlib) + PROPERTY STRINGS picolibc newlib llvmlibc) # Previously, the LLVM_TOOLCHAIN_LIBRARY_OVERLAY_INSTALL option was # called LLVM_TOOLCHAIN_NEWLIB_OVERLAY_INSTALL. Detect a setting of @@ -153,7 +153,7 @@ else() set(overlay_install_default OFF) endif() option(LLVM_TOOLCHAIN_LIBRARY_OVERLAY_INSTALL - "Make cpack build an overlay package that can be unpacked over the main toolchain to install a secondary set of libraries based on newlib." + "Make cpack build an overlay package that can be unpacked over the main toolchain to install a secondary set of libraries based on newlib or llvm-libc." ${overlay_install_default}) if(LLVM_TOOLCHAIN_LIBRARY_OVERLAY_INSTALL) if(LLVM_TOOLCHAIN_C_LIBRARY STREQUAL "picolibc") @@ -268,7 +268,9 @@ function(read_repo_version output_variable_prefix repo) set(${output_variable_prefix}_SHALLOW "${shallow}" PARENT_SCOPE) endfunction() read_repo_version(llvmproject llvm-project) -read_repo_version(${LLVM_TOOLCHAIN_C_LIBRARY} ${LLVM_TOOLCHAIN_C_LIBRARY}) +if(NOT (LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)) # libc in a separate repo? + read_repo_version(${LLVM_TOOLCHAIN_C_LIBRARY} ${LLVM_TOOLCHAIN_C_LIBRARY}) +endif() # The patches are generated from custom branch, with followin command: # git format-patch -k origin/main @@ -319,7 +321,9 @@ FetchContent_Declare(newlib ) FetchContent_MakeAvailable(llvmproject) -FetchContent_MakeAvailable(${LLVM_TOOLCHAIN_C_LIBRARY}) +if(NOT (LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)) # libc in a separate repo? + FetchContent_MakeAvailable(${LLVM_TOOLCHAIN_C_LIBRARY}) +endif() # We generally want to install to a local directory to see what the # output will look like rather than install into the system, so change @@ -334,6 +338,10 @@ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) ) endif() +# Whether to try to build C++ libraries. (We can't currently do this +# for all choices of C library.) +set(CXX_LIBS ON) + if(LLVM_TOOLCHAIN_C_LIBRARY STREQUAL newlib) install( FILES @@ -350,6 +358,42 @@ install( COMPONENT llvm-toolchain-omax-cfg ) +if(LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc) + install( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/llvmlibc.cfg + DESTINATION bin + COMPONENT llvm-toolchain-llvmlibc-configs + ) + + # We aren't yet able to build C++ libraries to go with llvm-libc + set(CXX_LIBS OFF) + + # We need to build libc-hdrgen + ExternalProject_Add( + libc_hdrgen + SOURCE_DIR ${llvmproject_SOURCE_DIR}/llvm + DEPENDS ${lib_tool_dependencies} + CMAKE_ARGS + -DLLVM_ENABLE_RUNTIMES=libc + -DLLVM_LIBC_FULL_BUILD=ON + -DCMAKE_BUILD_TYPE=Debug + STEP_TARGETS build install + BUILD_COMMAND ${CMAKE_COMMAND} --build . --target libc-hdrgen + INSTALL_COMMAND ${CMAKE_COMMAND} -E true + # Always run the build command so that incremental builds are correct. + BUILD_ALWAYS TRUE + CONFIGURE_HANDLED_BY_BUILD TRUE + ) + ExternalProject_Get_property(libc_hdrgen BINARY_DIR) + set(LIBC_HDRGEN ${BINARY_DIR}/bin/libc-hdrgen${CMAKE_EXECUTABLE_SUFFIX}) + + # Add an empty check target, to simplify the logic below that expects to + # find one for every libc type. We have no current setup to run the LLVM + # libc test suite. + add_custom_target(check-llvmlibc) +endif() + add_subdirectory( ${llvmproject_SOURCE_DIR}/llvm llvm ) @@ -396,7 +440,7 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL TRUE) # Don't create a separate archive for each component. set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) # When extracting the files put them in an ArmCompiler-.../ directory. -# Exception: the newlib overlay package does not do this, because it has +# Exception: the overlay packages do not do this, because they have # to be able to unpack over the top of an existing installation on all # platforms, and each platform has a different top-level directory name. if(LLVM_TOOLCHAIN_LIBRARY_OVERLAY_INSTALL) @@ -479,7 +523,7 @@ add_custom_target( -DLLVMEmbeddedToolchainForArm_VERSION=${LLVMEmbeddedToolchainForArm_VERSION} -DLLVMEmbeddedToolchainForArm_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -Dllvmproject_SOURCE_DIR=${llvmproject_SOURCE_DIR} - # only one of picolibc and newlib source dirs is needed, but easiest to + # at most one of picolibc and newlib source dirs is needed, but easiest to # specify both definitions -Dpicolibc_SOURCE_DIR=${picolibc_SOURCE_DIR} -Dnewlib_SOURCE_DIR=${newlib_SOURCE_DIR} @@ -817,6 +861,112 @@ function( ) endfunction() +function( + add_llvmlibc + directory + variant + target_triple + flags + test_executor_params + default_boot_flash_addr + default_boot_flash_size + default_flash_addr + default_flash_size + default_ram_addr + default_ram_size + default_stack_size +) + get_runtimes_flags("${directory}" "${flags}") + + set(runtimes_flags "${runtimes_flags} -Wno-error=atomic-alignment") + + set(common_cmake_args + -DCMAKE_AR=${LLVM_BINARY_DIR}/bin/llvm-ar${CMAKE_EXECUTABLE_SUFFIX} + -DCMAKE_ASM_COMPILER=${LLVM_BINARY_DIR}/bin/clang${CMAKE_EXECUTABLE_SUFFIX} + -DCMAKE_ASM_COMPILER_TARGET=${target_triple} + -DCMAKE_ASM_FLAGS=${runtimes_flags} + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_CXX_COMPILER=${LLVM_BINARY_DIR}/bin/clang++${CMAKE_EXECUTABLE_SUFFIX} + -DCMAKE_CXX_COMPILER_TARGET=${target_triple} + -DCMAKE_CXX_FLAGS=${runtimes_flags} + -DCMAKE_C_COMPILER=${LLVM_BINARY_DIR}/bin/clang${CMAKE_EXECUTABLE_SUFFIX} + -DCMAKE_C_COMPILER_TARGET=${target_triple} + -DCMAKE_C_FLAGS=${runtimes_flags} + -DCMAKE_INSTALL_MESSAGE=${CMAKE_INSTALL_MESSAGE} + -DCMAKE_INSTALL_PREFIX= + -DCMAKE_NM=${LLVM_BINARY_DIR}/bin/llvm-nm${CMAKE_EXECUTABLE_SUFFIX} + -DCMAKE_RANLIB=${LLVM_BINARY_DIR}/bin/llvm-ranlib${CMAKE_EXECUTABLE_SUFFIX} + # Let CMake know we're cross-compiling + -DCMAKE_SYSTEM_NAME=Generic + -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY + ) + + ExternalProject_Add( + llvmlibc_${variant} + SOURCE_DIR ${llvmproject_SOURCE_DIR}/runtimes + PREFIX llvmlibc/${variant} + INSTALL_DIR llvmlibc/${variant}/install + DEPENDS ${lib_tool_dependencies} ${libc_target} libc_hdrgen + CMAKE_ARGS + ${common_cmake_args} + -DLIBC_TARGET_TRIPLE=${target_triple} + -DLIBC_HDRGEN_EXE=${LIBC_HDRGEN} + -DLIBC_TARGET_OS=baremetal + -DLLVM_CMAKE_DIR=${LLVM_BINARY_DIR}/lib/cmake/llvm + -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON + -DLLVM_ENABLE_RUNTIMES=libc + -DLLVM_INCLUDE_TESTS=OFF # I haven't yet got the tests to build + -DLLVM_LIBC_FULL_BUILD=ON + STEP_TARGETS build install + USES_TERMINAL_CONFIGURE FALSE + USES_TERMINAL_BUILD TRUE + USES_TERMINAL_INSTALL TRUE + USES_TERMINAL_TEST TRUE + LIST_SEPARATOR , + # Always run the build command so that incremental builds are correct. + BUILD_ALWAYS TRUE + CONFIGURE_HANDLED_BY_BUILD TRUE + INSTALL_COMMAND ${CMAKE_COMMAND} --install . + # Copy llvm-libc lib directory, moving libraries out of their + # target-specific subdirectory. + COMMAND + ${CMAKE_COMMAND} + -E copy_directory + /lib/${target_triple} + "${LLVM_BINARY_DIR}/${directory}/lib" + # And copy the include directory, which is already arranged right. + COMMAND + ${CMAKE_COMMAND} + -E copy_directory + /include + "${LLVM_BINARY_DIR}/${directory}/include" + ) + + ExternalProject_Add( + llvmlibc-support_${variant} + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/llvmlibc-support + PREFIX llvmlibc-support/${variant} + INSTALL_DIR "${LLVM_BINARY_DIR}/${directory}" + DEPENDS ${lib_tool_dependencies} llvmlibc_${variant}-install + CMAKE_ARGS ${common_cmake_args} + STEP_TARGETS build install + USES_TERMINAL_CONFIGURE FALSE + USES_TERMINAL_BUILD TRUE + USES_TERMINAL_INSTALL TRUE + USES_TERMINAL_TEST TRUE + LIST_SEPARATOR , + # Always run the build command so that incremental builds are correct. + BUILD_ALWAYS TRUE + CONFIGURE_HANDLED_BY_BUILD TRUE + ) + + add_dependencies( + llvm-toolchain-runtimes + llvmlibc_${variant} + llvmlibc-support_${variant} + ) +endfunction() + macro( add_libc directory @@ -869,6 +1019,21 @@ macro( "${default_ram_size}" "${default_stack_size}" ) + elseif(LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc) + add_llvmlibc( + "${directory}" + "${variant}" + "${target_triple}" + "${flags}" + "${test_executor_params}" + "${default_boot_flash_addr}" + "${default_boot_flash_size}" + "${default_flash_addr}" + "${default_flash_size}" + "${default_ram_addr}" + "${default_ram_size}" + "${default_stack_size}" + ) endif() endmacro() @@ -1231,25 +1396,29 @@ function(add_library_variant target_arch) "${VARIANT_COMPILE_FLAGS}" "${lit_test_executor}" "${LLVM_TOOLCHAIN_C_LIBRARY}_${variant}-install" - ) - add_libcxx_libcxxabi_libunwind( - "${directory}" - "${variant}" - "${target_triple}" - "${VARIANT_COMPILE_FLAGS}" - "${lit_test_executor}" - "${LLVM_TOOLCHAIN_C_LIBRARY}_${variant}-install" - "${${LLVM_TOOLCHAIN_C_LIBRARY}_specific_runtimes_options}" - ${VARIANT_ENABLE_EXCEPTIONS} - ${VARIANT_ENABLE_RTTI} - ) + ) + if(CXX_LIBS) + add_libcxx_libcxxabi_libunwind( + "${directory}" + "${variant}" + "${target_triple}" + "${VARIANT_COMPILE_FLAGS}" + "${lit_test_executor}" + "${LLVM_TOOLCHAIN_C_LIBRARY}_${variant}-install" + "${${LLVM_TOOLCHAIN_C_LIBRARY}_specific_runtimes_options}" + ${VARIANT_ENABLE_EXCEPTIONS} + ${VARIANT_ENABLE_RTTI} + ) + endif() if(VARIANT_COMPILE_FLAGS MATCHES "-march=armv8") message("C++ runtime libraries tests disabled for ${variant}") else() add_custom_target(check-llvm-toolchain-runtimes-${variant}) add_dependencies(check-llvm-toolchain-runtimes check-llvm-toolchain-runtimes-${variant}) add_compiler_rt_tests("${variant}") - add_libcxx_libcxxabi_libunwind_tests("${variant}") + if(CXX_LIBS) + add_libcxx_libcxxabi_libunwind_tests("${variant}") + endif() endif() endif() @@ -1333,21 +1502,25 @@ set(multilib_yaml_content "") # For most variants, the "flash" memory is placed in address range, where # simulated boards have RAM. This is because code for some tests does not fit # the real flash. -add_library_variants_for_cpu( - aarch64 - COMPILE_FLAGS "-march=armv8-a" - MULTILIB_FLAGS "--target=aarch64-unknown-none-elf" - PICOLIBC_BUILD_TYPE "release" - QEMU_MACHINE "virt" - QEMU_CPU "cortex-a57" - BOOT_FLASH_ADDRESS 0x40000000 - BOOT_FLASH_SIZE 0x1000 - FLASH_ADDRESS 0x40001000 - FLASH_SIZE 0xfff000 - RAM_ADDRESS 0x41000000 - RAM_SIZE 0x1000000 - STACK_SIZE 8K -) +if(NOT (LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)) + # llvm-libc doesn't have a bare-metal configuration for AArch64, so we + # leave out the AArch64 library if we're building that libc. + add_library_variants_for_cpu( + aarch64 + COMPILE_FLAGS "-march=armv8-a" + MULTILIB_FLAGS "--target=aarch64-unknown-none-elf" + PICOLIBC_BUILD_TYPE "release" + QEMU_MACHINE "virt" + QEMU_CPU "cortex-a57" + BOOT_FLASH_ADDRESS 0x40000000 + BOOT_FLASH_SIZE 0x1000 + FLASH_ADDRESS 0x40001000 + FLASH_SIZE 0xfff000 + RAM_ADDRESS 0x41000000 + RAM_SIZE 0x1000000 + STACK_SIZE 8K + ) +endif() # For AArch32, clang uses different defaults for FPU selection than GCC, both # when "+fp" or "+fp.dp" are used and when no FPU specifier is provided in # "-march=". Using "-mfpu=" explicitly. diff --git a/README.md b/README.md index 802085fc..0bdb97b8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ bare-metal LLVM based toolchain targeting Arm based on: * libc++abi * libc++ * compiler-rt -* picolibc, or optionally newlib +* picolibc, or optionally newlib or LLVM's libc ## Goal diff --git a/cmake/generate_version_txt.cmake b/cmake/generate_version_txt.cmake index b36460cb..a3f70327 100644 --- a/cmake/generate_version_txt.cmake +++ b/cmake/generate_version_txt.cmake @@ -23,12 +23,14 @@ execute_process( OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY ) -execute_process( - COMMAND git -C ${${LLVM_TOOLCHAIN_C_LIBRARY}_SOURCE_DIR} rev-parse HEAD - OUTPUT_VARIABLE ${LLVM_TOOLCHAIN_C_LIBRARY}_COMMIT - OUTPUT_STRIP_TRAILING_WHITESPACE - COMMAND_ERROR_IS_FATAL ANY -) +if(NOT (LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)) # libc in a separate repo? + execute_process( + COMMAND git -C ${${LLVM_TOOLCHAIN_C_LIBRARY}_SOURCE_DIR} rev-parse HEAD + OUTPUT_VARIABLE ${LLVM_TOOLCHAIN_C_LIBRARY}_COMMIT + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY + ) +endif() configure_file( ${CMAKE_CURRENT_LIST_DIR}/VERSION.txt.in diff --git a/docs/llvmlibc.md b/docs/llvmlibc.md new file mode 100644 index 00000000..b9cc6fc8 --- /dev/null +++ b/docs/llvmlibc.md @@ -0,0 +1,66 @@ +# Experimental LLVM libc support + +LLVM Embedded Toolchain for Arm uses +[`picolibc`](https://github.com/picolibc/picolibc) as the standard C +library. For experimental and evaluation purposes, you can instead +choose to use the LLVM project's own C library. + +> **NOTE:** `llvmlibc` support in LLVM Embedded Toolchain for Arm is +> an experimental technology preview, with significant limitations. + +## Building the toolchain with LLVM libc + +> **NOTE:** Building the LLVM libc package is only supported on Linux +> and macOS. + +Configure the toolchain with the CMake setting +`-DLLVM_TOOLCHAIN_C_LIBRARY=llvmlibc` to build a version of the +toolchain based on LLVM libc. + +If you also add `-DLLVM_TOOLCHAIN_LIBRARY_OVERLAY_INSTALL=on` then the +`package-llvm-toolchain` CMake target will generate an overlay package +similar to the [newlib overlay +package](https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/blob/main/docs/newlib.md). +If you unpack this over an existing installation of the toolchain, +then you can switch to LLVM libc by adding `--config=llvmlibc.cfg` on +the command line. + +## Using LLVM libc + +To compile a program with this LLVM libc, you must provide the +following command line options, in addition to `--target`, `-march` or +`-mcpu`, and the input and output files: + +* `--config=llvmlibc.cfg` if you are using LLVM libc as an overlay + package (but you do not need this if you have built the whole + toolchain with only LLVM libc) + +* `-lcrt0` to include a library defining the `_start` symbol (or else + provide that symbol yourself) + +* `-lsemihost` to include a library that implements porting functions + in LLVM's libc in terms of the Arm semihosting API (or else provide + an alternative implementation of those functions yourself) + +* `-Wl,--defsym=__stack=0x`_nnnnnn_ to define the starting value of + your stack pointer. Alternatively, use a linker script that defines + the symbol `__stack` in addition to whatever other memory layout you + want. + +For example: + +``` +clang --config=llvmlibc.cfg --target=arm-none-eabi -march=armv7m -o hello hello.c -lsemihost -lcrt0 -Wl,--defsym=__stack=0x200000 +``` + +## Limitations of LLVM libc in LLVM Embedded Toolchain for Arm + +At present, this toolchain only builds LLVM libc for AArch32, not for +AArch64. + +At present, this toolchain does not build any C++ libraries to go with +LLVM libc. + +At the time of writing this (2024-07), LLVM libc is a work in +progress. It is incomplete: not all standard C library functionality +is provided. diff --git a/llvmlibc-support/CMakeLists.txt b/llvmlibc-support/CMakeLists.txt new file mode 100644 index 00000000..0e61b37f --- /dev/null +++ b/llvmlibc-support/CMakeLists.txt @@ -0,0 +1,46 @@ +# +# Copyright (c) 2022, 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. +# + +# This directory builds two additional library files to go with llvm-libc: +# +# libcrt0.a contains an implementation of the _start() default entry point, +# which sets up the stack and does any necessary initialization before +# calling main(). +# +# libsemihost.a implements llvm-libc's porting function API such as +# __llvm_libc_stdio_write, in terms of the Arm semihosting system. +# +# To use LLVM libc in a semihosting context, include both of these +# libraries. To use it in a different context, you will need to +# reimplement the same functions that libsemihost.a provides, but +# libcrt0.a might still be useful. + +cmake_minimum_required(VERSION 3.20.0) +project(llvmlibc-support LANGUAGES C ASM) + +add_library(semihost STATIC + init.c + exit.c + stdio_read.c + stdio_write.c +) + +add_library(crt0 STATIC + crt0.c +) + +install(TARGETS semihost crt0) diff --git a/llvmlibc-support/crt0.c b/llvmlibc-support/crt0.c new file mode 100644 index 00000000..b2f11693 --- /dev/null +++ b/llvmlibc-support/crt0.c @@ -0,0 +1,34 @@ +// +// Copyright (c) 2022, 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 "platform.h" + +int main(int, char **); + +__attribute__((used)) static void c_startup(void) { + _platform_init(); + _Exit(main(0, NULL)); +} + +extern long __stack[]; +__attribute__((naked)) void _start(void) { + __asm__("mov sp, %0" : : "r"(__stack)); + __asm__("b c_startup"); +} diff --git a/llvmlibc-support/exit.c b/llvmlibc-support/exit.c new file mode 100644 index 00000000..d784a597 --- /dev/null +++ b/llvmlibc-support/exit.c @@ -0,0 +1,32 @@ +// +// Copyright (c) 2022, 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 "semihost.h" + +void __llvm_libc_exit(int status) { + +#if defined(__ARM_64BIT_STATE) && __ARM_64BIT_STATE + size_t block[2]; + block[0] = ADP_Stopped_ApplicationExit; + block[1] = code; + semihosting_call(SYS_EXIT, block); +#else + semihosting_call(SYS_EXIT, (const void *)ADP_Stopped_ApplicationExit); +#endif + + __builtin_unreachable(); /* semihosting call doesn't return */ +} diff --git a/llvmlibc-support/init.c b/llvmlibc-support/init.c new file mode 100644 index 00000000..bbe8d3c8 --- /dev/null +++ b/llvmlibc-support/init.c @@ -0,0 +1,39 @@ +// +// Copyright (c) 2022, 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 "platform.h" +#include "semihost.h" + +struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie; +struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie; +struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie; + +static void stdio_open(struct __llvm_libc_stdio_cookie *cookie, int mode) { + size_t args[3]; + args[0] = (size_t) ":tt"; + args[1] = (size_t)mode; + args[2] = (size_t)3; /* name length */ + cookie->handle = semihosting_call(SYS_OPEN, args); +} + +void _platform_init(void) { + stdio_open(&__llvm_libc_stdin_cookie, OPENMODE_R); + stdio_open(&__llvm_libc_stdout_cookie, OPENMODE_W); + stdio_open(&__llvm_libc_stderr_cookie, OPENMODE_W); +} diff --git a/llvmlibc-support/platform.h b/llvmlibc-support/platform.h new file mode 100644 index 00000000..668c3304 --- /dev/null +++ b/llvmlibc-support/platform.h @@ -0,0 +1,34 @@ +// +// Copyright (c) 2022, 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. +// + +// This header file defines the interface between libcrt0.a, which defines +// the program entry point, and libsemihost.a, which implements the +// LLVM-libc porting functions in terms of semihosting. If you replace +// libsemihost.a with something else, this header file shows how to make +// that work with libcrt0.a. + +#ifndef LLVMET_LLVMLIBC_SUPPORT_PLATFORM_H +#define LLVMET_LLVMLIBC_SUPPORT_PLATFORM_H + +// libcrt0.a will call this function after the stack pointer is +// initialized. If any setup specific to the libc porting layer is +// needed, this is where to do it. For example, in semihosting, the +// standard I/O handles must be opened via the SYS_OPEN operation, and +// this function is where libsemihost.a does it. +void _platform_init(void); + +#endif // LLVMET_LLVMLIBC_SUPPORT_PLATFORM_H diff --git a/llvmlibc-support/semihost.h b/llvmlibc-support/semihost.h new file mode 100644 index 00000000..71c80949 --- /dev/null +++ b/llvmlibc-support/semihost.h @@ -0,0 +1,117 @@ +// +// Copyright (c) 2022, 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. +// + +// This header file provides internal definitions for libsemihost.a, +// including an inline function to make a semihosting call, and a lot +// of constant definitions. + +#ifndef LLVMET_LLVMLIBC_SUPPORT_SEMIHOST_H +#define LLVMET_LLVMLIBC_SUPPORT_SEMIHOST_H + +#include + +#if __ARM_64BIT_STATE +# define ARG_REG_0 "x0" +# define ARG_REG_1 "x1" +#else +# define ARG_REG_0 "r0" +# define ARG_REG_1 "r1" +#endif + +#if __ARM_64BIT_STATE // A64 +# define SEMIHOST_INSTRUCTION "hlt #0xf000" +#elif defined(__thumb__) // T32 +# if defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' +# define SEMIHOST_INSTRUCTION "bkpt #0xAB" +# elif defined(HLT_SEMIHOSTING) +# define SEMIHOST_INSTRUCTION ".inst.n 0xbabc" // hlt #60 +# else +# define SEMIHOST_INSTRUCTION "svc 0xab" +# endif +#else // A32 +# if defined(HLT_SEMIHOSTING) +# define SEMIHOST_INSTRUCTION ".inst 0xe10f0070" // hlt #0xf000 +# else +# define SEMIHOST_INSTRUCTION "svc 0x123456" +# endif +#endif + +__attribute__((always_inline)) +static long semihosting_call(long val, const void *ptr) { + register long v __asm__(ARG_REG_0) = val; + register const void *p __asm__(ARG_REG_1) = ptr; + __asm__ __volatile__(SEMIHOST_INSTRUCTION + : "+r"(v), "+r"(p) + : + : "memory", "cc"); + return v; +} + +#define SYS_CLOCK 0x10 +#define SYS_CLOSE 0x02 +#define SYS_ELAPSED 0x30 +#define SYS_ERRNO 0x13 +#define SYS_EXIT 0x18 +#define SYS_EXIT_EXTENDED 0x20 +#define SYS_FLEN 0x0c +#define SYS_GET_CMDLINE 0x15 +#define SYS_HEAPINFO 0x16 +#define SYS_ISERROR 0x08 +#define SYS_ISTTY 0x09 +#define SYS_OPEN 0x01 +#define SYS_READ 0x06 +#define SYS_READC 0x07 +#define SYS_REMOVE 0x0e +#define SYS_RENAME 0x0f +#define SYS_SEEK 0x0a +#define SYS_SYSTEM 0x12 +#define SYS_TICKFREQ 0x31 +#define SYS_TIME 0x11 +#define SYS_TMPNAM 0x0d +#define SYS_WRITE0 0x04 +#define SYS_WRITE 0x05 +#define SYS_WRITEC 0x03 + +#define ADP_Stopped_BranchThroughZero 0x20000 +#define ADP_Stopped_UndefinedInstr 0x20001 +#define ADP_Stopped_SoftwareInterrupt 0x20002 +#define ADP_Stopped_PrefetchAbort 0x20003 +#define ADP_Stopped_DataAbort 0x20004 +#define ADP_Stopped_AddressException 0x20005 +#define ADP_Stopped_IRQ 0x20006 +#define ADP_Stopped_FIQ 0x20007 +#define ADP_Stopped_BreakPoint 0x20020 +#define ADP_Stopped_WatchPoint 0x20021 +#define ADP_Stopped_StepComplete 0x20022 +#define ADP_Stopped_RunTimeErrorUnknown 0x20023 +#define ADP_Stopped_InternalError 0x20024 +#define ADP_Stopped_UserInterruption 0x20025 +#define ADP_Stopped_ApplicationExit 0x20026 +#define ADP_Stopped_StackOverflow 0x20027 +#define ADP_Stopped_DivisionByZero 0x20028 +#define ADP_Stopped_OSSpecific 0x20029 + +/* SYS_OPEN modes must be one of R,W,A, plus an optional B and optional PLUS */ +#define OPENMODE_R 0 +#define OPENMODE_W 4 +#define OPENMODE_A 8 +#define OPENMODE_B 1 +#define OPENMODE_PLUS 2 + +struct __llvm_libc_stdio_cookie { int handle; }; + +#endif // LLVMET_LLVMLIBC_SUPPORT_SEMIHOST_H diff --git a/llvmlibc-support/stdio_read.c b/llvmlibc-support/stdio_read.c new file mode 100644 index 00000000..aa2c00fe --- /dev/null +++ b/llvmlibc-support/stdio_read.c @@ -0,0 +1,33 @@ +// +// Copyright (c) 2022, 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 "semihost.h" + +ssize_t __llvm_libc_stdio_read(struct __llvm_libc_stdio_cookie *cookie, + const char *buf, size_t size) { + size_t args[4]; + args[0] = (size_t)cookie->handle; + args[1] = (size_t)buf; + args[2] = (size_t)size; + args[3] = 0; + ssize_t retval = semihosting_call(SYS_READ, args); + if (retval >= 0) + retval = size - retval; + return retval; +} diff --git a/llvmlibc-support/stdio_write.c b/llvmlibc-support/stdio_write.c new file mode 100644 index 00000000..9b7741fd --- /dev/null +++ b/llvmlibc-support/stdio_write.c @@ -0,0 +1,32 @@ +// +// Copyright (c) 2022, 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 "semihost.h" + +ssize_t __llvm_libc_stdio_write(struct __llvm_libc_stdio_cookie *cookie, + const char *buf, size_t size) { + size_t args[4]; + args[0] = (size_t)cookie->handle; + args[1] = (size_t)buf; + args[2] = (size_t)size; + ssize_t retval = semihosting_call(SYS_WRITE, args); + if (retval >= 0) + retval = size - retval; + return retval; +} diff --git a/llvmlibc.cfg b/llvmlibc.cfg new file mode 100644 index 00000000..14aba3bf --- /dev/null +++ b/llvmlibc.cfg @@ -0,0 +1 @@ +--sysroot /../lib/clang-runtimes/llvmlibc