svn commit: r338478 - in head: contrib/elftoolchain/elfcopy contrib/elftoolchain/libelf sys/sys

John Baldwin jhb at FreeBSD.org
Wed Sep 5 20:51:56 UTC 2018


Author: jhb
Date: Wed Sep  5 20:51:53 2018
New Revision: 338478
URL: https://svnweb.freebsd.org/changeset/base/338478

Log:
  Fix objcopy for little-endian MIPS64 objects.
  
  MIPS64 does not store the 'r_info' field of a relocation table entry as
  a 64-bit value consisting of a 32-bit symbol index in the high 32 bits
  and a 32-bit type in the low 32 bits as on other architectures.  Instead,
  the 64-bit 'r_info' field is really a 32-bit symbol index followed by four
  individual byte type fields.  For big-endian MIPS64, treating this as a
  64-bit integer happens to be compatible with the layout expected by other
  architectures (symbol index in upper 32-bits of resulting "native" 64-bit
  integer).  However, for little-endian MIPS64 the parsed 64-bit integer
  contains the symbol index in the low 32 bits and the 4 individual byte
  type fields in the upper 32-bits (but as if the upper 32-bits were
  byte-swapped).
  
  To cope, add two helper routines in gelf_getrel.c to translate between the
  correct native 'r_info' value and the value obtained after the normal
  byte-swap translation.  Use these routines in gelf_getrel(), gelf_getrela(),
  gelf_update_rel(), and gelf_update_rela().  This fixes 'readelf -r' on
  little-endian MIPS64 objects which was previously decoding incorrect
  relocations as well as 'objcopy: invalid symbox index' warnings from
  objcopy when extracting debug symbols from kernel modules.
  
  Even with this fixed, objcopy was still crashing when trying to extract
  debug symbols from little-endian MIPS64 modules.  The workaround in
  gelf_*rel*() depends on the current ELF object having a valid ELF header
  so that the 'e_machine' field can be compared against EM_MIPS.  objcopy
  was parsing the relocation entries to possibly rewrite the 'r_info' fields
  in the update_relocs() function before writing the initial ELF header to
  the destination object file.  Move the initial write of the ELF header
  earlier before copy_contents() so that update_relocs() uses the correct
  symbol index values.
  
  Note that this change should really go upstream.  The binutils readelf
  source has a similar hack for MIPS64EL though I implemented this version
  from scratch using the MIPS64 ABI PDF as a reference.
  
  Discussed with:	jkoshy
  Reviewed by:	emaste, imp
  Approved by:	re (gjb, kib)
  MFC after:	1 month
  Differential Revision:	https://reviews.freebsd.org/D15734

Added:
  head/contrib/elftoolchain/libelf/gelf_mips64el.c   (contents, props changed)
Modified:
  head/contrib/elftoolchain/elfcopy/main.c
  head/contrib/elftoolchain/libelf/Makefile
  head/contrib/elftoolchain/libelf/_libelf.h
  head/contrib/elftoolchain/libelf/gelf_rel.c
  head/contrib/elftoolchain/libelf/gelf_rela.c
  head/sys/sys/param.h

Modified: head/contrib/elftoolchain/elfcopy/main.c
==============================================================================
--- head/contrib/elftoolchain/elfcopy/main.c	Wed Sep  5 20:47:51 2018	(r338477)
+++ head/contrib/elftoolchain/elfcopy/main.c	Wed Sep  5 20:51:53 2018	(r338478)
@@ -372,6 +372,14 @@ create_elf(struct elfcopy *ecp)
 		create_symtab(ecp);
 
 	/*
+	 * Write the underlying ehdr. Note that it should be called
+	 * before elf_setshstrndx() since it will overwrite e->e_shstrndx.
+	 */
+	if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
+		errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
+		    elf_errmsg(-1));
+
+	/*
 	 * First processing of output sections: at this stage we copy the
 	 * content of each section from input to output object.  Section
 	 * content will be modified and printed (mcs) if need. Also content of
@@ -379,14 +387,6 @@ create_elf(struct elfcopy *ecp)
 	 * to symbol table changes.
 	 */
 	copy_content(ecp);
