Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 3 Jan 2003 16:09:37 +0100 (CET)
From:      Hartmut Brandt <brandt@fokus.gmd.de>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   sparc64/46730: kldxref does not work
Message-ID:  <200301031509.h03F9bmD003757@catssrv.fokus.gmd.de>

next in thread | raw e-mail | index | archive | help

>Number:         46730
>Category:       sparc64
>Synopsis:       kldxref does not work
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-sparc
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jan 03 07:10:00 PST 2003
>Closed-Date:
>Last-Modified:
>Originator:     Hartmut Brandt
>Release:        FreeBSD 5.0-CURRENT sparc64
>Organization:
FhI Fokus
>Environment:
System: FreeBSD catssrv.fokus.gmd.de 5.0-CURRENT FreeBSD 5.0-CURRENT #20: Fri Jan 3 14:08:11 CET 2003 hbb@catssrv.fokus.gmd.de:/opt/obj/usr/src/sys/CATSSRV sparc64


	
>Description:

kldxref fails to correctly read most kld files. The reason for this is that
kldxref assumes, that the ELF file containes only REL relocation records and
that only RELATIVE relocations need to be performed to read the module
meta data. If these two assumptions are true, effectively no relocation has
to be performed, because the module's load address is 0 in kldxref's case.
For sparc64 the first assumptions is certainly false, because sparc64
uses only RELA relocation records. Because of the missing relocation in this
case, the code tries to access 0 offsets in the file.
	
>How-To-Repeat:

Execute
	kldxref /boot/kernel

and watch the error messages.
	
>Fix:

The following patch fixes the problem in the following way:

1. It reads the REL and RELA relocation records.
2. At the places where pointers are accessed in the object file two additional
routines are called that look whether relocation records exist for the object
file part that is to be red. If there are it applies the relocations.

The patch currently handles REL records of type R_I386_RELATIVE and RELA
records of type R_SPARC_RELATIVE. Alle other combinations result in a
warning message. This means that alpha and ia64 user will probably see warning
messages. This can be overcome by either adding the corresponding alpha
and ia64 relocation types (R_ALPHA_RELATIVE and R_IA64_REL64LSB (I suppose)) or
removing the warning message itself.

The entire handling of REL records could be removed, because it effectively is
a NOP a.t.m. I suggest leaving it, because if in the future other relocation
types refer to the file offsets in question, we will get a warning message.

Index: ef.c
===================================================================
RCS file: /usr/ncvs/src/usr.sbin/kldxref/ef.c,v
retrieving revision 1.5
diff -c -r1.5 ef.c
*** ef.c	17 Jul 2002 23:41:58 -0000	1.5
--- ef.c	3 Jan 2003 14:56:09 -0000
***************
*** 151,157 ****
--- 151,166 ----
  	Elf_Hashelt hashhdr[2];
  /*	int plttype = DT_REL;*/
  	int error;
+ 	Elf_Off rel_off;
+ 	Elf_Off rela_off;
+ 	int rel_sz;
+ 	int rela_sz;
+ 	int rel_entry;
+ 	int rela_entry;
  
+ 	rel_off = rela_off = 0;
+ 	rel_sz = rela_sz = 0;
+ 	rel_entry = rela_entry = 0;
  	for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) {
  		switch (dp->d_tag) {
  		case DT_HASH:
***************
*** 187,192 ****
--- 196,231 ----
  			if (dp->d_un.d_val != sizeof(Elf_Sym))
  				return EFTYPE;
  			break;
+ 		case DT_REL:
+ 			if (rel_off != 0)
+ 				warnx("second DT_REL entry ignored");
+ 			rel_off = dp->d_un.d_ptr;
+ 			break;
+ 		case DT_RELSZ:
+ 			if (rel_sz != 0)
+ 				warnx("second DT_RELSZ entry ignored");
+ 			rel_sz = dp->d_un.d_val;
+ 			break;
+ 		case DT_RELENT:
+ 			if (rel_entry != 0)
+ 				warnx("second DT_RELENT entry ignored");
+ 			rel_entry = dp->d_un.d_val;
+ 			break;
+ 		case DT_RELA:
+ 			if (rela_off != 0)
+ 				warnx("second DT_RELA entry ignored");
+ 			rela_off = dp->d_un.d_ptr;
+ 			break;
+ 		case DT_RELASZ:
+ 			if (rela_sz != 0)
+ 				warnx("second DT_RELASZ entry ignored");
+ 			rela_sz = dp->d_un.d_val;
+ 			break;
+ 		case DT_RELAENT:
+ 			if (rela_entry != 0)
+ 				warnx("second DT_RELAENT entry ignored");
+ 			rela_entry = dp->d_un.d_val;
+ 			break;
  		}
  	}
  	if (ef->ef_symoff == 0) {
***************
*** 210,218 ****
--- 249,360 ----
  		warnx("can't load .dynstr section");
  		return EIO;
  	}
