/* * SPDX-License-Identifier: BSD-3-Clause * * Copyright © 2020 Keith Packard * * 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. */ #include #include #include #include #include #include #include #define DATA_VAL 0x600df00d #define DATA_VAL2 0x0a0a0a0a #define OVERALIGN_DATA 128 #define OVERALIGN_BSS 256 #define OVERALIGN_NON_TLS_BSS 512 #define TLS_ALIGN (OVERALIGN_DATA > OVERALIGN_BSS ? OVERALIGN_DATA : OVERALIGN_BSS) NEWLIB_THREAD_LOCAL volatile int32_t data_var = DATA_VAL; NEWLIB_THREAD_LOCAL volatile int32_t bss_var; _Alignas(OVERALIGN_DATA) NEWLIB_THREAD_LOCAL volatile int32_t overaligned_data_var = DATA_VAL2; _Alignas(OVERALIGN_BSS) NEWLIB_THREAD_LOCAL volatile int32_t overaligned_bss_var; _Alignas(OVERALIGN_NON_TLS_BSS) volatile int32_t overaligned_non_tls_bss_var; volatile int32_t *volatile data_addr; volatile int32_t *volatile overaligned_data_addr; volatile int32_t *volatile bss_addr; volatile int32_t *volatile overaligned_bss_addr; #ifdef PICOLIBC_TLS extern char __tdata_start[], __tdata_end[]; extern char __tdata_source[], __tdata_source_end[]; extern char __data_start[], __data_source[]; extern char __non_tls_bss_start[]; static bool inside_tls_region(void *ptr, const void *tls) { return (uintptr_t)ptr >= (uintptr_t)tls && (uintptr_t)ptr < (uintptr_t)tls + _tls_size(); } #define check_inside_tls_region(ptr, tls_start) \ if (!inside_tls_region(__DEVOLATILE(void *, ptr), tls_start)) { \ printf("%s (%p) is not inside TLS region [%p-%p)\n", #ptr, \ ptr, tls_start, (char *)tls_start + _tls_size()); \ result++; \ } #endif static int check_tls(char *where, bool check_addr, void *tls_region) { int result = 0; printf("tls check %s %p %p %p %p\n", where, &data_var, &overaligned_data_var, &bss_var, &overaligned_bss_var); #ifdef PICOLIBC_TLS if (_tls_align() & (OVERALIGN_DATA-1)) { printf("TLS alignment too small for data (need %ld, got %ld)\n", (unsigned long) OVERALIGN_DATA, (unsigned long) _tls_align()); result++; } if (_tls_align() & (OVERALIGN_BSS-1)) { printf("TLS alignment too small for bss (need %ld, got %ld)\n", (unsigned long) OVERALIGN_BSS, (unsigned long) _tls_align()); result++; } #endif if (!__is_aligned(tls_region, TLS_ALIGN)) { printf("TLS data region (%p) is not %ld aligned\n", tls_region, (unsigned long) TLS_ALIGN); result++; } if (!__is_aligned((uintptr_t)&overaligned_data_var, OVERALIGN_DATA)) { printf("overaligned_data_var (%p) is not %ld aligned\n", &overaligned_data_var, (unsigned long) OVERALIGN_DATA); result++; } if (!__is_aligned((uintptr_t)&overaligned_bss_var, OVERALIGN_BSS)) { printf("overaligned_bss_var (%p) is not %ld aligned\n", &overaligned_bss_var, (unsigned long) OVERALIGN_BSS); result++; } if (!__is_aligned((uintptr_t)&overaligned_non_tls_bss_var, OVERALIGN_NON_TLS_BSS)) { printf("overaligned_non_tls_bss_var (%p) is not %ld aligned\n", &overaligned_non_tls_bss_var, (unsigned long) OVERALIGN_NON_TLS_BSS); result++; } if (data_var != DATA_VAL) { printf("%s: initialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n", where, data_var, (int32_t) DATA_VAL); result++; } if (overaligned_data_var != DATA_VAL2) { printf("%s: initialized overaligned thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n", where, overaligned_data_var, (int32_t) DATA_VAL2); result++; } if (bss_var != 0) { printf("%s: uninitialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n", where, bss_var, (int32_t) 0); result++; } if (overaligned_bss_var != 0) { printf("%s: overaligned uninitialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n", where, overaligned_bss_var, (int32_t) 0); result++; } if (overaligned_non_tls_bss_var != 0) { printf("%s: overaligned uninitialized var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n", where, overaligned_non_tls_bss_var, (int32_t) 0); result++; } data_var = ~data_var; if (data_var != ~DATA_VAL) { printf("%s: initialized thread var set to wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n", where, data_var, ~((int32_t) DATA_VAL)); result++; } overaligned_data_var = ~overaligned_data_var; if (overaligned_data_var != ~DATA_VAL2) { printf("%s: overaligned initialized thread var set to wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n", where, overaligned_data_var, ~((int32_t) DATA_VAL2)); result++; } bss_var = ~bss_var; if (bss_var != ~0) { printf("%s: uninitialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n", where, bss_var, ~((int32_t) 0)); result++; } overaligned_bss_var = ~overaligned_bss_var; if (overaligned_bss_var != ~0) { printf("%s: overaligned uninitialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n", where, overaligned_bss_var, ~((int32_t) 0)); result++; } if (check_addr) { if (data_addr == &data_var) { printf("_set_tls didn't affect initialized addr %p\n", data_addr); result++; } if (overaligned_data_addr == &overaligned_data_var) { printf("_set_tls didn't affect initialized addr %p\n", overaligned_data_addr); result++; } if (bss_addr == &bss_var) { printf("_set_tls didn't affect uninitialized addr %p\n", bss_addr); result++; } if (overaligned_bss_addr == &overaligned_bss_var) { printf("_set_tls didn't affect uninitialized addr %p\n", overaligned_bss_addr); result++; } } #ifdef PICOLIBC_TLS check_inside_tls_region(&data_var, tls_region); check_inside_tls_region(&overaligned_data_var, tls_region); check_inside_tls_region(&bss_var, tls_region); check_inside_tls_region(&overaligned_bss_var, tls_region); char *tdata_end = __tdata_start + _tls_size(); /* * We allow for up to OVERALIGN_NON_TLS_BSS -1 bytes of padding after * the end of .tbss and the start of aligned .bss since in theory the * linker could fill this space with smaller .bss variables before the * overaligned value that we define in this file. */ char *non_tls_bss_start_latest = __align_up( tdata_end + OVERALIGN_NON_TLS_BSS, OVERALIGN_NON_TLS_BSS); if (__non_tls_bss_start < tdata_end || __non_tls_bss_start > non_tls_bss_start_latest) { printf("non-TLS bss data doesn't start shortly after TLS data " "(is %p should be between %p and %p)\n", __non_tls_bss_start, tdata_end, non_tls_bss_start_latest); result++; } #endif return result; } #ifdef PICOLIBC_TLS static void hexdump(const void *ptr, int length, const char *hdr) { const unsigned char *cp = ptr; for (int i = 0; i < length; i += 16) { printf("%s%08zx ", hdr, i + (size_t)ptr); for (int j = 0; j < 16; j++) { int offset = i + j; if (offset < length) printf(" %02x", cp[offset]); else printf(" "); } printf("\n"); } } #endif int main(void) { int result = 0; data_addr = &data_var; overaligned_data_addr = &overaligned_data_var; bss_addr = &bss_var; overaligned_bss_addr = &overaligned_bss_var; #ifdef PICOLIBC_TLS printf("TLS region: %p-%p (%zd bytes)\n", __tdata_start, __tdata_start + _tls_size(), _tls_size()); size_t tdata_source_size = __tdata_source_end - __tdata_source; size_t tdata_size = __tdata_end - __tdata_start; if (__tdata_start - __data_start != __tdata_source - __data_source) { printf("ROM/RAM .tdata offset from .data mismatch. " "VMA offset=%zd, LMA offset =%zd." "Linker behaviour changed?\n", __tdata_start - __data_start, __tdata_source - __data_source); } if (tdata_source_size != tdata_size || memcmp(&__tdata_source, &__tdata_start, tdata_size) != 0) { printf("TLS data in RAM does not match ROM\n"); hexdump(__tdata_source, tdata_source_size, "ROM:"); hexdump(__tdata_start, tdata_size, "RAM:"); result++; } result += check_tls("pre-defined", false, __tdata_start); #else result += check_tls("pre-defined", false, NULL); #endif #ifdef _HAVE_PICOLIBC_TLS_API size_t tls_align = _tls_align(); size_t tls_size = _tls_size(); void *tls = aligned_alloc(tls_align, tls_size); if (tls) { /* * Fill the region with data to make sure even bss * gets correctly initialized */ memset(tls, 0x55, tls_size); _init_tls(tls); _set_tls(tls); if (memcmp(tls, &__tdata_source, tdata_size) != 0) { printf("New TLS data in RAM does not match ROM\n"); hexdump(&__tdata_source, tdata_source_size, "ROM:"); hexdump(tls, tdata_size, "RAM:"); result++; } result += check_tls("allocated", true, tls); } else { printf("TLS allocation failed\n"); result = 1; } #endif printf("tls test result %d\n", result); return result; }