diff --git a/Makefile b/Makefile index 69467c08..40738c9a 100644 --- a/Makefile +++ b/Makefile @@ -15,9 +15,8 @@ include ../phoenix-rtos-build/Makefile.common SYSROOT := $(shell $(CC) $(CFLAGS) -print-sysroot) MULTILIB_DIR := $(shell $(CC) $(CFLAGS) -print-multi-directory) LIBC_INSTALL_DIR := $(SYSROOT)/lib/$(MULTILIB_DIR) -LIBC_INSTALL_NAMES := libc.a libm.a libg.a libpthread.a libubsan.a +LIBC_INSTALL_NAMES_A := libc.a libm.a libg.a libpthread.a libubsan.a HEADERS_INSTALL_DIR := $(SYSROOT)/usr/include -LIBNAME := libphoenix.a ifeq (,$(filter-out /,$(SYSROOT))) $(error SYSROOT is not supported by the toolchain. Use cross-toolchain to compile.) @@ -25,14 +24,10 @@ endif CFLAGS += -Iinclude -fno-builtin-malloc - OBJS := # crt0.o should have all necessary initialization + call to main() CRT0_OBJS := $(PREFIX_O)crt0-common.o -LIB_TARGETS := $(PREFIX_A)libphoenix.a $(PREFIX_A)crt0.o - -all: $(LIB_TARGETS) ifneq (,$(findstring arm,$(TARGET_SUFF))) include arch/arm/Makefile @@ -54,6 +49,7 @@ include posix/Makefile include phoenix/Makefile include pthread/Makefile include regex/Makefile +include rtld/Makefile include signal/Makefile include stdio/Makefile include stdlib/Makefile @@ -66,7 +62,21 @@ include unistd/Makefile include wchar/Makefile include ubsan/Makefile -#include test/Makefile + +LIB_TARGETS := $(PREFIX_A)libphoenix.a $(PREFIX_A)crt0.o +INSTALL_TARGETS := install-headers install-libs + +ifeq ($(LIBPHOENIX_PIC), y) +CFLAGS += $(TARGET_PIC_FLAG) +ifeq ($(LIBPHOENIX_SHARED), y) +CPPFLAGS += -DLIBPHOENIX_SHARED +include shared.mk +endif +endif + + +all: $(LIB_TARGETS) + $(PREFIX_A)libphoenix.a: $(OBJS) $(ARCH) @@ -77,7 +87,8 @@ $(PREFIX_A)crt0.o: $(CRT0_OBJS) SRCHEADERS := $(shell find include -name \*.h) -install: install-headers install-libs + +install: $(INSTALL_TARGETS) install-headers: $(SRCHEADERS) @echo INSTALL "$(HEADERS_INSTALL_DIR)/*"; \ @@ -86,16 +97,16 @@ install-headers: $(SRCHEADERS) # TODO: remove `rm crt0.o` when we will be sure it's not a symlink to libphoenix.a anymore install-libs: $(LIB_TARGETS) - @echo INSTALL "$(LIBC_INSTALL_DIR)/*"; \ + $(SIL)echo INSTALL "$(LIBC_INSTALL_DIR)/*"; \ mkdir -p "$(LIBC_INSTALL_DIR)"; \ rm -rf "$(LIBC_INSTALL_DIR)/crt0.o"; \ cp -a $^ "$(LIBC_INSTALL_DIR)"; \ (cd $(LIBC_INSTALL_DIR) && \ - for lib in $(LIBC_INSTALL_NAMES); do \ + for lib in $(LIBC_INSTALL_NAMES_A); do \ if [ ! -e "$$lib" ]; then \ ln -sf "libphoenix.a" "$$lib"; \ fi \ - done) + done) .PHONY: clean install install-headers install-libs clean: diff --git a/arch/arm/v7a/Makefile b/arch/arm/v7a/Makefile index ae752782..42ae98f8 100644 --- a/arch/arm/v7a/Makefile +++ b/arch/arm/v7a/Makefile @@ -5,4 +5,4 @@ # Author: Pawel Pisarczyk # -OBJS += $(addprefix $(PREFIX_O)arch/arm/v7a/, syscalls.o reboot.o) +OBJS += $(addprefix $(PREFIX_O)arch/arm/v7a/, syscalls.o reboot.o tls.o) diff --git a/arch/arm/v7a/tls.S b/arch/arm/v7a/tls.S new file mode 100644 index 00000000..82f911d1 --- /dev/null +++ b/arch/arm/v7a/tls.S @@ -0,0 +1,49 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * tls access function + * + * Copyright 2023 Phoenix Systems + * Author: Hubert Badocha + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + + /* NOTE: + * When libphoenix is compiled as a PIC, even when linked into a static NOPIC binary, + * compiler generates accesses to TLS as calls to this function. + * This functions is simple version just for static binaries purposes, + * in dynamic binaries dynamic linker handles TLS accesses. */ + + +/* typedef struct { + * unsigned long ti_moduleid; + * unsigned long ti_tlsoffset; + * } TLS_index; + * + * void *__tls_get_addr(TLS_index *ti) */ +.thumb +.syntax unified + +.text + +.thumb_func +/* Declared as weak to be overwritten by dynamic linker */ +.weak __tls_get_addr; +.type __tls_get_addr, %function; +.align 2 +__tls_get_addr: + /* Access ti_tlsoffset. */ + ldr r0, [r0, #4] + /* Get thread pointer. */ + mrc p15, 0, r1, cr13, cr0, 3 + /* GCC expects that TLS block has 8 byte TCB pointer at the begging. */ + add r1, r1, #8 + add r0, r0, r1 + bx lr +.size __tls_get_addr, .-__tls_get_addr diff --git a/arch/ia32/jmp.S b/arch/ia32/jmp.S index 13351d97..9efc42b2 100644 --- a/arch/ia32/jmp.S +++ b/arch/ia32/jmp.S @@ -5,127 +5,149 @@ * * setjmp, longjmp * - * Copyright 2018-2019 Phoenix Systems - * Author: Jan Sikorski, Andrzej Glowinski + * Copyright 2018-2019, 2023 Phoenix Systems + * Author: Jan Sikorski, Andrzej Glowinski, Hubert Badocha * * This file is part of Phoenix-RTOS. * * %LICENSE% */ + /* jmp_buf layout: + * esi + * ebx + * edi + * ebp + * esp + 4 + * return address + * signal mask indicator (0 - not saved, otherwise saved(GOT addr on PIC)) + * signal mask (if saved) + */ + #define __ASSEMBLY__ .text -.globl _setjmp -.type _setjmp, %function -_setjmp: + +.macro SETJMP_SAVE_REGS /* Store registers into jmpbuf */ - movl 4(%esp), %edx - movl %ebx, (%edx) - movl %esi, 4(%edx) - movl %edi, 8(%edx) - movl %ebp, 12(%edx) + leal 4(%esp), %ecx + movl (%ecx), %edx + movl %esi, (%edx) + movl %ebx, 4(%edx) + movl %edi, 8(%edx) + movl %ebp, 12(%edx) /* Store stack pointer pointing before return address */ - leal 4(%esp), %ecx - movl %ecx, 16(%edx) - /* Store jump address */ - leal (1f), %ecx - movl %ecx, 20(%edx) + movl %ecx, 16(%edx) /* Store return address */ - movl (%esp), %ecx - movl %ecx, 24(%edx) - /* Store result */ - movl $0, %eax - ret -1: - /* Jump point. Push return address to stack */ - pushl 24(%edx) + movl (%esp), %ecx + movl %ecx, 20(%edx) +.endm + + +.globl _setjmp +.type _setjmp, %function +.align 4 +_setjmp: + SETJMP_SAVE_REGS + /* Set no mask indicator */ + movl $0, 24(%edx) + /* Store result (0) */ + xorl %eax, %eax ret .size _setjmp, .-_setjmp .globl setjmp .type setjmp, %function +.align 4 setjmp: - /* Get signal mask */ - pushl $0 - pushl $0 - call signalMask - addl $8, %esp - /* Store registers into jmpbuf */ - movl 4(%esp), %edx - movl %ebx, (%edx) - movl %esi, 4(%edx) - movl %edi, 8(%edx) - movl %ebp, 12(%edx) - /* Store stack pointer pointing before return address */ - leal 4(%esp), %ecx - movl %ecx, 16(%edx) - /* Store jump address */ - leal (1f), %ecx - movl %ecx, 20(%edx) - /* Store return address */ - movl (%esp), %ecx - movl %ecx, 24(%edx) - /* Store signal mask */ - movl %eax, 28(%edx) - /* Store result */ - movl $0, %eax - ret -1: - /* Jump point. Push return address to stack */ - pushl 24(%edx) - /* Store longjmp return value */ - pushl %eax - /* Restore signal mask */ - movl 28(%edx), %ecx - pushl $0xffffffff - pushl %ecx + SETJMP_SAVE_REGS + /* Get signal mask */ +#if __pic__ == 0 + pushl $0 + pushl $0 call signalMask - addl $8, %esp - /* Restore longjmp return value */ - popl %eax + addl $8, %esp + /* Store signal mask indicator */ + movl $1, 24(%edx) +#else + pushl %ebx + + call .l1 +.l1: + popl %ebx + addl $_GLOBAL_OFFSET_TABLE_+(.-.l1), %ebx + + pushl $0 + pushl $0 + call *signalMask@got(%ebx) + addl $8, %esp + /* Save GOT address as an indicator as it will not be 0 */ + movl %ebx, 24(%edx) + + popl %ebx +#endif + /* Store signal mask */ + movl %eax, 28(%edx) + /* Store result (0) */ + xorl %eax, %eax ret .size setjmp, .-setjmp .globl _longjmp .type _longjmp, %function +.align 4 _longjmp: - movl 4(%esp), %edx - movl 8(%esp), %eax - test %eax, %eax - jnz 1f - inc %eax + /* Since esi will be restored later it can be used as a scratch register */ + /* Using it allows to safely call signalMask */ + movl 4(%esp), %esi + /* Check mask indicator */ + /* Since ebx will be restored later it can be used as a scratch register */ + /* ebx as the mask indicator is a GOT address in PIC */ + movl 24(%esi), %ebx + test %ebx, %ebx + jz 1f + /* Restore signal mask */ + pushl $0xffffffff + pushl 28(%esi) + +#if __pic__ == 0 + call signalMask +#else + call *signalMask@got(%ebx) +#endif + + addl $8, %esp 1: + /* Get second argument */ + movl 8(%esp), %eax + /* _longjmp shall return 1 if 0 is provided as the second argument */ + movl $1, %ecx + test %eax, %eax + cmove %ecx, %eax /* Restore registers from jmpbuf */ - movl (%edx), %ebx - movl 4(%edx), %esi - movl 8(%edx), %edi - movl 12(%edx), %ebp - movl 16(%edx), %esp - /* Restore jump address */ - movl 20(%edx), %ecx - /* Jump */ - jmp *%ecx + movl 4(%esi), %ebx + movl 8(%esi), %edi + movl 12(%esi), %ebp + movl 16(%esi), %esp + /* Jump point. Push return address to stack */ + pushl 20(%esi) + /* At last restore esi */ + movl (%esi), %esi + ret .size _longjmp, .-_longjmp .globl sigsetjmp .type sigsetjmp, %function sigsetjmp: - /* Modify stack frame to skip this function when returning from setjmp */ - movl 8(%esp), %edx - movl 4(%esp), %ecx - movl (%esp), %eax - addl $8, %esp - pushl %ecx - pushl %eax - +.align 4 + movl 8(%esp), %edx /* Call proper setjmp */ - test %edx, %edx - jne setjmp - jmp _setjmp + test %edx, %edx + jz _setjmp + jmp setjmp .size sigsetjmp, .-sigsetjmp diff --git a/arch/ia32/signal.S b/arch/ia32/signal.S index 38131c99..7f60e0f0 100644 --- a/arch/ia32/signal.S +++ b/arch/ia32/signal.S @@ -5,8 +5,8 @@ * * Signal trampoline (ia32) * - * Copyright 2019 Phoenix Systems - * Author: Jan Sikorski + * Copyright 2019, 2023 Phoenix Systems + * Author: Jan Sikorski, Hubert Badocha * * This file is part of Phoenix-RTOS. * @@ -19,14 +19,29 @@ .globl _signal_trampoline .type _signal_trampoline, %function +.align 4 _signal_trampoline: /* Signal number on stack */ - call _signal_handler +#if __pic__ == 0 + call _signal_handler +#else + /* ebx will be restored by sigreturn */ + call .l1 +.l1: + popl %ebx + addl $_GLOBAL_OFFSET_TABLE_+(.-.l1), %ebx - addl $4, %esp + call *_signal_handler@got(%ebx) +#endif + + addl $4, %esp /* Put old mask on stack */ - push %eax + pushl %eax /* cpu context *, eip, esp on stack */ - call sigreturn +#if __pic__ == 0 + call sigreturn +#else + call *sigreturn@got(%ebx) +#endif .size _signal_trampoline, .-_signal_trampoline diff --git a/arch/riscv64/Makefile b/arch/riscv64/Makefile index 5674f637..cdd606cc 100644 --- a/arch/riscv64/Makefile +++ b/arch/riscv64/Makefile @@ -5,5 +5,5 @@ # Author: Pawel Pisarczyk # -OBJS += $(addprefix $(PREFIX_O)arch/riscv64/, syscalls.o string.o signal.o reboot.o jmp.o) +OBJS += $(addprefix $(PREFIX_O)arch/riscv64/, syscalls.o string.o signal.o reboot.o jmp.o tls.o) CRT0_OBJS += $(addprefix $(PREFIX_O)arch/riscv64/, crt0.o) diff --git a/arch/riscv64/jmp.S b/arch/riscv64/jmp.S index 1537a4eb..b2b96cfb 100644 --- a/arch/riscv64/jmp.S +++ b/arch/riscv64/jmp.S @@ -30,7 +30,11 @@ .type _setjmp, %function _setjmp: mv a1, zero +#if __pic__ == 0 j sigsetjmp +#else + tail sigsetjmp +#endif .size _setjmp, .-_setjmp @@ -38,7 +42,11 @@ _setjmp: .type setjmp, %function setjmp: li a1, 1 +#if __pic__ == 0 j sigsetjmp +#else + tail sigsetjmp +#endif .size setjmp, .-setjmp diff --git a/arch/riscv64/syscalls.S b/arch/riscv64/syscalls.S index c518695e..99a5c621 100644 --- a/arch/riscv64/syscalls.S +++ b/arch/riscv64/syscalls.S @@ -54,7 +54,11 @@ sym: \ .globl vfork; .type vfork, %function; vfork: +#if __pic__ == 0 j vforksvc +#else + tail vforksvc +#endif .size vfork, .-vfork diff --git a/arch/riscv64/tls.S b/arch/riscv64/tls.S new file mode 100644 index 00000000..a85b5ee5 --- /dev/null +++ b/arch/riscv64/tls.S @@ -0,0 +1,41 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * tls access function + * + * Copyright 2023 Phoenix Systems + * Author: Hubert Badocha + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +/* NOTE: + * When libphoenix is compiled as a PIC, even when linked into a static NOPIC binary, + * compiler generates accesses to TLS as calls to this function. + * This functions is simple version just for static binaries purposes, + * in dynamic binaries dynamic linker handles TLS accesses. */ + + +/* typedef struct { + * unsigned long ti_moduleid; + * unsigned long ti_tlsoffset; + * } TLS_index; + * + * void *__tls_get_addr(TLS_index *ti) */ + +/* Declared as weak to be overwritten by dynamic linker */ +.weak __tls_get_addr +.type __tls_get_addr, %function +__tls_get_addr: + /* Access ti_tlsoffset. */ + ld a0, 8(a0) + /* On RISC-V Dynamic Thread Vector pointer points at TLS block start + 0x800 */ + li t0, 0x800 + add a0, a0, t0 + add a0, a0, tp + ret +.size __tls_get_addr, .-__tls_get_addr diff --git a/arch/sparcv8leon/jmp.S b/arch/sparcv8leon/jmp.S index 80d9481c..69bcfe83 100644 --- a/arch/sparcv8leon/jmp.S +++ b/arch/sparcv8leon/jmp.S @@ -112,8 +112,19 @@ _longjmp: .type sigsetjmp, #function sigsetjmp: cmp %o1, 0 - bne setjmp + bne 1f nop +#if __pic__ == 0 ba _setjmp +#else + jmp _setjmp +#endif + nop +1: +#if __pic__ == 0 + ba setjmp +#else + jmp setjmp +#endif nop .size sigsetjmp, .-sigsetjmp diff --git a/arch/sparcv8leon/syscalls.S b/arch/sparcv8leon/syscalls.S index 136dff11..67d61c27 100644 --- a/arch/sparcv8leon/syscalls.S +++ b/arch/sparcv8leon/syscalls.S @@ -35,11 +35,7 @@ sym: \ .globl vfork -.type vfork, #function -vfork: - ba vforksvc - nop -.size vfork, .-vfork +vfork = vforksvc #define SYSCALLS_LIBC(name) \ diff --git a/crt0-common.c b/crt0-common.c index 7842f9e6..61d9af45 100644 --- a/crt0-common.c +++ b/crt0-common.c @@ -72,6 +72,8 @@ extern int main(int argc, char **argv); char **environ; const char *argv_progname; +/* TLS cannot be used before initialized by dynamic linker in dynamically linked binaries. */ +int can_use_tls; __attribute__((noreturn)) void _startc(void (*cleanup)(void), int argc, char **argv, char **env) @@ -81,6 +83,9 @@ __attribute__((noreturn)) void _startc(void (*cleanup)(void), int argc, char **a _libc_init(); + /* At this point TLS is setup, as dynamic linker calls _startc after dealing with TLS. */ + can_use_tls = 1; + /* cleanup function is not NULL when the dynamic linker is used */ if (cleanup != NULL) { atexit(cleanup); diff --git a/errno/errno.c b/errno/errno.c index fccd3468..28345540 100644 --- a/errno/errno.c +++ b/errno/errno.c @@ -23,6 +23,9 @@ static __thread int __errno_tls; +extern int can_use_tls; +static int errno_single = 0; + #else static int errno_global; @@ -58,7 +61,7 @@ int *__errno_location(void) return &errno_global; #else - return &__errno_tls; + return (can_use_tls == 0) ? &errno_single : &__errno_tls; #endif } diff --git a/include/arch/armv7a/arch.h b/include/arch/armv7a/arch.h index c35b31c0..44969c87 100644 --- a/include/arch/armv7a/arch.h +++ b/include/arch/armv7a/arch.h @@ -46,5 +46,6 @@ #define SIZE_PAGE _Pragma("GCC warning \"'SIZE_PAGE' is deprecated. Use _PAGE_SIZE from arch.h or PAGE_SIZE from limits.h (POSIX only)\"") _PAGE_SIZE #define __LIBPHOENIX_ARCH_TLS_SUPPORTED +#define __LIBPHOENIX_ARCH_HAVE__TLS_GET_ADDR #endif diff --git a/include/arch/riscv64/arch.h b/include/arch/riscv64/arch.h index 98e70814..daed33f4 100644 --- a/include/arch/riscv64/arch.h +++ b/include/arch/riscv64/arch.h @@ -52,5 +52,6 @@ static inline float __ieee754_sqrtf(float x) #define SIZE_PAGE _Pragma("GCC warning \"'SIZE_PAGE' is deprecated. Use _PAGE_SIZE from arch.h or PAGE_SIZE from limits.h (POSIX only)\"") _PAGE_SIZE #define __LIBPHOENIX_ARCH_TLS_SUPPORTED +#define __LIBPHOENIX_ARCH_HAVE__TLS_GET_ADDR #endif diff --git a/include/dlfcn.h b/include/dlfcn.h new file mode 100644 index 00000000..808efd31 --- /dev/null +++ b/include/dlfcn.h @@ -0,0 +1,51 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * dlfcn.h + * + * Copyright 2024 Phoenix Systems + * Author: Hubert Badocha + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _LIBPHOENIX_DLFCN_H_ +#define _LIBPHOENIX_DLFCN_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define RTLD_LAZY 1 +#define RTLD_NOW 2 +#define RTLD_GLOBAL 0x100 +#define RTLD_LOCAL 0x200 + + +typedef struct { + const char *dli_fname; /* Pathname of mapped object file. */ + void *dli_fbase; /* Base of mapped address range. */ + const char *dli_sname; /* Symbol name or null pointer. */ + void *dli_saddr; /* Symbol address or null pointer. */ +} Dl_info_t; + + +int dladdr(const void *restrict, Dl_info_t *restrict); +int dlclose(void *); +char *dlerror(void); +void *dlopen(const char *, int); +void *dlsym(void *restrict, const char *restrict); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/sys/threads.h b/include/sys/threads.h index 04d47197..49989481 100644 --- a/include/sys/threads.h +++ b/include/sys/threads.h @@ -70,11 +70,16 @@ extern int spawnSyspage(const char *imap, const char *dmap, const char *name, ch extern int threadJoin(int tid, time_t timeout); +extern int beginthreadexsvc(void (*start)(void *), unsigned int priority, void *stack, unsigned int stacksz, void *arg, handle_t *id); + + +__attribute__((noreturn)) extern void endthreadsvc(void); + + extern int beginthreadex(void (*start)(void *), unsigned int priority, void *stack, unsigned int stacksz, void *arg, handle_t *id); -__attribute__((noreturn)) -extern void endthread(void); +__attribute__((noreturn)) extern void endthread(void); static inline int beginthread(void (*start)(void *), unsigned int priority, void *stack, unsigned int stacksz, void *arg) diff --git a/misc/init.c b/misc/init.c index eba32b43..4daa4255 100644 --- a/misc/init.c +++ b/misc/init.c @@ -24,8 +24,21 @@ extern void _init_array(void); extern void _pthread_init(void); -void _libc_init(void) +static int _libc_initialized; + + +/* _libc_init is called twice once explicitly in crt0 and once during constructor handling. + * If libc is linked statically first it is called explicitly and then during constructor handling. + * If libc is linked dynamically first it is called by the constructor handling mechanism in dynamic linker, + * to allow constructors from other objects to call libc functions. + */ +__attribute__((constructor)) void _libc_init(void) { + if (_libc_initialized != 0) { + return; + } + _libc_initialized = 1; + _atexit_init(); _errno_init(); _malloc_init(); diff --git a/rtld/Makefile b/rtld/Makefile new file mode 100644 index 00000000..719e9c25 --- /dev/null +++ b/rtld/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for libphoenix/rtld +# +# Copyright 2024 Phoenix Systems +# Author: Hubert Badocha +# + +OBJS += $(addprefix $(PREFIX_O)rtld/, stubs.o) diff --git a/rtld/stubs.c b/rtld/stubs.c new file mode 100644 index 00000000..96a017bb --- /dev/null +++ b/rtld/stubs.c @@ -0,0 +1,70 @@ +/* + * Phoenix-RTOS + * + * libphoenix/rtld + * + * stubs.c + * + * Copyright 2024 Phoenix Systems + * Author: Hubert Badocha + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#include +#include +#include + + +/* As dynamic linker is not added as a NEEDED dependency in libphoenix, stub implementation are required. + * They are overridden by dynamic linkers functions in runtime. */ + +__attribute__((weak)) int dladdr(const void *restrict a, Dl_info_t *restrict b) +{ + return 0; +} + + +__attribute__((weak)) int dlclose(void *a) +{ + return -1; +} + + +__attribute__((weak)) char *dlerror(void) +{ + return "dlfcn: not supported in statically linked binaries"; +} + + +__attribute__((weak)) void *dlopen(const char *a, int b) +{ + return NULL; +} + + +__attribute__((weak)) void *dlsym(void *restrict a, const char *restrict b) +{ + return NULL; +} + + +/* Allow override on platforms requiring __tls_get_addr in PIC compiled libraries(eg. RISC-V, ARM). */ +#ifndef __LIBPHOENIX_ARCH_HAVE__TLS_GET_ADDR + +/* Stub __tls_get_addr implementations. */ +__attribute__((weak)) void *__tls_get_addr(void *d) +{ + abort(); +} + + +__attribute__((weak)) void *___tls_get_addr(void *d) +{ + abort(); +} + +#endif diff --git a/shared.mk b/shared.mk new file mode 100644 index 00000000..3a44cb30 --- /dev/null +++ b/shared.mk @@ -0,0 +1,94 @@ +# +# Makefile for compiling libphoenix as shared lib +# +# Copyright 2024 Phoenix Systems +# +# %LICENSE% +# + +# FIXME: In shared versions each library should only have needed parts of libphoenix. +LIBC_INSTALL_NAMES_SO := libc.so libm.so libpthread.so libubsan.so + +# TODO: automatically get the version +VERSION=3.2.1 +MAJOR_VERSION=$(word 1, $(subst ., ,$(VERSION))) + +LIBNAME=libphoenix.so +SONAME=$(LIBNAME).$(MAJOR_VERSION) +REALNAME=$(LIBNAME).$(VERSION) + +LIB_TARGETS += $(PREFIX_SO)$(REALNAME) +INSTALL_TARGETS += install-shared + + +SHARED_LDFLAGS := -nolibc +SHARED_LDFLAGS += -z initfirst # Tell dynamic linker to init libphoenix first. +SHARED_LDFLAGS += -static-libgcc # Use static libgcc to prevent each program from including it separately. +SHARED_LDFLAGS += $(TARGET_PIC_FLAG) -shared # Common shared lib flags +SHARED_LDFLAGS += -z text # Tell linker to abort if .text contains relocations +SHARED_LDFLAGS += $(LDFLAGS_PREFIX)-soname,$(SONAME) + + +$(PREFIX_SO)$(REALNAME): LDFLAGS:=$(LDFLAGS) $(SHARED_LDFLAGS) + +$(PREFIX_SO)$(REALNAME): $(OBJS) + $(LINK) + + +LOCAL_INSTALL_PATH := $(or $(LOCAL_INSTALL_PATH),$(DEFAULT_INSTALL_PATH_SO)) + +install-shared: $(PREFIX_SO)$(REALNAME) $(PREFIX_ROOTFS)$(LOCAL_INSTALL_PATH)/$(REALNAME) install-shared-libs + +# Install and strip binary +$(PREFIX_ROOTFS)$(LOCAL_INSTALL_PATH)/$(REALNAME): $(PREFIX_SO)$(REALNAME) + $(INSTALL_FS) + $(STRIP) $(PREFIX_ROOTFS)$(LOCAL_INSTALL_PATH)/$(REALNAME) + +install-shared-libs: $(PREFIX_SO)$(REALNAME) install-libs + $(SIL)(cd $(LIBC_INSTALL_DIR) && \ + for lib in $(LIBC_INSTALL_NAMES_SO); do \ + if [ ! -e "$$lib.$(VERSION)" ]; then \ + ln -sf "$(REALNAME)" "$$lib.$(VERSION)"; \ + fi \ + done && \ + for lib in $(LIBC_INSTALL_NAMES_SO) $(LIBNAME); do \ + if [ ! -e "$$lib.$(MAJOR_VERSION)" ]; then \ + ln -sf "$$lib.$(VERSION)" "$$lib.$(MAJOR_VERSION)"; \ + fi \ + done && \ + for lib in $(LIBC_INSTALL_NAMES_SO) $(LIBNAME); do \ + if [ ! -e "$$lib" ]; then \ + ln -sf "$$lib.$(MAJOR_VERSION)" "$$lib"; \ + fi \ + done \ + ) + +# During toolchain build shared toolchain libs are not yet ready. +ifneq ($(TOOLCHAIN_BUILD),y) + +install-shared: rootfs-install-shared-toolchain-libs install-shared-toolchain-libs + +TOOLCHAIN_LIBS_PATH := $(shell $(CC) --print-sysroot)/lib/$(MULTILIB_DIR) + +rootfs-install-shared-toolchain-libs install-shared-toolchain-libs: TOOLCHAIN_LIBS_PATH := $(TOOLCHAIN_LIBS_PATH) + + +# libstdc++ version differs depending on compilers version. +LIBSTDCXX=$(shell find $(TOOLCHAIN_LIBS_PATH) -regex '.*/libstdc\+\+\.so\.[0-9]+\.[0-9]+' -exec basename {} \; | head -n 1) +LIBSTDCXX_MAJOR=$(shell echo $(LIBSTDCXX) | sed "s/\.[0-9]\+$//") + + +rootfs-install-shared-toolchain-libs: $(PREFIX_ROOTFS)$(LOCAL_INSTALL_PATH)/$(REALNAME) + $(SIL)$(STRIP) $(TOOLCHAIN_LIBS_PATH)/$(LIBSTDCXX) -o $(PREFIX_ROOTFS)$(LOCAL_INSTALL_PATH)/$(LIBSTDCXX) + $(SIL)$(STRIP) $(TOOLCHAIN_LIBS_PATH)/libgcc_s.so.1 -o $(PREFIX_ROOTFS)$(LOCAL_INSTALL_PATH)/libgcc_s.so.1 + + +install-shared-toolchain-libs: install-shared-libs + $(SIL)ln -sf $(TOOLCHAIN_LIBS_PATH)/$(LIBSTDCXX) $(LIBC_INSTALL_DIR) + $(SIL)ln -sf $(TOOLCHAIN_LIBS_PATH)/libgcc_s.so.1 $(LIBC_INSTALL_DIR) + $(SIL)(cd $(LIBC_INSTALL_DIR) && \ + ln -sf $(LIBSTDCXX) $(LIBSTDCXX_MAJOR) && ln -sf $(LIBSTDCXX) libstdc++.so && \ + ln -sf libgcc_s.so.1 libgcc_s.so \ + ) + +endif diff --git a/stdlib/Makefile b/stdlib/Makefile index 0d9fbf1d..d1c1e528 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -4,4 +4,4 @@ # Copyright 2018, 2019, 2020 Phoenix Systems # -OBJS += $(addprefix $(PREFIX_O)stdlib/, abort.o bsearch.o div.o exit.o mktemp.o qsort.o random.o strtoul.o atexit.o env.o malloc_dl.o pty.o rand.o strtod.o strtoull.o) +OBJS += $(addprefix $(PREFIX_O)stdlib/, abort.o bsearch.o div.o exit.o mktemp.o qsort.o random.o strtoul.o atexit.o env.o malloc_dl.o pty.o rand.o strtod.o strtoull.o cxa_thread_atexit.o) diff --git a/stdlib/atexit.c b/stdlib/atexit.c index 6e8b151a..2d16e3c2 100644 --- a/stdlib/atexit.c +++ b/stdlib/atexit.c @@ -45,6 +45,9 @@ static struct { } atexit_common = { .head = &((struct atexit_node) {}) }; +extern void __cxa_thread_atexit_init(void); + + /* Initialise atexit_common structure before main */ void _atexit_init(void) { @@ -52,6 +55,8 @@ void _atexit_init(void) memset(atexit_common.head, 0, sizeof(struct atexit_node)); atexit_common.idx = 0; atexit_common.newestNode = atexit_common.head; + + __cxa_thread_atexit_init(); } diff --git a/stdlib/cxa_thread_atexit.c b/stdlib/cxa_thread_atexit.c new file mode 100644 index 00000000..c98cf35f --- /dev/null +++ b/stdlib/cxa_thread_atexit.c @@ -0,0 +1,130 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * atexit.c + * + * Copyright 2019 2022 Phoenix Systems + * Author: Kamil Amanowicz, Dawid Szpejna + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#include +#include + + +void __cxa_thread_atexit_init(void); +void __cxa_thread_atexit_run(void); +int __cxa_thread_atexit_impl(void (*)(void *), void *, void *); + + +#ifdef __LIBPHOENIX_ARCH_TLS_SUPPORTED + + +void __libc__dl_cxa_refcount(void *addr, ssize_t delta) +{ + /* Do nothing in static lib */ + (void)addr; + (void)delta; +} + + +extern void __dl_cxa_refcount(void *, ssize_t) __attribute__((weak, alias("__libc__dl_cxa_refcount"))); + + +struct __cxa_atexit_node { + struct __cxa_atexit_node *prev; + void (*dtor)(void *); + void *obj; + void *handle; +}; + + +static __thread struct __cxa_atexit_node *__cxa_thread_atexit_head; + + +extern int can_use_tls; + + +void __cxa_thread_atexit_init(void) +{ + /* Special case for dynamic linker exit */ + if (can_use_tls != 0) { + atexit(__cxa_thread_atexit_run); + } +} + + +void __cxa_thread_atexit_run(void) +{ + while (__cxa_thread_atexit_head != NULL) { + struct __cxa_atexit_node *last = __cxa_thread_atexit_head; + + __cxa_thread_atexit_head->dtor(__cxa_thread_atexit_head->obj); + __cxa_thread_atexit_head = __cxa_thread_atexit_head->prev; + + if (last->handle != NULL) { + __dl_cxa_refcount(last->handle, -1); + } + + free(last); + } +} + + +/* C++ thread-local destructor handler */ +int __cxa_thread_atexit_impl(void (*dtor)(void *), void *obj, void *handle) +{ + struct __cxa_atexit_node *node = malloc(sizeof(struct __cxa_atexit_node)); + + if (node == NULL) { + return -1; + } + + node->dtor = dtor; + node->obj = obj; + node->handle = handle; + node->prev = __cxa_thread_atexit_head; + + if (node->handle != NULL) { + __dl_cxa_refcount(node->handle, 1); + } + + __cxa_thread_atexit_head = node; + + return 0; +} + + +#else + + +void __cxa_thread_atexit_init(void) +{ + /* Nothing to do */ +} + + +void __cxa_thread_atexit_run(void) +{ + /* Nothing to do */ +} + + +/* NOTE: no support for __cxa_thread_atexit_impl on platforms not supporting TLS. */ +int __cxa_thread_atexit_impl(void (*dtor)(void *), void *obj, void *handle) +{ + (void)dtor; + (void)obj; + (void)handle; + + abort(); +} + + +#endif diff --git a/sys/threads.c b/sys/threads.c index b3545325..a945f872 100644 --- a/sys/threads.c +++ b/sys/threads.c @@ -17,6 +17,52 @@ #include +#include +#include + +/* Provide stub definition of tls managing functions for statically linked programs. */ + +static void _libphoenix_stub(void) +{ +} + +extern void __attribute__((weak, alias("_libphoenix_stub"))) _rtld_tls_free_curr(void); +extern void __attribute__((weak, alias("_libphoenix_stub"))) _rtld_tls_allocate_curr(void); + + +/* FIXME: Hack to provide tls support in dynamically loaded binaries. */ +void beginthreadex_wrapper(void *arg) +{ + _rtld_tls_allocate_curr(); + + void *(*start)(void *) = ((void **)arg)[0]; + void *startArg = ((void **)arg)[1]; + + free(arg); + + start(startArg); +} + +int beginthreadex(void (*start)(void *), unsigned int priority, void *stack, unsigned int stacksz, void *arg, handle_t *id) +{ + void **startAndArg = (void **)malloc(sizeof(void *) * 2); + startAndArg[0] = start; + startAndArg[1] = arg; + return beginthreadexsvc(beginthreadex_wrapper, priority, stack, stacksz, startAndArg, id); +} + + +void __cxa_thread_atexit_run(void); + + +__attribute__((noreturn)) void endthread(void) +{ + __cxa_thread_atexit_run(); + _rtld_tls_free_curr(); + endthreadsvc(); +} + + int mutexCreate(handle_t *h) { static const struct lockAttr defaultAttr = { .type = PH_LOCK_NORMAL };