diff mbox series

[uclibc-ng-devel,11/32,ARM,FDPIC] rtld: Add lazy binding support

Message ID 20180704155605.1892-12-christophe.lyon@st.com
State Accepted
Headers show
Series FDPIC ABI for ARM | expand

Commit Message

Christophe Lyon July 4, 2018, 3:55 p.m. UTC
Add support for R_ARM_FUNCDESC_VALUE and implement _dl_linux_resolver
for FDPIC on ARM.

	* ldso/ldso/arm/elfinterp.c (_dl_linux_resolver): Support __FDPIC__.
	(_dl_do_lazy_reloc): Likewise.
	* ldso/ldso/arm/resolve.S (_dl_linux_resolve): Likewise.

Signed-off-by: Mickaël Guêné <mickael.guene@st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@st.com>

Comments

Christophe Lyon Aug. 6, 2018, 1:25 p.m. UTC | #1
On 04/07/2018 17:55, Christophe Lyon wrote:
> Add support for R_ARM_FUNCDESC_VALUE and implement _dl_linux_resolver
> for FDPIC on ARM.

Here is v2 of this patch.
From 060a6ac2e81f9aa739d2eaf31b151f9e7e7d468f Mon Sep 17 00:00:00 2001
From: Christophe Lyon <christophe.lyon@st.com>
Date: Thu, 28 Mar 2013 10:46:55 +0100
Subject: [PATCH 11/32] [ARM][FDPIC] rtld: Add lazy binding support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add support for R_ARM_FUNCDESC_VALUE and implement _dl_linux_resolver
for FDPIC on ARM.

	* ldso/ldso/arm/elfinterp.c (_dl_linux_resolver): Support __FDPIC__.
	(_dl_do_lazy_reloc): Likewise.
	* ldso/ldso/arm/resolve.S (_dl_linux_resolve): Likewise.

Signed-off-by: Mickaël Guêné <mickael.guene@st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@st.com>

diff --git a/ldso/ldso/arm/elfinterp.c b/ldso/ldso/arm/elfinterp.c
index 402ba96..1d79d92 100644
--- a/ldso/ldso/arm/elfinterp.c
+++ b/ldso/ldso/arm/elfinterp.c
@@ -34,13 +34,69 @@
 
 extern int _dl_linux_resolve(void);
 
-unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
-{
 #if __FDPIC__
-  /* FIXME: implement.  */
-  while(1) ;
-  return 0;
+unsigned long _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_offet)
+{
+	ELF_RELOC *this_reloc;
+	char *strtab;
+	ElfW(Sym) *symtab;
+	int symtab_index;
+	char *rel_addr;
+	char *new_addr;
+	struct funcdesc_value funcval;
+	struct funcdesc_value volatile *got_entry;
+	char *symname;
+	struct symbol_ref sym_ref;
+
+	rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
+
+	this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_offet);
+	symtab_index = ELF_R_SYM(this_reloc->r_info);
+
+	symtab = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB];
+	strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
+	sym_ref.sym = &symtab[symtab_index];
+	sym_ref.tpnt = NULL;
+	symname= strtab + symtab[symtab_index].st_name;
+
+	/* Address of GOT entry fix up */
+	got_entry = (struct funcdesc_value *) DL_RELOC_ADDR(tpnt->loadaddr, this_reloc->r_offset);
+
+	/* Get the address to be used to fill in the GOT entry.  */
+	new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, NULL, 0, &sym_ref);
+	if (!new_addr) {
+		new_addr = _dl_find_hash(symname, NULL, NULL, 0, &sym_ref);
+		if (!new_addr) {
+			_dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
+			_dl_exit(1);
+		}
+	}
+
+	funcval.entry_point = new_addr;
+	funcval.got_value = sym_ref.tpnt->loadaddr.got_value;
+
+#if defined (__SUPPORT_LD_DEBUG__)
+	if (_dl_debug_bindings) {
+		_dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
+		if (_dl_debug_detail)
+			_dl_dprintf(_dl_debug_file,
+				    "\n\tpatched (%x,%x) ==> (%x,%x) @ %x\n",
+				    got_entry->entry_point, got_entry->got_value,
+				    funcval.entry_point, funcval.got_value,
+				    got_entry);
+	}
+	if (1 || !_dl_debug_nofixups) {
+		*got_entry = funcval;
+	}
+#else
+	*got_entry = funcval;
+#endif
+
+	return got_entry;
+}
 #else
+unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
+{
 	ELF_RELOC *this_reloc;
 	char *strtab;
 	char *symname;
@@ -93,8 +149,8 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
 #endif
 
 	return new_addr;
-#endif
 }
+#endif
 
 static int
 _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope,
@@ -348,7 +404,7 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 	int reloc_type;
 	unsigned long *reloc_addr;
 
-	reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
+	reloc_addr = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
 	reloc_type = ELF_R_TYPE(rpnt->r_info);
 
 #if defined (__SUPPORT_LD_DEBUG__)
@@ -358,9 +414,20 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 		switch (reloc_type) {
 			case R_ARM_NONE:
 				break;
+
 			case R_ARM_JUMP_SLOT:
-				*reloc_addr += (unsigned long) tpnt->loadaddr;
+				*reloc_addr = DL_RELOC_ADDR(tpnt->loadaddr, *reloc_addr);
+				break;
+#ifdef __FDPIC__
+			case R_ARM_FUNCDESC_VALUE:
+				{
+					struct funcdesc_value *dst = (struct funcdesc_value *) reloc_addr;
+
+					dst->entry_point = DL_RELOC_ADDR(tpnt->loadaddr, dst->entry_point);
+					dst->got_value = tpnt->loadaddr.got_value;
+				}
 				break;
+#endif
 			default:
 				return -1; /*call _dl_exit(1) */
 		}
diff --git a/ldso/ldso/arm/resolve.S b/ldso/ldso/arm/resolve.S
index 2a51643..039a6b7 100644
--- a/ldso/ldso/arm/resolve.S
+++ b/ldso/ldso/arm/resolve.S
@@ -107,6 +107,27 @@
  .type _dl_linux_resolve,%function
  .align 4;
 
+#if __FDPIC__
+/*
+ *    _dl_linux_resolve() FDPIC version receives the following parameters from
+ *    lazy PLT entry:
+ *    R12: GOT address for the resolver GOT
+ *    SP[0]: funcdesc_value_reloc_offset(foo)
+ *    R9: GOT address for the caller GOT
+ *    _dl_linux_resolver() will return a function descriptor address in R0.
+ */
+_dl_linux_resolve:
+	push  {r0, r1, r2, r3, r14}
+	ldr   r0, [r9, #8]
+	ldr   r1, [sp, #20]
+	mov   r9, r12
+	blx   _dl_linux_resolver
+	ldr   r9, [r0, #4]
+	ldr   r12, [r0]
+	pop   {r0, r1, r2, r3, r14}
+	add   sp, sp, #4
+	bx    r12
+#else
 _dl_linux_resolve:
          @ _dl_linux_resolver is a standard subroutine call, therefore it
          @ preserves everything except r0-r3 (a1-a4), ip and lr.  This
@@ -129,6 +150,7 @@ _dl_linux_resolve:
         ldmia sp!, {r0, r1, r2, r3, r4, lr}
 
         BX(ip)
+#endif /* __FDPIC__ */
 #else
        @ In the thumb case _dl_linux_resolver is thumb.  If a bl is used
        @ from arm code the linker will insert a stub call which, with
diff mbox series

Patch

diff --git a/ldso/ldso/arm/elfinterp.c b/ldso/ldso/arm/elfinterp.c
index 1435c2c..3bcd675 100644
--- a/ldso/ldso/arm/elfinterp.c
+++ b/ldso/ldso/arm/elfinterp.c
@@ -34,13 +34,69 @@ 
 
 extern int _dl_linux_resolve(void);
 
-unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
-{
 #if __FDPIC__
-  /* FIXME: implement.  */
-  while(1) ;
-  return 0;
+unsigned long _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_offet)
+{
+	ELF_RELOC *this_reloc;
+	char *strtab;
+	ElfW(Sym) *symtab;
+	int symtab_index;
+	char *rel_addr;
+	char *new_addr;
+	struct funcdesc_value funcval;
+	struct funcdesc_value volatile *got_entry;
+	char *symname;
+	struct symbol_ref sym_ref;
+
+	rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
+
+	this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_offet);
+	symtab_index = ELF_R_SYM(this_reloc->r_info);
+
+	symtab = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB];
+	strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
+	sym_ref.sym = &symtab[symtab_index];
+	sym_ref.tpnt = NULL;
+	symname= strtab + symtab[symtab_index].st_name;
+
+	/* Address of GOT entry fix up */
+	got_entry = (struct funcdesc_value *) DL_RELOC_ADDR(tpnt->loadaddr, this_reloc->r_offset);
+
+	/* Get the address to be used to fill in the GOT entry.  */
+	new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, NULL, 0, &sym_ref);
+	if (!new_addr) {
+		new_addr = _dl_find_hash(symname, NULL, NULL, 0, &sym_ref);
+		if (!new_addr) {
+			_dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
+			_dl_exit(1);
+		}
+	}
+
+	funcval.entry_point = new_addr;
+	funcval.got_value = sym_ref.tpnt->loadaddr.got_value;
+
+#if defined (__SUPPORT_LD_DEBUG__)
+	if (_dl_debug_bindings) {
+		_dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
+		if (_dl_debug_detail)
+			_dl_dprintf(_dl_debug_file,
+				    "\n\tpatched (%x,%x) ==> (%x,%x) @ %x\n",
+				    got_entry->entry_point, got_entry->got_value,
+				    funcval.entry_point, funcval.got_value,
+				    got_entry);
+	}
+	if (1 || !_dl_debug_nofixups) {
+		*got_entry = funcval;
+	}
+#else
+	*got_entry = funcval;
+#endif
+
+	return got_entry;
+}
 #else
+unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
+{
 	ELF_RELOC *this_reloc;
 	char *strtab;
 	char *symname;
@@ -93,8 +149,8 @@  unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
 #endif
 
 	return new_addr;
-#endif
 }
+#endif
 
 static int
 _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope,
@@ -346,7 +402,7 @@  _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 	int reloc_type;
 	unsigned long *reloc_addr;
 
-	reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
+	reloc_addr = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
 	reloc_type = ELF_R_TYPE(rpnt->r_info);
 
 #if defined (__SUPPORT_LD_DEBUG__)
@@ -356,8 +412,17 @@  _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 		switch (reloc_type) {
 			case R_ARM_NONE:
 				break;
+
 			case R_ARM_JUMP_SLOT:
-				*reloc_addr += (unsigned long) tpnt->loadaddr;
+				*reloc_addr = DL_RELOC_ADDR(tpnt->loadaddr, *reloc_addr);
+				break;
+			case R_ARM_FUNCDESC_VALUE:
+				{
+					struct funcdesc_value *dst = (struct funcdesc_value *) reloc_addr;
+
+					dst->entry_point = DL_RELOC_ADDR(tpnt->loadaddr, dst->entry_point);
+					dst->got_value = tpnt->loadaddr.got_value;
+				}
 				break;
 			default:
 				return -1; /*call _dl_exit(1) */
diff --git a/ldso/ldso/arm/resolve.S b/ldso/ldso/arm/resolve.S
index 2a51643..039a6b7 100644
--- a/ldso/ldso/arm/resolve.S
+++ b/ldso/ldso/arm/resolve.S
@@ -107,6 +107,27 @@ 
  .type _dl_linux_resolve,%function
  .align 4;
 
+#if __FDPIC__
+/*
+ *    _dl_linux_resolve() FDPIC version receives the following parameters from
+ *    lazy PLT entry:
+ *    R12: GOT address for the resolver GOT
+ *    SP[0]: funcdesc_value_reloc_offset(foo)
+ *    R9: GOT address for the caller GOT
+ *    _dl_linux_resolver() will return a function descriptor address in R0.
+ */
+_dl_linux_resolve:
+	push  {r0, r1, r2, r3, r14}
+	ldr   r0, [r9, #8]
+	ldr   r1, [sp, #20]
+	mov   r9, r12
+	blx   _dl_linux_resolver
+	ldr   r9, [r0, #4]
+	ldr   r12, [r0]
+	pop   {r0, r1, r2, r3, r14}
+	add   sp, sp, #4
+	bx    r12
+#else
 _dl_linux_resolve:
          @ _dl_linux_resolver is a standard subroutine call, therefore it
          @ preserves everything except r0-r3 (a1-a4), ip and lr.  This
@@ -129,6 +150,7 @@  _dl_linux_resolve:
         ldmia sp!, {r0, r1, r2, r3, r4, lr}
 
         BX(ip)
+#endif /* __FDPIC__ */
 #else
        @ In the thumb case _dl_linux_resolver is thumb.  If a bl is used
        @ from arm code the linker will insert a stub call which, with