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 LLVM libc sample #538

Merged
merged 4 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
5 changes: 5 additions & 0 deletions docs/llvmlibc.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ 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
Expand Down
37 changes: 37 additions & 0 deletions llvmlibc-samples/src/llvmlibc/baremetal-semihosting/Makefile
Original file line number Diff line number Diff line change
@@ -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
41 changes: 41 additions & 0 deletions llvmlibc-samples/src/llvmlibc/baremetal-semihosting/crt0llvmlibc.c
Original file line number Diff line number Diff line change
@@ -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 <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

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, (uintptr_t) __data_size);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pedantry: formally, memcpy and memset take a size_t, not a uintptr_t.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks will fix.

memset(__bss_start, '\0', (uintptr_t) __bss_size);
_platform_init();
_Exit(main(0, NULL));
}
38 changes: 38 additions & 0 deletions llvmlibc-samples/src/llvmlibc/baremetal-semihosting/hello.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

/* Example that uses heap, string and math library */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure you have used the math library, have you? I can only see a call to abs, which is defined in stdlib.h, not math.h.

The problem with the actual math.h functions in embedded LLVM libc is that they won't make a good demo, because you can't printf the floating-point results, because FP printf is left out in the embedded build profile!

I suppose if you really wanted to demonstrate math.h functions, you could do something that has an integer final result, along the lines of lround(400000 * atan(1)) to get 314159. But it's probably also worth adding a comment explaining why you picked that example, so that when the user inevitably tries tweaking it to printf a floating-point value, they're warned why it won't work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will teach me for using the list of math functions in cppreference. Was trying to find one that returned an integer.

I've used atanf as llvm-libc doesn't define atan. This also showed up that I hadn't defined int *__llvm_libc_errno() so I've added that to the docs aswell.


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);
printf("%s %d\n", out_s, abs(-3));
free(out_s);
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright © 2019 Keith Packard
Copy link
Contributor

@voltur01 voltur01 Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to add Arm copyright as well? Presumably this was tweaked to meet the needs of the sample.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was a trivial modification, but it is probably best to add the additional copyright anyway.

* 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");

Loading
Loading