Subject: ld.elf_so and hppa
To: None <tech-userlevel@netbsd.org>
From: Matt Fredette <fredette@theory.lcs.mit.edu>
List: tech-userlevel
Date: 07/01/2002 13:05:24
Hi.  I'm looking for comments on the following patch to ld.elf_so.

Background: HPPA's runtime architecture is tough when it comes to PIC 
code.  Before you can call a PIC function, you must load a register with 
the _GLOBAL_OFFSET_TABLE_ of the module the function is in.

This means that a pointer to a function isn't necessarily the address
of the first instruction of the function.  It might instead be the
address of a two-word blob of data: one word being the address of the 
first instruction of the function, and the other word being the GOT 
value that you must load.  In the HPPA world this blob is called a 
PLABEL.  Adding to the fun, you can tell what kind of function pointer 
you have by looking at bit 1 - if set, you have a pointer to a PLABEL,
otherwise you have a normal function pointer.

IA-64 has an almost identical system.

Normally things work out because the compiler hides this magic from you.
It does bite you when you need to conjure up a pointer to a PIC function 
yourself from its raw address, or when you're handed a function pointer 
and you really need to know the address of the first instruction of the 
function.  ld.elf_so does both.

So I came up with two simple functions that handle this, that could 
someday also have IA-64 versions.

/*
 * This allocates a PLABEL.  If called with a non-NULL def, the
 * plabel is for the function associated with that definition  
 * in the defining object defobj, plus the given addend.  If
 * called with a NULL def, the plabel is for the function at
 * the (unrelocated) address in addend in the object defobj.
 */
Elf_Addr _rtld_alloc_plabel(const Obj_Entry *defobj, const Elf_Sym *def,
    Elf_Addr addend);

/*              
 * If a pointer is a PLABEL, this unwraps it and returns the
 * function's true address.
 */ 
const void *_rtld_unwrap_plabel(const void *addr);

Comments?  Thanks -

Matt

-- 
Matt Fredette

[snip]
Index: headers.c
===================================================================
RCS file: /cvsroot/basesrc/libexec/ld.elf_so/headers.c,v
retrieving revision 1.9
diff -u -r1.9 headers.c
--- headers.c	2001/04/25 12:24:50	1.9
+++ headers.c	2002/07/01 16:23:21
@@ -66,6 +66,7 @@
 	Elf_Sword	plttype = DT_REL;
 	Elf_Addr        relsz = 0, relasz = 0;
 	Elf_Addr	pltrelsz = 0;
+	Elf_Addr	init = 0, fini = 0;
 
 	for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; ++dynp) {
 		switch (dynp->d_tag) {
@@ -198,13 +199,11 @@
 			break;
 
 		case DT_INIT:
-			obj->init = (void (*) __P((void)))
-			    (obj->relocbase + dynp->d_un.d_ptr);
+			init = dynp->d_un.d_ptr;
 			break;
 
 		case DT_FINI:
-			obj->fini = (void (*) __P((void)))
-			    (obj->relocbase + dynp->d_un.d_ptr);
+			fini = dynp->d_un.d_ptr;
 			break;
 
 		case DT_DEBUG:
@@ -245,6 +244,22 @@
 		obj->pltrellim = 0;
 		obj->pltrelalim = (const Elf_Rela *)((caddr_t)obj->pltrela + pltrelsz);
 	}
+
+#if defined(RTLD_LOADER) && defined(__hppa__)
+	if (init != 0)
+		obj->init = (void (*) __P((void)))
+		    _rtld_alloc_plabel(obj, NULL, init);
+	if (fini != 0)
+		obj->fini = (void (*) __P((void)))
+		    _rtld_alloc_plabel(obj, NULL, fini);
+#else
+	if (init != 0)
+		obj->init = (void (*) __P((void)))
+		    (obj->relocbase + init);
+	if (fini != 0)
+		obj->fini = (void (*) __P((void)))
+		    (obj->relocbase + fini);
+#endif
 
 	if (dyn_rpath != NULL) {
 		_rtld_add_paths(&obj->rpaths, obj->strtab +
Index: rtld.c
===================================================================
RCS file: /cvsroot/basesrc/libexec/ld.elf_so/rtld.c,v
retrieving revision 1.50
diff -u -r1.50 rtld.c
--- rtld.c	2002/06/01 23:50:53	1.50
+++ rtld.c	2002/07/01 16:23:21
@@ -800,8 +800,13 @@
 		}
 	}
 	
-	if (def != NULL)
+	if (def != NULL) {
+#if defined(__hppa__)
+		if (ELF_ST_TYPE(def->st_info) == STT_FUNC)
+			return (void *)_rtld_alloc_plabel(defobj, def, 0);
+#endif /* __hppa__ */
 		return defobj->relocbase + def->st_value;
+	}
 	
 	_rtld_error("Undefined symbol \"%s\"", name);
 	return NULL;
@@ -813,10 +818,14 @@
 	Dl_info *info;
 {
 	const Obj_Entry *obj;
-	const Elf_Sym *def;
+	const Elf_Sym *def, *best_def;
 	void *symbol_addr;
 	unsigned long symoffset;
 	
+#if defined(__hppa__)
+	addr = _rtld_unwrap_plabel(addr);
+#endif /* __hppa__ */
+
 	obj = _rtld_obj_from_addr(addr);
 	if (obj == NULL) {
 		_rtld_error("No shared object contains address");
@@ -831,6 +840,7 @@
 	 * Walk the symbol list looking for the symbol whose address is
 	 * closest to the address sent in.
 	 */
+	best_def = NULL;
 	for (symoffset = 0; symoffset < obj->nchains; symoffset++) {
 		def = obj->symtab + symoffset;
 
@@ -853,11 +863,18 @@
 		/* Update our idea of the nearest symbol. */
 		info->dli_sname = obj->strtab + def->st_name;
 		info->dli_saddr = symbol_addr;
+		best_def = def;
 
 		/* Exact match? */
 		if (info->dli_saddr == addr)
 			break;
 	}
+
+#if defined(__hppa__)
+	if (best_def != NULL && ELF_ST_TYPE(best_def->st_info) == STT_FUNC)
+		info->dli_saddr = (void *)_rtld_alloc_plabel(obj, best_def, 0);
+#endif /* __hppa__ */
+
 	return 1;
 }
 
[snip]