Static PIE ELFs may be loaded on noMMU linux platforms with FDPIC
support, but they don't have adjustable brk, and thus cannot allocate
memory for the TLS. Use mmap instead of sbrk to allocate initial TLS
memory when building with static PIE support for noMMU.
Signed-off-by: Max Filippov <jcmvbkbc(a)gmail.com>
---
Change v1->v2:
- fix typo and add clarification in comment
libpthread/nptl/sysdeps/generic/libc-tls.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/libpthread/nptl/sysdeps/generic/libc-tls.c b/libpthread/nptl/sysdeps/generic/libc-tls.c
index d9c09fdd6667..7cfe9ac1a85c 100644
--- a/libpthread/nptl/sysdeps/generic/libc-tls.c
+++ b/libpthread/nptl/sysdeps/generic/libc-tls.c
@@ -169,10 +169,13 @@ __libc_setup_tls (size_t tcbsize, size_t tcbalign)
for FDPIC MMU-less platforms:
fs/binfmt_elf_fdpic.c: fix brk area overlap with stack on NOMMU
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/fs/b…
+ Loading static PIE ELFs on noMMU is possible since the linux kernel commit
+ 1bde925d2354 ("fs/binfmt_elf_fdpic.c: provide NOMMU loader for regular ELF binaries")
+ and it is subject to the same brk restriction.
*/
# if defined(TLS_TCB_AT_TP)
tcb_offset = roundup (memsz + GL(dl_tls_static_size), tcbalign);
-# if defined(__FDPIC__)
+# if defined(__FDPIC__) || (!defined(__ARCH_USE_MMU__) && defined(STATIC_PIE))
tlsblock = mmap (NULL, tcb_offset + tcbsize + max_align,
PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
# else
@@ -180,7 +183,7 @@ __libc_setup_tls (size_t tcbsize, size_t tcbalign)
# endif
# elif defined(TLS_DTV_AT_TP)
tcb_offset = roundup (tcbsize, align ?: 1);
-# if defined(__FDPIC__)
+# if defined(__FDPIC__) || (!defined(__ARCH_USE_MMU__) && defined(STATIC_PIE))
tlsblock = mmap (NULL, tcb_offset + memsz + max_align + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size),
PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
# else
--
2.30.2
Static PIE ELFs may be loaded on noMMU linux platforms with FDPIC
support, but they don't have adjustable brk, and thus cannot allocate
memory for the TLS. Use mmap instead of sbrk to allocate initial TLS
memory when building with static PIE support for noMMU.
Signed-off-by: Max Filippov <jcmvbkbc(a)gmail.com>
---
libpthread/nptl/sysdeps/generic/libc-tls.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/libpthread/nptl/sysdeps/generic/libc-tls.c b/libpthread/nptl/sysdeps/generic/libc-tls.c
index d9c09fdd6667..374ba2a48620 100644
--- a/libpthread/nptl/sysdeps/generic/libc-tls.c
+++ b/libpthread/nptl/sysdeps/generic/libc-tls.c
@@ -169,10 +169,13 @@ __libc_setup_tls (size_t tcbsize, size_t tcbalign)
for FDPIC MMU-less platforms:
fs/binfmt_elf_fdpic.c: fix brk area overlap with stack on NOMMU
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/fs/b…
+ Loading static PIE ELFs is possible since linux kernel commit
+ 1bde925d2354 ("fs/binfmt_elf_fdpic.c: provide NOMMU loader for regular ELF binaries")
+ and it is subject to the same brk restrixtion.
*/
# if defined(TLS_TCB_AT_TP)
tcb_offset = roundup (memsz + GL(dl_tls_static_size), tcbalign);
-# if defined(__FDPIC__)
+# if defined(__FDPIC__) || (!defined(__ARCH_USE_MMU__) && defined(STATIC_PIE))
tlsblock = mmap (NULL, tcb_offset + tcbsize + max_align,
PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
# else
@@ -180,7 +183,7 @@ __libc_setup_tls (size_t tcbsize, size_t tcbalign)
# endif
# elif defined(TLS_DTV_AT_TP)
tcb_offset = roundup (tcbsize, align ?: 1);
-# if defined(__FDPIC__)
+# if defined(__FDPIC__) || (!defined(__ARCH_USE_MMU__) && defined(STATIC_PIE))
tlsblock = mmap (NULL, tcb_offset + memsz + max_align + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size),
PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
# else
--
2.30.2
Hello,
This is the second iteration of the patch for mips static pie support. This
version fixes two truncation errors which were occurring.
The first problem came from calling reloc_static_pie() directly from
__start which caused gcc to request a R_MIPS_PC16 relocation. Unfortunately
that type of relocation would not be large enough for the linker to place
the PC relative address if the supplied user code was too large. By
changing the call to be a register relative jump which we were already
correctly calculating, the problem went away.
The other bug was the improper usage of the mips assembly macros. I had
chosen to use INT_* functions for the calculation to determine the load
address. These macros ended up truncating the results since they can use
the 32bit instructions on mips64. This was remedied by changing to PTR_*
macros.
-------
From 051a7536e8f3bfb96c290bda74749886ae7d1693 Mon Sep 17 00:00:00 2001
From: linted <linted(a)users.noreply.github.com>
Date: Sat, 20 Aug 2022 16:41:38 -0400
Subject: [PATCH] Added support for creation of Static Position-Independent
Executables (PIE) on mips
Updated config to allow compilation of rcrt1.o for mips and modified it's
crt1.S to perform relocates in __start.
The mips architecture performs relocations differently then most other
architectures. reloc_static_pie was rewritten, taking code from
dl-startup.c, in order to perfrom the additional relocations. Modifications
were made to mips' dl-startup.h to allow for the use of contained macros
without including _start definition.
Signed-off-by: linted <linted(a)users.noreply.github.com>
---
extra/Configs/Config.in | 2 +-
ldso/ldso/mips/dl-startup.h | 3 +-
libc/misc/internals/Makefile.in | 2 +-
libc/misc/internals/reloc_static_pie.c | 93 +++++++++++++++++++++-----
libc/sysdeps/linux/mips/crt1.S | 29 ++++++++
5 files changed, 111 insertions(+), 18 deletions(-)
diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in
index e0905e956..43c04fd0a 100644
--- a/extra/Configs/Config.in
+++ b/extra/Configs/Config.in
@@ -324,7 +324,7 @@ config DOPIC
config STATIC_PIE
bool "Add support for Static Position Independent Executables (PIE)"
default n
- depends on DOPIC && !UCLIBC_FORMAT_FDPIC_ELF && (TARGET_arm ||
TARGET_i386 || TARGET_x86_64 || TARGET_aarch64)
+ depends on DOPIC && !UCLIBC_FORMAT_FDPIC_ELF && (TARGET_arm ||
TARGET_i386 || TARGET_x86_64 || TARGET_aarch64 || TARGET_mips)
config ARCH_HAS_NO_SHARED
bool
diff --git a/ldso/ldso/mips/dl-startup.h b/ldso/ldso/mips/dl-startup.h
index 8026f1702..c2168d774 100644
--- a/ldso/ldso/mips/dl-startup.h
+++ b/ldso/ldso/mips/dl-startup.h
@@ -7,6 +7,7 @@
#include <sgidefs.h>
+#ifndef L_rcrt1
__asm__(""
" .text\n"
" .globl _start\n"
@@ -114,6 +115,7 @@ __asm__(""
"\n\n"
".previous\n"
);
+#endif
/*
* Get a pointer to the argv array. On many platforms this can be just
@@ -191,6 +193,5 @@ do { \
case R_MIPS_NONE: \
break; \
default: \
- SEND_STDERR("Aiieeee!"); \
_dl_exit(1); \
}
diff --git a/libc/misc/internals/Makefile.in
b/libc/misc/internals/Makefile.in
index 69af8b76e..908b18321 100644
--- a/libc/misc/internals/Makefile.in
+++ b/libc/misc/internals/Makefile.in
@@ -17,7 +17,7 @@ MISC_INTERNALS_SRC := $(patsubst
%.c,$(MISC_INTERNALS_DIR)/%.c,$(CSRC-y))
MISC_INTERNALS_OBJ := $(patsubst %.c,$(MISC_INTERNALS_OUT)/%.o,$(CSRC-y))
CFLAGS-__uClibc_main.c := $(SSP_DISABLE_FLAGS)
-CFLAGS-reloc_static_pie.c := $(SSP_DISABLE_FLAGS)
+CFLAGS-reloc_static_pie.c := $(SSP_DISABLE_FLAGS) -DL_rcrt1
libc-y += $(MISC_INTERNALS_OBJ)
ifneq ($(UCLIBC_FORMAT_SHARED_FLAT),y)
diff --git a/libc/misc/internals/reloc_static_pie.c
b/libc/misc/internals/reloc_static_pie.c
index 578202d23..c0027de6f 100644
--- a/libc/misc/internals/reloc_static_pie.c
+++ b/libc/misc/internals/reloc_static_pie.c
@@ -15,33 +15,96 @@
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-
+#define IS_IN_rtld // force inline function calls
#include <link.h>
#include <elf.h>
#include <dl-elf.h>
+#include <ldso.h>
+#ifdef __mips__
+#include <dl-startup.h>
+#endif
+
ElfW(Addr) _dl_load_base = NULL;
void
reloc_static_pie (ElfW(Addr) load_addr);
void
-reloc_static_pie (ElfW(Addr) load_addr)
+reloc_static_pie(ElfW(Addr) load_addr)
{
- ElfW(Word) relative_count = 0;
- ElfW(Addr) rel_addr = 0;
- ElfW(Dyn) * dyn_addr = NULL;
- unsigned long dynamic_info[DYNAMIC_SIZE] = {0};
+ int indx;
+ ElfW(Addr) got;
+ ElfW(Dyn) *dpnt;
+ struct elf_resolve tpnt_tmp;
+ struct elf_resolve *tpnt = &tpnt_tmp;
+
+ DL_BOOT_COMPUTE_GOT(got);
+ DL_BOOT_COMPUTE_DYN(dpnt, got, (DL_LOADADDR_TYPE)load_addr);
+
+ _dl_memset(tpnt, 0, sizeof(struct elf_resolve));
+ tpnt->loadaddr = load_addr;
+ tpnt->dynamic_addr = dpnt;
+
+ __dl_parse_dynamic_info(dpnt, tpnt->dynamic_info, NULL, load_addr);
+
+#if defined(PERFORM_BOOTSTRAP_GOT)
+ /* some arches (like MIPS) we have to tweak the GOT before relocations */
+ PERFORM_BOOTSTRAP_GOT(tpnt);
+#endif
+
+
+#if defined(ELF_MACHINE_PLTREL_OVERLAP)
+# define INDX_MAX 1
+#else
+# define INDX_MAX 2
+#endif
+
+ for (indx = 0; indx < INDX_MAX; indx++) {
+ unsigned long rel_addr, rel_size;
+ ElfW(Word) relative_count = tpnt->dynamic_info[DT_RELCONT_IDX];
+
+ rel_addr = (indx ? tpnt->dynamic_info[DT_JMPREL] :
+ tpnt->dynamic_info[DT_RELOC_TABLE_ADDR]);
+ rel_size = (indx ? tpnt->dynamic_info[DT_PLTRELSZ] :
+ tpnt->dynamic_info[DT_RELOC_TABLE_SIZE]);
+
+ if (!rel_addr)
+ continue;
- /* Read our own dynamic section and fill in the info array. */
- dyn_addr = ((void *) load_addr + elf_machine_dynamic ());
+ if((0 == indx) && relative_count) {
+ rel_size -= relative_count * sizeof(ELF_RELOC);
+ elf_machine_relative(load_addr, rel_addr, relative_count);
+ rel_addr += relative_count * sizeof(ELF_RELOC);
+ }
- /* Use the underlying function to avoid TLS access before
initialization */
- __dl_parse_dynamic_info(dyn_addr, dynamic_info, NULL, load_addr);
+#ifdef ARCH_NEEDS_BOOTSTRAP_RELOCS
+ {
+ ELF_RELOC *rpnt;
+ unsigned int i;
+ ElfW(Sym) *sym;
+ unsigned long symbol_addr;
+ int symtab_index;
+ unsigned long *reloc_addr;
- /* Perform relocations */
- relative_count = dynamic_info[DT_RELCONT_IDX];
- rel_addr = dynamic_info[DT_RELOC_TABLE_ADDR];
- elf_machine_relative(load_addr, rel_addr, relative_count);
+ /* Now parse the relocation information */
+ rpnt = (ELF_RELOC *) rel_addr;
+ for (i = 0; i < rel_size; i += sizeof(ELF_RELOC), rpnt++) {
+ reloc_addr = (unsigned long *) DL_RELOC_ADDR(load_addr, (unsigned
long)rpnt->r_offset);
+ symtab_index = ELF_R_SYM(rpnt->r_info);
+ symbol_addr = 0;
+ sym = NULL;
+ if (symtab_index) {
+ ElfW(Sym) *symtab;
+ symtab = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB];
+ sym = &symtab[symtab_index];
+ symbol_addr = (unsigned long) DL_RELOC_ADDR(load_addr, sym->st_value);
+ }
+ /* Use this machine-specific macro to perform the actual relocation. */
+ PERFORM_BOOTSTRAP_RELOC(rpnt, reloc_addr, symbol_addr, load_addr, sym);
+ }
+ }
+#endif
+ }
_dl_load_base = load_addr;
-}
+}
\ No newline at end of file
diff --git a/libc/sysdeps/linux/mips/crt1.S b/libc/sysdeps/linux/mips/crt1.S
index 083615515..6a4257b1f 100644
--- a/libc/sysdeps/linux/mips/crt1.S
+++ b/libc/sysdeps/linux/mips/crt1.S
@@ -77,6 +77,10 @@
#ifndef __UCLIBC_CTOR_DTOR__
.weak _init
.weak _fini
+#endif
+#ifdef L_rcrt1
+ .type reloc_static_pie,@function
+ .hidden .L0
#endif
.type main,@function
.type __uClibc_main,@function
@@ -89,6 +93,31 @@ __start:
#else
PTR_LA $28, _gp /* Setup GP correctly if we're non-PIC. */
move $31, $0
+#endif
+#ifdef L_rcrt1
+/* #if _MIPS_SIM == _MIPS_SIM_ABI32 */
+ PTR_LA $4, _DYNAMIC /* Place _DYNAMIC into the GOT */
+ REG_S $4, -0x7ff0($28) /* offset to GOT stolen from dl-startup */
+ jal .L0 /* Get the current $pc address */
+.L0:
+ PTR_SUBU $4, $31, $25 /* Calculate load addr */
+ move $31, $0 /* Clear ra */
+ and $29, -2 * SZREG /* Ensure stack is aligned */
+ PTR_ADDIU $29, (-2 * SZREG) /* Allocate 2 register spaces on stack */
+ REG_S $2, SZREG($29) /* Store atexit in case it exists */
+ PTR_LA $5, reloc_static_pie /* function calls before relocation
+ don't work unless we set $t9 manually */
+ PTR_ADDU $25, $4, $5 /* store reloc_static_pie in $t9 */
+ jalr $25 /* call reloc_static_pie */
+ nop /* delay slot, just in case */
+ REG_L $2, SZREG($29) /* cleanup stack */
+ PTR_ADDIU $29, $29, (2 * SZREG)
+#else
+ PTR_LA $4, _DYNAMIC /* Place _DYNAMIC into the GOT */
+ REG_S $4, -0x7ff0($28)
+
+/* #endif */
+
#endif
PTR_LA $4, main /* main */
--
2.34.1
Just polling to see if anyone is using aarch64 in uclibc-ng. I created a
toolchain from buildroot for some new hardware I got but noticed a lot
of segfaulting with c++ applications. Thinking it was possibly a
libstdc++ issue I opened a bug report.
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106581
But it seems to concluded that uclibc-ng tls support for aarch64 isn't
quite there. Some of the segfaulting is fixed if libstdc++ is static
linked, but doesn't quite fix all the issues.
https://lists.buildroot.org/pipermail/buildroot/2022-August/648961.html
Also got this response on the buildroot mailing list.
thank,
Lanec
Hello,
While looking at some uClibc code, I stumbled across the following
snippet in Rules.mak:
UCLIBC_LDSO_NAME := ld-uClibc
ARCH_NATIVE_BIT := 32
ifneq ($(findstring $(TARGET_ARCH) , hppa64 ia64 powerpc64 s390x sparc64 x86_64 kvx ),)
UCLIBC_LDSO_NAME := ld64-uClibc
ARCH_NATIVE_BIT := 64
else
ifeq ($(CONFIG_MIPS_N64_ABI),y)
UCLIBC_LDSO_NAME := ld64-uClibc
ARCH_NATIVE_BIT := 64
endif
endif
I was surprised not to see aarch64 in the list of 64-bit architectures
here. Turns out that the situation is not that simple in fact. Indeed,
gcc does not expect all 64-bit architecture to have their dynamic
loader called ld64-uClibc. For aarch64 specifically, it indeed assumes
the dynamic loader is called ld-uClibc, which explains why it is
working today.
However, it means that ARCH_NATIVE_BIT is defined to 32 on aarch64,
which is obviously (?) wrong.
But turns out that ARCH_NATIVE_BIT is only used in utils/porting.h to
define __WORDSIZE and __WORDSIZE is only used in utils/ldd.c.
So to me, it seems like this would gain in being clarified. Something
like (completely untested):
diff --git a/Rules.mak b/Rules.mak
index 3fb64c728..a0b012d7f 100644
--- a/Rules.mak
+++ b/Rules.mak
@@ -142,17 +142,8 @@ export MAJOR_VERSION MINOR_VERSION SUBLEVEL VERSION ABI_VERSION LC_ALL
LIBC := libc
SHARED_LIBNAME := $(LIBC).so.$(ABI_VERSION)
-UCLIBC_LDSO_NAME := ld-uClibc
-ARCH_NATIVE_BIT := 32
-ifneq ($(findstring $(TARGET_ARCH) , hppa64 ia64 powerpc64 s390x sparc64 x86_64 kvx ),)
-UCLIBC_LDSO_NAME := ld64-uClibc
-ARCH_NATIVE_BIT := 64
-else
-ifeq ($(CONFIG_MIPS_N64_ABI),y)
-UCLIBC_LDSO_NAME := ld64-uClibc
-ARCH_NATIVE_BIT := 64
-endif
-endif
+UCLIBC_LDSO_NAME := $(call qstrip,$(TARGET_LDSO_NAME))
+ARCH_NATIVE_BIT := $(call qstrip,$(TARGET_ARCH_BITS))
UCLIBC_LDSO := $(UCLIBC_LDSO_NAME).so.$(ABI_VERSION)
NONSHARED_LIBNAME := uclibc_nonshared.a
diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in
index a49278b30..e6369bd82 100644
--- a/extra/Configs/Config.in
+++ b/extra/Configs/Config.in
@@ -145,6 +145,26 @@ config TARGET_xtensa
endchoice
+config TARGET_LDSO_NAME
+ string
+ default "ld64-uClibc" if TARGET_ia64
+ default "ld64-uClibc" if TARGET_powerpc64
+ default "ld64-uClibc" if TARGET_sparc64
+ default "ld64-uClibc" if TARGET_x86_64
+ default "ld64-uClibc" if TARGET_kvx
+ default "ld64-uClibc" if CONFIG_MIPS_N64_ABI
+ default "ld-uClibc"
+
+config TARGET_ARCH_BITS
+ int
+ default 64 if TARGET_aarch64
+ default 64 if TARGET_ia64
+ default 64 if TARGET_powerpc64
+ default 64 if TARGET_sparc64
+ default 64 if TARGET_x86_64
+ default 64 if TARGET_kvx
+ default 64 if CONFIG_MIPS_N64_ABI
+ default 32
menu "Target Architecture Features and Options"
Best regards,
Thomas Petazzoni
--
Thomas Petazzoni, co-owner and CEO, Bootlin
Embedded Linux and Kernel engineering and training
https://bootlin.com
Hello,
uClibc-ng is missing the definition of RUSAGE_THREAD. It is available
in glibc:
sysdeps/unix/sysv/linux/alpha/bits/resource.h: RUSAGE_THREAD = 1
sysdeps/unix/sysv/linux/bits/resource.h: RUSAGE_THREAD = 1
sysdeps/unix/sysv/linux/mips/bits/resource.h: RUSAGE_THREAD = 1
sysdeps/unix/sysv/linux/sparc/bits/resource.h: RUSAGE_THREAD = 1
We have a real-world usage of RUSAGE_THREAD by the pistache project,
https://github.com/oktal/pistache.
Best regards,
Thomas
--
Thomas Petazzoni, co-owner and CEO, Bootlin
Embedded Linux and Kernel engineering and training
https://bootlin.com