diff --git a/components/libc/compilers/common/clocale.c b/components/libc/compilers/common/clocale.c new file mode 100644 index 000000000000..1636884e9978 --- /dev/null +++ b/components/libc/compilers/common/clocale.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Kernel-mode musl is linked with native RT-Thread threads, not musl's Linux + * pthread runtime. A few musl locale functions reach the Linux pthread + * locale object through the architecture thread pointer. Supply the fixed + * UTF-8/C-locale subset used by kernel applications until a complete musl + * pthread locale runtime is available. + */ + +#include + +#if defined(RT_USING_MUSLLIBC) && !defined(RT_USING_SMART) + +#include +#include +#include + +char *nl_langinfo(nl_item item) +{ + static char empty[] = ""; + + if (item == CODESET) + { + return (char *)"UTF-8"; + } + + return empty; +} + +wint_t btowc(int character) +{ + if (character == EOF) + { + return WEOF; + } + + /* A single UTF-8 byte is a character only in the ASCII range. */ + if ((unsigned int)character > 0x7f) + { + return WEOF; + } + + return (wint_t)character; +} + +#endif /* RT_USING_MUSLLIBC && !RT_USING_SMART */ diff --git a/libcpu/aarch64/common/mp/context_gcc.S b/libcpu/aarch64/common/mp/context_gcc.S index 96743391bf25..477dc0e4d089 100644 --- a/libcpu/aarch64/common/mp/context_gcc.S +++ b/libcpu/aarch64/common/mp/context_gcc.S @@ -33,6 +33,14 @@ #endif /* ARCH_USING_HW_THREAD_SELF */ .endm +.macro update_tls, srcx +#ifdef RT_USING_CPLUSPLUS + mov x0, \srcx + bl rt_hw_thread_tls_base + msr tpidr_el0, x0 +#endif /* RT_USING_CPLUSPLUS */ +.endm + /* * void rt_hw_context_switch_to(rt_uint3 to, struct rt_thread *to_thread); * X0 --> to (thread stack) @@ -41,10 +49,11 @@ rt_hw_context_switch_to: ldr x0, [x0] mov sp, x0 - update_tidr x1 /* reserved to_thread */ mov x19, x1 + update_tidr x19 + update_tls x19 mov x0, x19 bl rt_cpus_lock_status_restore @@ -69,10 +78,11 @@ rt_hw_context_switch: str x3, [x0] // store sp in preempted tasks TCB ldr x0, [x1] // get new task stack pointer mov sp, x0 - update_tidr x2 /* backup thread self */ mov x19, x2 + update_tidr x19 + update_tls x19 mov x0, x19 bl rt_cpus_lock_status_restore @@ -128,6 +138,7 @@ rt_hw_context_switch_interrupt: ldr x0, [TO_SPP] mov sp, x0 update_tidr TO_TCB + update_tls TO_TCB mov x0, TO_TCB bl rt_cpus_lock_status_restore diff --git a/libcpu/aarch64/common/stack.c b/libcpu/aarch64/common/stack.c index e3e8ccbc9dd6..a88f37ddc56e 100644 --- a/libcpu/aarch64/common/stack.c +++ b/libcpu/aarch64/common/stack.c @@ -16,6 +16,40 @@ #define INITIAL_SPSR_EL1 (PSTATE_EL1 | SP_ELx) +#ifdef RT_USING_CPLUSPLUS +extern char __tdata_start, __tdata_end; +extern char __tbss_start, __tbss_end; + +static rt_size_t rt_hw_thread_tls_size(void) +{ + /* Preserve any linker-inserted alignment gap between .tdata and .tbss. */ + rt_size_t size = &__tbss_end - &__tdata_start; + + return RT_ALIGN(size, 16); +} + +static void rt_hw_thread_tls_init(void *tls) +{ + rt_size_t tdata_size = &__tdata_end - &__tdata_start; + rt_size_t tls_size = rt_hw_thread_tls_size(); + + rt_memcpy(tls, &__tdata_start, tdata_size); + rt_memset((rt_uint8_t *)tls + tdata_size, 0, tls_size - tdata_size); +} + +/* + * rt_hw_stack_init() reserves TLS below the initial stack pointer. Derive + * the same aligned address from the TCB for every subsequent context switch. + */ +void *rt_hw_thread_tls_base(struct rt_thread *thread) +{ + rt_ubase_t top = (rt_ubase_t)thread->stack_addr + thread->stack_size - + sizeof(rt_ubase_t); + + return (void *)RT_ALIGN_DOWN(top - rt_hw_thread_tls_size(), 16); +} +#endif /* RT_USING_CPLUSPLUS */ + /** * This function will initialize thread stack * @@ -31,6 +65,12 @@ rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter, { rt_ubase_t *stk; +#ifdef RT_USING_CPLUSPLUS + stack_addr = (rt_uint8_t *)RT_ALIGN_DOWN( + (rt_ubase_t)stack_addr - rt_hw_thread_tls_size(), 16); + rt_hw_thread_tls_init(stack_addr); +#endif + /* The AAPCS64 requires 128-bit (16 byte) stack alignment */ stk = (rt_ubase_t*)RT_ALIGN_DOWN((rt_ubase_t)stack_addr, 16); diff --git a/libcpu/aarch64/link.lds.S b/libcpu/aarch64/link.lds.S index a09842ca7db9..f4cda6969550 100644 --- a/libcpu/aarch64/link.lds.S +++ b/libcpu/aarch64/link.lds.S @@ -122,6 +122,30 @@ SECTIONS *(.rel.local) } :data +#ifdef RT_USING_CPLUSPLUS + /* + * Keep the static TLS image so the kernel can instantiate it for each + * RT-Thread thread. Musl and Qt both use TPIDR_EL0 on AArch64. + */ + . = ALIGN(16); + .tdata : + { + PROVIDE(__tdata_start = .); + *(.tdata) + *(.tdata.*) + PROVIDE(__tdata_end = .); + } :data + + . = ALIGN(16); + .tbss : + { + PROVIDE(__tbss_start = .); + *(.tbss) + *(.tbss.*) + PROVIDE(__tbss_end = .); + } +#endif /* RT_USING_CPLUSPLUS */ + . = ALIGN(8); .ctors : { diff --git a/libcpu/risc-v/common/stack.c b/libcpu/risc-v/common/stack.c index d7d428b745d3..dd786cd12ad5 100644 --- a/libcpu/risc-v/common/stack.c +++ b/libcpu/risc-v/common/stack.c @@ -21,6 +21,30 @@ #include #endif +#if defined(RT_USING_CPLUSPLUS) && defined(ARCH_MM_MMU) && !defined(ARCH_RISCV_M_MODE) +extern char __tdata_start, __tdata_end; +extern char __tbss_start, __tbss_end; + +static rt_size_t riscv_thread_tls_size(void) +{ + /* .tbss can have stricter alignment than .tdata. Its linker-inserted + * gap is part of the ABI-visible TLS layout and must be retained. */ + rt_size_t size = &__tbss_end - &__tdata_start; + + return RT_ALIGN(size, 16); +} + +static void riscv_thread_tls_init(void *tls) +{ + rt_size_t tdata_size = &__tdata_end - &__tdata_start; + rt_size_t tls_size = riscv_thread_tls_size(); + + rt_memcpy(tls, &__tdata_start, tdata_size); + rt_memset((rt_uint8_t *)tls + tdata_size, 0, tls_size - tdata_size); +} + +#endif /* RT_USING_CPLUSPLUS && ARCH_MM_MMU && !ARCH_RISCV_M_MODE */ + /** * This function will initialize thread stack, we assuming * when scheduler restore this new thread, context will restore @@ -41,6 +65,12 @@ rt_uint8_t *rt_hw_stack_init(void *tentry, { rt_ubase_t *sp; rt_hw_switch_frame_t frame; +#if defined(RT_USING_CPLUSPLUS) && defined(ARCH_MM_MMU) && !defined(ARCH_RISCV_M_MODE) + void *tls = (void *)RT_ALIGN_DOWN((rt_ubase_t)stack_addr - riscv_thread_tls_size(), 16); + + riscv_thread_tls_init(tls); + stack_addr = tls; +#endif /* We use a strict alignment requirement for Q extension */ sp = (rt_ubase_t *)RT_ALIGN_DOWN((rt_ubase_t)stack_addr, 16); @@ -56,7 +86,10 @@ rt_uint8_t *rt_hw_stack_init(void *tentry, frame->regs[RT_HW_SWITCH_CONTEXT_RA] = (rt_ubase_t)_thread_start; frame->regs[RT_HW_SWITCH_CONTEXT_XSTATUS] = K_XSTATUS_DEFAULT; - /* Bare SMP keeps the current hart's tp across context switches. */ +#if defined(RT_USING_CPLUSPLUS) && defined(ARCH_MM_MMU) && !defined(ARCH_RISCV_M_MODE) + frame->regs[RT_HW_SWITCH_CONTEXT_TP] = (rt_ubase_t)tls; +#endif + /* Non-MMU and M-mode configurations keep their existing tp policy. */ return (void *)frame; } diff --git a/libcpu/risc-v/link.lds.S b/libcpu/risc-v/link.lds.S index 67d49bf0e4a4..ff60bbf7450a 100644 --- a/libcpu/risc-v/link.lds.S +++ b/libcpu/risc-v/link.lds.S @@ -139,6 +139,35 @@ SECTIONS *(.rel.local) } :data +#if defined(RT_USING_CPLUSPLUS) && defined(ARCH_MM_MMU) && !defined(ARCH_RISCV_M_MODE) + /* + * Keep the static TLS image in the kernel image. On RISC-V, tp points + * at the beginning of the current thread's TLS block. stack.c makes a + * private copy for every RT-Thread thread before its initial context is + * built. + * + * Only S-mode MMU configurations opt in. Other RISC-V libcpu targets + * retain their existing tp policy. + */ + . = ALIGN(16); + .tdata : + { + PROVIDE(__tdata_start = .); + *(.tdata) + *(.tdata.*) + PROVIDE(__tdata_end = .); + } :data + + . = ALIGN(16); + .tbss (NOLOAD) : + { + PROVIDE(__tbss_start = .); + *(.tbss) + *(.tbss.*) + PROVIDE(__tbss_end = .); + } +#endif /* RT_USING_CPLUSPLUS && ARCH_MM_MMU && !ARCH_RISCV_M_MODE */ + . = ALIGN(RISCV_SZPTR); .ctors : {