hello,
While I am trying to execute a simple bubble sort program in c as provided as an example in the arm development studio IDE, I am facing 3 errors like as shown below: (Target and source must be capability)
/bubblesort.c:45:37: error: invalid target type 'uint32_t *' (aka 'unsigned int *') for __cheri_tocap: target must be a capability seq_t sequence = (__cheri_tocap seq_t)malloc(sizeof(uint32_t) * SEQ_LEN);
../bubblesort.c:57:33: error: invalid source type 'uint32_t *' (aka 'unsigned int *') for __cheri_fromcap: source must be a capability free((__cheri_fromcap void*)sequence);
/bubblesort.c:70:6: error: conflicting types for 'print'void print (seq_t __cheri_input sequence, int size) {
N.B: I am using LLVM 13.0.0 with Morello support.
Hello,
I am trying to compile (using llvm release 1.6 for baremetal) a standalone bare-metal program of the bubblesort as shown before, and not using CHERIBSD in purecap mode. However, I am getting the same error with target and souce must be a capability:
bubblesort_cam.c:123:37: error: invalid target type 'uint32_t *' (aka 'unsigned int *') for __cheri_tocap: target must be a capability seq_t sequence = (__cheri_tocap seq_t)malloc(sizeof(uint32_t) * SEQ_LEN); ^bubblesort_cam.c:106:15: note: expanded from macro 'seq_t'#define seq_t uint32_t*__capability ^bubblesort_cam.c:135:33: error: invalid source type 'uint32_t *' (aka 'unsigned int *') for __cheri_fromcap: source must be a capability free((__cheri_fromcap void*)sequence);
I am using the linker script from the one in the below link:
git.morello-project.org/.../standalone-baremetal-readme.rst
N.B: Since it is a standalone baremetal program I already commented out the print statements and I have enclosed the cheri.h file code below. This header file is already provided in the sample example of ARM MORELLO.
============bubblesort.c code below====================================
//==========bubblesort============= #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <time.h> #include "cheri.h" #define seq_t uint32_t*__capability // The __capability qualifier here is not necessary when compiling for "pure" mode // (because all pointers have capabilities in "pure" mode), but might be needed // if compiling for "hybrid" mode, so has been left in for convenience and clarity. #define SEQ_LEN 15 void populate (seq_t __cheri_output sequence, int size); void sort (seq_t sequence, int size); void swap (seq_t first, seq_t second); int main (void) { //printf("Morello bare-metal bubble sort example\n\n"); seq_t sequence = (__cheri_tocap seq_t)malloc(sizeof(uint32_t) * SEQ_LEN); populate(sequence, SEQ_LEN); //printf("Original sequence:\n "); //print(sequence, SEQ_LEN); sort(sequence, SEQ_LEN); //printf("Sorted sequence:\n "); //print(sequence, SEQ_LEN); free((__cheri_fromcap void*)sequence); return 0; } void populate (seq_t __cheri_output sequence, int size) { srand(time(NULL)); for (int i = 0; i < size; i++) { sequence[i] = rand() % (101); } } void sort (seq_t sequence, int size) { for (int i = 0; i < (size - 1); i++) { for (int j = 0; j < (size - i - 1); j++) { if (sequence[j] > sequence[j+1]) { swap(&sequence[j], &sequence[j+1]); } } } } void swap (seq_t first, seq_t second) { uint32_t temp = *first; *first = *second; *second = temp; }
=============== cheri.h code below ======================================
/*===---- cheri.h - Header for CHERI capabilities -----------------------===*\ * * Copyright (c) 2014 David Chisnall * Copyright (c) 2018 Alex Richardson * * 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. * \*===----------------------------------------------------------------------===*/ #ifndef _CHERI_H #define _CHERI_H #pragma once #ifdef __aarch64__ typedef unsigned int cheri_perms_t; #else typedef unsigned short cheri_perms_t; #endif typedef unsigned short cheri_flags_t; typedef unsigned __INT32_TYPE__ cheri_type_t; #ifdef __cplusplus #define __cheri_bool bool #else #define __cheri_bool _Bool #endif #if __has_feature(capabilities) #define __IF_CAPS(x, y) x typedef __intcap_t intcap_t; typedef __uintcap_t uintcap_t; #ifdef WANT_CHERI_QUALIFIER_MACROS #define capability __capability #define output __cheri_output #define input __cheri_input #endif #else #define __IF_CAPS(x, y) y typedef __INTPTR_TYPE__ intcap_t; typedef __UINTPTR_TYPE__ uintcap_t; #define __capability #ifdef WANT_CHERI_QUALIFIER_MACROS #define capability #define output #define input #endif #endif #define __CHERI_GET(__name, __type, __get, __default) \ static inline __type \ cheri_##__name##__get(void * __capability __cap) \ { \ return __IF_CAPS(__builtin_cheri_##__name##__get(__cap), __default); \ } #define __CHERI_SET(__name, __type, __set) \ static inline void *__capability cheri_##__name##__set( \ void *__capability __cap, __type __val) { \ return __IF_CAPS(__builtin_cheri_##__name##__set(__cap, __val), \ (void *)__cap); \ } #define __CHERI_ACCESSOR(__name, __type, __set, __get, __default) \ __CHERI_GET(__name, __type, __get, __default) \ __CHERI_SET(__name, __type, __set) \ __CHERI_GET(length, __SIZE_TYPE__, _get, __SIZE_MAX__) __CHERI_GET(base, __SIZE_TYPE__, _get, __SIZE_MAX__) __CHERI_ACCESSOR(offset, __SIZE_TYPE__, _set, _get, __SIZE_MAX__) __CHERI_GET(type, cheri_type_t, _get, 0) __CHERI_ACCESSOR(perms, cheri_perms_t, _and, _get, 0) __CHERI_ACCESSOR(flags, cheri_flags_t, _set, _get, 0) __CHERI_GET(tag, __cheri_bool, _get, 0) __CHERI_GET(sealed, __cheri_bool, _get, 0) static inline void * __capability cheri_offset_increment(void *__capability __cap, __PTRDIFF_TYPE__ __offset) { return __IF_CAPS(__builtin_cheri_offset_increment(__cap, __offset), ((char*)__cap) + __offset); } static inline void * __capability cheri_tag_clear(void * __capability __cap) { return __IF_CAPS(__builtin_cheri_tag_clear(__cap), (void*)__cap); } static inline void * __capability cheri_seal(void * __capability __cap, const void * __capability __type) { return __IF_CAPS(__builtin_cheri_seal(__cap, __type), (void*)__cap); } static inline void * __capability cheri_unseal(void * __capability __cap, const void * __capability __type) { return __IF_CAPS(__builtin_cheri_unseal(__cap, __type), (void*)__cap); } static inline void * __capability cheri_bounds_set(void *__capability __cap, __SIZE_TYPE__ __bounds) { return __IF_CAPS(__builtin_cheri_bounds_set(__cap, __bounds), (void*)__cap); } static inline __SIZE_TYPE__ cheri_round_representable_length(__SIZE_TYPE__ __length) { return __IF_CAPS(__builtin_cheri_round_representable_length(__length), __length); } static inline __SIZE_TYPE__ cheri_round_representable_mask(__SIZE_TYPE__ __mask) { return __IF_CAPS(__builtin_cheri_representable_alignment_mask(__mask), __mask); } static inline __SIZE_TYPE__ cheri_copy_from_high(void *__capability __cap) { return __IF_CAPS(__builtin_cheri_copy_from_high(__cap), __SIZE_MAX__); } static inline void * __capability cheri_copy_to_high(const void *__capability __cap, __SIZE_TYPE__ __high) { return __IF_CAPS(__builtin_cheri_copy_to_high(__cap, __high), (void*)__cap); } static inline __SIZE_TYPE__ cheri_equal_exact(const void * __capability __cap_a, const void * __capability __cap_b) { return __IF_CAPS(__builtin_cheri_equal_exact(__cap_a, __cap_b), 0); } static inline __SIZE_TYPE__ cheri_subset_test(const void * __capability __cap_a, const void * __capability __cap_b) { return __IF_CAPS(__builtin_cheri_subset_test(__cap_a, __cap_b), 0); } #ifndef __CHERI_PURE_CAPABILITY__ static inline void * __capability cheri_cap_from_pointer(const void * __capability __cap, void *__ptr) { return __IF_CAPS(__builtin_cheri_cap_from_pointer(__cap, __ptr), (void *)__ptr); } static inline void * cheri_cap_to_pointer(const void * __capability __cap, void * __capability __offset) { return __IF_CAPS(__builtin_cheri_cap_to_pointer(__cap, __offset), (void *)__offset); } #endif static inline void cheri_perms_check(const void * __capability __cap, cheri_perms_t __perms) { __IF_CAPS(__builtin_cheri_perms_check(__cap, __perms), ); } static inline void cheri_type_check(const void * __capability __cap, const void * __capability __type) { __IF_CAPS(__builtin_cheri_type_check(__cap, __type), ); } static inline void * __capability cheri_global_data_get(void) { return __IF_CAPS(__builtin_cheri_global_data_get(), 0); } static inline void * __capability cheri_program_counter_get(void) { return __IF_CAPS(__builtin_cheri_program_counter_get(), 0); } /* TODO: Should these be builtins to get better diagnostics? */ static inline __attribute__((always_inline)) __attribute__((warn_unused_result)) __PTRADDR_TYPE__ __cheri_low_bits_get(uintcap_t ptr, __PTRADDR_TYPE__ mask) { /* * We need to return a NULL-derived capability here, so we need to explicitly * cast the LHS to a non-capability integer. */ return (__PTRADDR_TYPE__)ptr & mask; } static inline __attribute__((always_inline)) __attribute__((warn_unused_result)) uintcap_t __cheri_low_bits_or(uintcap_t ptr, __PTRADDR_TYPE__ bits) { /* * We want to return a LHS-derived capability here so using the default * uintcap_t semantics is fine. */ return ptr | bits; } static inline __attribute__((always_inline)) __attribute__((warn_unused_result)) uintcap_t __cheri_low_bits_clear(uintcap_t ptr, __PTRADDR_TYPE__ bits_mask) { /* * We want to return a LHS-derived capability here so using the default * uintcap_t semantics is fine. */ return ptr & (~bits_mask); } #ifndef __cheri_usable_low_bits_mask #define __cheri_usable_low_bits_mask 31 #endif #define __static_assert_sensible_low_bits(bits) \ __extension__({ \ _Static_assert(bits < (__cheri_usable_low_bits_mask + 1), \ "Using too many low pointer bits"); \ _Static_assert((bits & (bits + 1)) == 0, "Mask must be all ones"); \ (__PTRADDR_TYPE__)(bits); \ }) #define __runtime_assert_sensible_low_bits(bits, mask) \ __extension__({ \ __PTRADDR_TYPE__ _bits = (__PTRADDR_TYPE__)(bits); \ __PTRADDR_TYPE__ _mask = __static_assert_sensible_low_bits(mask); \ assert(((_bits & _mask) == _bits) && "Bits outside mask used!"); \ _bits; \ }) /* * Get the low bits defined in @p mask from the capability/pointer @p ptr. * @p mask must be a compile-time constant less than 31. * TODO: should we allow non-constant masks? * * @param ptr the uintptr_t that may have low bits sets * @param mask the mask for the low pointer bits to retrieve * @return a size_t containing the the low bits from @p ptr * * Rationale: this function is needed because extracting the low bits using a * bitwise-and operation returns a LHS-derived capability with the offset * field set to LHS.offset & mask. This is almost certainly not what the user * wanted since it will always compare not equal to any integer constant. * For example lots of mutex code uses something like `if ((x & 1) == 1)` to * detect if the lock is currently contented. This comparison always returns * false under CHERI the LHS of the == is a valid capability with offset 3 and * the RHS is an untagged intcap_t with offset 3. * See https://github.com/CTSRD-CHERI/clang/issues/189 */ #define cheri_low_bits_get(ptr, mask) \ __cheri_low_bits_get(ptr, __static_assert_sensible_low_bits(mask)) /* * Bitwise-OR of low bits in a uintptr_t * * @param ptr the uintptr_t that may have low bits sets * @param bits the value to bitwise-or with @p ptr. * @return a uintptr_t that has the low bits @p bits * * @note this function is not strictly required since a plain bitwise or will * generally give the behaviour that is expected from other platforms. * However, we can't really make the warning "-Wcheri-bitwise-operations" * trigger based on of the right hand side expression since it may not be a * compile-time constant. */ #define cheri_low_bits_or(ptr, bits) \ __cheri_low_bits_or(ptr, __runtime_assert_sensible_low_bits( \ bits, __cheri_usable_low_bits_mask)) /* * Set low bits in a uintptr_t * * @param ptr the uintptr_t that may have low bits sets * @param mask the mask for the low pointer bits to be cleared before setting * them to @p bits. * @param bits the value to bitwise-or with @p ptr. * @return a uintptr_t that has the low bits defined in @p mask set to @p bits * * @note this function is not strictly required since a plain bitwise or will * generally give the behaviour that is expected from other platforms. * However, we can't really make the warning "-Wcheri-bitwise-operations" * trigger based on of the right hand side expression since it may not be a * compile-time constant. */ #define cheri_low_bits_set(ptr, mask, bits) \ __cheri_low_bits_or(cheri_low_bits_clear(ptr, mask), \ __runtime_assert_sensible_low_bits(bits, mask)) /* * Clear the bits in @p mask from the capability/pointer @p ptr. Mask must be * a compile-time constant less than 31 * * TODO: should we allow non-constant masks? * * @param ptr the uintptr_t that may have low bits sets * @param mask this is the mask for the low pointer bits, not the mask for * the bits that should remain set. * @return a uintptr_t that has the low bits defined in @p mask set to zeroes * * */ #define cheri_low_bits_clear(ptr, mask) \ __cheri_low_bits_clear(ptr, __static_assert_sensible_low_bits(mask)) #undef __CHERI_ACCESSOR #undef __CHERI_GET #undef __CHERI_SET #undef __cheri_bool #undef __IF_CAPS #endif /* _CHERI_H */
Like before, this code builds fine on Compiler Explorer (latest Morello LLVM):
https://cheri-compiler-explorer.cl.cam.ac.uk/z/5Ys3Pz
I suspect you are not actually enabling Morello, causing cheri.h to #define __capability to nothing. Make sure to pass -march=morello to Clang.
-march=morello
Many thanks, Kevin, by introducing this the above error got removed however it introduced another error which failed to identify few functions as denoted by this below:
ld.lld: error: undefined symbol: malloc >>> referenced by bubblesort_cam.c:123 >>> bubblesort_cam.o:(main) ld.lld: error: undefined symbol: time >>> referenced by bubblesort_cam.c:141 >>> bubblesort_cam.o:(main) >>> referenced by bubblesort_cam.c:141 >>> bubblesort_cam.o:(populate) ld.lld: error: undefined symbol: srand >>> referenced by bubblesort_cam.c:141 >>> bubblesort_cam.o:(main) >>> referenced by bubblesort_cam.c:141 >>> bubblesort_cam.o:(populate) ld.lld: error: undefined symbol: rand >>> referenced by bubblesort_cam.c:144 >>> bubblesort_cam.o:(main) >>> referenced by bubblesort_cam.c:144 >>> bubblesort_cam.o:(main) >>> referenced by bubblesort_cam.c:144 >>> bubblesort_cam.o:(main) >>> referenced 13 more times ld.lld: error: undefined symbol: free >>> referenced by bubblesort_cam.c:135 >>> bubblesort_cam.o:(main)
Sorry for the delay. It appears that the instructions do not link in libc, as LLD is directly used to link. I wonder if linking with Clang and simply specifying the base address instead of a linker script wouldn't be sufficient. That is, replace the call to ld.lld with:
<absolute path of toolchain>/bin/clang -target aarch64-none-elf -o helloworld helloworld.o -Wl,--image-base,0xe0000000
This links fine for me, but I haven't actually tested it. Let me know if it works for you, and if so I'll make sure to get the instructions updated.
Hello Kevin,
Many thanks for the help. I have enclosed the compilation script below which I am following after making the amendments you suggested. Can you please have a look if it makes sense. However, during compilation I am getting the linking error:
ld.lld: error: bubblesort_cam.o: cannot link object files with different EF_AARCH64_CHERI_PURECAPclang: error: ld.lld command failed with exit code 1 (use -v to see invocation)
# COMPILATION SCRIPT # Script based on instructions from: # https://git.morello-project.org/morello/docs/-/blob/morello/release-1.6/standalone-baremetal-readme.rst # First argument = program name (must match basename of .c file) PROGRAM_NAME=$1 PWD=`pwd` TOOLCHAIN=/opt/llvm-project-releases-morello-baremetal-release-1.6 ################################################ # COMPILE PROGRAM #new technique $TOOLCHAIN/bin/clang -target aarch64-none-elf -march=morello+c64 -mabi=purecap -c $PROGRAM_NAME.c -o $PROGRAM_NAME.o -O3 -g $TOOLCHAIN/bin/clang -target aarch64-none-elf -o $PROGRAM_NAME $PROGRAM_NAME.o -Wl,--image-base,0xe0000000 $TOOLCHAIN/bin/llvm-objcopy -O binary $PROGRAM_NAME.elf $PROGRAM_NAME ################################################ # OUTPUT OBJDUMP (disassembly) $TOOLCHAIN/bin/llvm-objdump -sSD $PROGRAM_NAME.elf > $PROGRAM_NAME.objdump
Hi Sanu, almost there :) Your example is not purecap, it is hybrid, since you use capabilities explicitly. You should keep just -march=morello (and no -mabi) in the compiler flags, like before.
Many thanks, Kevin for helping me to reach almost the end I believe. It compiled and created the object file in line number 23 and 24 of the compilation script. However, at line 25 I think it's not able to generate and hence identify the corresponding elf and hence it is throwing the error shown below: I have enclosed the compilation script also.
/opt/llvm-project-releases-morello-baremetal-release-1.6/bin/llvm-objcopy: error: 'bubblesort.elf': No such file or directory
# COMPILATION SCRIPT # Script based on instructions from: # https://git.morello-project.org/morello/docs/-/blob/morello/release-1.6/standalone-baremetal-readme.rst # First argument = program name (must match basename of .c file) PROGRAM_NAME=$1 PWD=`pwd` TOOLCHAIN=/opt/llvm-project-releases-morello-baremetal-release-1.6 ################################################ # COMPILE PROGRAM #new technique $TOOLCHAIN/bin/clang -target aarch64-none-elf -march=morello -c $PROGRAM_NAME.c -o $PROGRAM_NAME.o -O3 -g $TOOLCHAIN/bin/clang -target aarch64-none-elf -o $PROGRAM_NAME $PROGRAM_NAME.o -Wl,--image-base,0xe0000000 $TOOLCHAIN/bin/llvm-objcopy -O binary $PROGRAM_NAME.elf $PROGRAM_NAME # #
llvm-objcopy takes the input as first argument, and optionally the output as second argument (see the man page). Looks like they're swapped.
Many thanks, Kevin. What I noticed is that in line number 24 as compared to my previous code instead of $PROGRAM_NAME, if I replace it with $PROGRAM_NAME.elf after -o, there itself the elf is getting generated. However, is there are any need to run line 25 to generate the elf then..with the llvm-objcopy? Or what is the purpose of executing line 25 please?
# COMPILATION SCRIPT # Script based on instructions from: # https://git.morello-project.org/morello/docs/-/blob/morello/release-1.6/standalone-baremetal-readme.rst # First argument = program name (must match basename of .c file) PROGRAM_NAME=$1 PWD=`pwd` TOOLCHAIN=/opt/llvm-project-releases-morello-baremetal-release-1.6 ################################################ # COMPILE PROGRAM #new technique $TOOLCHAIN/bin/clang -target aarch64-none-elf -march=morello -c $PROGRAM_NAME.c -o $PROGRAM_NAME.o -O3 -g $TOOLCHAIN/bin/clang -target aarch64-none-elf -o $PROGRAM_NAME.elf $PROGRAM_NAME.o -Wl,--image-base,0xe0000000 #$TOOLCHAIN/bin/llvm-objcopy -O binary $PROGRAM_NAME.elf $PROGRAM_NAME # #
Your new version is correct. Using llvm-objcopy is required to generate a raw binary that can be used as non-secure payload, as suggested in the instructions. You cannot directly use an ELF executable as payload.
Many thanks, Kevin for the information. Once I apply the script to generate the elf as shown in line no 25 (script shown below), the elf is generating but I am not sure once I try to view the elf file by right-clicking and viewing through the elf content editor it displays that it is not a valid elf file.
# COMPILATION SCRIPT # Script based on instructions from: # https://git.morello-project.org/morello/docs/-/blob/morello/release-1.6/standalone-baremetal-readme.rst # First argument = program name (must match basename of .c file) PROGRAM_NAME=$1 PWD=`pwd` TOOLCHAIN=/opt/llvm-project-releases-morello-baremetal-release-1.6 ################################################ # COMPILE PROGRAM #new technique $TOOLCHAIN/bin/clang -target aarch64-none-elf -march=morello -c $PROGRAM_NAME.c -o $PROGRAM_NAME.o -O3 -g $TOOLCHAIN/bin/clang -target aarch64-none-elf -o $PROGRAM_NAME $PROGRAM_NAME.o -Wl,--image-base,0xe0000000 $TOOLCHAIN/bin/llvm-objcopy -O binary $PROGRAM_NAME $PROGRAM_NAME.elf # #
ok so is this version correct? changed line 24 with $PROGRAM_NAME.elf after -o and in line 25 used $PROGRAM_NAME.elf as first argument instead of the second as shown below.
_
# COMPILATION SCRIPT # Script based on instructions from: # https://git.morello-project.org/morello/docs/-/blob/morello/release-1.6/standalone-baremetal-readme.rst # First argument = program name (must match basename of .c file) PROGRAM_NAME=$1 PWD=`pwd` TOOLCHAIN=/opt/llvm-project-releases-morello-baremetal-release-1.6 ################################################ # COMPILE PROGRAM #new technique $TOOLCHAIN/bin/clang -target aarch64-none-elf -march=morello -c $PROGRAM_NAME.c -o $PROGRAM_NAME.o -O3 -g $TOOLCHAIN/bin/clang -target aarch64-none-elf -o $PROGRAM_NAME.elf $PROGRAM_NAME.o -Wl,--image-base,0xe0000000 $TOOLCHAIN/bin/llvm-objcopy -O binary $PROGRAM_NAME.elf $PROGRAM_NAME # #