diff mbox series

[uclibc-ng-devel] Re: use __ehdr_start to compute load address

Message ID 563157f631271f15cefd89297dbe48cd6cd1a0e8.camel@kernkonzept.com
State Accepted
Headers show
Series [uclibc-ng-devel] Re: use __ehdr_start to compute load address | expand

Commit Message

Marcus Haehnel March 20, 2025, 8:44 a.m. UTC
... 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
> 
> 
> [1] https://sourceware.org/bugzilla/show_bug.cgi?id=28203
> 
> 
> 
>

Comments

Waldemar Brodkorb March 27, 2025, 10:43 a.m. UTC | #1
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
> > 
> > 
> > [1] https://sourceware.org/bugzilla/show_bug.cgi?id=28203
> > 
> > 
> > 
> > 
> 
> -- 
> +++++++++++++++++++
> 
> 
> 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
diff mbox series

Patch

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