Date: Wed, 5 Sep 2018 20:51:54 +0000 (UTC) From: John Baldwin <jhb@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r338478 - in head: contrib/elftoolchain/elfcopy contrib/elftoolchain/libelf sys/sys Message-ID: <201809052051.w85KpsCl064064@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
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,
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201809052051.w85KpsCl064064>