+ 	if (rel_off != 0) {
+ 		if (rel_entry == 0) {
+ 			warnx("%s: no DT_RELENT for DT_REL", ef->ef_name);
+ 			return (EFTYPE);
+ 		}
+ 		if (rel_entry != sizeof(Elf_Rel)) {
+ 			warnx("%s: inconsistent DT_RELENT value",
+ 			    ef->ef_name);
+ 			return (EFTYPE);
+ 		}
+ 		if (rel_sz % rel_entry != 0) {
+ 			warnx("%s: inconsistent values for DT_RELSZ and "
+ 			    "DT_RELENT", ef->ef_name);
+ 			return (EFTYPE);
+ 		}
+ 		if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz,
+ 		    (void **)&ef->ef_rel) != 0) {
+ 			warnx("%s: cannot load DT_REL section", ef->ef_name);
+ 			return (EIO);
+ 		}
+ 		ef->ef_relsz = rel_sz / rel_entry;
+ 		if (ef->ef_verbose)
+ 			warnx("%s: %d REL entries", ef->ef_name,
+ 			    ef->ef_relsz);
+ 	}
+ 	if (rela_off != 0) {
+ 		if (rela_entry == 0) {
+ 			warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name);
+ 			return (EFTYPE);
+ 		}
+ 		if (rela_entry != sizeof(Elf_Rela)) {
+ 			warnx("%s: inconsistent DT_RELAENT value",
+ 			    ef->ef_name);
+ 			return (EFTYPE);
+ 		}
+ 		if (rela_sz % rela_entry != 0) {
+ 			warnx("%s: inconsistent values for DT_RELASZ and "
+ 			    "DT_RELAENT", ef->ef_name);
+ 			return (EFTYPE);
+ 		}
+ 		if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz,
+ 		    (void **)&ef->ef_rela) != 0) {
+ 			warnx("%s: cannot load DT_RELA section", ef->ef_name);
+ 			return (EIO);
+ 		}
+ 		ef->ef_relasz = rela_sz / rela_entry;
+ 		if (ef->ef_verbose)
+ 			warnx("%s: %d RELA entries", ef->ef_name,
+ 			    ef->ef_relasz);
+ 	}
  	return 0;
  }
  
+ /*
+  * Apply relocations to the values we got from the file.
+  */
+ static int
+ ef_reloc(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
+ {
+ 	const Elf_Rel *r;
+ 	const Elf_Rela *a;
+ #ifdef R_SPARC_RELATIVE
+ 	Elf_Word w;
+ #endif
+ 
+ 	for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
+ 		if (r->r_offset >= offset && r->r_offset < offset + len) {
+ 			switch (ELF_R_TYPE(r->r_info)) {
+ 
+ #ifdef R_386_RELATIVE
+ 			  case R_386_RELATIVE:
+ 				/* load address is 0 - nothing to do */
+ 				break;
+ #endif
+ 
+ 			  default:
+ 				warnx("unhandled relocation type %u",
+ 				    ELF_R_TYPE(r->r_info));
+ 				break;
+ 			}
+ 		}
+ 	}
+ 
+ 	for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
+ 		if (a->r_offset >= offset && a->r_offset < offset + len) {
+ 			switch (ELF_R_TYPE(a->r_info)) {
+ 
+ #ifdef R_SPARC_RELATIVE
+ 			  case R_SPARC_RELATIVE:
+ 				/* load address is 0 */
+ 				w = a->r_addend;
+ 				memcpy((u_char *)dest + (a->r_offset - offset),
+ 				    &w, sizeof(w));
+ 				break;
+ #endif
+ 
+ 			  default:
+ 				warnx("unhandled relocation type %u",
+ 				    ELF_R_TYPE(a->r_info));
+ 				break;
+ 			}
+ 		}
+ 	}
+ 	return (0);
+ }
+ 
  int
  ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
  {
***************
*** 259,264 ****
--- 401,423 ----
  }
  
  int
+ ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+ {
+ 	u_long ofs = ef_get_offset(ef, offset);
+ 	int error;
+ 
+ 	if (ofs == 0) {
+ 		if (ef->ef_verbose)
+ 			warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
+ 			    ef->ef_name, (long)offset, ofs);
+ 		return EFAULT;
+ 	}
+ 	if ((error = ef_read(ef, ofs, len, dest)) != 0)
+ 		return (error);
+ 	return (ef_reloc(ef, offset, len, dest));
+ }
+ 
+ int
  ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
  {
  	int error;
***************
*** 267,272 ****
--- 426,445 ----
  	if (*ptr == NULL)
  		return ENOMEM;
  	error = ef_seg_read(ef, offset, len, *ptr);
+ 	if (error)
+ 		free(*ptr);
+ 	return error;
+ }
+ 
+ int
+ ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
+ {
+ 	int error;
+ 
+ 	*ptr = malloc(len);
+ 	if (*ptr == NULL)
+ 		return ENOMEM;
+ 	error = ef_seg_read_rel(ef, offset, len, *ptr);
  	if (error)
  		free(*ptr);
  	return error;
Index: ef.h
===================================================================
RCS file: /usr/ncvs/src/usr.sbin/kldxref/ef.h,v
retrieving revision 1.2
diff -c -r1.2 ef.h
*** ef.h	11 Apr 2002 09:30:15 -0000	1.2
--- ef.h	3 Jan 2003 14:56:09 -0000
***************
*** 28,33 ****
--- 28,37 ----
  	int		ef_nsegs;
  	Elf_Phdr *	ef_segs[2];
  	int		ef_verbose;
+ 	Elf_Rel *	ef_rel;			/* relocation table */
+ 	int		ef_relsz;		/* number of entries */
+ 	Elf_Rela *	ef_rela;		/* relocation table */
+ 	int		ef_relasz;		/* number of entries */
  } *elf_file_t;
  
  __BEGIN_DECLS
***************
*** 36,42 ****
--- 40,48 ----
  int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
  int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr);
  int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
+ int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
  int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr);
+ int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void**ptr);
  int ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym);
  __END_DECLS
  
Index: kldxref.c
===================================================================
RCS file: /usr/ncvs/src/usr.sbin/kldxref/kldxref.c,v
retrieving revision 1.4
diff -c -r1.4 kldxref.c
*** kldxref.c	22 Apr 2002 13:44:44 -0000	1.4
--- kldxref.c	3 Jan 2003 14:56:09 -0000
***************
*** 229,238 ****
  		check(ef_lookup_symbol(&ef, "__stop_set_" MDT_SETNAME, &sym));
  		finish = sym->st_value;
  		entries = (finish - start) / sizeof(void *);
! 		check(ef_seg_read_entry(&ef, start, sizeof(*p) * entries, (void**)&p));
  		orgp = p;
  		while(entries--) {
! 			check(ef_seg_read(&ef, (Elf_Off)*p, sizeof(md), &md));
  			p++;
  			check(ef_seg_read(&ef, (Elf_Off)md.md_cval, sizeof(cval), cval));
  			cval[MAXMODNAME] = '\0';
--- 229,239 ----
  		check(ef_lookup_symbol(&ef, "__stop_set_" MDT_SETNAME, &sym));
  		finish = sym->st_value;
  		entries = (finish - start) / sizeof(void *);
! 		check(ef_seg_read_entry_rel(&ef, start, sizeof(*p) * entries,
! 		    (void**)&p));
  		orgp = p;
  		while(entries--) {
! 			check(ef_seg_read_rel(&ef, (Elf_Off)*p, sizeof(md), &md));
  			p++;
  			check(ef_seg_read(&ef, (Elf_Off)md.md_cval, sizeof(cval), cval));
  			cval[MAXMODNAME] = '\0';
	


>Release-Note:
>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-sparc" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200301031509.h03F9bmD003757>