Hi Marcus,
applied and pushed, after intensive testing with Qemu and uClibc-ng-test.
thanks Waldemar
Marcus Hähnel wrote,
... and here is the attachment ...
On Thu, 2025-03-20 at 09:39 +0100, Marcus Hähnel wrote:
Dear List
So far, uclibc relied on the informal behaviour of GNU ld to put the link address of the _DYNAMIC symbol in the first GOT entry. This does not work with LLVM lld which does not follow this convention. Consequently, glibc has abandoned its usage in favour of using __ehdr_start as magic symbol to infer the load address [1].
Note that this implies that the link time address of __ehdr_start is *always* 0! So far, this seems to be the case on all platforms.
We implemented this change only for the platforms which we actively support and test at Kernkonzept (arm64, arm, i386, x86_64 and and riscv). The riscv64 dl-sysdep.h already used the method we converted the others to. For the other platforms we do not have the capacity to port this, but as long as noone wants to build them using lld this should not be an issue.
Please feel free to test this on the relevant platforms before merging, to be sure it works as intended with the upstream version of the lib.
Best regards,
- Marcus
-- +++++++++++++++++++
Register now for our workshop "Get to know L4Re in 3 days" on April 8-10. Learn to design and deploy secure system architectures for your product with L4Re: https://www.kernkonzept.com/workshop-getting-started-with-l4re/
+++++++++++++++++++
Kernkonzept GmbH Sitz: Dresden HRB 31129 Geschäftsführer: Dr.-Ing. Michael Hohmuth
From 9a4929ce522d34bed13c24e88c58ae48f1c538c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= jan.kloetzke@kernkonzept.com Date: Tue, 18 Mar 2025 07:41:47 +0100 Subject: [PATCH] Use __ehdr_start to compute load address
So far, uclibc relied on the informal behaviour of GNU ld to put the link address of the _DYNAMIC symbol in the first GOT entry. This does not work with LLVM lld which does not follow this convention. Consequently, glibc has abandoned its usage in favour of using __ehdr_start as magic symbol to infer the load address [1].
Note that this implies that the link time address of __ehdr_start is *always* 0! So far, this seems to be the case on all platforms.
[1] https://sourceware.org/bugzilla/show_bug.cgi?id=28203
Signed-off-by: Marcus Haehnel marcus.haehnel@kernkonzept.com
ldso/ldso/aarch64/dl-sysdep.h | 25 +++++-------- ldso/ldso/arm/dl-sysdep.h | 69 +++++------------------------------ ldso/ldso/i386/dl-sysdep.h | 27 +++++--------- ldso/ldso/riscv32/dl-sysdep.h | 22 +++++------ ldso/ldso/x86_64/dl-sysdep.h | 45 +++++------------------ 5 files changed, 48 insertions(+), 140 deletions(-)
diff --git a/ldso/ldso/aarch64/dl-sysdep.h b/ldso/ldso/aarch64/dl-sysdep.h index 6d9d2c1fb..3466920d9 100644 --- a/ldso/ldso/aarch64/dl-sysdep.h +++ b/ldso/ldso/aarch64/dl-sysdep.h @@ -54,28 +54,21 @@ unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry); || (type) == R_AARCH64_TLSDESC) * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_AARCH64_COPY) * ELF_RTYPE_CLASS_COPY))
-/* Return the link-time address of _DYNAMIC. Conveniently, this is the
- first element of the GOT. */
-extern const ElfW(Addr) _GLOBAL_OFFSET_TABLE_[] attribute_hidden; -static __always_inline ElfW(Addr) __attribute__ ((unused)) -elf_machine_dynamic (void) -{
- return _GLOBAL_OFFSET_TABLE_[0];
-}
/* Return the run-time load address of the shared object. */
static __always_inline ElfW(Addr) __attribute__ ((unused)) elf_machine_load_address (void) {
- /* To figure out the load address we use the definition that for any symbol:
dynamic_addr(symbol) = static_addr(symbol) + load_addr
- _DYNAMIC sysmbol is used here as its link-time address stored in
- the special unrelocated first GOT entry. */
- extern const ElfW(Ehdr) __ehdr_start attribute_hidden;
- return (ElfW(Addr)) &__ehdr_start;
+}
- extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
- return (ElfW(Addr)) &_DYNAMIC - elf_machine_dynamic ();
+/* Return the link-time address of _DYNAMIC. */ +static __always_inline ElfW(Addr) __attribute__ ((unused)) +elf_machine_dynamic (void) +{
- extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
- return (ElfW(Addr)) _DYNAMIC - elf_machine_load_address ();
}
static __always_inline void diff --git a/ldso/ldso/arm/dl-sysdep.h b/ldso/ldso/arm/dl-sysdep.h index 0f783e1c4..93e36b694 100644 --- a/ldso/ldso/arm/dl-sysdep.h +++ b/ldso/ldso/arm/dl-sysdep.h @@ -96,43 +96,6 @@ unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry); | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY)) #endif /* __FDPIC__ */
-/* Return the link-time address of _DYNAMIC. Conveniently, this is the
- first element of the GOT. We used to use the PIC register to do this
- without a constant pool reference, but GCC 4.2 will use a pseudo-register
- for the PIC base, so it may not be in r10. */
-static __always_inline Elf32_Addr __attribute__ ((unused)) -elf_machine_dynamic (void) -{
- Elf32_Addr dynamic;
-#if !defined __thumb__
- __asm__ ("ldr %0, 2f\n"
"1: ldr %0, [pc, %0]\n"
"b 3f\n"
"2: .word _GLOBAL_OFFSET_TABLE_ - (1b+8)\n"
"3:" : "=r" (dynamic));
-#else
- int tmp;
- __asm__ (".align 2\n"
"bx pc\n"
"nop\n"
".arm\n"
"ldr %0, 2f\n"
"1: ldr %0, [pc, %0]\n"
"b 3f\n"
"2: .word _GLOBAL_OFFSET_TABLE_ - (1b+8)\n"
"3:"
".align 2\n"
"orr %1, pc, #1\n"
"bx %1\n"
".force_thumb\n"
: "=r" (dynamic), "=&r" (tmp));
-#endif
- return dynamic;
-}
-extern char __dl_start[] __asm__("_dl_start");
#ifdef __FDPIC__ /* We must force strings used early in the bootstrap into the data segment. */ @@ -148,28 +111,16 @@ extern char __dl_start[] __asm__("_dl_start"); static __always_inline Elf32_Addr __attribute__ ((unused)) elf_machine_load_address (void) { -#if defined(__FDPIC__)
- return 0;
-#else
- Elf32_Addr got_addr = (Elf32_Addr) &__dl_start;
- Elf32_Addr pcrel_addr;
-#if defined __OPTIMIZE__ && !defined __thumb__
- __asm__ ("adr %0, _dl_start" : "=r" (pcrel_addr));
-#else
- /* A simple adr does not work in Thumb mode because the offset is
negative, and for debug builds may be too large. */
- int tmp;
- __asm__ ("adr %1, 1f\n\t"
"ldr %0, [%1]\n\t"
"add %0, %0, %1\n\t"
"b 2f\n\t"
".align 2\n\t"
"1: .word _dl_start - 1b\n\t"
"2:"
: "=r" (pcrel_addr), "=r" (tmp));
-#endif
- return pcrel_addr - got_addr;
-#endif
- extern const Elf32_Ehdr __ehdr_start attribute_hidden;
- return (Elf32_Addr) &__ehdr_start;
+}
+/* Return the link-time address of _DYNAMIC. */ +static __always_inline Elf32_Addr __attribute__ ((unused)) +elf_machine_dynamic (void) +{
- extern Elf32_Dyn _DYNAMIC[] attribute_hidden;
- return (Elf32_Addr) _DYNAMIC - elf_machine_load_address ();
}
static __always_inline void diff --git a/ldso/ldso/i386/dl-sysdep.h b/ldso/ldso/i386/dl-sysdep.h index b95328df4..8fc80a145 100644 --- a/ldso/ldso/i386/dl-sysdep.h +++ b/ldso/ldso/i386/dl-sysdep.h @@ -35,28 +35,21 @@ extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_ent || (type) == R_386_TLS_TPOFF) * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_386_COPY) * ELF_RTYPE_CLASS_COPY))
-/* Return the link-time address of _DYNAMIC. Conveniently, this is the
- first element of the GOT, a special entry that is never relocated. */
-extern const Elf32_Addr _GLOBAL_OFFSET_TABLE_[] attribute_hidden; -static __always_inline Elf32_Addr __attribute__ ((unused, const)) -elf_machine_dynamic (void) -{
- /* This produces a GOTOFF reloc that resolves to zero at link time, so in
fact just loads from the GOT register directly. By doing it without
an asm we can let the compiler choose any register. */
- return _GLOBAL_OFFSET_TABLE_[0];
-}
-extern Elf32_Dyn bygotoff[] __asm__ ("_DYNAMIC") attribute_hidden; /* Return the run-time load address of the shared object. */ static __always_inline Elf32_Addr attribute_unused elf_machine_load_address (void) {
- /* Compute the difference between the runtime address of _DYNAMIC as seen
by a GOTOFF reference, and the link-time address found in the special
unrelocated first GOT entry. */
- return (Elf32_Addr) &bygotoff - elf_machine_dynamic ();
- extern const Elf32_Ehdr __ehdr_start attribute_hidden;
- return (Elf32_Addr) &__ehdr_start;
+}
+/* Return the link-time address of _DYNAMIC. */ +static __always_inline Elf32_Addr __attribute__ ((unused, const)) +elf_machine_dynamic (void) +{
- extern Elf32_Dyn _DYNAMIC[] attribute_hidden;
- return (Elf32_Addr) _DYNAMIC - elf_machine_load_address ();
}
static __always_inline void diff --git a/ldso/ldso/riscv32/dl-sysdep.h b/ldso/ldso/riscv32/dl-sysdep.h index e0a59fddd..02296b148 100644 --- a/ldso/ldso/riscv32/dl-sysdep.h +++ b/ldso/ldso/riscv32/dl-sysdep.h @@ -59,22 +59,20 @@ unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry); | (ELF_RTYPE_CLASS_COPY * ((type) == R_RISCV_COPY)))
-/* Return the link-time address of _DYNAMIC. */ -static inline ElfW(Addr) -elf_machine_dynamic (void) -{
- extern ElfW(Addr) _GLOBAL_OFFSET_TABLE_ __attribute__ ((visibility ("hidden")));
- return _GLOBAL_OFFSET_TABLE_;
-}
/* Return the run-time load address of the shared object. */ static __always_inline ElfW(Addr) __attribute__ ((unused)) elf_machine_load_address (void) {
- ElfW(Addr) load_addr;
- __asm__ ("lla %0, _DYNAMIC" : "=r" (load_addr));
- return load_addr - elf_machine_dynamic ();
- extern const ElfW(Ehdr) __ehdr_start attribute_hidden;
- return (ElfW(Addr)) &__ehdr_start;
+}
+/* Return the link-time address of _DYNAMIC. */ +static inline ElfW(Addr) +elf_machine_dynamic (void) +{
- extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
- return (ElfW(Addr)) _DYNAMIC - elf_machine_load_address ();
}
static __always_inline void diff --git a/ldso/ldso/x86_64/dl-sysdep.h b/ldso/ldso/x86_64/dl-sysdep.h index ccf9a8851..58447a951 100644 --- a/ldso/ldso/x86_64/dl-sysdep.h +++ b/ldso/ldso/x86_64/dl-sysdep.h @@ -52,48 +52,21 @@ extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_ent * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_X86_64_COPY) * ELF_RTYPE_CLASS_COPY))
-/* Return the link-time address of _DYNAMIC. Conveniently, this is the
- first element of the GOT. This must be inlined in a function which
- uses global data. */
-static __always_inline Elf64_Addr __attribute__ ((unused)) -elf_machine_dynamic (void) -{
- Elf64_Addr addr;
- /* This works because we have our GOT address available in the small PIC
model. */
- addr = (Elf64_Addr) &_DYNAMIC;
- return addr;
-}
/* Return the run-time load address of the shared object. */ static __always_inline Elf64_Addr __attribute__ ((unused)) elf_machine_load_address (void) {
- register Elf64_Addr addr, tmp;
- /* The easy way is just the same as on x86:
leaq _dl_start, %0
leaq _dl_start(%%rip), %1
subq %0, %1
but this does not work with binutils since we then have
a R_X86_64_32S relocation in a shared lib.
Instead we store the address of _dl_start in the data section
and compare it with the current value that we can get via
an RIP relative addressing mode. */
- __asm__ ("movq 1f(%%rip), %1\n"
"0:\tleaq _dl_start(%%rip), %0\n\t"
"subq %1, %0\n\t"
".section\t.data\n"
"1:\t.quad _dl_start\n\t"
".previous\n\t"
: "=r" (addr), "=r" (tmp) : : "cc");
- extern const Elf64_Ehdr __ehdr_start attribute_hidden;
- return (Elf64_Addr) &__ehdr_start;
+}
- return addr;
+/* Return the link-time address of _DYNAMIC. */ +static __always_inline Elf64_Addr __attribute__ ((unused)) +elf_machine_dynamic (void) +{
- extern Elf64_Dyn _DYNAMIC[] attribute_hidden;
- return (Elf64_Addr) _DYNAMIC - elf_machine_load_address ();
}
static __always_inline void
2.47.1
devel mailing list -- devel@uclibc-ng.org To unsubscribe send an email to devel-leave@uclibc-ng.org