-
-	/*
-	 * Write the underlying ehdr. Note that it should be called
-	 * before elf_setshstrndx() since it will overwrite e->e_shstrndx.
-	 */
-	if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
-		errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
-		    elf_errmsg(-1));
 
 	/* Generate section name string table (.shstrtab). */
 	set_shstrtab(ecp);

Modified: head/contrib/elftoolchain/libelf/Makefile
==============================================================================
--- head/contrib/elftoolchain/libelf/Makefile	Wed Sep  5 20:47:51 2018	(r338477)
+++ head/contrib/elftoolchain/libelf/Makefile	Wed Sep  5 20:51:53 2018	(r338478)
@@ -35,6 +35,7 @@ SRCS=	elf.c							\
 	gelf_ehdr.c						\
 	gelf_getclass.c						\
 	gelf_fsize.c						\
+	gelf_mips64el.c						\
 	gelf_move.c						\
 	gelf_phdr.c						\
 	gelf_rel.c						\

Modified: head/contrib/elftoolchain/libelf/_libelf.h
==============================================================================
--- head/contrib/elftoolchain/libelf/_libelf.h	Wed Sep  5 20:47:51 2018	(r338477)
+++ head/contrib/elftoolchain/libelf/_libelf.h	Wed Sep  5 20:51:53 2018	(r338478)
@@ -216,12 +216,15 @@ int	(*_libelf_get_translator(Elf_Type _t, int _directi
 void	*_libelf_getphdr(Elf *_e, int _elfclass);
 void	*_libelf_getshdr(Elf_Scn *_scn, int _elfclass);
 void	_libelf_init_elf(Elf *_e, Elf_Kind _kind);
+int	_libelf_is_mips64el(Elf *e);
 int	_libelf_load_section_headers(Elf *e, void *ehdr);
 unsigned int _libelf_malign(Elf_Type _t, int _elfclass);
 Elf	*_libelf_memory(unsigned char *_image, size_t _sz, int _reporterror);
 size_t	_libelf_msize(Elf_Type _t, int _elfclass, unsigned int _version);
 void	*_libelf_newphdr(Elf *_e, int _elfclass, size_t _count);
 Elf	*_libelf_open_object(int _fd, Elf_Cmd _c, int _reporterror);
+Elf64_Xword _libelf_mips64el_r_info_tof(Elf64_Xword r_info);
+Elf64_Xword _libelf_mips64el_r_info_tom(Elf64_Xword r_info);
 struct _Libelf_Data *_libelf_release_data(struct _Libelf_Data *_d);
 Elf	*_libelf_release_elf(Elf *_e);
 Elf_Scn	*_libelf_release_scn(Elf_Scn *_s);

Added: head/contrib/elftoolchain/libelf/gelf_mips64el.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/contrib/elftoolchain/libelf/gelf_mips64el.c	Wed Sep  5 20:51:53 2018	(r338478)
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2018 John Baldwin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <gelf.h>
+
+#include "_libelf.h"
+
+ELFTC_VCSID("$Id$");
+
+int
+_libelf_is_mips64el(Elf *e)
+{
+
+	return (e->e_kind == ELF_K_ELF && e->e_byteorder == ELFDATA2LSB &&
+	    e->e_u.e_elf.e_ehdr.e_ehdr64->e_machine == EM_MIPS);
+}
+
+/*
+ * For MIPS64, the r_info field is actually stored as a 32-bit symbol
+ * index (r_sym) followed by four single-byte fields (r_ssym, r_type3,
+ * r_type2, and r_type).  The byte-swap for the little-endian case
+ * jumbles this incorrectly so compensate.
+ */
+Elf64_Xword
+_libelf_mips64el_r_info_tof(Elf64_Xword r_info)
+{
+	Elf64_Xword new_info;
+	uint8_t ssym, type3, type2, type;
+
+	ssym = r_info >> 24;
+	type3 = r_info >> 16;
+	type2 = r_info >> 8;
+	type = r_info;
+	new_info = r_info >> 32;
+	new_info |= (Elf64_Xword)ssym << 32;
+	new_info |= (Elf64_Xword)type3 << 40;
+	new_info |= (Elf64_Xword)type2 << 48;
+	new_info |= (Elf64_Xword)type << 56;
+	return (new_info);
+}
+
+Elf64_Xword
+_libelf_mips64el_r_info_tom(Elf64_Xword r_info)
+{
+	Elf64_Xword new_info;
+	uint8_t ssym, type3, type2, type;
+
+	ssym = r_info >> 32;
+	type3 = r_info >> 40;
+	type2 = r_info >> 48;
+	type = r_info >> 56;
+	new_info = (r_info & 0xffffffff) << 32;
+	new_info |= (Elf64_Xword)ssym << 24;
+	new_info |= (Elf64_Xword)type3 << 16;
+	new_info |= (Elf64_Xword)type2 << 8;
+	new_info |= (Elf64_Xword)type;
+	return (new_info);
+}

Modified: head/contrib/elftoolchain/libelf/gelf_rel.c
==============================================================================
--- head/contrib/elftoolchain/libelf/gelf_rel.c	Wed Sep  5 20:47:51 2018	(r338477)
+++ head/contrib/elftoolchain/libelf/gelf_rel.c	Wed Sep  5 20:51:53 2018	(r338478)
@@ -90,6 +90,9 @@ gelf_getrel(Elf_Data *ed, int ndx, GElf_Rel *dst)
 		rel64 = (Elf64_Rel *) d->d_data.d_buf + ndx;
 
 		*dst = *rel64;
+
+		if (_libelf_is_mips64el(e))
+			dst->r_info = _libelf_mips64el_r_info_tom(rel64->r_info);
 	}
 
 	return (dst);
@@ -156,6 +159,9 @@ gelf_update_rel(Elf_Data *ed, int ndx, GElf_Rel *dr)
 		rel64 = (Elf64_Rel *) d->d_data.d_buf + ndx;
 
 		*rel64 = *dr;
+
+		if (_libelf_is_mips64el(e))
+			rel64->r_info = _libelf_mips64el_r_info_tof(dr->r_info);
 	}
 
 	return (1);

Modified: head/contrib/elftoolchain/libelf/gelf_rela.c
==============================================================================
--- head/contrib/elftoolchain/libelf/gelf_rela.c	Wed Sep  5 20:47:51 2018	(r338477)
+++ head/contrib/elftoolchain/libelf/gelf_rela.c	Wed Sep  5 20:51:53 2018	(r338478)
@@ -91,6 +91,10 @@ gelf_getrela(Elf_Data *ed, int ndx, GElf_Rela *dst)
 		rela64 = (Elf64_Rela *) d->d_data.d_buf + ndx;
 
 		*dst = *rela64;
+
+		if (_libelf_is_mips64el(e))
+			dst->r_info =
+			    _libelf_mips64el_r_info_tom(rela64->r_info);
 	}
 
 	return (dst);
@@ -159,6 +163,9 @@ gelf_update_rela(Elf_Data *ed, int ndx, GElf_Rela *dr)
 		rela64 = (Elf64_Rela *) d->d_data.d_buf + ndx;
 
 		*rela64 = *dr;
+
+		if (_libelf_is_mips64el(e))
+			rela64->r_info = _libelf_mips64el_r_info_tof(dr->r_info);
 	}
 
 	return (1);

Modified: head/sys/sys/param.h
==============================================================================
--- head/sys/sys/param.h	Wed Sep  5 20:47:51 2018	(r338477)
+++ head/sys/sys/param.h	Wed Sep  5 20:51:53 2018	(r338478)
@@ -60,7 +60,7 @@
  *		in the range 5 to 9.
  */
 #undef __FreeBSD_version
-#define __FreeBSD_version 1200083	/* Master, propagated to newvers */
+#define __FreeBSD_version 1200084	/* Master, propagated to newvers */
 
 /*
  * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,


More information about the svn-src-all mailing list