diff options
Diffstat (limited to 'src')
74 files changed, 40315 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..7372fc1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,43 @@ +## Process this file with automake to create Makefile.in + +AUTOMAKE_OPTIONS = 1.4 gnu + +SUBDIRS = rtld + +PKGVERSION = "\"@PKGVERSION@\"" +REPORT_BUGS_TO = "\"@REPORT_BUGS_TO@\"" + +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -Wall -Wno-pointer-sign +AM_CFLAGS = -Wall -Wno-pointer-sign +AM_CPPFLAGS = -DSBINDIR='"@sbindir@"' -DBINDIR='"@bindir@"' \ + -DEXECSTACK_PROG="\"`echo execstack | sed '$(transform)'`\"" \ + -DPRELINK_PROG="\"`echo prelink | sed '$(transform)'`\"" \ + -DPRELINK_RTLD_PROG="\"`echo prelink-rtld | \ + sed '$(transform)'`\"" \ + -DEXEEXT='"$(EXEEXT)"' \ + -DPKGVERSION=$(PKGVERSION) \ + -DREPORT_BUGS_TO=$(REPORT_BUGS_TO) +INCLUDES = @GELFINCLUDE@ + +sbin_PROGRAMS = prelink +bin_PROGRAMS = execstack + +arch_SOURCES = arch-i386.c arch-alpha.c arch-ppc.c arch-ppc64.c \ + arch-sparc.c arch-sparc64.c arch-x86_64.c arch-mips.c \ + arch-s390.c arch-s390x.c arch-arm.c arch-sh.c arch-ia64.c +common_SOURCES = checksum.c data.c dso.c dwarf2.c dwarf2.h fptr.c fptr.h \ + hashtab.c hashtab.h mdebug.c prelink.h stabs.c crc32.c \ + wrap-file.c canonicalize.c reloc-info.c reloc-info.h +prelink_SOURCES = cache.c conflict.c cxx.c doit.c exec.c execle_open.c get.c \ + gather.c layout.c main.c prelink.c \ + prelinktab.h reloc.c reloc.h space.c undo.c undoall.c \ + verify.c md5.c md5.h sha.c sha.h \ + $(common_SOURCES) $(arch_SOURCES) +prelink_LDADD = @LIBGELF@ -liberty +prelink_LDFLAGS = + +execstack_SOURCES = execstack.c $(common_SOURCES) $(arch_SOURCES) +execstack_LDADD = -liberty +execstack_LDFLAGS = + +extra_DIST = makecrc.c diff --git a/src/arch-alpha.c b/src/arch-alpha.c new file mode 100644 index 0000000..7802a3e --- /dev/null +++ b/src/arch-alpha.c @@ -0,0 +1,505 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" + +static int +alpha_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + return 0; +} + +static int +alpha_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", dso->filename); + return 1; +} + +static int +alpha_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + if (GELF_R_TYPE (rela->r_info) == R_ALPHA_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_ALPHA_JMP_SLOT) + { + GElf_Addr val = read_ule64 (dso, rela->r_offset); + + if (val >= start) + { + write_le64 (dso, rela->r_offset, val + adjust); + if (val == rela->r_addend) + rela->r_addend += adjust; + } + } + else if (GELF_R_TYPE (rela->r_info) == R_ALPHA_GLOB_DAT) + { + GElf_Addr val = read_ule64 (dso, rela->r_offset) - rela->r_addend; + + if (val && val >= start) + write_le64 (dso, rela->r_offset, val + adjust + rela->r_addend); + } + return 0; +} + +static int +alpha_prelink_rel (struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", info->dso->filename); + return 1; +} + +static void +alpha_fixup_plt (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr, + GElf_Addr value) +{ + Elf64_Sxword disp; + Elf64_Addr plt; + + relaaddr -= dso->info[DT_JMPREL]; + relaaddr /= sizeof (Elf64_Rela); + relaaddr *= 12; + plt = dso->info[DT_PLTGOT] + 32 + relaaddr; + disp = ((Elf64_Sxword) (value - plt - 12)) / 4; + if (disp >= -0x100000 && disp < 0x100000) + { + int32_t hi, lo; + + hi = value - plt; + lo = (int16_t) hi; + hi = (hi - lo) >> 16; + + /* ldah $27,hi($27) + lda $27,lo($27) + br $31,value */ + write_le32 (dso, plt, 0x277b0000 | (hi & 0xffff)); + write_le32 (dso, plt + 4, 0x237b0000 | (lo & 0xffff)); + write_le32 (dso, plt + 8, 0xc3e00000 | (disp & 0x1fffff)); + } + else + { + int32_t hi, lo; + + hi = rela->r_offset - plt; + lo = (int16_t) hi; + hi = (hi - lo) >> 16; + + /* ldah $27,hi($27) + ldq $27,lo($27) + jmp $31,($27) */ + write_le32 (dso, plt, 0x277b0000 | (hi & 0xffff)); + write_le32 (dso, plt + 4, 0xa77b0000 | (lo & 0xffff)); + write_le32 (dso, plt + 8, 0x6bfb0000); + } +} + +static int +alpha_is_indirect_plt (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + Elf64_Addr pltaddr; + uint32_t plt[3]; + int32_t hi, lo; + + relaaddr -= dso->info[DT_JMPREL]; + relaaddr /= sizeof (Elf64_Rela); + relaaddr *= 12; + pltaddr = dso->info[DT_PLTGOT] + 32 + relaaddr; + hi = rela->r_offset - pltaddr; + lo = (int16_t) hi; + hi = (hi - lo) >> 16; + plt[0] = read_ule32 (dso, pltaddr); + plt[1] = read_ule32 (dso, pltaddr + 4); + plt[2] = read_ule32 (dso, pltaddr + 8); + if (plt[0] == (0x277b0000 | (hi & 0xffff)) + && plt[1] == (0xa77b0000 | (lo & 0xffff)) + && plt[2] == 0x6bfb0000) + return 1; + return 0; +} + +static int +alpha_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso; + GElf_Addr value; + + if (GELF_R_TYPE (rela->r_info) == R_ALPHA_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_ALPHA_NONE) + /* Fast path: nothing to do. */ + return 0; + dso = info->dso; + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ALPHA_GLOB_DAT: + case R_ALPHA_REFQUAD: + case R_ALPHA_DTPREL64: + write_le64 (dso, rela->r_offset, value); + break; + case R_ALPHA_JMP_SLOT: + write_le64 (dso, rela->r_offset, value); + alpha_fixup_plt (dso, rela, relaaddr, value); + break; + /* DTPMOD64 and TPREL64 is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_ALPHA_DTPMOD64: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_ALPHA_DTPMOD64 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_ALPHA_TPREL64: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_le64 (dso, rela->r_offset, value + info->resolvetls->offset); + break; + default: + error (0, 0, "%s: Unknown alpha relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +alpha_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + switch (GELF_R_TYPE (rela->r_info) & 0xff) + { + case R_ALPHA_GLOB_DAT: + case R_ALPHA_REFQUAD: + case R_ALPHA_JMP_SLOT: + buf_write_le64 (buf, rela->r_addend); + break; + default: + abort (); + } + return 0; +} + +static int +alpha_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +alpha_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ALPHA_NONE: + break; + case R_ALPHA_GLOB_DAT: + case R_ALPHA_REFQUAD: + case R_ALPHA_JMP_SLOT: + buf_write_le64 (buf, value + rela->r_addend); + break; + case R_ALPHA_RELATIVE: + error (0, 0, "%s: R_ALPHA_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +alpha_prelink_conflict_rel (DSO *dso, struct prelink_info *info, + GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", dso->filename); + return 1; +} + +static int +alpha_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + + if (GELF_R_TYPE (rela->r_info) == R_ALPHA_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_ALPHA_NONE + || info->dso == dso) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + { + if (info->curtls == NULL) + return 0; + switch (GELF_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD64 and TPREL64 relocs need conflicts. */ + case R_ALPHA_DTPMOD64: + case R_ALPHA_TPREL64: + break; + default: + return 0; + } + value = 0; + } + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on Alpha yet", + dso->filename); + return 1; + } + else + { + /* DTPREL64 wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rela->r_info) == R_ALPHA_DTPREL64 + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ALPHA_GLOB_DAT: + case R_ALPHA_REFQUAD: + ret->r_addend = value + rela->r_addend; + break; + case R_ALPHA_JMP_SLOT: + ret->r_addend = value + rela->r_addend; + if (alpha_is_indirect_plt (dso, rela, relaaddr)) + ret->r_info = GELF_R_INFO (0, R_ALPHA_GLOB_DAT); + else + { + relaaddr -= dso->info[DT_JMPREL]; + relaaddr /= sizeof (Elf64_Rela); + if (relaaddr > 0xffffff) + { + error (0, 0, "%s: Cannot create R_ALPHA_JMP_SLOT conflict against .rel.plt with more than 16M entries", + dso->filename); + return 1; + } + ret->r_info = GELF_R_INFO (0, (relaaddr << 8) | R_ALPHA_JMP_SLOT); + } + break; + case R_ALPHA_DTPMOD64: + case R_ALPHA_DTPREL64: + case R_ALPHA_TPREL64: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + ret->r_info = GELF_R_INFO (0, R_ALPHA_GLOB_DAT); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ALPHA_DTPMOD64: + ret->r_addend = tls->modid; + break; + case R_ALPHA_DTPREL64: + ret->r_addend = value + rela->r_addend; + break; + case R_ALPHA_TPREL64: + ret->r_addend = value + rela->r_addend + tls->offset; + break; + } + break; + default: + error (0, 0, "%s: Unknown Alpha relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +alpha_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: Alpha doesn't support REL relocs", dso->filename); + return 1; +} + +static int +alpha_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +alpha_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + + /* Correct sh_entsize on .plt sections. */ + dso = info->dso; + if (dso->info[DT_PLTGOT]) + { + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT] + 16); + assert (sec != -1); + if (dso->shdr[sec].sh_type == SHT_PROGBITS + && dso->shdr[sec].sh_entsize == 32) + dso->shdr[sec].sh_entsize = 0; + } + return 0; +} + +static int +alpha_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + int sec; + Elf_Scn *scn; + Elf_Data *data; + GElf_Sym sym; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ALPHA_NONE: + case R_ALPHA_RELATIVE: + break; + case R_ALPHA_JMP_SLOT: + relaaddr -= dso->info[DT_JMPREL]; + relaaddr /= sizeof (Elf64_Rela); + relaaddr *= 12; + relaaddr += dso->info[DT_PLTGOT] + 32; + /* br at,.plt */ + write_le32 (dso, relaaddr, + 0xc39fffff - (relaaddr - dso->info[DT_PLTGOT]) / 4); + write_le64 (dso, relaaddr + 4, 0); + write_le64 (dso, rela->r_offset, relaaddr); + break; + case R_ALPHA_GLOB_DAT: + /* This is ugly. Linker doesn't clear memory at r_offset of GLOB_DAT + reloc, but instead puts in sym.st_value + addend. */ + sec = addr_to_sec (dso, relaaddr); + assert (sec != -1); + sec = dso->shdr[sec].sh_link; + assert (sec > 0 && sec < dso->ehdr.e_shnum); + scn = dso->scn[sec]; + data = elf_getdata (scn, NULL); + assert (data != NULL && elf_getdata (scn, data) == NULL); + assert (GELF_R_SYM (rela->r_info) + <= dso->shdr[sec].sh_size / sizeof (Elf64_Sym)); + gelfx_getsym (dso->elf, data, GELF_R_SYM (rela->r_info), &sym); + write_le64 (dso, rela->r_offset, sym.st_value + rela->r_addend); + break; + case R_ALPHA_REFQUAD: + case R_ALPHA_DTPMOD64: + case R_ALPHA_DTPREL64: + case R_ALPHA_TPREL64: + write_le64 (dso, rela->r_offset, 0); + break; + default: + error (0, 0, "%s: Unknown alpha relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +alpha_reloc_size (int reloc_type) +{ + return 8; +} + +static int +alpha_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_ALPHA_JMP_SLOT: + return RTYPE_CLASS_PLT; + case R_ALPHA_DTPMOD64: + case R_ALPHA_DTPREL64: + case R_ALPHA_TPREL64: + return RTYPE_CLASS_TLS; + default: + return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(alpha) = { + .name = "Alpha", + .class = ELFCLASS64, + .machine = EM_ALPHA, + .alternate_machine = { EM_FAKE_ALPHA }, + .R_JMP_SLOT = R_ALPHA_JMP_SLOT, + .R_COPY = -1, + .R_RELATIVE = R_ALPHA_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld-linux.so.2", + .adjust_dyn = alpha_adjust_dyn, + .adjust_rel = alpha_adjust_rel, + .adjust_rela = alpha_adjust_rela, + .prelink_rel = alpha_prelink_rel, + .prelink_rela = alpha_prelink_rela, + .prelink_conflict_rel = alpha_prelink_conflict_rel, + .prelink_conflict_rela = alpha_prelink_conflict_rela, + .apply_conflict_rela = alpha_apply_conflict_rela, + .apply_rel = alpha_apply_rel, + .apply_rela = alpha_apply_rela, + .rel_to_rela = alpha_rel_to_rela, + .need_rel_to_rela = alpha_need_rel_to_rela, + .reloc_size = alpha_reloc_size, + .reloc_class = alpha_reloc_class, + .max_reloc_size = 8, + .arch_prelink = alpha_arch_prelink, + .undo_prelink_rela = alpha_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0x0000020000000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x0000020001000000LL, + .mmap_end = 0x0000020100000000LL, + .max_page_size = 0x10000, + .page_size = 0x02000 +}; diff --git a/src/arch-arm.c b/src/arch-arm.c new file mode 100644 index 0000000..c8febcd --- /dev/null +++ b/src/arch-arm.c @@ -0,0 +1,950 @@ +/* Copyright (C) 2001, 2002, 2004, 2009, 2011, 2013 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" + +#ifndef R_ARM_TLS_DTPMOD32 +#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ +#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ +#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ +#endif + +static int +arm_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PLTGOT) + { + int sec = addr_to_sec (dso, dyn->d_un.d_ptr); + Elf32_Addr data; + + if (sec == -1) + return 0; + + data = read_une32 (dso, dyn->d_un.d_ptr); + /* If .got.plt[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_ne32 (dso, dyn->d_un.d_ptr, data + adjust); + + data = read_une32 (dso, dyn->d_un.d_ptr + 4); + /* If .got.plt[1] points to .plt, it needs to be adjusted. */ + if (data && data >= start) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (data == dso->shdr[i].sh_addr + && dso->shdr[i].sh_type == SHT_PROGBITS + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt") == 0) + { + write_ne32 (dso, dyn->d_un.d_ptr + 4, data + adjust); + break; + } + } + } + return 0; +} + +static int +arm_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + Elf32_Addr data; + switch (GELF_R_TYPE (rel->r_info)) + { + case R_ARM_RELATIVE: + case R_ARM_JUMP_SLOT: + case R_ARM_IRELATIVE: + data = read_une32 (dso, rel->r_offset); + if (data >= start) + write_ne32 (dso, rel->r_offset, data + adjust); + break; + } + return 0; +} + +static int +arm_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + Elf32_Addr data; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ARM_RELATIVE: + case R_ARM_IRELATIVE: + if ((Elf32_Addr) rela->r_addend >= start) + { + rela->r_addend += (Elf32_Sword) adjust; + /* Write it to the memory location as well. + Not necessary, but we can do it. */ + write_ne32 (dso, rela->r_offset, rela->r_addend); + } + break; + case R_ARM_JUMP_SLOT: + data = read_une32 (dso, rela->r_offset); + if (data >= start) + write_ne32 (dso, rela->r_offset, data + adjust); + break; + break; + } + return 0; +} + +static int +arm_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) +{ + DSO *dso; + GElf_Addr value; + Elf32_Sword val; + + if (GELF_R_TYPE (rel->r_info) == R_ARM_RELATIVE + || GELF_R_TYPE (rel->r_info) == R_ARM_IRELATIVE + || GELF_R_TYPE (rel->r_info) == R_ARM_NONE) + /* Fast path: nothing to do. */ + return 0; + dso = info->dso; + value = info->resolve (info, GELF_R_SYM (rel->r_info), + GELF_R_TYPE (rel->r_info)); + switch (GELF_R_TYPE (rel->r_info)) + { + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + write_ne32 (dso, rel->r_offset, value); + break; + case R_ARM_ABS32: + { + if (read_une32 (dso, rel->r_offset)) + { + error (0, 0, "%s: R_ARM_ABS32 relocs with non-zero addend should not be present in prelinked REL sections", + dso->filename); + return 1; + } + rel->r_info = GELF_R_INFO (GELF_R_SYM (rel->r_info), R_ARM_GLOB_DAT); + write_ne32 (dso, rel->r_offset, value); + /* Tell prelink_rel routine *rel has changed. */ + return 2; + } + case R_ARM_PC24: + error (0, 0, "%s: R_ARM_PC24 relocs with non-zero addend should not be present in prelinked REL sections", + dso->filename); + return 1; + case R_ARM_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_ARM_COPY reloc in shared library?", dso->filename); + return 1; + case R_ARM_TLS_DTPOFF32: + write_ne32 (dso, rel->r_offset, value); + break; + /* DTPMOD32 and TPOFF32 is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_ARM_TLS_DTPMOD32: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_ARM_TLS_DTPMOD32 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_ARM_TLS_TPOFF32: + if (dso->ehdr.e_type == ET_EXEC) + error (0, 0, "%s: R_ARM_TLS_TPOFF32 relocs should not be present in " + "prelinked ET_EXEC REL sections", + dso->filename); + break; + case R_ARM_TLS_DESC: + if (!dso->info_DT_TLSDESC_PLT) + { + error (0, 0, + "%s: Unsupported R_ARM_TLS_DESC relocation in non-lazily bound object.", + dso->filename); + return 1; + } + val = read_une32 (dso, rel->r_offset + 4); + if (val != 0 && !dynamic_info_is_set (dso, DT_GNU_PRELINKED_BIT)) + { + error (0, 0, + "%s: Unexpected non-zero value (0x%x) in R_ARM_TLS_DESC?", + dso->filename, val); + return 1; + } + write_ne32 (dso, rel->r_offset + 4, dso->info_DT_TLSDESC_PLT); + break; + default: + error (0, 0, "%s: Unknown arm relocation type %d", dso->filename, + (int) GELF_R_TYPE (rel->r_info)); + return 1; + } + return 0; +} + +static int +arm_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso; + GElf_Addr value; + Elf32_Sword val; + + if (GELF_R_TYPE (rela->r_info) == R_ARM_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_ARM_IRELATIVE + || GELF_R_TYPE (rela->r_info) == R_ARM_NONE) + /* Fast path: nothing to do. */ + return 0; + dso = info->dso; + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + write_ne32 (dso, rela->r_offset, value + rela->r_addend); + break; + case R_ARM_ABS32: + write_ne32 (dso, rela->r_offset, value + rela->r_addend); + break; + case R_ARM_PC24: + val = value + rela->r_addend - rela->r_offset; + val >>= 2; + if ((Elf32_Word) val + 0x800000 >= 0x1000000) + { + error (0, 0, "%s: R_ARM_PC24 overflow", dso->filename); + return 1; + } + val &= 0xffffff; + write_ne32 (dso, rela->r_offset, + (read_une32 (dso, rela->r_offset) & 0xff000000) | val); + break; + case R_ARM_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_ARM_COPY reloc in shared library?", dso->filename); + return 1; + case R_ARM_TLS_DTPOFF32: + write_ne32 (dso, rela->r_offset, value + rela->r_addend); + break; + /* DTPMOD32 and TPOFF32 is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_ARM_TLS_DTPMOD32: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_ARM_TLS_DTPMOD32 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_ARM_TLS_TPOFF32: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_ne32 (dso, rela->r_offset, + value + rela->r_addend + info->resolvetls->offset); + break; + case R_ARM_TLS_DESC: + if (!dso->info_DT_TLSDESC_PLT) + { + error (0, 0, + "%s: Unsupported R_ARM_TLS_DESC relocation in non-lazily bound object.", + dso->filename); + return 1; + } + val = read_une32 (dso, rela->r_offset + 4); + if (val != 0 && !dynamic_info_is_set (dso, DT_GNU_PRELINKED_BIT)) + { + error (0, 0, + "%s: Unexpected non-zero value (0x%x) in R_ARM_TLS_DESC?", + dso->filename, val); + return 1; + } + write_ne32 (dso, rela->r_offset + 4, dso->info_DT_TLSDESC_PLT); + break; + default: + error (0, 0, "%s: Unknown arm relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +arm_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + GElf_Rela *ret; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + case R_ARM_ABS32: + buf_write_ne32 (info->dso, buf, rela->r_addend); + break; + case R_ARM_IRELATIVE: + if (dest_addr == 0) + return 5; + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = dest_addr; + ret->r_info = GELF_R_INFO (0, R_ARM_IRELATIVE); + ret->r_addend = rela->r_addend; + break; + default: + abort (); + } + return 0; +} + +static int +arm_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + GElf_Addr value; + Elf32_Sword val; + + value = info->resolve (info, GELF_R_SYM (rel->r_info), + GELF_R_TYPE (rel->r_info)); + switch (GELF_R_TYPE (rel->r_info)) + { + case R_ARM_NONE: + break; + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + buf_write_ne32 (info->dso, buf, value); + break; + case R_ARM_ABS32: + buf_write_ne32 (info->dso, buf, value + read_une32 (info->dso, rel->r_offset)); + break; + case R_ARM_PC24: + val = value + rel->r_offset; + value = read_une32 (info->dso, rel->r_offset) << 8; + value = ((Elf32_Sword) value) >> 6; + val += value; + val >>= 2; + if ((Elf32_Word) val + 0x800000 >= 0x1000000) + { + error (0, 0, "%s: R_ARM_PC24 overflow", info->dso->filename); + return 1; + } + val &= 0xffffff; + buf_write_ne32 (info->dso, buf, (buf_read_une32 (info->dso, buf) & 0xff000000) | val); + break; + case R_ARM_COPY: + abort (); + case R_ARM_RELATIVE: + error (0, 0, "%s: R_ARM_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +arm_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + Elf32_Sword val; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ARM_NONE: + break; + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + case R_ARM_ABS32: + buf_write_ne32 (info->dso, buf, value + rela->r_addend); + break; + case R_ARM_PC24: + val = value + rela->r_addend - rela->r_offset; + val >>= 2; + if ((Elf32_Word) val + 0x800000 >= 0x1000000) + { + error (0, 0, "%s: R_ARM_PC24 overflow", info->dso->filename); + return 1; + } + val &= 0xffffff; + buf_write_ne32 (info->dso, buf, (buf_read_une32 (info->dso, buf) & 0xff000000) | val); + break; + case R_ARM_COPY: + abort (); + case R_ARM_RELATIVE: + error (0, 0, "%s: R_ARM_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +arm_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + + if (GELF_R_TYPE (rel->r_info) == R_ARM_RELATIVE + || GELF_R_TYPE (rel->r_info) == R_ARM_NONE) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rel->r_info), + GELF_R_TYPE (rel->r_info)); + if (conflict == NULL) + { + switch (GELF_R_TYPE (rel->r_info)) + { + /* Even local DTPMOD and TPOFF relocs need conflicts. */ + case R_ARM_TLS_DTPMOD32: + case R_ARM_TLS_TPOFF32: + if (info->curtls == NULL || info->dso == dso) + return 0; + break; + /* Similarly IRELATIVE relocations always need conflicts. */ + case R_ARM_IRELATIVE: + break; + /* Likewise TLS_DESC. */ + case R_ARM_TLS_DESC: + break; + default: + return 0; + } + value = 0; + } + else if (info->dso == dso && !conflict->ifunc) + return 0; + else + { + /* DTPOFF32 wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rel->r_info) == R_ARM_TLS_DTPOFF32 + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rel->r_offset; + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rel->r_info)); + switch (GELF_R_TYPE (rel->r_info)) + { + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + ret->r_addend = (Elf32_Sword) value; + if (conflict != NULL && conflict->ifunc) + ret->r_info = GELF_R_INFO (0, R_ARM_IRELATIVE); + break; + case R_ARM_IRELATIVE: + ret->r_addend = (Elf32_Sword) read_une32 (dso, rel->r_offset); + break; + case R_ARM_ABS32: + case R_ARM_PC24: + error (0, 0, "%s: R_ARM_%s relocs should not be present in prelinked REL sections", + dso->filename, GELF_R_TYPE (rel->r_info) == R_ARM_ABS32 ? "ABS32" : "PC24"); + return 1; + case R_ARM_COPY: + error (0, 0, "R_ARM_COPY should not be present in shared libraries"); + return 1; + case R_ARM_TLS_DTPMOD32: + case R_ARM_TLS_DTPOFF32: + case R_ARM_TLS_TPOFF32: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + ret->r_info = GELF_R_INFO (0, R_ARM_ABS32); + switch (GELF_R_TYPE (rel->r_info)) + { + case R_ARM_TLS_DTPMOD32: + ret->r_addend = tls->modid; + break; + case R_ARM_TLS_DTPOFF32: + ret->r_addend = value + read_une32 (dso, rel->r_offset); + break; + case R_ARM_TLS_TPOFF32: + ret->r_addend = (value + read_une32 (dso, rel->r_offset) + + tls->offset); + break; + } + break; + case R_ARM_TLS_DESC: + /* Nothing to do. */ + break; + default: + error (0, 0, "%s: Unknown arm relocation type %d", dso->filename, + (int) GELF_R_TYPE (rel->r_info)); + return 1; + } + return 0; +} + +static int +arm_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + Elf32_Sword val; + + if (GELF_R_TYPE (rela->r_info) == R_ARM_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_ARM_NONE) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + + if (conflict == NULL) + { + if (info->curtls == NULL) + return 0; + + switch (GELF_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD and TPOFF relocs need conflicts. */ + case R_ARM_TLS_DTPMOD32: + case R_ARM_TLS_TPOFF32: + break; + /* Likewise TLS_DESC. */ + case R_ARM_TLS_DESC: + break; + default: + return 0; + } + value = 0; + } + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on ARM yet", + dso->filename); + return 1; + } + else + { + /* DTPOFF32 wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rela->r_info) == R_ARM_TLS_DTPOFF32 + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + case R_ARM_ABS32: + case R_ARM_IRELATIVE: + ret->r_addend = (Elf32_Sword) (value + rela->r_addend); + if (conflict && conflict->ifunc) + ret->r_info = GELF_R_INFO (0, R_ARM_IRELATIVE); + break; + case R_ARM_PC24: + val = value + rela->r_addend - rela->r_offset; + val >>= 2; + if ((Elf32_Word) val + 0x800000 >= 0x1000000) + { + error (0, 0, "%s: R_ARM_PC24 overflow", dso->filename); + return 1; + } + value = read_une32 (dso, rela->r_offset) & 0xff000000; + ret->r_addend = (Elf32_Sword) (value | (val & 0xffffff)); + ret->r_info = GELF_R_INFO (0, R_ARM_ABS32); + break; + case R_ARM_COPY: + error (0, 0, "R_ARM_COPY should not be present in shared libraries"); + return 1; + case R_ARM_TLS_DTPMOD32: + case R_ARM_TLS_DTPOFF32: + case R_ARM_TLS_TPOFF32: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + ret->r_info = GELF_R_INFO (0, R_ARM_ABS32); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_ARM_TLS_DTPMOD32: + ret->r_addend = tls->modid; + break; + case R_ARM_TLS_DTPOFF32: + ret->r_addend = value + rela->r_addend; + break; + case R_ARM_TLS_TPOFF32: + ret->r_addend = value + rela->r_addend + tls->offset; + break; + } + break; + case R_ARM_TLS_DESC: + /* Nothing to do. */ + break; + default: + error (0, 0, "%s: Unknown arm relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +arm_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + rela->r_offset = rel->r_offset; + rela->r_info = rel->r_info; + switch (GELF_R_TYPE (rel->r_info)) + { + case R_ARM_JUMP_SLOT: + /* We should be never converting .rel.plt into .rela.plt. */ + abort (); + case R_ARM_RELATIVE: + case R_ARM_IRELATIVE: + case R_ARM_ABS32: + case R_ARM_TLS_TPOFF32: + case R_ARM_TLS_DTPOFF32: + rela->r_addend = (Elf32_Sword) read_une32 (dso, rel->r_offset); + break; + case R_ARM_PC24: + rela->r_addend = read_une32 (dso, rel->r_offset) << 8; + rela->r_addend = ((Elf32_Sword) rela->r_addend) >> 6; + break; + case R_ARM_COPY: + case R_ARM_GLOB_DAT: + case R_ARM_TLS_DTPMOD32: + case R_ARM_TLS_DESC: + rela->r_addend = 0; + break; + } + return 0; +} + +static int +arm_rela_to_rel (DSO *dso, GElf_Rela *rela, GElf_Rel *rel) +{ + rel->r_offset = rela->r_offset; + rel->r_info = rela->r_info; + switch (GELF_R_TYPE (rel->r_info)) + { + case R_ARM_JUMP_SLOT: + /* We should be never converting .rel.plt into .rela.plt + and thus never .rela.plt back to .rel.plt. */ + abort (); + case R_ARM_RELATIVE: + case R_ARM_IRELATIVE: + case R_ARM_ABS32: + case R_ARM_TLS_TPOFF32: + case R_ARM_TLS_DTPOFF32: + write_ne32 (dso, rela->r_offset, rela->r_addend); + break; + case R_ARM_PC24: + write_ne32 (dso, rela->r_offset, + (read_une32 (dso, rela->r_offset) & 0xff000000) + | ((rela->r_addend >> 2) & 0xffffff)); + break; + case R_ARM_GLOB_DAT: + case R_ARM_TLS_DTPMOD32: + write_ne32 (dso, rela->r_offset, 0); + break; + } + return 0; +} + +static int +arm_need_rel_to_rela (DSO *dso, int first, int last) +{ + Elf_Data *data; + Elf_Scn *scn; + Elf32_Rel *rel, *relend; + unsigned int val; + + while (first <= last) + { + data = NULL; + scn = dso->scn[first++]; + while ((data = elf_getdata (scn, data)) != NULL) + { + rel = (Elf32_Rel *) data->d_buf; + relend = rel + data->d_size / sizeof (Elf32_Rel); + for (; rel < relend; rel++) + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_ARM_ABS32: + val = read_une32 (dso, rel->r_offset); + /* R_ARM_ABS32 with addend 0 can be converted + to R_ARM_GLOB_DAT and we don't have to convert + to RELA because of that. */ + if (val == 0) + break; + /* FALLTHROUGH */ + case R_ARM_PC24: + return 1; + case R_ARM_TLS_TPOFF32: + /* In shared libraries TPOFF is changed always into + conflicts, for executables we need to preserve + original addend. */ + if (dso->ehdr.e_type == ET_EXEC) + return 1; + case R_ARM_TLS_DTPOFF32: + /* We can prelink these fields, and the addend is relative + to the symbol value. A RELA entry is needed. */ + return 1; + + break; + } + } + } + return 0; +} + +static int +arm_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + int i; + + dso = info->dso; + if (dso->info[DT_PLTGOT]) + { + /* Write address of .plt into got[1]. + .plt is what got[3] contains unless prelinking. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = dso->shdr[i].sh_addr; + write_ne32 (dso, dso->info[DT_PLTGOT] + 4, data); + } + + return 0; +} + +static int +arm_arch_undo_prelink (DSO *dso) +{ + int i; + + if (dso->info[DT_PLTGOT]) + { + /* Clear got[1] if it contains address of .plt. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = read_une32 (dso, dso->info[DT_PLTGOT] + 4); + if (data == dso->shdr[i].sh_addr) + write_ne32 (dso, dso->info[DT_PLTGOT] + 4, 0); + } + + return 0; +} + +static int +arm_undo_prelink_rel (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr) +{ + int sec; + const char *name; + + switch (GELF_R_TYPE (rel->r_info)) + { + case R_ARM_RELATIVE: + case R_ARM_IRELATIVE: + case R_ARM_NONE: + break; + case R_ARM_JUMP_SLOT: + sec = addr_to_sec (dso, rel->r_offset); + name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name); + if (sec == -1 || (strcmp (name, ".got") && strcmp (name, ".got.plt"))) + { + error (0, 0, "%s: R_ARM_JMP_SLOT not pointing into .got section", + dso->filename); + return 1; + } + else + { + Elf32_Addr data = read_une32 (dso, dso->shdr[sec].sh_addr + 4); + + assert (rel->r_offset >= dso->shdr[sec].sh_addr + 12); + assert (((rel->r_offset - dso->shdr[sec].sh_addr) & 3) == 0); + write_ne32 (dso, rel->r_offset, data); + } + break; + case R_ARM_GLOB_DAT: + sec = addr_to_sec (dso, rel->r_offset); + + write_ne32 (dso, rel->r_offset, 0); + if (sec != -1) + { + if (strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[sec].sh_name), + ".got")) + { + rel->r_info = GELF_R_INFO (GELF_R_SYM (rel->r_info), R_ARM_ABS32); + return 2; + } + } + break; + case R_ARM_ABS32: + case R_ARM_PC24: + error (0, 0, "%s: R_ARM_%s relocs should not be present in prelinked REL sections", + GELF_R_TYPE (rel->r_info) == R_ARM_ABS32 ? "ABS32" : "PC24", + dso->filename); + return 1; + case R_ARM_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_ARM_COPY reloc in shared library?", dso->filename); + return 1; + case R_ARM_TLS_DTPMOD32: + case R_ARM_TLS_DTPOFF32: + write_ne32 (dso, rel->r_offset, 0); + break; + case R_ARM_TLS_TPOFF32: + break; + case R_ARM_TLS_DESC: + write_ne32 (dso, rel->r_offset + 4, 0); + break; + default: + error (0, 0, "%s: Unknown arm relocation type %d", dso->filename, + (int) GELF_R_TYPE (rel->r_info)); + return 1; + } + return 0; +} + +static int +arm_reloc_size (int reloc_type) +{ + assert (reloc_type != R_ARM_COPY); + return 4; +} + +static int +arm_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_ARM_COPY: return RTYPE_CLASS_COPY; + case R_ARM_JUMP_SLOT: return RTYPE_CLASS_PLT; + case R_ARM_TLS_DTPMOD32: + case R_ARM_TLS_DTPOFF32: + case R_ARM_TLS_TPOFF32: + case R_ARM_TLS_DESC: + return RTYPE_CLASS_TLS; + default: return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(arm) = { + .name = "ARM", + .class = ELFCLASS32, + .machine = EM_ARM, + .alternate_machine = { EM_NONE }, + .R_JMP_SLOT = R_ARM_JUMP_SLOT, + .R_COPY = R_ARM_COPY, + .R_RELATIVE = R_ARM_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld-linux.so.3", + .dynamic_linker_alt = "/lib/ld-linux-armhf.so.3", + .adjust_dyn = arm_adjust_dyn, + .adjust_rel = arm_adjust_rel, + .adjust_rela = arm_adjust_rela, + .prelink_rel = arm_prelink_rel, + .prelink_rela = arm_prelink_rela, + .prelink_conflict_rel = arm_prelink_conflict_rel, + .prelink_conflict_rela = arm_prelink_conflict_rela, + .apply_conflict_rela = arm_apply_conflict_rela, + .apply_rel = arm_apply_rel, + .apply_rela = arm_apply_rela, + .rel_to_rela = arm_rel_to_rela, + .rela_to_rel = arm_rela_to_rel, + .need_rel_to_rela = arm_need_rel_to_rela, + .reloc_size = arm_reloc_size, + .reloc_class = arm_reloc_class, + .max_reloc_size = 4, + .arch_prelink = arm_arch_prelink, + .arch_undo_prelink = arm_arch_undo_prelink, + .undo_prelink_rel = arm_undo_prelink_rel, + /* Although TASK_UNMAPPED_BASE is 0x40000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x41000000, + .mmap_end = 0x50000000, + .max_page_size = 0x10000, + .page_size = 0x1000 +}; diff --git a/src/arch-cris.c b/src/arch-cris.c new file mode 100644 index 0000000..3272779 --- /dev/null +++ b/src/arch-cris.c @@ -0,0 +1,412 @@ +/* Copyright (C) 2001, 2002, 2004, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" + +static int +cris_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PLTGOT) + { + int sec = addr_to_sec (dso, dyn->d_un.d_ptr); + Elf32_Addr data; + + if (sec == -1) + return 0; + + data = read_ule32 (dso, dyn->d_un.d_ptr); + /* If .got[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_le32 (dso, dyn->d_un.d_ptr, data + adjust); + + data = read_ule32 (dso, dyn->d_un.d_ptr + 4); + /* If .got[1] points to .plt + 28, it needs to be adjusted. */ + if (data && data >= start) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (data == dso->shdr[i].sh_addr + 28 + && dso->shdr[i].sh_type == SHT_PROGBITS + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt") == 0) + { + write_le32 (dso, dyn->d_un.d_ptr + 4, data + adjust); + break; + } + } + } + return 0; +} + +static int +cris_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: CRIS doesn't support REL relocs", dso->filename); + return 1; +} + +static int +cris_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + Elf32_Addr data; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_CRIS_RELATIVE: + if ((Elf32_Addr) rela->r_addend >= start) + rela->r_addend += (Elf32_Sword) adjust; + break; + case R_CRIS_JUMP_SLOT: + data = read_ule32 (dso, rela->r_offset); + if (data >= start) + write_le32 (dso, rela->r_offset, data + adjust); + break; + break; + } + return 0; +} + +static int +cris_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: CRIS doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +cris_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso; + GElf_Addr value; + + dso = info->dso; + if (GELF_R_TYPE (rela->r_info) == R_CRIS_NONE) + /* Fast path: nothing to do. */ + return 0; + else if (GELF_R_TYPE (rela->r_info) == R_CRIS_RELATIVE) + { + write_le32 (dso, rela->r_offset, rela->r_addend); + return 0; + } + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_CRIS_GLOB_DAT: + case R_CRIS_JUMP_SLOT: + case R_CRIS_32: + write_le32 (dso, rela->r_offset, value); + break; + case R_CRIS_16: + write_le16 (dso, rela->r_offset, value); + break; + case R_CRIS_8: + write_8 (dso, rela->r_offset, value); + break; + case R_CRIS_32_PCREL: + write_le32 (dso, rela->r_offset, value - rela->r_offset - 4); + break; + case R_CRIS_16_PCREL: + write_le16 (dso, rela->r_offset, value - rela->r_offset - 2); + break; + case R_CRIS_8_PCREL: + write_8 (dso, rela->r_offset, value - rela->r_offset - 1); + break; + case R_CRIS_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_CRIS_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown cris relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +cris_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + switch (GELF_R_TYPE (rela->r_info)) + { + case R_CRIS_GLOB_DAT: + case R_CRIS_JUMP_SLOT: + case R_CRIS_32: + buf_write_le32 (buf, rela->r_addend); + break; + case R_CRIS_16: + buf_write_le16 (buf, rela->r_addend); + break; + case R_CRIS_8: + buf_write_8 (buf, rela->r_addend); + break; + default: + abort (); + } + return 0; +} + +static int +cris_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: CRIS doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +cris_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_CRIS_NONE: + break; + case R_CRIS_GLOB_DAT: + case R_CRIS_JUMP_SLOT: + case R_CRIS_32: + buf_write_le32 (buf, value); + break; + case R_CRIS_16: + buf_write_le16 (buf, value); + break; + case R_CRIS_8: + buf_write_8 (buf, value); + break; + case R_CRIS_32_PCREL: + buf_write_le32 (buf, value - rela->r_offset - 4); + break; + case R_CRIS_16_PCREL: + buf_write_le16 (buf, value - rela->r_offset - 2); + break; + case R_CRIS_8: + buf_write_8 (buf, value - rela->r_offset - 1); + break; + case R_CRIS_COPY: + abort (); + case R_CRIS_RELATIVE: + error (0, 0, "%s: R_CRIS_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +cris_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: CRIS doesn't support REL relocs", dso->filename); + return 1; +} + +static int +cris_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + GElf_Rela *ret; + + if (GELF_R_TYPE (rela->r_info) == R_CRIS_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_CRIS_NONE + || info->dso == dso) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + return 0; + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on CRIS yet", + dso->filename); + return 1; + } + value = conflict_lookup_value (conflict); + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_CRIS_GLOB_DAT: + case R_CRIS_JUMP_SLOT: + case R_CRIS_32: + case R_CRIS_16: + case R_CRIS_8: + ret->r_addend = (Elf32_Sword) (value + rela->r_addend); + break; + case R_CRIS_32_PCREL: + ret->r_addend = (Elf32_Sword) (value + rela->r_addend + - rela->r_offset - 4); + ret->r_info = GELF_R_INFO (0, R_CRIS_32); + break; + case R_CRIS_16_PCREL: + ret->r_addend = (Elf32_Sword) (value + rela->r_addend + - rela->r_offset - 2); + ret->r_info = GELF_R_INFO (0, R_CRIS_16); + break; + case R_CRIS_8_PCREL: + ret->r_addend = (Elf32_Sword) (value + rela->r_addend + - rela->r_offset - 1); + ret->r_info = GELF_R_INFO (0, R_CRIS_8); + break; + case R_CRIS_COPY: + error (0, 0, "R_CRIS_COPY should not be present in shared libraries"); + return 1; + default: + error (0, 0, "%s: Unknown cris relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +cris_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + return 0; +} + +static int +cris_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +cris_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + int i; + + dso = info->dso; + if (dso->info[DT_PLTGOT]) + { + /* Write address of .plt + 28 into got[1]. + .plt + 28 is what got[3] contains unless prelinking. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + assert (i < dso->ehdr.e_shnum); + data = dso->shdr[i].sh_addr + 28; + write_le32 (dso, dso->info[DT_PLTGOT] + 4, data); + } + + return 0; +} + +static int +cris_reloc_size (int reloc_type) +{ + switch (reloc_type) + { + case R_CRIS_16: + case R_CRIS_16_PCREL: + return 2; + case R_CRIS_8: + case R_CRIS_8_PCREL: + return 1; + default: + return 4; + } +} + +static int +cris_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_CRIS_COPY: return RTYPE_CLASS_COPY; + case R_CRIS_JUMP_SLOT: return RTYPE_CLASS_PLT; + default: return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(cris) = { + .name = "CRIS", + .class = ELFCLASS32, + .machine = EM_CRIS, + .alternate_machine = { EM_NONE }, + .R_JUMP_SLOT = R_CRIS_JUMP_SLOT, + .R_COPY = R_CRIS_COPY, + .R_RELATIVE = R_CRIS_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld.so.1", + .adjust_dyn = cris_adjust_dyn, + .adjust_rel = cris_adjust_rel, + .adjust_rela = cris_adjust_rela, + .prelink_rel = cris_prelink_rel, + .prelink_rela = cris_prelink_rela, + .prelink_conflict_rel = cris_prelink_conflict_rel, + .prelink_conflict_rela = cris_prelink_conflict_rela, + .apply_conflict_rela = cris_apply_conflict_rela, + .apply_rel = cris_apply_rel, + .apply_rela = cris_apply_rela, + .rel_to_rela = cris_rel_to_rela, + .need_rel_to_rela = cris_need_rel_to_rela, + .reloc_size = cris_reloc_size, + .reloc_class = cris_reloc_class, + .max_reloc_size = 4, + .arch_prelink = cris_arch_prelink, + /* Although TASK_UNMAPPED_BASE is 0x3aaaa000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x3c000000, + .mmap_end = 0x48000000, + .max_page_size = 0x2000, + .page_size = 0x2000 +}; diff --git a/src/arch-i386.c b/src/arch-i386.c new file mode 100644 index 0000000..a1e0fcc --- /dev/null +++ b/src/arch-i386.c @@ -0,0 +1,1133 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2009, 2011 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" +#include "layout.h" + +static int +i386_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PLTGOT) + { + int sec = addr_to_sec (dso, dyn->d_un.d_ptr); + Elf32_Addr data; + + if (sec == -1) + return 0; + + data = read_ule32 (dso, dyn->d_un.d_ptr); + /* If .got.plt[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_le32 (dso, dyn->d_un.d_ptr, data + adjust); + + data = read_ule32 (dso, dyn->d_un.d_ptr + 4); + /* If .got.plt[1] points to .plt + 0x16, it needs to be adjusted. */ + if (data && data >= start) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (data == dso->shdr[i].sh_addr + 0x16 + && dso->shdr[i].sh_type == SHT_PROGBITS + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt") == 0) + { + write_le32 (dso, dyn->d_un.d_ptr + 4, data + adjust); + break; + } + } + } + return 0; +} + +static int +i386_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + Elf32_Addr data; + switch (GELF_R_TYPE (rel->r_info)) + { + case R_386_RELATIVE: + case R_386_JMP_SLOT: + case R_386_IRELATIVE: + data = read_ule32 (dso, rel->r_offset); + if (data >= start) + write_le32 (dso, rel->r_offset, data + adjust); + break; + } + return 0; +} + +static int +i386_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + Elf32_Addr data; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_386_RELATIVE: + case R_386_IRELATIVE: + if ((Elf32_Addr) rela->r_addend >= start) + { + rela->r_addend += (Elf32_Sword) adjust; + /* Write it to the memory location as well. + Not necessary, but we can do it. */ + write_le32 (dso, rela->r_offset, rela->r_addend); + } + break; + case R_386_JMP_SLOT: + data = read_ule32 (dso, rela->r_offset); + if (data >= start) + write_le32 (dso, rela->r_offset, data + adjust); + break; + break; + } + return 0; +} + +static int +i386_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) +{ + DSO *dso; + GElf_Addr value; + + if (GELF_R_TYPE (rel->r_info) == R_386_RELATIVE + || GELF_R_TYPE (rel->r_info) == R_386_IRELATIVE + || GELF_R_TYPE (rel->r_info) == R_386_NONE) + /* Fast path: nothing to do. */ + return 0; + dso = info->dso; + value = info->resolve (info, GELF_R_SYM (rel->r_info), + GELF_R_TYPE (rel->r_info)); + switch (GELF_R_TYPE (rel->r_info)) + { + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + write_le32 (dso, rel->r_offset, value); + break; + case R_386_32: + { + if (read_ule32 (dso, rel->r_offset)) + { + error (0, 0, "%s: R_386_32 relocs with non-zero addend should not be present in prelinked REL sections", + dso->filename); + return 1; + } + rel->r_info = GELF_R_INFO (GELF_R_SYM (rel->r_info), R_386_GLOB_DAT); + write_le32 (dso, rel->r_offset, value); + /* Tell prelink_rel routine *rel has changed. */ + return 2; + } + case R_386_PC32: + error (0, 0, "%s: R_386_PC32 relocs should not be present in prelinked REL sections", + dso->filename); + return 1; + case R_386_TLS_DTPOFF32: + write_le32 (dso, rel->r_offset, value); + break; + /* DTPMOD32 and TPOFF{32,} is impossible to predict unless prelink + sets the rules. Also for TPOFF{32,} there is REL->RELA problem. */ + case R_386_TLS_DTPMOD32: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_386_TLS_DTPMOD32 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + if (dso->ehdr.e_type == ET_EXEC) + error (0, 0, "%s: R_386_TLS_TPOFF relocs should not be present in prelinked ET_EXEC REL sections", + dso->filename); + break; + case R_386_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_386_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rel->r_info)); + return 1; + } + return 0; +} + +static int +i386_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso; + GElf_Addr value; + + if (GELF_R_TYPE (rela->r_info) == R_386_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_386_IRELATIVE + || GELF_R_TYPE (rela->r_info) == R_386_NONE) + /* Fast path: nothing to do. */ + return 0; + dso = info->dso; + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + write_le32 (dso, rela->r_offset, value + rela->r_addend); + break; + case R_386_32: + write_le32 (dso, rela->r_offset, value + rela->r_addend); + break; + case R_386_PC32: + write_le32 (dso, rela->r_offset, value + rela->r_addend - rela->r_offset); + break; + case R_386_TLS_DTPOFF32: + write_le32 (dso, rela->r_offset, value + rela->r_addend); + break; + /* DTPMOD32 and TPOFF{32,} is impossible to predict unless prelink + sets the rules. */ + case R_386_TLS_DTPMOD32: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_386_TLS_DTPMOD32 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_386_TLS_TPOFF32: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_le32 (dso, rela->r_offset, + -(value + rela->r_addend - info->resolvetls->offset)); + break; + case R_386_TLS_TPOFF: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_le32 (dso, rela->r_offset, + value + rela->r_addend - info->resolvetls->offset); + break; + case R_386_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_386_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +i386_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + GElf_Rela *ret; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + case R_386_32: + buf_write_le32 (buf, rela->r_addend); + break; + case R_386_IRELATIVE: + if (dest_addr == 0) + return 5; + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = dest_addr; + ret->r_info = GELF_R_INFO (0, R_386_IRELATIVE); + ret->r_addend = rela->r_addend; + break; + default: + abort (); + } + return 0; +} + +static int +i386_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rel->r_info), + GELF_R_TYPE (rel->r_info)); + switch (GELF_R_TYPE (rel->r_info)) + { + case R_386_NONE: + break; + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + buf_write_le32 (buf, value); + break; + case R_386_32: + buf_write_le32 (buf, value + read_ule32 (info->dso, rel->r_offset)); + break; + case R_386_PC32: + buf_write_le32 (buf, value + read_ule32 (info->dso, rel->r_offset) + - rel->r_offset); + break; + case R_386_COPY: + abort (); + case R_386_RELATIVE: + error (0, 0, "%s: R_386_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +i386_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_386_NONE: + break; + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + case R_386_32: + buf_write_le32 (buf, value + rela->r_addend); + break; + case R_386_PC32: + buf_write_le32 (buf, value + rela->r_addend - rela->r_offset); + break; + case R_386_COPY: + abort (); + case R_386_RELATIVE: + error (0, 0, "%s: R_386_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +i386_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + + if (GELF_R_TYPE (rel->r_info) == R_386_RELATIVE + || GELF_R_TYPE (rel->r_info) == R_386_NONE) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rel->r_info), + GELF_R_TYPE (rel->r_info)); + if (conflict == NULL) + { + switch (GELF_R_TYPE (rel->r_info)) + { + /* Even local DTPMOD and TPOFF relocs need conflicts. */ + case R_386_TLS_DTPMOD32: + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + if (info->curtls == NULL || info->dso == dso) + return 0; + break; + /* Similarly IRELATIVE relocations always need conflicts. */ + case R_386_IRELATIVE: + break; + default: + return 0; + } + value = 0; + } + else if (info->dso == dso && !conflict->ifunc) + return 0; + else + { + /* DTPOFF32 wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rel->r_info) == R_386_TLS_DTPOFF32 + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rel->r_offset; + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rel->r_info)); + switch (GELF_R_TYPE (rel->r_info)) + { + case R_386_GLOB_DAT: + ret->r_info = GELF_R_INFO (0, R_386_32); + /* FALLTHROUGH */ + case R_386_JMP_SLOT: + ret->r_addend = (Elf32_Sword) value; + if (conflict != NULL && conflict->ifunc) + ret->r_info = GELF_R_INFO (0, R_386_IRELATIVE); + break; + case R_386_IRELATIVE: + ret->r_addend = (Elf32_Sword) read_ule32 (dso, rel->r_offset); + break; + case R_386_32: + case R_386_PC32: + error (0, 0, "%s: R_386_%s32 relocs should not be present in prelinked REL sections", + dso->filename, GELF_R_TYPE (rel->r_info) == R_386_32 ? "" : "PC"); + return 1; + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: R_386_TLS not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + ret->r_info = GELF_R_INFO (0, R_386_32); + switch (GELF_R_TYPE (rel->r_info)) + { + case R_386_TLS_DTPMOD32: + ret->r_addend = tls->modid; + break; + case R_386_TLS_DTPOFF32: + ret->r_addend = value; + break; + case R_386_TLS_TPOFF32: + ret->r_addend = -(value + read_ule32 (dso, rel->r_offset) + - tls->offset); + break; + case R_386_TLS_TPOFF: + ret->r_addend = value + read_ule32 (dso, rel->r_offset) + - tls->offset; + } + break; + case R_386_COPY: + error (0, 0, "R_386_COPY should not be present in shared libraries"); + return 1; + default: + error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rel->r_info)); + return 1; + } + return 0; +} + +static int +i386_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + + if (GELF_R_TYPE (rela->r_info) == R_386_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_386_NONE) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + { + switch (GELF_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD and TPOFF relocs need conflicts. */ + case R_386_TLS_DTPMOD32: + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + if (info->curtls == NULL || info->dso == dso) + return 0; + break; + /* Similarly IRELATIVE relocations always need conflicts. */ + case R_386_IRELATIVE: + break; + default: + return 0; + } + value = 0; + } + else if (info->dso == dso && !conflict->ifunc) + return 0; + else + { + /* DTPOFF32 wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rela->r_info) == R_386_TLS_DTPOFF32 + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_386_GLOB_DAT: + ret->r_info = GELF_R_INFO (0, R_386_32); + /* FALLTHROUGH */ + case R_386_JMP_SLOT: + case R_386_IRELATIVE: + ret->r_addend = (Elf32_Sword) (value + rela->r_addend); + if (conflict != NULL && conflict->ifunc) + ret->r_info = GELF_R_INFO (0, R_386_IRELATIVE); + break; + case R_386_32: + value += rela->r_addend; + ret->r_addend = (Elf32_Sword) value; + if (conflict != NULL && conflict->ifunc) + ret->r_info = GELF_R_INFO (0, R_386_IRELATIVE); + break; + case R_386_PC32: + ret->r_addend = (Elf32_Sword) (value + rela->r_addend - rela->r_offset); + ret->r_info = GELF_R_INFO (0, R_386_32); + break; + case R_386_COPY: + error (0, 0, "R_386_COPY should not be present in shared libraries"); + return 1; + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: R_386_TLS not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + ret->r_info = GELF_R_INFO (0, R_386_32); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_386_TLS_DTPMOD32: + ret->r_addend = tls->modid; + break; + case R_386_TLS_DTPOFF32: + ret->r_addend += value; + break; + case R_386_TLS_TPOFF32: + ret->r_addend = -(value + rela->r_addend - tls->offset); + break; + case R_386_TLS_TPOFF: + ret->r_addend = value + rela->r_addend - tls->offset; + break; + } + break; + default: + error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +i386_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + rela->r_offset = rel->r_offset; + rela->r_info = rel->r_info; + switch (GELF_R_TYPE (rel->r_info)) + { + case R_386_JMP_SLOT: + /* We should be never converting .rel.plt into .rela.plt. */ + abort (); + case R_386_RELATIVE: + case R_386_IRELATIVE: + case R_386_32: + case R_386_PC32: + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + rela->r_addend = (Elf32_Sword) read_ule32 (dso, rel->r_offset); + break; + case R_386_COPY: + case R_386_GLOB_DAT: + case R_386_TLS_DTPOFF32: + case R_386_TLS_DTPMOD32: + rela->r_addend = 0; + break; + } + return 0; +} + +static int +i386_need_rel_to_rela (DSO *dso, int first, int last) +{ + Elf_Data *data; + Elf_Scn *scn; + Elf32_Rel *rel, *relend; + unsigned int val; + + while (first <= last) + { + data = NULL; + scn = dso->scn[first++]; + while ((data = elf_getdata (scn, data)) != NULL) + { + rel = (Elf32_Rel *) data->d_buf; + relend = rel + data->d_size / sizeof (Elf32_Rel); + for (; rel < relend; rel++) + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_386_32: + val = read_ule32 (dso, rel->r_offset); + /* R_386_32 with addend 0 can be converted + to R_386_GLOB_DAT and we don't have to convert + to RELA because of that. */ + if (val == 0) + break; + /* FALLTHROUGH */ + case R_386_PC32: + return 1; + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + /* In shared libraries TPOFF is changed always into + conflicts, for executables we need to preserve + original addend. */ + if (dso->ehdr.e_type == ET_EXEC) + return 1; + break; + } + } + } + return 0; +} + +static int +i386_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + int i; + + dso = info->dso; + if (dso->info[DT_PLTGOT]) + { + /* Write address of .plt + 0x16 into got[1]. + .plt + 0x16 is what got[3] contains unless prelinking. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = dso->shdr[i].sh_addr + 0x16; + write_le32 (dso, dso->info[DT_PLTGOT] + 4, data); + } + + return 0; +} + +static int +i386_arch_undo_prelink (DSO *dso) +{ + int i; + + if (dso->info[DT_PLTGOT]) + { + /* Clear got[1] if it contains address of .plt + 0x16. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = read_ule32 (dso, dso->info[DT_PLTGOT] + 4); + if (data == dso->shdr[i].sh_addr + 0x16) + write_le32 (dso, dso->info[DT_PLTGOT] + 4, 0); + } + + return 0; +} + +static int +i386_undo_prelink_rel (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr) +{ + int sec; + const char *name; + + switch (GELF_R_TYPE (rel->r_info)) + { + case R_386_NONE: + case R_386_RELATIVE: + case R_386_IRELATIVE: + break; + case R_386_JMP_SLOT: + sec = addr_to_sec (dso, rel->r_offset); + name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name); + if (sec == -1 || (strcmp (name, ".got") && strcmp (name, ".got.plt"))) + { + error (0, 0, "%s: R_386_JMP_SLOT not pointing into .got section", + dso->filename); + return 1; + } + else + { + Elf32_Addr data = read_ule32 (dso, dso->shdr[sec].sh_addr + 4); + + assert (rel->r_offset >= dso->shdr[sec].sh_addr + 12); + assert (((rel->r_offset - dso->shdr[sec].sh_addr) & 3) == 0); + write_le32 (dso, rel->r_offset, + 4 * (rel->r_offset - dso->shdr[sec].sh_addr - 12) + + data); + } + break; + case R_386_GLOB_DAT: + sec = addr_to_sec (dso, rel->r_offset); + + write_le32 (dso, rel->r_offset, 0); + if (sec != -1) + { + if (strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[sec].sh_name), + ".got")) + { + rel->r_info = GELF_R_INFO (GELF_R_SYM (rel->r_info), R_386_32); + return 2; + } + } + break; + case R_386_32: + case R_386_PC32: + error (0, 0, "%s: R_386_%s32 relocs should not be present in prelinked REL sections", + GELF_R_TYPE (rel->r_info) == R_386_32 ? "" : "PC", dso->filename); + return 1; + case R_386_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_386_COPY reloc in shared library?", dso->filename); + return 1; + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + write_le32 (dso, rel->r_offset, 0); + break; + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + break; + default: + error (0, 0, "%s: Unknown i386 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rel->r_info)); + return 1; + } + return 0; +} + +static int +i386_rela_to_rel (DSO *dso, GElf_Rela *rela, GElf_Rel *rel) +{ + rel->r_offset = rela->r_offset; + rel->r_info = rela->r_info; + switch (GELF_R_TYPE (rel->r_info)) + { + case R_386_JMP_SLOT: + /* We should be never converting .rel.plt into .rela.plt + and thus never .rela.plt back to .rel.plt. */ + abort (); + case R_386_RELATIVE: + case R_386_IRELATIVE: + case R_386_32: + case R_386_PC32: + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + write_le32 (dso, rela->r_offset, rela->r_addend); + break; + case R_386_COPY: + case R_386_GLOB_DAT: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + write_le32 (dso, rela->r_offset, 0); + break; + } + return 0; +} + +static int +i386_reloc_size (int reloc_type) +{ + assert (reloc_type != R_386_COPY); + return 4; +} + +static int +i386_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_386_COPY: return RTYPE_CLASS_COPY; + case R_386_JMP_SLOT: return RTYPE_CLASS_PLT; + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + case R_386_TLS_TPOFF32: + case R_386_TLS_TPOFF: + return RTYPE_CLASS_TLS; + default: return RTYPE_CLASS_VALID; + } +} + +/* Library memory regions if --exec-shield in order of precedence: + 0x00101000 + (rand % 0x00cff000) .. 0x00e00000 bottom to top + 0x00101000 .. 0x00101000 + (rand % 0x00cff000) bottom to top + 0x02000000 + (rand % 0x06000000) .. 0x08000000 bottom to top + 0x02000000 .. 0x02000000 + (rand % 0x06000000) bottom to top + 0x41000000 + (rand % 0x0f000000) .. 0x50000000 bottom to top + 0x41000000 .. 0x41000000 + (rand % 0x0f000000) bottom to top */ + +#define REG0S 0x00101000 +#define REG0E 0x00e00000 +#define REG1S 0x02000000 +#define REG1E 0x08000000 +#define REG2S 0x41000000 +#define REG2E 0x50000000 + +struct i386_layout_data +{ + struct prelink_entry e[6]; + Elf32_Addr addrs[12]; +}; + +static inline void +list_append (struct prelink_entry *x, struct prelink_entry *e) +{ + x->prev->next = e; + e->prev = x->prev; + e->next = NULL; + x->prev = e; +} + +static inline void +list_merge (struct prelink_entry *x, struct prelink_entry *e) +{ + struct prelink_entry *end = e->prev; + x->prev->next = e; + e->prev = x->prev; + x->prev = end; +} + +static int +i386_layout_libs_init (struct layout_libs *l) +{ + if (exec_shield) + { + int i; + struct prelink_entry *e; + Elf32_Addr reg0s; + + if (l->max_page_size > 0x200000) + error (EXIT_FAILURE, 0, "--layout-page-size too large"); + + reg0s = (REG0S + l->max_page_size - 1) & ~(l->max_page_size - 1); + l->mmap_base = reg0s; + l->mmap_end = REG2E; + /* Don't allow this to be overridden. */ + mmap_reg_start = ~(GElf_Addr) 0; + mmap_reg_end = ~(GElf_Addr) 0; + for (i = 0; i < l->nlibs; ++i) + { + e = l->libs[i]; + if (e->done == 0) + continue; + if (e->base < reg0s + || (e->base < REG1S && e->layend > REG0E) + || (e->base < REG2S && e->layend > REG1E) + || e->layend > REG2E) + e->done = 0; + } + } + else + { + l->mmap_base = REG2S; + l->mmap_end = REG2E; + } + return 0; +} + +static void +i386_find_free_addr (struct layout_libs *l, Elf32_Addr *ret, + Elf32_Addr beg, Elf32_Addr end, Elf32_Addr start) +{ + struct prelink_entry *e; + Elf32_Addr low, hi; + + ret[0] = beg; + ret[3] = end; + for (e = l->list; e != NULL; e = e->next) + if (e->base >= start) + break; + if (e == l->list) + { + ret[1] = ret[2] = start; + return; + } + + if (e == NULL) + e = l->list; + low = start; + for (e = e->prev; ; e = e->prev) + { + if (e->base < beg) + break; + if (e->layend > low) + low = e->base; + if (e == l->list) + break; + } + + if (low == start) + { + ret[1] = ret[2] = start; + return; + } + + hi = start; + for (; e; e = e->next) + { + if (e->base >= end) + break; + if (e->base >= hi) + break; + if (e->layend > hi) + hi = e->layend; + } + + assert (low >= beg && hi <= end); + + if (hi - start > start - low) + start = low; + else + start = hi; + + ret[1] = ret[2] = start; +} + +static int +i386_layout_libs_pre (struct layout_libs *l) +{ + Elf32_Addr mmap_start, virt, reg0s; + struct prelink_entry *e, *next; + struct i386_layout_data *pld; + int i; + + if (!exec_shield) + { + l->mmap_fin = l->mmap_end; + l->fake = NULL; + l->fakecnt = 0; + return 0; + } + + pld = calloc (sizeof (*pld), 1); + if (pld == NULL) + error (EXIT_FAILURE, ENOMEM, "Cannot lay libraries out"); + + l->arch_data = pld; + reg0s = (REG0S + l->max_page_size - 1) & ~(l->max_page_size - 1); + + mmap_start = l->mmap_start - reg0s; + /* Unless not randomizing, try not to make the first region + too small, because otherwise it is likely libc.so as first + big library would often end up at reg0s. */ + virt = mmap_start % (REG0E - reg0s - 0x200000); + i386_find_free_addr (l, pld->addrs + 0, reg0s, REG0E, reg0s + virt); + virt = mmap_start % (REG1E - REG1S - 0x200000); + i386_find_free_addr (l, pld->addrs + 4, REG1S, REG1E, REG1S + virt); + virt = mmap_start % (REG2E - REG2S - 0x200000); + i386_find_free_addr (l, pld->addrs + 8, REG2S, REG2E, REG2S + virt); + i = 0; + virt = pld->addrs[3] - pld->addrs[2]; + pld->e[0].u.tmp = -1; + pld->e[0].base = virt; + pld->e[0].end = pld->e[0].base; + pld->e[0].layend = pld->e[0].end; + pld->e[0].prev = &pld->e[0]; + next = NULL; + for (e = l->list; e != NULL; e = next) + { + next = e->next; + while (i < 5 + && (e->base >= pld->addrs[2 * i + 1] + || pld->addrs[2 * i] == pld->addrs[2 * i + 1])) + { + ++i; + pld->e[i].u.tmp = -1; + if (i & 1) + virt -= pld->addrs[2 * i + 1] - pld->addrs[2 * i]; + else + { + virt += pld->addrs[2 * i - 1] - pld->addrs[2 * i - 4]; + virt += pld->addrs[2 * i + 3] - pld->addrs[2 * i + 2]; + } + pld->e[i].base = virt; + pld->e[i].end = pld->e[i].base; + pld->e[i].layend = pld->e[i].end; + pld->e[i].prev = &pld->e[i]; + } + e->base += (Elf32_Sword) (virt - pld->addrs[2 * i]); + e->end += (Elf32_Sword) (virt - pld->addrs[2 * i]); + e->layend += (Elf32_Sword) (virt - pld->addrs[2 * i]); + list_append (&pld->e[i], e); + } + while (i < 5) + { + ++i; + pld->e[i].u.tmp = -1; + if (i & 1) + virt -= pld->addrs[2 * i + 1] - pld->addrs[2 * i]; + else + { + virt += pld->addrs[2 * i - 1] - pld->addrs[2 * i - 4]; + virt += pld->addrs[2 * i + 3] - pld->addrs[2 * i + 2]; + } + pld->e[i].base = virt; + pld->e[i].end = pld->e[i].base; + pld->e[i].layend = pld->e[i].end; + pld->e[i].prev = &pld->e[i]; + } + l->list = &pld->e[1]; + list_merge (&pld->e[1], &pld->e[0]); + list_merge (&pld->e[1], &pld->e[3]); + list_merge (&pld->e[1], &pld->e[2]); + list_merge (&pld->e[1], &pld->e[5]); + list_merge (&pld->e[1], &pld->e[4]); + + l->mmap_start = 0; + l->mmap_base = 0; + l->mmap_fin = virt + pld->addrs[2 * i + 1] - pld->addrs[2 * i]; + l->mmap_end = l->mmap_fin; + l->fakecnt = 6; + l->fake = pld->e; + + return 0; +} + +static int +i386_layout_libs_post (struct layout_libs *l) +{ + struct prelink_entry *e; + struct i386_layout_data *pld = (struct i386_layout_data *) l->arch_data; + Elf32_Sword adj = 0; + int i; + + if (!exec_shield) + return 0; + + for (i = 0, e = l->list; e != NULL; e = e->next) + { + if (e == &pld->e[i ^ 1]) + { + adj = pld->addrs[2 * (i ^ 1)] - e->base; + ++i; + } + else + { + e->base += adj; + e->end += adj; + e->layend += adj; + } + } + + free (l->arch_data); + return 0; +} + +PL_ARCH(i386) = { + .name = "i386", + .class = ELFCLASS32, + .machine = EM_386, + .alternate_machine = { EM_NONE }, + .R_JMP_SLOT = R_386_JMP_SLOT, + .R_COPY = R_386_COPY, + .R_RELATIVE = R_386_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld-linux.so.2", + .adjust_dyn = i386_adjust_dyn, + .adjust_rel = i386_adjust_rel, + .adjust_rela = i386_adjust_rela, + .prelink_rel = i386_prelink_rel, + .prelink_rela = i386_prelink_rela, + .prelink_conflict_rel = i386_prelink_conflict_rel, + .prelink_conflict_rela = i386_prelink_conflict_rela, + .apply_conflict_rela = i386_apply_conflict_rela, + .apply_rel = i386_apply_rel, + .apply_rela = i386_apply_rela, + .rel_to_rela = i386_rel_to_rela, + .rela_to_rel = i386_rela_to_rel, + .need_rel_to_rela = i386_need_rel_to_rela, + .reloc_size = i386_reloc_size, + .reloc_class = i386_reloc_class, + .max_reloc_size = 4, + .arch_prelink = i386_arch_prelink, + .arch_undo_prelink = i386_arch_undo_prelink, + .undo_prelink_rel = i386_undo_prelink_rel, + .layout_libs_init = i386_layout_libs_init, + .layout_libs_pre = i386_layout_libs_pre, + .layout_libs_post = i386_layout_libs_post, + /* Although TASK_UNMAPPED_BASE is 0x40000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = REG2S, + .mmap_end = REG2E, + .max_page_size = 0x1000, + .page_size = 0x1000 +}; diff --git a/src/arch-ia64.c b/src/arch-ia64.c new file mode 100644 index 0000000..6039115 --- /dev/null +++ b/src/arch-ia64.c @@ -0,0 +1,533 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" +#include "fptr.h" + +static int +ia64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_IA_64_PLT_RESERVE) + { + int sec = addr_to_sec (dso, dyn->d_un.d_ptr); + Elf64_Addr data; + + if (sec != -1) + { + data = read_ule64 (dso, dyn->d_un.d_ptr + 8); + + /* If .got[1] points to .plt + 0x30, it needs to be adjusted. */ + if (data && data >= start) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (data == dso->shdr[i].sh_addr + 0x30 + && dso->shdr[i].sh_type == SHT_PROGBITS + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt") == 0) + { + write_le64 (dso, dyn->d_un.d_ptr + 8, data + adjust); + break; + } + } + } + + if (dyn->d_un.d_ptr >= start) + dyn->d_un.d_ptr += adjust; + return 1; + } + + return 0; +} + +static int +ia64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: IA-64 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +ia64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_REL32MSB + && rela->r_addend >= start) + { + rela->r_addend += adjust; + switch (GELF_R_TYPE (rela->r_info) & 3) + { + case 0: write_be32 (dso, rela->r_offset, rela->r_addend); break; + case 1: write_le32 (dso, rela->r_offset, rela->r_addend); break; + case 2: write_be64 (dso, rela->r_offset, rela->r_addend); break; + case 3: write_le64 (dso, rela->r_offset, rela->r_addend); break; + } + } + else if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB) + { + GElf_Addr val, gp; + + if (GELF_R_TYPE (rela->r_info) & 1) + { + val = read_ule64 (dso, rela->r_offset); + gp = read_ule64 (dso, rela->r_offset + 8); + } + else + { + val = read_ube64 (dso, rela->r_offset); + gp = read_ube64 (dso, rela->r_offset + 8); + } + if (gp == dso->info[DT_PLTGOT]) + { + if (val >= start) + val += adjust; + if (gp >= start) + gp += adjust; + } + if (GELF_R_TYPE (rela->r_info) & 1) + { + write_le64 (dso, rela->r_offset, val); + write_le64 (dso, rela->r_offset + 8, gp); + } + else + { + write_le64 (dso, rela->r_offset, val); + write_le64 (dso, rela->r_offset + 8, gp); + } + } + return 0; +} + +static int +ia64_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: IA-64 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +ia64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso; + GElf_Addr value; + + if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_REL32MSB + || GELF_R_TYPE (rela->r_info) == R_IA64_NONE) + /* Fast path: nothing to do. */ + return 0; + dso = info->dso; + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_DIR32MSB) + { + /* Nothing to do. */ + } + else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_PCREL32MSB) + { + value -= rela->r_offset & -16; + } + else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_FPTR32MSB) + { + /* FIXME */ + } + else if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB) + { + GElf_Addr gp = info->resolveent->pltgot; + + if (GELF_R_TYPE (rela->r_info) & 1) + { + write_le64 (dso, rela->r_offset, value); + write_le64 (dso, rela->r_offset + 8, gp); + } + else + { + write_be64 (dso, rela->r_offset, value); + write_be64 (dso, rela->r_offset + 8, gp); + } + + return 0; + } + else + { + error (0, 0, "%s: Unknown ia64 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + + switch (GELF_R_TYPE (rela->r_info) & 3) + { + case 0: write_be32 (dso, rela->r_offset, value); break; + case 1: write_le32 (dso, rela->r_offset, value); break; + case 2: write_be64 (dso, rela->r_offset, value); break; + case 3: write_le64 (dso, rela->r_offset, value); break; + } + return 0; +} + +static int +ia64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB) + { + GElf_Addr gp = 0; + int i; + + for (i = 0; i < info->ent->ndepends; ++i) + if (info->ent->depends[i]->base <= rela->r_addend + && info->ent->depends[i]->end > rela->r_addend) + { + gp = info->ent->depends[i]->pltgot; + break; + } + + if (i == info->ent->ndepends) + abort (); + + if (GELF_R_TYPE (rela->r_info) & 1) + { + buf_write_le64 (buf, rela->r_addend); + buf_write_le64 (buf + 8, gp); + } + else + { + buf_write_be64 (buf, rela->r_addend); + buf_write_be64 (buf + 8, gp); + } + return 0; + } + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_IA64_DIR32MSB: buf_write_be32 (buf, rela->r_addend); break; + case R_IA64_DIR32LSB: buf_write_le32 (buf, rela->r_addend); break; + case R_IA64_DIR64MSB: buf_write_be64 (buf, rela->r_addend); break; + case R_IA64_DIR64LSB: buf_write_le64 (buf, rela->r_addend); break; + default: + abort (); + } + return 0; +} + +static int +ia64_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: IA-64 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +ia64_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_DIR32MSB) + { + /* Nothing to do. */ + } + else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_PCREL32MSB) + { + value -= rela->r_offset & -16; + } + else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_FPTR32MSB) + { + /* FIXME */ + } + else if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB) + { + GElf_Addr gp = info->resolveent->pltgot; + + if (GELF_R_TYPE (rela->r_info) & 1) + { + buf_write_le64 (buf, value); + buf_write_le64 (buf + 8, gp); + } + else + { + buf_write_be64 (buf, value); + buf_write_be64 (buf + 8, gp); + } + return 0; + } + else + return 1; + + switch (GELF_R_TYPE (rela->r_info) & 3) + { + case 0: buf_write_be32 (buf, value); break; + case 1: buf_write_le32 (buf, value); break; + case 2: buf_write_be64 (buf, value); break; + case 3: buf_write_le64 (buf, value); break; + } + return 0; +} + +static int +ia64_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: IA-64 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +ia64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + GElf_Rela *ret; + + if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_REL32MSB + || GELF_R_TYPE (rela->r_info) == R_IA64_NONE + || info->dso == dso) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + return 0; + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on IA-64 yet", + dso->filename); + return 1; + } + value = conflict_lookup_value (conflict); + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info)); + if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_DIR32MSB + || (GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB) + { + ret->r_addend = value + rela->r_addend; + return 0; + } + else if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_PCREL32MSB) + { + ret->r_addend = value + rela->r_addend - (rela->r_offset & -16); + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info) + + R_IA64_DIR32MSB - R_IA64_PCREL32MSB); + return 0; + } + else if (GELF_R_TYPE (rela->r_info) == R_IA64_COPY) + { + error (0, 0, "R_IA64_COPY should not be present in shared libraries"); + return 1; + } + error (0, 0, "%s: Unknown ia64 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; +} + +static int +ia64_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: IA-64 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +ia64_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static GElf_Addr +ia64_create_opd (struct prelink_info *info, int first, int last, int plt) +{ + Elf_Data *d; + Elf_Scn *scn; + Elf64_Rela *rela, *relaend; + DSO *dso = info->dso; + int sec; + + if (opd_init (info)) + return -1; + + if (plt) + info->ent->opd->plt_start = dso->shdr[dso->shdr[plt].sh_info].sh_addr; + else + info->ent->opd->plt_start = dso->shdr[dso->dynamic].sh_addr; + sec = first; + while (sec <= last) + { + d = NULL; + scn = dso->scn[sec++]; + while ((d = elf_getdata (scn, d)) != NULL) + { + rela = (Elf64_Rela *) d->d_buf; + relaend = rela + d->d_size / sizeof (Elf64_Rela); + for (; rela < relaend; rela++) + if ((ELF64_R_TYPE (rela->r_info) & ~3) == R_IA64_FPTR32MSB + && opd_add (info, ELF64_R_SYM (rela->r_info), + R_IA64_FPTR64LSB)) + return -1; + } + } + + sec = first; + while (sec) + { + d = NULL; + if (sec == plt) + break; + scn = dso->scn[sec++]; + if (sec == last + 1) + sec = plt; + while ((d = elf_getdata (scn, d)) != NULL) + { + rela = (Elf64_Rela *) d->d_buf; + relaend = rela + d->d_size / sizeof (Elf64_Rela); + for (; rela < relaend; rela++) + if ((ELF64_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB) + opd_note_plt (info, ELF64_R_SYM (rela->r_info), R_IA64_IPLTLSB, + rela->r_offset); + } + } + + return opd_size (info, 16); +} + +static int +ia64_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + int plt = -1, got = -1, i; + const char *name; + + /* Write address of .plt + 0x30 into .got[1]. + .plt + 0x30 is what .IA_64.pltoff[0] contains unless prelinking. */ + + dso = info->dso; + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS) + { + name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name); + if (! strcmp (name, ".got")) + { + if (got != -1) + { + error (0, 0, "%s: More than one .got section", dso->filename); + return 1; + } + got = i; + } + else if (! strcmp (name, ".plt")) + { + if (plt != -1) + { + error (0, 0, "%s: More than one .plt section", dso->filename); + return 1; + } + plt = i; + } + } + + if (plt == -1) + return 0; + + if (got == -1) + { + error (0, 0, "%s: Has .plt section but not .got section", dso->filename); + return 1; + } + + write_le64 (dso, dso->shdr[got].sh_addr + 8, dso->shdr[plt].sh_addr + 0x30); + return 0; +} + +static int +ia64_reloc_size (int reloc_type) +{ + if ((reloc_type & ~1) == R_IA64_IPLTMSB) + return 16; + return (reloc_type & 2) ? 8 : 4; +} + +static int +ia64_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_IA64_IPLTLSB: + case R_IA64_IPLTMSB: + return RTYPE_CLASS_PLT; + default: return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(ia64) = { + .name = "IA-64", + .class = ELFCLASS64, + .machine = EM_IA_64, + .alternate_machine = { EM_NONE }, + .R_JMP_SLOT = R_IA64_IPLTLSB, + .R_COPY = -1, + .R_RELATIVE = R_IA64_REL64LSB, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld-linux-ia64.so.2", + .adjust_dyn = ia64_adjust_dyn, + .adjust_rel = ia64_adjust_rel, + .adjust_rela = ia64_adjust_rela, + .prelink_rel = ia64_prelink_rel, + .prelink_rela = ia64_prelink_rela, + .prelink_conflict_rel = ia64_prelink_conflict_rel, + .prelink_conflict_rela = ia64_prelink_conflict_rela, + .apply_conflict_rela = ia64_apply_conflict_rela, + .apply_rel = ia64_apply_rel, + .apply_rela = ia64_apply_rela, + .rel_to_rela = ia64_rel_to_rela, + .need_rel_to_rela = ia64_need_rel_to_rela, + .create_opd = ia64_create_opd, + .reloc_size = ia64_reloc_size, + .reloc_class = ia64_reloc_class, + .max_reloc_size = 16, + .arch_prelink = ia64_arch_prelink, + /* Although TASK_UNMAPPED_BASE is 0x2000000000000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x2000000010000000LL, + .mmap_end = 0x4000000000000000LL, + .max_page_size = 0x10000, + /* The kernel can be configured for 4K, 8K, 16K and 64K, + but most kernels have at least 8K. */ + .page_size = 0x02000 +}; diff --git a/src/arch-mips.c b/src/arch-mips.c new file mode 100644 index 0000000..ccb1834 --- /dev/null +++ b/src/arch-mips.c @@ -0,0 +1,1444 @@ +/* Copyright (C) 2006, 2008 CodeSourcery. + Written by Richard Sandiford <richard@codesourcery.com>, 2006 + Updated by Maciej W. Rozycki <macro@codesourcery.com>, 2008. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* GENERAL NOTES + + The psABI defines R_MIPS_REL32 as A - EA + S, where the value of EA + depends on the symbol index. If the index is less than DT_MIPS_GOTSYM, + EA is the symbol's st_value "plus displacement". If the index is greater + than or equal to DT_MIPS_GOTSYM, EA is the original value of the + associated GOT entry. + + However, glibc's dynamic linker implements a different definition. + If the index is less than DT_MIPS_GOTSYM, the dynamic linker adds the + symbol's st_value and the base address to the addend. If the index + is greater than or equal to DT_MIPS_GOTSYM, the dynamic linker adds + the final symbol value to the addend. + + MIPS GOTs are divided into three parts: + + - Reserved entries (of which GNU objects have 2) + - Local entries + - Global entries + + DT_MIPS_LOCAL_GOTNO gives the total number of reserved and local + entries. The local entries all hold virtual addresses and the + dynamic linker will add the base address to each one. + + Unlike most other architectures, the MIPS ABI does not use + relocations to initialize the global GOT entries. Instead, global + GOT entry X is mapped to dynamic symbol DT_MIPS_GOTSYM + X, and there + are a total of DT_MIPS_SYMTABNO - DT_MIPS_GOTSYM global GOT entries. + + The interpretation of a global GOT entry depends on the symbol entry + and the initial GOT contents. The psABI lists the following cases: + + st_shndx st_type st_value initial GOT value + -------- ------- -------- ----------------- + A: SHN_UNDEF STT_FUNC 0 st_value (== 0) / QS + B: SHN_UNDEF STT_FUNC stub address st_value / QS + C: SHN_UNDEF all others 0 st_value (== 0) / QS + D: SHN_COMMON any alignment 0 / QS + E: all others STT_FUNC value st_value / stub address + F: all others all others value st_value + + (wording slightly modified from the psABI table). Here, QS denotes + Quickstart values. + + The dynamic linker treats each case as follows: + + - [A, B when not binding lazily, C, D, E when not binding lazily, F] + Resolve the symbol and store its value in the GOT. + + - [B when binding lazily] Set the GOT entry to the st_value plus + the base address. + + - [E when binding lazily] If the GOT entry is different from the st_value, + add the base addreess to the GOT entry. Otherwise resolve the symbol + and store its value in the GOT (as for A, C, etc). + + As the table shows, we can install Quickstart values for types A-D. + Installing Quickstart values for type F should be a no-op, because the + GOT should already hold the desired value. Installing Quickstart values + for type E would either be a no-op (if the GOT entry already contains + st_value) or would lose the address of the lazy binding stub. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" +#include "layout.h" +#include "reloc-info.h" + +/* The thread pointer points 0x7000 past the first static TLS block. */ +#define TLS_TP_OFFSET 0x7000 + +/* Dynamic thread vector pointers point 0x8000 past the start of each + TLS block. */ +#define TLS_DTV_OFFSET 0x8000 + +/* The number of reserved entries at the beginning of the GOT. + The dynamic linker points entry 0 to the resolver function + and entry 1 to the link_map. */ +#define RESERVED_GOTNO 2 + +/* A structure for iterating over local GOT entries. */ +struct mips_local_got_iterator { + /* The DSO containing the GOT. */ + DSO *dso; + + /* The size of a GOT entry. */ + GElf_Word entry_size; + + /* The index of the current GOT entry. */ + GElf_Word got_index; + + /* A pointer to the current GOT entry. */ + unsigned char *got_entry; + + /* True if we failed to read an entry correctly. */ + int failed; + + /* Used internally to obtain GOT_ENTRY. */ + struct data_iterator got_iterator; +}; + +/* Read native-endian address-type data. */ + +static uint64_t +mips_buf_read_addr (DSO *dso, unsigned char *data) +{ + if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64) + return buf_read_une64 (dso, data); + else + return buf_read_une32 (dso, data); +} + +/* Write native-endian address-type data. */ + +static void +mips_buf_write_addr (DSO *dso, unsigned char *data, uint64_t val) +{ + if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64) + buf_write_ne64 (dso, data, val); + else + buf_write_ne32 (dso, data, val); +} + +/* Set up LGI to iterate over DSO's local GOT. The caller should use + mips_get_local_got_entry to read the first entry. */ + +static inline void +mips_init_local_got_iterator (struct mips_local_got_iterator *lgi, DSO *dso) +{ + lgi->dso = dso; + lgi->entry_size = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); + lgi->got_index = RESERVED_GOTNO - 1; + lgi->failed = 0; + init_data_iterator (&lgi->got_iterator, dso, + dso->info[DT_PLTGOT] + + (lgi->got_index + 1) * lgi->entry_size); +} + +/* Return true if LGI has not reached the end of the GOT and if the next + entry can be accessed. When returning true, use LGI's fields to + describe the next entry. */ + +static inline int +mips_get_local_got_entry (struct mips_local_got_iterator *lgi) +{ + lgi->got_index++; + if (lgi->got_index >= lgi->dso->info_DT_MIPS_LOCAL_GOTNO) + return 0; + + lgi->got_entry = get_data_from_iterator (&lgi->got_iterator, + lgi->entry_size); + if (lgi->got_entry == NULL) + { + error (0, 0, "%s: Malformed local GOT\n", lgi->dso->filename); + lgi->failed = 1; + return 0; + } + + return 1; +} + +/* A structure for iterating over global GOT entries. */ +struct mips_global_got_iterator { + /* The DSO containing the GOT. */ + DSO *dso; + + /* The size of a GOT entry. */ + GElf_Word entry_size; + + /* The virtual address of the current GOT entry. */ + GElf_Addr got_addr; + + /* The index of the associated entry in the dynamic symbol table. */ + GElf_Word sym_index; + + /* A pointer to the current GOT entry. */ + unsigned char *got_entry; + + /* The symbol associated with the current GOT entry. */ + GElf_Sym sym; + + /* True if we failed to read an entry correctly. */ + int failed; + + /* Used internally to obtain GOT_ENTRY and SYM. */ + struct data_iterator got_iterator; + struct data_iterator sym_iterator; +}; + +/* Set up GGI to iterate over DSO's global GOT. The caller should use + mips_get_global_got_entry to read the first entry. */ + +static inline void +mips_init_global_got_iterator (struct mips_global_got_iterator *ggi, DSO *dso) +{ + GElf_Word sym_size; + + ggi->dso = dso; + ggi->entry_size = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); + ggi->got_addr = (dso->info[DT_PLTGOT] + + (dso->info_DT_MIPS_LOCAL_GOTNO - 1) * ggi->entry_size); + ggi->sym_index = dso->info_DT_MIPS_GOTSYM - 1; + ggi->failed = 0; + + sym_size = gelf_fsize (dso->elf, ELF_T_SYM, 1, EV_CURRENT); + init_data_iterator (&ggi->got_iterator, dso, + ggi->got_addr + ggi->entry_size); + init_data_iterator (&ggi->sym_iterator, dso, + dso->info[DT_SYMTAB] + (ggi->sym_index + 1) * sym_size); +} + +/* Return true if GGI has not reached the end of the GOT and if the next + entry can be accessed. When returning true, use GGI's fields to + describe the next entry. */ + +static inline int +mips_get_global_got_entry (struct mips_global_got_iterator *ggi) +{ + ggi->sym_index++; + ggi->got_addr += ggi->entry_size; + if (ggi->sym_index >= ggi->dso->info_DT_MIPS_SYMTABNO) + return 0; + + ggi->got_entry = get_data_from_iterator (&ggi->got_iterator, + ggi->entry_size); + if (ggi->got_entry == NULL + || !get_sym_from_iterator (&ggi->sym_iterator, &ggi->sym)) + { + error (0, 0, "%s: Malformed global GOT\n", ggi->dso->filename); + ggi->failed = 1; + return 0; + } + + return 1; +} + +static int +mips_arch_adjust (DSO *dso, GElf_Addr start, GElf_Addr adjust) +{ + struct mips_local_got_iterator lgi; + struct mips_global_got_iterator ggi; + GElf_Addr value; + + if (dso->info[DT_PLTGOT] == 0) + return 0; + + /* Adjust every local GOT entry by ADJUST. Every adjustment moves + the code and data, so we do not need to check START here. */ + mips_init_local_got_iterator (&lgi, dso); + while (mips_get_local_got_entry (&lgi)) + { + value = mips_buf_read_addr (dso, lgi.got_entry); + mips_buf_write_addr (dso, lgi.got_entry, value + adjust); + } + + /* Adjust every global GOT entry. Referring to the table above: + + For [A, B, C]: Adjust the GOT entry if it contains st_value + and if the symbol's value will be adjusted. + + For [D]: Do nothing. SHN_COMMON entries never need adjusting. + + For [E, F]: Adjust the GOT entry if it does not contain st_value + -- in other words, if it is a type E entry that points to a lazy + binding stub -- or if the symbol's value will also be adjusted. */ + mips_init_global_got_iterator (&ggi, dso); + while (mips_get_global_got_entry (&ggi)) + { + value = mips_buf_read_addr (dso, ggi.got_entry); + if (ggi.sym.st_shndx != SHN_COMMON + && value >= start + && (value == ggi.sym.st_value + ? adjust_symbol_p (dso, &ggi.sym) + : ggi.sym.st_shndx != SHN_UNDEF)) + mips_buf_write_addr (dso, ggi.got_entry, value + adjust); + } + + return lgi.failed || ggi.failed; +} + +static int +mips_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + switch (dyn->d_tag) + { + case DT_MIPS_TIME_STAMP: + case DT_MIPS_ICHECKSUM: + case DT_MIPS_IVERSION: + case DT_MIPS_CONFLICT: + case DT_MIPS_CONFLICTNO: + case DT_MIPS_LIBLIST: + case DT_MIPS_LIBLISTNO: + error (0, 0, "%s: File contains QuickStart information", dso->filename); + return 1; + + case DT_MIPS_BASE_ADDRESS: + case DT_MIPS_RLD_MAP: + case DT_MIPS_OPTIONS: + if (dyn->d_un.d_ptr >= start) + dyn->d_un.d_ptr += adjust; + return 1; + + case DT_MIPS_LOCAL_GOTNO: + case DT_MIPS_UNREFEXTNO: + case DT_MIPS_SYMTABNO: + case DT_MIPS_HIPAGENO: + case DT_MIPS_GOTSYM: + /* We don't change the layout of the GOT or symbol table. */ + return 1; + + case DT_MIPS_RLD_VERSION: + case DT_MIPS_FLAGS: + /* We don't change these properties. */ + return 1; + } + return 0; +} + +/* Read the addend for a relocation in DSO. If RELA is nonnull, + use its r_addend, otherwise read a 32-bit in-place addend from + address R_OFFSET. */ + +static inline uint32_t +mips_read_32bit_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela) +{ + return rela ? rela->r_addend : read_une32 (dso, r_offset); +} + +/* Like mips_read_32bit_addend, but change the addend to VALUE. */ + +static inline void +mips_write_32bit_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela, + uint32_t value) +{ + if (rela) + rela->r_addend = (int32_t) value; + else + write_ne32 (dso, r_offset, value); +} + +/* Like mips_read_32bit_addend, but 64-bit. */ + +static inline uint64_t +mips_read_64bit_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela) +{ + return rela ? rela->r_addend : read_une64 (dso, r_offset); +} + +/* Like mips_read_64bit_addend, but change the addend to VALUE. */ + +static inline void +mips_write_64bit_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela, + uint64_t value) +{ + if (rela) + rela->r_addend = value; + else + write_ne64 (dso, r_offset, value); +} + +/* There is a relocation of type R_INFO against address R_OFFSET in DSO. + Adjust it so that virtual addresses >= START are increased by ADJUST + If the relocation is in a RELA section, RELA points to the relocation, + otherwise it is null. */ + +static int +mips_adjust_reloc (DSO *dso, GElf_Addr r_offset, GElf_Xword r_info, + GElf_Addr start, GElf_Addr adjust, GElf_Rela *rela) +{ + GElf_Addr value; + GElf_Word r_sym; + + if (reloc_r_type (dso, r_info) == R_MIPS_REL32) + { + r_sym = reloc_r_sym (dso, r_info); + if (r_sym < dso->info_DT_MIPS_GOTSYM) + { + /* glibc's dynamic linker adds the symbol's st_value and the + base address to the addend. It therefore treats all symbols + as being relative, even if they would normally be considered + absolute. For example, the special null symbol should always + have the value zero, even when the base address is nonzero, + but R_MIPS_REL32 relocations against the null symbol must + nevertheles be adjusted as if that symbol were relative. + The same would apply to SHN_ABS symbols too. + + Thus the result of the relocation calculation must always + be adjusted by ADJUST. (We do not need to check START because + every adjustment requested by the caller will affect all + legitimate local relocation values.) This means that we + should add ADJUST to the addend if and only if the symbol's + value is not being adjusted. + + In general, we can only check whether a symbol's value is + being adjusted by reading its entry in the dynamic symbol + table and then querying adjust_symbol_p. However, this + generality is fortunately not needed. Modern versions + of binutils will never generate R_MIPS_REL32 relocations + against symbols in the range [1, DT_MIPS_GOTSYM), so we + only need to handle relocations against the null symbol. */ + if (r_sym != 0) + { + error (0, 0, "%s: The prelinker does not support R_MIPS_REL32" + " relocs against local symbols", dso->filename); + return 1; + } + if (reloc_r_type2 (dso, r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE); + assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF); + value = mips_read_64bit_addend (dso, r_offset, rela); + mips_write_64bit_addend (dso, r_offset, rela, value + adjust); + } + else + { + value = mips_read_32bit_addend (dso, r_offset, rela); + mips_write_32bit_addend (dso, r_offset, rela, value + adjust); + } + } + } + return 0; +} + +static int +mips_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, GElf_Addr adjust) +{ + return mips_adjust_reloc (dso, rel->r_offset, rel->r_info, + start, adjust, NULL); +} + +static int +mips_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, GElf_Addr adjust) +{ + return mips_adjust_reloc (dso, rela->r_offset, rela->r_info, + start, adjust, rela); +} + +/* Calculate relocation RELA as A + VALUE and store the result in DSO. */ + +static void +mips_prelink_32bit_reloc (DSO *dso, GElf_Rela *rela, GElf_Addr value) +{ + assert (rela != NULL); + write_ne32 (dso, rela->r_offset, value + rela->r_addend); +} + +static void +mips_prelink_64bit_reloc (DSO *dso, GElf_Rela *rela, GElf_Addr value) +{ + assert (rela != NULL); + write_ne64 (dso, rela->r_offset, value + rela->r_addend); +} + +/* There is a relocation of type R_INFO against address R_OFFSET in DSO. + Prelink the relocation field, using INFO to look up symbol values. + If the relocation is in a RELA section, RELA points to the relocation, + otherwise it is null. */ + +static int +mips_prelink_reloc (struct prelink_info *info, GElf_Addr r_offset, + GElf_Xword r_info, GElf_Rela *rela) +{ + DSO *dso; + GElf_Addr value; + GElf_Word r_sym; + int r_type; + + dso = info->dso; + r_sym = reloc_r_sym (dso, r_info); + r_type = reloc_r_type (dso, r_info); + switch (r_type) + { + case R_MIPS_NONE: + break; + + case R_MIPS_REL32: + /* An in-place R_MIPS_REL32 relocation against symbol 0 needs no + adjustment. */ + if (rela != NULL || r_sym != 0) + { + value = info->resolve (info, r_sym, r_type); + if (reloc_r_type2 (dso, r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE); + assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF); + mips_prelink_64bit_reloc (dso, rela, value); + } + else + mips_prelink_32bit_reloc (dso, rela, value); + } + break; + + case R_MIPS_GLOB_DAT: + if (reloc_r_type2 (dso, r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE); + assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF); + write_ne64 (dso, r_offset, info->resolve (info, r_sym, r_type)); + } + else + write_ne32 (dso, r_offset, info->resolve (info, r_sym, r_type)); + break; + + case R_MIPS_JUMP_SLOT: + write_ne32 (dso, r_offset, info->resolve (info, r_sym, r_type)); + break; + + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + /* Relocations in a shared library will be resolved using a conflict. + We need not change the relocation field here. */ + if (dso->ehdr.e_type == ET_EXEC) + { + struct prelink_tls *tls = info->symbols[r_sym].u.tls; + + if (tls == NULL) + break; + value = tls->modid; + if (r_type == R_MIPS_TLS_DTPMOD32) + mips_prelink_32bit_reloc (dso, rela, value); + else + mips_prelink_64bit_reloc (dso, rela, value); + } + break; + + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_DTPREL64: + value = info->resolve (info, r_sym, r_type); + if (r_type == R_MIPS_TLS_DTPREL32) + mips_prelink_32bit_reloc (dso, rela, value - TLS_DTV_OFFSET); + else + mips_prelink_64bit_reloc (dso, rela, value - TLS_DTV_OFFSET); + break; + + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_TPREL64: + /* Relocations in a shared library will be resolved using a conflict. + We need not change the relocation field here. */ + if (dso->ehdr.e_type == ET_EXEC) + { + value = info->resolve (info, r_sym, r_type); + value += info->resolvetls->offset - TLS_TP_OFFSET; + if (r_type == R_MIPS_TLS_TPREL32) + mips_prelink_32bit_reloc (dso, rela, value); + else + mips_prelink_64bit_reloc (dso, rela, value); + } + break; + + case R_MIPS_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_MIPS_COPY reloc in shared library?", dso->filename); + return 1; + + default: + error (0, 0, "%s: Unknown MIPS relocation type %d", + dso->filename, (int) reloc_r_type (dso, r_info)); + return 1; + } + return 0; +} + +static int +mips_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) +{ + GElf_Xword r_info; + GElf_Word r_sym; + int r_type; + DSO *dso; + + /* Convert R_MIPS_REL32 relocations against global symbols into + R_MIPS_GLOB_DAT if the addend is zero. */ + dso = info->dso; + r_sym = reloc_r_sym (dso, rel->r_info); + r_type = reloc_r_type (dso, rel->r_info); + if (r_type == R_MIPS_REL32 && r_sym >= dso->info_DT_MIPS_GOTSYM) + { + r_type = R_MIPS_GLOB_DAT; + r_info = reloc_r_info_ext (dso, r_sym, reloc_r_ssym (dso, rel->r_info), + r_type, + reloc_r_type2 (dso, rel->r_info), + reloc_r_type3 (dso, rel->r_info)); + if (reloc_r_type2 (dso, rel->r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, rel->r_info) == R_MIPS_NONE); + assert (reloc_r_ssym (dso, rel->r_info) == RSS_UNDEF); + if (read_une64 (dso, rel->r_offset) == 0) + { + rel->r_info = r_info; + write_ne64 (dso, rel->r_offset, + info->resolve (info, r_sym, r_type)); + return 2; + } + } + else if (read_une32 (dso, rel->r_offset) == 0) + { + rel->r_info = r_info; + write_ne32 (dso, rel->r_offset, info->resolve (info, r_sym, r_type)); + return 2; + } + } + return mips_prelink_reloc (info, rel->r_offset, rel->r_info, NULL); +} + +static int +mips_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + return mips_prelink_reloc (info, rela->r_offset, rela->r_info, rela); +} + +/* CONFLICT is a conflict returned by prelink_conflict for a symbol + belonging to DSO. Set *TLS_OUT to the associated TLS information. + Return 1 on failure. */ + +static int +mips_get_tls (DSO *dso, struct prelink_conflict *conflict, + struct prelink_tls **tls_out) +{ + if (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL) + { + error (0, 0, "%s: R_MIPS_TLS not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + + *tls_out = conflict->lookup.tls; + return 0; +} + +/* There is a relocation of type R_INFO against address R_OFFSET in DSO. + See if the relocation field must be adjusted by a conflict when DSO + is used in the context described by INFO. Add a conflict entry if so. + If the relocation is in a RELA section, RELA points to the relocation, + otherwise it is null. */ + +static int +mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info, + GElf_Addr r_offset, GElf_Xword r_info, + GElf_Rela *rela) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls = NULL; + GElf_Rela *entry; + GElf_Word r_sym; + int r_type; + + if (info->dso == dso) + return 0; + + r_sym = reloc_r_sym (dso, r_info); + r_type = reloc_r_type (dso, r_info); + conflict = prelink_conflict (info, r_sym, r_type); + if (conflict == NULL) + { + switch (r_type) + { + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_TPREL64: + tls = info->curtls; + if (tls == NULL) + return 0; + /* A relocation against symbol 0. A shared library cannot + know what the final module IDs or TP-relative offsets are, + so the executable must always have a conflict for them. */ + value = 0; + break; + default: + return 0; + } + } + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on MIPS yet", + dso->filename); + return 1; + } + else + { + /* DTPREL32/DTPREL64 relocations just involve the symbol value; + no other TLS information is needed. Ignore conflicts created + from a lookup of type RTYPE_CLASS_TLS if no real conflict + exists. */ + if ((r_type == R_MIPS_TLS_DTPREL32 || r_type == R_MIPS_TLS_DTPREL64) + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + /* VALUE now contains the final symbol value. Change it to the + value we want to store at R_OFFSET. */ + switch (r_type) + { + case R_MIPS_REL32: + if (reloc_r_type2 (dso, r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE); + assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF); + value += mips_read_64bit_addend (dso, r_offset, rela); + } + else + value += mips_read_32bit_addend (dso, r_offset, rela); + break; + + case R_MIPS_GLOB_DAT: + break; + + case R_MIPS_COPY: + error (0, 0, "R_MIPS_COPY should not be present in shared libraries"); + return 1; + + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + if (conflict != NULL && mips_get_tls (dso, conflict, &tls) == 1) + return 1; + value = tls->modid; + break; + + case R_MIPS_TLS_DTPREL32: + value += mips_read_32bit_addend (dso, r_offset, rela) - TLS_DTV_OFFSET; + break; + case R_MIPS_TLS_DTPREL64: + value += mips_read_64bit_addend (dso, r_offset, rela) - TLS_DTV_OFFSET; + break; + + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_TPREL64: + if (conflict != NULL && mips_get_tls (dso, conflict, &tls) == 1) + return 1; + if (r_type == R_MIPS_TLS_TPREL32) + value += mips_read_32bit_addend (dso, r_offset, rela); + else + value += mips_read_64bit_addend (dso, r_offset, rela); + value += tls->offset - TLS_TP_OFFSET; + break; + + default: + error (0, 0, "%s: Unknown MIPS relocation type %d", dso->filename, + r_type); + return 1; + } + /* Create and initialize a conflict entry. */ + entry = prelink_conflict_add_rela (info); + if (entry == NULL) + return 1; + entry->r_offset = r_offset; + entry->r_info = reloc_r_info_ext (dso, 0, RSS_UNDEF, + R_MIPS_REL32, R_MIPS_64, R_MIPS_NONE); + if (reloc_r_type2 (dso, entry->r_info) == R_MIPS_64) + entry->r_addend = value; + else + entry->r_addend = (int32_t) value; + return 0; +} + +static int +mips_prelink_conflict_rel (DSO *dso, struct prelink_info *info, + GElf_Rel *rel, GElf_Addr reladdr) +{ + return mips_prelink_conflict_reloc (dso, info, rel->r_offset, + rel->r_info, NULL); +} + +static int +mips_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + return mips_prelink_conflict_reloc (dso, info, rela->r_offset, + rela->r_info, rela); +} + +static int +mips_arch_prelink_conflict (DSO *dso, struct prelink_info *info) +{ + struct mips_global_got_iterator ggi; + GElf_Addr value; + struct prelink_conflict *conflict; + GElf_Rela *entry; + + if (info->dso == dso || dso->info[DT_PLTGOT] == 0) + return 0; + + /* Add a conflict for every global GOT entry that does not hold the + right value, either because of a conflict, or because the DSO has + a lazy binding stub for a symbol that it also defines. */ + mips_init_global_got_iterator (&ggi, dso); + while (mips_get_global_got_entry (&ggi)) + { + conflict = prelink_conflict (info, ggi.sym_index, R_MIPS_REL32); + if (conflict != NULL) + value = conflict_lookup_value (conflict); + else if (ggi.sym.st_shndx != SHN_UNDEF + && ggi.sym.st_shndx != SHN_COMMON) + value = ggi.sym.st_value; + else + continue; + if (mips_buf_read_addr (dso, ggi.got_entry) != value) + { + entry = prelink_conflict_add_rela (info); + if (entry == NULL) + return 1; + entry->r_offset = ggi.got_addr; + entry->r_info = reloc_r_info_ext (dso, 0, RSS_UNDEF, + R_MIPS_REL32, R_MIPS_64, + R_MIPS_NONE); + if (reloc_r_type2 (dso, entry->r_info) == R_MIPS_64) + entry->r_addend = value; + else + entry->r_addend = (int32_t) value; + } + } + + return ggi.failed; +} + +static int +mips_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + DSO *dso; + + dso = info->dso; + switch (reloc_r_type (dso, rela->r_info)) + { + case R_MIPS_REL32: + if (reloc_r_type2 (dso, rela->r_info) == R_MIPS_64) + { + assert (reloc_r_ssym (dso, rela->r_info) == RSS_UNDEF); + assert (reloc_r_type3 (dso, rela->r_info) == R_MIPS_NONE); + buf_write_ne64 (info->dso, buf, rela->r_addend); + } + else + buf_write_ne32 (info->dso, buf, rela->r_addend); + break; + + case R_MIPS_JUMP_SLOT: + buf_write_ne32 (info->dso, buf, rela->r_addend); + break; + + default: + abort (); + } + return 0; +} + +/* BUF points to a 32-bit field in DSO that is subject to relocation. + If the relocation is in a RELA section, RELA points to the relocation, + otherwise it is null. Add the addend to ADJUSTMENT and install the + result. */ + +static inline void +mips_apply_adjustment (DSO *dso, GElf_Rela *rela, char *buf, + GElf_Addr adjustment) +{ + if (rela) + adjustment += rela->r_addend; + else + adjustment += mips_buf_read_addr (dso, buf); + mips_buf_write_addr (dso, buf, adjustment); +} + +static int +mips_apply_reloc (struct prelink_info *info, GElf_Xword r_info, + GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + GElf_Word r_sym; + int r_type; + DSO *dso; + + dso = info->dso; + r_sym = reloc_r_sym (dso, r_info); + r_type = reloc_r_type (dso, r_info); + value = info->resolve (info, r_sym, r_type); + switch (r_type) + { + case R_MIPS_NONE: + break; + + case R_MIPS_JUMP_SLOT: + buf_write_ne32 (info->dso, buf, value); + break; + + case R_MIPS_COPY: + abort (); + + case R_MIPS_REL32: + if (reloc_r_type2 (dso, r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE); + assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF); + } + mips_apply_adjustment (dso, rela, buf, value); + break; + + default: + return 1; + } + return 0; +} + +static int +mips_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + return mips_apply_reloc (info, rel->r_info, NULL, buf); +} + +static int +mips_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + return mips_apply_reloc (info, rela->r_info, rela, buf); +} + +static int +mips_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + GElf_Word r_sym; + int r_type; + + r_sym = reloc_r_sym (dso, rel->r_info); + r_type = reloc_r_type (dso, rel->r_info); + rela->r_offset = rel->r_offset; + rela->r_info = rel->r_info; + switch (r_type) + { + case R_MIPS_REL32: + /* This relocation has an in-place addend. */ + if (reloc_r_type2 (dso, rel->r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, rel->r_info) == R_MIPS_NONE); + assert (reloc_r_ssym (dso, rel->r_info) == RSS_UNDEF); + rela->r_addend = read_une64 (dso, rel->r_offset); + } + else + rela->r_addend = (int32_t) read_une32 (dso, rel->r_offset); + break; + + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + /* These relocations have an in-place addend. */ + rela->r_addend = (int32_t) read_une32 (dso, rel->r_offset); + break; + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + /* These relocations have an in-place addend. */ + rela->r_addend = read_une64 (dso, rel->r_offset); + break; + + case R_MIPS_NONE: + case R_MIPS_COPY: + case R_MIPS_GLOB_DAT: + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + /* These relocations have no addend. */ + rela->r_addend = 0; + break; + + default: + error (0, 0, "%s: Unknown MIPS relocation type %d", dso->filename, + r_type); + return 1; + } + return 0; +} + +static int +mips_rela_to_rel (DSO *dso, GElf_Rela *rela, GElf_Rel *rel) +{ + GElf_Sxword r_addend; + GElf_Word r_sym; + int r_type; + + r_sym = reloc_r_sym (dso, rela->r_info); + r_type = reloc_r_type (dso, rela->r_info); + r_addend = rela->r_addend; + rel->r_offset = rela->r_offset; + rel->r_info = rela->r_info; + switch (r_type) + { + case R_MIPS_NONE: + case R_MIPS_COPY: + break; + + case R_MIPS_GLOB_DAT: + /* This relocation has no addend. */ + r_addend = 0; + /* FALLTHROUGH */ + case R_MIPS_REL32: + /* This relocation has an in-place addend. */ + if (reloc_r_type2 (dso, rel->r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, rel->r_info) == R_MIPS_NONE); + assert (reloc_r_ssym (dso, rel->r_info) == RSS_UNDEF); + write_ne64 (dso, rela->r_offset, rela->r_addend); + } + else + write_ne32 (dso, rela->r_offset, rela->r_addend); + break; + + case R_MIPS_TLS_DTPMOD32: + /* This relocation has no addend. */ + r_addend = 0; + /* FALLTHROUGH */ + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + /* These relocations have an in-place addend. */ + write_ne32 (dso, rela->r_offset, rela->r_addend); + break; + case R_MIPS_TLS_DTPMOD64: + /* This relocation has no addend. */ + r_addend = 0; + /* FALLTHROUGH */ + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + /* These relocations have an in-place addend. */ + write_ne64 (dso, rela->r_offset, rela->r_addend); + break; + break; + + default: + error (0, 0, "%s: Unknown MIPS relocation type %d", dso->filename, + r_type); + return 1; + } + return 0; +} + +static int +mips_need_rel_to_rela (DSO *dso, int first, int last) +{ + Elf_Data *data; + Elf_Scn *scn; + GElf_Shdr shdr; + GElf_Rel rel; + GElf_Word r_sym; + int r_type; + int count; + int i; + int n; + + for (n = first; n <= last; n++) + { + data = NULL; + scn = dso->scn[n]; + gelfx_getshdr (dso->elf, scn, &shdr); + while ((data = elf_getdata (scn, data)) != NULL) + { + count = data->d_size / shdr.sh_entsize; + for (i = 0; i < count; i++) + { + gelfx_getrel (dso->elf, data, i, &rel); + r_type = reloc_r_type (dso, rel.r_info); + r_sym = reloc_r_sym (dso, rel.r_info); + switch (r_type) + { + case R_MIPS_NONE: + case R_MIPS_COPY: + case R_MIPS_JUMP_SLOT: + break; + + case R_MIPS_REL32: + /* The SVR4 definition was designed to allow exactly the + sort of prelinking we want to do here, in combination + with Quickstart. Unfortunately, glibc's definition + makes it impossible for relocations against anything + other than the null symbol. We get around this for + zero addends by using a R_MIPS_GLOB_DAT relocation + instead, where R_MIPS_GLOB_DAT is a GNU extension + added specifically for this purpose. */ + if (r_sym != 0) + { + if (r_sym < dso->info_DT_MIPS_GOTSYM) + return 1; + if (reloc_r_type2 (dso, rel.r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, rel.r_info) + == R_MIPS_NONE); + assert (reloc_r_ssym (dso, rel.r_info) + == RSS_UNDEF); + if (read_une64 (dso, rel.r_offset) != 0) + return 1; + } + else if (read_une32 (dso, rel.r_offset) != 0) + return 1; + } + break; + + case R_MIPS_GLOB_DAT: + /* This relocation has no addend. */ + break; + + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + /* The relocation will be resolved using a conflict. */ + break; + + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_DTPREL64: + /* We can prelink these fields, and the addend is relative + to the symbol value. A RELA entry is needed. */ + return 1; + + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_TPREL64: + /* Relocations in shared libraries will be resolved by a + conflict. Relocations in executables will not, and the + addend is relative to the symbol value. */ + if (dso->ehdr.e_type == ET_EXEC) + return 1; + break; + + default: + error (0, 0, "%s: Unknown MIPS relocation type %d", + dso->filename, r_type); + return 1; + } + } + } + } + return 0; +} + +static int +mips_reloc_size (int reloc_type) +{ + return 4; +} + +static int +mips_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_MIPS_COPY: + return RTYPE_CLASS_COPY; + case R_MIPS_JUMP_SLOT: + return RTYPE_CLASS_PLT; + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_TPREL64: + return RTYPE_CLASS_TLS; + default: + return RTYPE_CLASS_VALID; + } +} + +static int +mips_arch_prelink (struct prelink_info *info) +{ + struct mips_global_got_iterator ggi; + DSO *dso; + GElf_Addr value; + int i; + + dso = info->dso; + + if (dso->info_DT_MIPS_PLTGOT) + { + /* Write address of .plt into gotplt[1]. This is in each + normal gotplt entry unless prelinking. */ + int sec = addr_to_sec (dso, dso->info_DT_MIPS_PLTGOT); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = dso->shdr[i].sh_addr; + write_ne32 (dso, dso->info_DT_MIPS_PLTGOT + 4, data); + } + + if (dso->info[DT_PLTGOT] == 0) + return 0; + + /* Install Quickstart values for all global GOT entries of type A-D + in the table above. */ + mips_init_global_got_iterator (&ggi, dso); + while (mips_get_global_got_entry (&ggi)) + { + value = info->resolve (info, ggi.sym_index, R_MIPS_REL32); + if (ggi.sym.st_shndx == SHN_UNDEF + || ggi.sym.st_shndx == SHN_COMMON) + mips_buf_write_addr (dso, ggi.got_entry, value); + else + { + /* Type E and F in the table above. We cannot install Quickstart + values for type E, but we should never need to in executables, + because an executable should not use lazy binding stubs for + symbols it defines itself. Although we could in theory just + discard any such stub address, it goes against the principle + that prelinking should be reversible. + + When type E entries occur in shared libraries, we can fix + them up using conflicts. + + Type F entries should never need a Quickstart value -- the + current value should already be correct. However, the conflict + code will cope correctly with malformed type F entries in + shared libraries, so we only complain about executables here. */ + if (dso->ehdr.e_type == ET_EXEC + && value != mips_buf_read_addr (dso, ggi.got_entry)) + { + error (0, 0, "%s: The global GOT entries for defined symbols" + " do not match their st_values\n", dso->filename); + return 1; + } + } + } + return ggi.failed; +} + +static int +mips_arch_undo_prelink (DSO *dso) +{ + struct mips_global_got_iterator ggi; + int i; + + if (dso->info_DT_MIPS_PLTGOT) + { + /* Clear gotplt[1] if it contains the address of .plt. */ + int sec = addr_to_sec (dso, dso->info_DT_MIPS_PLTGOT); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = read_une32 (dso, dso->info_DT_MIPS_PLTGOT + 4); + if (data == dso->shdr[i].sh_addr) + write_ne32 (dso, dso->info_DT_MIPS_PLTGOT + 4, 0); + } + + if (dso->info[DT_PLTGOT] == 0) + return 0; + + mips_init_global_got_iterator (&ggi, dso); + while (mips_get_global_got_entry (&ggi)) + if (ggi.sym.st_shndx == SHN_UNDEF) + /* Types A-C in the table above. */ + mips_buf_write_addr (dso, ggi.got_entry, ggi.sym.st_value); + else if (ggi.sym.st_shndx == SHN_COMMON) + /* Type D in the table above. */ + mips_buf_write_addr (dso, ggi.got_entry, 0); + return ggi.failed; +} + +static int +mips_undo_prelink_rel (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr) +{ + int sec; + const char *name; + GElf_Word r_sym; + int r_type; + + /* Convert R_MIPS_GLOB_DAT relocations back into R_MIPS_REL32 + relocations. Ideally we'd have some mechanism for recording + these changes in the undo section, but in the absence of that, + it's better to assume that the original relocation was + R_MIPS_REL32; R_MIPS_GLOB_DAT was added specifically for the + prelinker and shouldn't be used in non-prelinked binaries. */ + r_sym = reloc_r_sym (dso, rel->r_info); + r_type = reloc_r_type (dso, rel->r_info); + if (r_type == R_MIPS_GLOB_DAT) + { + if (reloc_r_type2 (dso, rel->r_info) == R_MIPS_64) + { + assert (reloc_r_type3 (dso, rel->r_info) == R_MIPS_NONE); + assert (reloc_r_ssym (dso, rel->r_info) == RSS_UNDEF); + write_ne64 (dso, rel->r_offset, 0); + } + else + write_ne32 (dso, rel->r_offset, 0); + rel->r_info = reloc_r_info_ext (dso, + r_sym, reloc_r_ssym (dso, rel->r_info), + R_MIPS_REL32, + reloc_r_type2 (dso, rel->r_info), + reloc_r_type3 (dso, rel->r_info)); + return 2; + } + else if (r_type == R_MIPS_JUMP_SLOT) + { + sec = addr_to_sec (dso, rel->r_offset); + name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name); + if (sec == -1 || strcmp (name, ".got.plt")) + { + error (0, 0, + "%s: R_MIPS_JUMP_SLOT not pointing into .got.plt section", + dso->filename); + return 1; + } + else + { + Elf32_Addr data = read_une32 (dso, dso->shdr[sec].sh_addr + 4); + + assert (rel->r_offset >= dso->shdr[sec].sh_addr + 8); + assert (((rel->r_offset - dso->shdr[sec].sh_addr) & 3) == 0); + write_ne32 (dso, rel->r_offset, data); + } + } + + return 0; +} + +PL_ARCH(mips) = { + .name = "MIPS", + .class = ELFCLASS32, + .machine = EM_MIPS, + .max_reloc_size = 4, + .dynamic_linker = "/lib/ld.so.1", + .dynamic_linker_alt = "/lib32/ld.so.1", + .R_COPY = R_MIPS_COPY, + .R_JMP_SLOT = R_MIPS_JUMP_SLOT, + /* R_MIPS_REL32 relocations against symbol 0 do act as relative relocs, + but those against other symbols don't. */ + .R_RELATIVE = ~0U, + .rtype_class_valid = RTYPE_CLASS_VALID, + .arch_adjust = mips_arch_adjust, + .adjust_dyn = mips_adjust_dyn, + .adjust_rel = mips_adjust_rel, + .adjust_rela = mips_adjust_rela, + .prelink_rel = mips_prelink_rel, + .prelink_rela = mips_prelink_rela, + .prelink_conflict_rel = mips_prelink_conflict_rel, + .prelink_conflict_rela = mips_prelink_conflict_rela, + .arch_prelink_conflict = mips_arch_prelink_conflict, + .apply_conflict_rela = mips_apply_conflict_rela, + .apply_rel = mips_apply_rel, + .apply_rela = mips_apply_rela, + .rel_to_rela = mips_rel_to_rela, + .rela_to_rel = mips_rela_to_rel, + .need_rel_to_rela = mips_need_rel_to_rela, + .reloc_size = mips_reloc_size, + .reloc_class = mips_reloc_class, + .arch_prelink = mips_arch_prelink, + .arch_undo_prelink = mips_arch_undo_prelink, + .undo_prelink_rel = mips_undo_prelink_rel, + /* Although TASK_UNMAPPED_BASE is 0x2aaa8000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x2c000000, + .mmap_end = 0x3c000000, + .max_page_size = 0x10000, + .page_size = 0x1000 +}; + +PL_ARCH(mips64) = { + .name = "MIPS64", + .class = ELFCLASS64, + .machine = EM_MIPS, + .max_reloc_size = 8, + .dynamic_linker = "/lib/ld.so.1", + .dynamic_linker_alt = "/lib64/ld.so.1", + .R_COPY = R_MIPS_COPY, + .R_JMP_SLOT = R_MIPS_JUMP_SLOT, + /* R_MIPS_REL32 relocations against symbol 0 do act as relative relocs, + but those against other symbols don't. */ + .R_RELATIVE = ~0U, + .rtype_class_valid = RTYPE_CLASS_VALID, + .arch_adjust = mips_arch_adjust, + .adjust_dyn = mips_adjust_dyn, + .adjust_rel = mips_adjust_rel, + .adjust_rela = mips_adjust_rela, + .prelink_rel = mips_prelink_rel, + .prelink_rela = mips_prelink_rela, + .prelink_conflict_rel = mips_prelink_conflict_rel, + .prelink_conflict_rela = mips_prelink_conflict_rela, + .arch_prelink_conflict = mips_arch_prelink_conflict, + .apply_conflict_rela = mips_apply_conflict_rela, + .apply_rel = mips_apply_rel, + .apply_rela = mips_apply_rela, + .rel_to_rela = mips_rel_to_rela, + .rela_to_rel = mips_rela_to_rel, + .need_rel_to_rela = mips_need_rel_to_rela, + .reloc_size = mips_reloc_size, + .reloc_class = mips_reloc_class, + .arch_prelink = mips_arch_prelink, + .arch_undo_prelink = mips_arch_undo_prelink, + .undo_prelink_rel = mips_undo_prelink_rel, + /* Although TASK_UNMAPPED_BASE is 0x5555556000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x5800000000LL, + .mmap_end = 0x9800000000LL, + .max_page_size = 0x10000, + .page_size = 0x1000 +}; diff --git a/src/arch-ppc.c b/src/arch-ppc.c new file mode 100644 index 0000000..e22e5d5 --- /dev/null +++ b/src/arch-ppc.c @@ -0,0 +1,1191 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2009, 2011 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" +#include "layout.h" + +#ifndef DT_PPC_GOT +# define DT_PPC_GOT (DT_LOPROC + 0) +#endif + +#define DT_PPC_GOT_BIT DT_LOPROC_BIT + +static int +ppc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PPC_GOT) + { + Elf32_Addr data; + + data = read_ube32 (dso, dyn->d_un.d_ptr); + /* DT_PPC_GOT[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_be32 (dso, dyn->d_un.d_ptr, data + adjust); + + data = read_ube32 (dso, dyn->d_un.d_ptr + 4); + /* DT_PPC_GOT[1] points to .glink in prelinked libs. */ + if (data && data >= start) + write_be32 (dso, dyn->d_un.d_ptr + 4, data + adjust); + + if (dyn->d_un.d_ptr >= start) + { + dyn->d_un.d_ptr += adjust; + return 1; + } + } + else if (dyn->d_tag == DT_PLTGOT + && !dynamic_info_is_set (dso, DT_PPC_GOT_BIT)) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".got") + && dso->shdr[i].sh_size >= 16) + { + Elf32_Addr data, addr; + int step; + + /* If .got[1] points to _DYNAMIC, it needs to be adjusted. + Other possible locations of the .got header are at the + end of .got or around offset 32768 in it. */ + for (addr = dso->shdr[i].sh_addr, step = 0; step < 18; step++) + { + if (read_ube32 (dso, addr) == 0x4e800021 + && (data = read_ube32 (dso, addr + 4)) + == dso->shdr[n].sh_addr + && data >= start + && read_ube32 (dso, addr + 8) == 0 + && read_ube32 (dso, addr + 12) == 0) + { + /* Probably should use here a check that neither of + the 4 addresses contains a dynamic relocation against + it. */ + write_be32 (dso, addr + 4, data + adjust); + break; + } + if (step == 0) + addr = dso->shdr[i].sh_addr + dso->shdr[i].sh_size - 16; + else if (step == 1) + { + if (dso->shdr[i].sh_size >= 32768 - 32) + addr = dso->shdr[i].sh_addr + 32768 - 32 - 16; + else + break; + } + else + { + addr += 4; + if (addr + 16 + > dso->shdr[i].sh_addr + dso->shdr[i].sh_size) + break; + } + } + break; + } + } + + return 0; +} + +static int +ppc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: PowerPC doesn't support REL relocs", dso->filename); + return 1; +} + +static int +ppc_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_PPC_IRELATIVE) + { + if ((Elf32_Word) rela->r_addend >= start) + rela->r_addend += (Elf32_Sword) adjust; + } + if (GELF_R_TYPE (rela->r_info) == R_PPC_JMP_SLOT + && dynamic_info_is_set (dso, DT_PPC_GOT_BIT)) + { + Elf32_Addr data = read_ube32 (dso, rela->r_offset); + if (data >= start) + write_be32 (dso, rela->r_offset, data + adjust); + } + return 0; +} + +static int +ppc_prelink_rel (struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: PowerPC doesn't support REL relocs", info->dso->filename); + return 1; +} + +static void +ppc_fixup_plt (DSO *dso, GElf_Rela *rela, GElf_Addr value) +{ + Elf32_Sword disp = value - rela->r_offset; + + if (disp >= -0x2000000 && disp < 0x2000000) + { + /* b value */ + write_be32 (dso, rela->r_offset, 0x48000000 | (disp & 0x3fffffc)); + } + else if ((Elf32_Addr) value >= -0x2000000 || value < 0x2000000) + { + /* ba value */ + write_be32 (dso, rela->r_offset, 0x48000002 | (value & 0x3fffffc)); + } + else + { + Elf32_Addr plt = dso->info[DT_PLTGOT]; + + if (rela->r_offset - plt < (8192 * 2 + 18) * 4) + { + Elf32_Word index = (rela->r_offset - plt - 18 * 4) / (4 * 2); + Elf32_Word count = dso->info[DT_PLTRELSZ] / sizeof (Elf32_Rela); + Elf32_Addr data; + + data = plt + (18 + 2 * count + + (count > 8192 ? (count - 8192) * 2 : 0)) * 4; + write_be32 (dso, data + 4 * index, value); + /* li %r11, 4*index + b .plt+0 */ + write_be32 (dso, rela->r_offset, + 0x39600000 | ((index * 4) & 0xffff)); + write_be32 (dso, rela->r_offset + 4, + 0x48000000 | ((plt - rela->r_offset - 4) & 0x3fffffc)); + } + else + { + /* lis %r12, %hi(finaladdr) + addi %r12, %r12, %lo(finaladdr) + mtctr %r12 + bctr */ + write_be32 (dso, rela->r_offset, + 0x39800000 | (((value + 0x8000) >> 16) & 0xffff)); + write_be32 (dso, rela->r_offset + 4, 0x398c0000 | (value & 0xffff)); + write_be32 (dso, rela->r_offset + 8, 0x7d8903a6); + write_be32 (dso, rela->r_offset + 12, 0x4e800420); + } + } +} + +static int +ppc_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso = info->dso; + GElf_Addr value; + + if (GELF_R_TYPE (rela->r_info) == R_PPC_NONE + || GELF_R_TYPE (rela->r_info) == R_PPC_IRELATIVE) + return 0; + else if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE) + { + write_be32 (dso, rela->r_offset, rela->r_addend); + return 0; + } + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC_GLOB_DAT: + case R_PPC_ADDR32: + case R_PPC_UADDR32: + write_be32 (dso, rela->r_offset, value); + break; + case R_PPC_DTPREL32: + write_be32 (dso, rela->r_offset, value - 0x8000); + break; + case R_PPC_JMP_SLOT: + if (dynamic_info_is_set (dso, DT_PPC_GOT_BIT)) + write_be32 (dso, rela->r_offset, value); + else + ppc_fixup_plt (dso, rela, value); + break; + case R_PPC_ADDR16: + case R_PPC_UADDR16: + case R_PPC_ADDR16_LO: + write_be16 (dso, rela->r_offset, value); + break; + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_LO: + write_be16 (dso, rela->r_offset, value - 0x8000); + break; + case R_PPC_ADDR16_HI: + case R_PPC_DTPREL16_HA: + write_be16 (dso, rela->r_offset, value >> 16); + break; + case R_PPC_DTPREL16_HI: + write_be16 (dso, rela->r_offset, (value - 0x8000) >> 16); + break; + case R_PPC_ADDR16_HA: + write_be16 (dso, rela->r_offset, (value + 0x8000) >> 16); + break; + case R_PPC_ADDR24: + write_be32 (dso, rela->r_offset, + (value & 0x03fffffc) + | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); + break; + case R_PPC_ADDR14: + write_be32 (dso, rela->r_offset, + (value & 0xfffc) + | (read_ube32 (dso, rela->r_offset) & 0xffff0003)); + break; + case R_PPC_ADDR14_BRTAKEN: + case R_PPC_ADDR14_BRNTAKEN: + write_be32 (dso, rela->r_offset, + (value & 0xfffc) + | (read_ube32 (dso, rela->r_offset) & 0xffdf0003) + | ((((GELF_R_TYPE (rela->r_info) == R_PPC_ADDR14_BRTAKEN) + << 21) + ^ (value >> 10)) & 0x00200000)); + break; + case R_PPC_REL24: + write_be32 (dso, rela->r_offset, + ((value - rela->r_offset) & 0x03fffffc) + | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); + break; + case R_PPC_REL32: + write_be32 (dso, rela->r_offset, value - rela->r_offset); + break; + /* DTPMOD32 and TPREL* is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_PPC_DTPMOD32: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_PPC_DTPMOD32 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_PPC_TPREL32: + case R_PPC_TPREL16: + case R_PPC_TPREL16_LO: + case R_PPC_TPREL16_HI: + case R_PPC_TPREL16_HA: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + { + value += info->resolvetls->offset - 0x7000; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC_TPREL32: + write_be32 (dso, rela->r_offset, value); + break; + case R_PPC_TPREL16: + case R_PPC_TPREL16_LO: + write_be16 (dso, rela->r_offset, value); + break; + case R_PPC_TPREL16_HI: + write_be16 (dso, rela->r_offset, value >> 16); + break; + case R_PPC_TPREL16_HA: + write_be16 (dso, rela->r_offset, (value + 0x8000) >> 16); + break; + } + } + break; + case R_PPC_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_PPC_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown ppc relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +ppc_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + GElf_Rela *ret; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC_ADDR32: + case R_PPC_UADDR32: + buf_write_be32 (buf, rela->r_addend); + break; + case R_PPC_ADDR16: + case R_PPC_UADDR16: + buf_write_be16 (buf, rela->r_addend); + break; + case R_PPC_IRELATIVE: + if (dest_addr == 0) + return 5; + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = dest_addr; + ret->r_info = GELF_R_INFO (0, R_PPC_IRELATIVE); + ret->r_addend = rela->r_addend; + break; + default: + abort (); + } + return 0; +} + +static int +ppc_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: PowerPC doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +ppc_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC_NONE: + break; + case R_PPC_GLOB_DAT: + case R_PPC_ADDR32: + case R_PPC_UADDR32: + buf_write_be32 (buf, value); + break; + case R_PPC_ADDR16_HA: + value += 0x8000; + /* FALLTHROUGH */ + case R_PPC_ADDR16_HI: + value = value >> 16; + /* FALLTHROUGH */ + case R_PPC_ADDR16: + case R_PPC_UADDR16: + case R_PPC_ADDR16_LO: + buf_write_be16 (buf, value); + break; + case R_PPC_ADDR24: + buf_write_be32 (buf, (value & 0x03fffffc) + | (buf_read_ube32 (buf) & 0xfc000003)); + break; + case R_PPC_ADDR14: + buf_write_be32 (buf, (value & 0xfffc) + | (buf_read_ube32 (buf) & 0xffff0003)); + break; + case R_PPC_ADDR14_BRTAKEN: + case R_PPC_ADDR14_BRNTAKEN: + buf_write_be32 (buf, (value & 0xfffc) + | (buf_read_ube32 (buf) & 0xffdf0003) + | ((((GELF_R_TYPE (rela->r_info) + == R_PPC_ADDR14_BRTAKEN) << 21) + ^ (value >> 10)) & 0x00200000)); + break; + case R_PPC_REL24: + buf_write_be32 (buf, ((value - rela->r_offset) & 0x03fffffc) + | (buf_read_ube32 (buf) & 0xfc000003)); + break; + case R_PPC_REL32: + buf_write_be32 (buf, value - rela->r_offset); + break; + case R_PPC_RELATIVE: + error (0, 0, "%s: R_PPC_RELATIVE in ET_EXEC object?", + info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +ppc_prelink_conflict_rel (DSO *dso, struct prelink_info *info, + GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: PowerPC doesn't support REL relocs", dso->filename); + return 1; +} + +static int +ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + int r_type; + + if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_PPC_NONE) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + { + switch (GELF_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD and TPREL relocs need conflicts. */ + case R_PPC_DTPMOD32: + case R_PPC_TPREL32: + case R_PPC_TPREL16: + case R_PPC_TPREL16_LO: + case R_PPC_TPREL16_HI: + case R_PPC_TPREL16_HA: + if (info->curtls == NULL || info->dso == dso) + return 0; + break; + /* Similarly IRELATIVE relocations always need conflicts. */ + case R_PPC_IRELATIVE: + break; + default: + return 0; + } + value = 0; + } + else if (info->dso == dso && !conflict->ifunc) + return 0; + else + { + /* DTPREL wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC_DTPREL32: + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_LO: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_HA: + return 0; + } + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + value += rela->r_addend; + r_type = GELF_R_TYPE (rela->r_info); + switch (r_type) + { + case R_PPC_GLOB_DAT: + r_type = R_PPC_ADDR32; + case R_PPC_ADDR32: + case R_PPC_UADDR32: + case R_PPC_IRELATIVE: + if (conflict != NULL && conflict->ifunc) + r_type = R_PPC_IRELATIVE; + break; + case R_PPC_JMP_SLOT: + if (dynamic_info_is_set (dso, DT_PPC_GOT_BIT)) + { + r_type = R_PPC_ADDR32; + if (conflict != NULL && conflict->ifunc) + r_type = R_PPC_IRELATIVE; + } + break; + case R_PPC_ADDR16_HA: + value += 0x8000; + /* FALLTHROUGH */ + case R_PPC_ADDR16_HI: + value = value >> 16; + /* FALLTHROUGH */ + case R_PPC_ADDR16: + case R_PPC_UADDR16: + case R_PPC_ADDR16_LO: + if (r_type != R_PPC_UADDR16) + r_type = R_PPC_ADDR16; + value = ((value & 0xffff) ^ 0x8000) - 0x8000; + break; + case R_PPC_ADDR24: + r_type = R_PPC_ADDR32; + value = (value & 0x03fffffc) + | (read_ube32 (dso, rela->r_offset) & 0xfc000003); + break; + case R_PPC_ADDR14: + r_type = R_PPC_ADDR32; + value = (value & 0xfffc) + | (read_ube32 (dso, rela->r_offset) & 0xffff0003); + break; + case R_PPC_ADDR14_BRTAKEN: + case R_PPC_ADDR14_BRNTAKEN: + r_type = R_PPC_ADDR32; + value = (value & 0xfffc) + | (read_ube32 (dso, rela->r_offset) & 0xffdf0003) + | ((((r_type == R_PPC_ADDR14_BRTAKEN) << 21) + ^ (value >> 10)) & 0x00200000); + break; + case R_PPC_REL24: + r_type = R_PPC_ADDR32; + value = ((value - rela->r_offset) & 0x03fffffc) + | (read_ube32 (dso, rela->r_offset) & 0xfc000003); + break; + case R_PPC_REL32: + r_type = R_PPC_ADDR32; + value -= rela->r_offset; + break; + case R_PPC_DTPMOD32: + case R_PPC_DTPREL32: + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_LO: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_HA: + case R_PPC_TPREL32: + case R_PPC_TPREL16: + case R_PPC_TPREL16_LO: + case R_PPC_TPREL16_HI: + case R_PPC_TPREL16_HA: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + r_type = R_PPC_ADDR16; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC_DTPMOD32: + r_type = R_PPC_ADDR32; + value = tls->modid; + break; + case R_PPC_DTPREL32: + r_type = R_PPC_ADDR32; + value -= 0x8000; + break; + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_LO: + value -= 0x8000; + break; + case R_PPC_DTPREL16_HI: + value = (value - 0x8000) >> 16; + break; + case R_PPC_DTPREL16_HA: + value >>= 16; + break; + case R_PPC_TPREL32: + r_type = R_PPC_ADDR32; + value += tls->offset - 0x7000; + break; + case R_PPC_TPREL16: + case R_PPC_TPREL16_LO: + value += tls->offset - 0x7000; + break; + case R_PPC_TPREL16_HI: + value = (value + tls->offset - 0x7000) >> 16; + break; + case R_PPC_TPREL16_HA: + value = (value + tls->offset - 0x7000 + 0x8000) >> 16; + break; + } + if (r_type == R_PPC_ADDR16) + value = ((value & 0xffff) ^ 0x8000) - 0x8000; + break; + default: + error (0, 0, "%s: Unknown PowerPC relocation type %d", dso->filename, + r_type); + return 1; + } + if (conflict != NULL && conflict->ifunc && r_type != R_PPC_IRELATIVE) + { + error (0, 0, "%s: relocation %d against IFUNC symbol", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + ret->r_info = GELF_R_INFO (0, r_type); + ret->r_addend = (Elf32_Sword) value; + return 0; +} + +static int +ppc_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: PowerPC doesn't support REL relocs", dso->filename); + return 1; +} + +static int +ppc_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +ppc_arch_pre_prelink (DSO *dso) +{ + Elf_Data *data = NULL; + Elf_Scn *scn; + GElf_Dyn dyn; + Elf32_Addr val; + int i; + + if (!dynamic_info_is_set (dso, DT_PPC_GOT_BIT)) + return 0; + + assert (dso->shdr[dso->dynamic].sh_type == SHT_DYNAMIC); + + scn = dso->scn[dso->dynamic]; + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + + maxndx = data->d_size / dso->shdr[dso->dynamic].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getdyn (dso->elf, data, ndx, &dyn); + assert (dyn.d_tag != DT_NULL); + if (dyn.d_tag == DT_PPC_GOT) + break; + } + if (ndx < maxndx) + break; + } + + /* DT_PPC_GOT[1] should point to .glink in prelinked libs. */ + val = read_ube32 (dso, dyn.d_un.d_ptr + 4); + if (val) + return 0; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + + val = read_ube32 (dso, dso->shdr[i].sh_addr); + write_be32 (dso, dyn.d_un.d_ptr + 4, val); + + return 0; +} + +static int +ppc_arch_prelink (struct prelink_info *info) +{ + DSO *dso = info->dso; + Elf32_Addr plt = dso->info[DT_PLTGOT]; + + if (plt && !dynamic_info_is_set (dso, DT_PPC_GOT_BIT)) + { + Elf32_Word count = dso->info[DT_PLTRELSZ] / sizeof (Elf32_Rela); + Elf32_Addr data; + + data = plt + (18 + 2 * count + + (count > 8192 ? (count - 8192) * 2 : 0)) * 4; + + /* addis %r11, %r11, %hi(data) + lwz %r11, %r11, %lo(data) + mtctr %r11 + bctr */ + write_be32 (dso, plt, 0x3d6b0000 | (((data + 0x8000) >> 16) & 0xffff)); + write_be32 (dso, plt + 4, 0x816b0000 | (data & 0xffff)); + write_be32 (dso, plt + 8, 0x7d6903a6); + write_be32 (dso, plt + 12, 0x4e800420); + } + return 0; +} + +static int +ppc_arch_undo_prelink (DSO *dso) +{ + Elf_Data *data = NULL; + Elf_Scn *scn; + GElf_Dyn dyn; + Elf32_Addr val, addr, endaddr; + int i; + + if (!dynamic_info_is_set (dso, DT_PPC_GOT_BIT)) + return 0; + + assert (dso->shdr[dso->dynamic].sh_type == SHT_DYNAMIC); + + scn = dso->scn[dso->dynamic]; + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + + maxndx = data->d_size / dso->shdr[dso->dynamic].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getdyn (dso->elf, data, ndx, &dyn); + assert (dyn.d_tag != DT_NULL); + if (dyn.d_tag == DT_PPC_GOT) + break; + } + if (ndx < maxndx) + break; + } + + /* DT_PPC_GOT[1] should point to .glink in prelinked libs. */ + val = read_ube32 (dso, dyn.d_un.d_ptr + 4); + if (!val) + return 0; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt")) + break; + + if (i == dso->ehdr.e_shnum || (dso->shdr[i].sh_size & 3)) + return 0; + + addr = dso->shdr[i].sh_addr; + endaddr = addr + dso->shdr[i].sh_size; + for (; addr < endaddr; addr += 4, val += 4) + write_be32 (dso, addr, val); + + write_be32 (dso, dyn.d_un.d_ptr + 4, 0); + + return 0; +} + + +static int +ppc_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC_NONE: + return 0; + case R_PPC_RELATIVE: + case R_PPC_GLOB_DAT: + case R_PPC_ADDR32: + case R_PPC_UADDR32: + case R_PPC_REL32: + case R_PPC_DTPMOD32: + case R_PPC_DTPREL32: + case R_PPC_TPREL32: + write_be32 (dso, rela->r_offset, 0); + break; + case R_PPC_JMP_SLOT: + /* .plt section will become SHT_NOBITS if DT_PPC_GOT is not present, + otherwise .plt section will be unprelinked in + ppc_arch_undo_prelink. */ + return 0; + case R_PPC_IRELATIVE: + /* .iplt section will become SHT_NOBITS. */ + return 0; + case R_PPC_ADDR16: + case R_PPC_UADDR16: + case R_PPC_ADDR16_LO: + case R_PPC_ADDR16_HI: + case R_PPC_ADDR16_HA: + case R_PPC_DTPREL16: + case R_PPC_TPREL16: + case R_PPC_DTPREL16_LO: + case R_PPC_TPREL16_LO: + case R_PPC_DTPREL16_HI: + case R_PPC_TPREL16_HI: + case R_PPC_DTPREL16_HA: + case R_PPC_TPREL16_HA: + write_be16 (dso, rela->r_offset, 0); + break; + case R_PPC_ADDR24: + case R_PPC_REL24: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xfc000003); + break; + case R_PPC_ADDR14: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffff0003); + break; + case R_PPC_ADDR14_BRTAKEN: + case R_PPC_ADDR14_BRNTAKEN: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffdf0003); + break; + case R_PPC_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_PPC_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown ppc relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +ppc_reloc_size (int reloc_type) +{ + switch (reloc_type) + { + case R_PPC_ADDR16: + case R_PPC_UADDR16: + case R_PPC_ADDR16_LO: + case R_PPC_ADDR16_HI: + case R_PPC_ADDR16_HA: + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_LO: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_HA: + case R_PPC_TPREL16: + case R_PPC_TPREL16_LO: + case R_PPC_TPREL16_HI: + case R_PPC_TPREL16_HA: + return 2; + default: + break; + } + return 4; +} + +static int +ppc_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_PPC_COPY: return RTYPE_CLASS_COPY; + case R_PPC_JMP_SLOT: return RTYPE_CLASS_PLT; + default: + if (reloc_type >= R_PPC_DTPMOD32 && reloc_type <= R_PPC_DTPREL32) + return RTYPE_CLASS_TLS; + return RTYPE_CLASS_VALID; + } +} + +/* Library memory regions in order of precedence: + 0xe800000 .. 0x10000000 top to bottom + 0x40000 .. 0xe800000 bottom to top + 0x18000000 .. 0x30000000 bottom to top */ + +#define REG0S 0x0e800000 +#define REG0E 0x10000000 +#define REG1S 0x00040000 +#define REG1E REG0S +#define REG2S 0x18000000 +#define REG2E 0x30000000 + +struct ppc_layout_data +{ + int cnt; + struct prelink_entry e[3]; + Elf32_Addr mmap_start, first_start, last_start; + struct + { + struct prelink_entry *e; + Elf32_Addr base, end, layend; + } ents[0]; +}; + +static inline void +list_append (struct prelink_entry *x, struct prelink_entry *e) +{ + x->prev->next = e; + e->prev = x->prev; + e->next = NULL; + x->prev = e; +} + +static int +addr_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + + if (a->base < b->base) + return -1; + else if (a->base > b->base) + return 1; + if (a->layend < b->layend) + return -1; + else if (a->layend > b->layend) + return 1; + return 0; +} + +static void +list_sort (struct prelink_entry *x) +{ + int cnt, i; + struct prelink_entry *e; + struct prelink_entry **a; + + if (x->next == NULL) + return; + for (cnt = 0, e = x->next; e != NULL; e = e->next) + ++cnt; + a = alloca (cnt * sizeof (*a)); + for (i = 0, e = x->next; e != NULL; e = e->next) + a[i++] = e; + qsort (a, cnt, sizeof (*a), addr_cmp); + x->next = NULL; + x->prev = x; + for (i = 0; i < cnt; ++i) + list_append (x, a[i]); +} + +static int +ppc_layout_libs_pre (struct layout_libs *l) +{ + Elf32_Addr mmap_start = l->mmap_start - REG1S; + Elf32_Addr first_start = REG0S, last_start = REG2S; + struct prelink_entry *e, e0, *next = NULL; + struct ppc_layout_data *pld; + int cnt; + + if (l->max_page_size > 0x10000) + error (EXIT_FAILURE, 0, "--layout-page-size too large"); + + mmap_start = REG0E - (mmap_start & 0xff0000); + for (cnt = 0, e = l->list; e != NULL; e = e->next, ++cnt) + { + if (e->base < mmap_start && e->layend > mmap_start) + mmap_start = (e->layend + 0xffff) & ~0xffff; + if (e->base < REG0S && e->layend > REG0S && first_start > e->base) + first_start = e->base; + if (e->base < REG0E && e->layend > REG2S && last_start < e->layend) + last_start = e->layend; + } + if (mmap_start > REG0E) + mmap_start = REG0E; + + pld = calloc (sizeof (*pld) + cnt * sizeof (pld->ents[0]), 1); + if (pld == NULL) + error (EXIT_FAILURE, ENOMEM, "Cannot lay libraries out"); + + l->arch_data = pld; + memset (&e0, 0, sizeof (e0)); + e0.prev = &e0; + pld->cnt = cnt; + pld->e[0].u.tmp = -1; + pld->e[0].base = REG1S + REG0E - mmap_start; + pld->e[0].end = pld->e[0].base; + pld->e[0].layend = pld->e[0].end; + pld->e[0].prev = &pld->e[0]; + pld->e[1].u.tmp = -1; + pld->e[1].base = pld->e[0].end + mmap_start - REG0S; + pld->e[1].end = pld->e[1].base; + pld->e[1].layend = pld->e[1].end; + pld->e[1].prev = &pld->e[1]; + pld->e[2].u.tmp = -1; + pld->e[2].base = pld->e[1].end + first_start - REG1S; + pld->e[2].end = pld->e[1].base; + pld->e[2].layend = pld->e[2].end; + pld->e[2].prev = &pld->e[2]; + for (cnt = 0, e = l->list; e != NULL; e = next, ++cnt) + { + next = e->next; + pld->ents[cnt].e = e; + pld->ents[cnt].base = e->base; + pld->ents[cnt].end = e->end; + pld->ents[cnt].layend = e->layend; + if (e->layend <= REG0S) + { + if (e->base < REG1S) + e->base = REG1S; + else if (e->base > first_start) + e->base = first_start; + if (e->layend < REG1S) + e->layend = REG1S; + else if (e->layend > first_start) + e->layend = first_start; + e->base += pld->e[1].end - REG1S; + e->layend += pld->e[1].end - REG1S; + list_append (&pld->e[1], e); + } + else if (e->base < mmap_start) + { + if (e->base < REG0S) + e->base = REG0S; + if (e->layend > mmap_start) + e->layend = mmap_start; + e->base = pld->e[0].end + mmap_start - e->layend; + e->layend = pld->e[0].layend + mmap_start - pld->ents[cnt].base; + list_append (&pld->e[0], e); + } + else if (e->base < REG0E) + { + if (e->layend > REG0E) + e->layend = REG0E; + e->base = REG1S + REG0E - e->layend; + e->layend = REG1S + REG0E - pld->ents[cnt].base; + list_append (&e0, e); + } + else if (e->layend >= last_start) + { + if (e->base < last_start) + e->base = last_start; + e->base += pld->e[2].end - last_start; + e->layend += pld->e[2].end - last_start; + list_append (&pld->e[2], e); + } + e->end = e->layend; + } + + list_sort (&pld->e[0]); + if (e0.next == NULL) + l->list = &pld->e[0]; + else + { + list_sort (&e0); + l->list = e0.next; + l->list->prev = pld->e[0].prev; + e0.prev->next = &pld->e[0]; + pld->e[0].prev = e0.prev; + } + + e0.prev = l->list->prev; + l->list->prev = pld->e[1].prev; + e0.prev->next = &pld->e[1]; + pld->e[1].prev = e0.prev; + + e0.prev = l->list->prev; + l->list->prev = pld->e[2].prev; + e0.prev->next = &pld->e[2]; + pld->e[2].prev = e0.prev; + + pld->mmap_start = mmap_start; + pld->first_start = first_start; + pld->last_start = last_start; + + l->mmap_start = REG1S; + l->mmap_fin = pld->e[2].end + REG2E - last_start; + l->mmap_end = l->mmap_fin; + l->fakecnt = 3; + l->fake = pld->e; + + return 0; +} + +static int +ppc_layout_libs_post (struct layout_libs *l) +{ + struct prelink_entry *e; + struct ppc_layout_data *pld = (struct ppc_layout_data *) l->arch_data; + Elf32_Addr base, end; + int i; + + /* First fix up base and end fields we saved. */ + for (i = 0; i < pld->cnt; ++i) + { + pld->ents[i].e->base = pld->ents[i].base; + pld->ents[i].e->layend = pld->ents[i].layend; + pld->ents[i].e->end = pld->ents[i].end; + pld->ents[i].e->done |= 0x40; + } + pld->e[0].done |= 0x40; + pld->e[1].done |= 0x40; + pld->e[2].done |= 0x40; + + /* Now fix up the newly created items. */ + for (e = l->list; e != NULL; e = e->next) + if (e->done & 0x40) + e->done &= ~0x40; + else + { + base = e->base; + end = e->layend; + if (e->base < pld->e[0].base) + { + e->base = REG1S + REG0E - end; + e->end += e->base - base; + e->layend = REG1S + REG0E - base; + } + else if (e->base < pld->e[1].base) + { + e->base = pld->e[0].end + pld->mmap_start - end; + e->end += e->base - base; + e->layend = pld->e[0].end + pld->mmap_start - base; + } + else if (e->base < pld->e[2].base) + { + e->base -= pld->e[1].end - REG1S; + e->end -= pld->e[1].end - REG1S; + e->layend -= pld->e[1].end - REG1S; + } + else + { + e->base -= pld->e[2].end - pld->last_start; + e->end -= pld->e[2].end - pld->last_start; + e->layend -= pld->e[2].end - pld->last_start; + } + } + + for (i = 0; i < pld->cnt; ++i) + pld->ents[i].e->done &= ~0x40; + + free (l->arch_data); + return 0; +} + +PL_ARCH(ppc) = { + .name = "PowerPC", + .class = ELFCLASS32, + .machine = EM_PPC, + .alternate_machine = { EM_NONE }, + .R_JMP_SLOT = R_PPC_JMP_SLOT, + .R_COPY = R_PPC_COPY, + .R_RELATIVE = R_PPC_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld.so.1", + .adjust_dyn = ppc_adjust_dyn, + .adjust_rel = ppc_adjust_rel, + .adjust_rela = ppc_adjust_rela, + .prelink_rel = ppc_prelink_rel, + .prelink_rela = ppc_prelink_rela, + .prelink_conflict_rel = ppc_prelink_conflict_rel, + .prelink_conflict_rela = ppc_prelink_conflict_rela, + .apply_conflict_rela = ppc_apply_conflict_rela, + .apply_rel = ppc_apply_rel, + .apply_rela = ppc_apply_rela, + .rel_to_rela = ppc_rel_to_rela, + .need_rel_to_rela = ppc_need_rel_to_rela, + .reloc_size = ppc_reloc_size, + .reloc_class = ppc_reloc_class, + .max_reloc_size = 4, + .arch_pre_prelink = ppc_arch_pre_prelink, + .arch_prelink = ppc_arch_prelink, + .arch_undo_prelink = ppc_arch_undo_prelink, + .undo_prelink_rela = ppc_undo_prelink_rela, + .layout_libs_pre = ppc_layout_libs_pre, + .layout_libs_post = ppc_layout_libs_post, + /* This will need some changes in layout.c. + PowerPC prefers addresses right below REG0E + and can use the region above REG2S if libs don't fit. */ + .mmap_base = REG1S, + .mmap_end = REG2E, + .max_page_size = 0x10000, + .page_size = 0x1000 +}; diff --git a/src/arch-ppc64.c b/src/arch-ppc64.c new file mode 100644 index 0000000..a764b99 --- /dev/null +++ b/src/arch-ppc64.c @@ -0,0 +1,900 @@ +/* Copyright (C) 2002, 2003, 2004, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2002. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" +#include "layout.h" + +struct opd_rec +{ + GElf_Addr fn, toc, chain; +}; + +struct opd_lib +{ + GElf_Addr start, size; + GElf_Addr table[1]; +}; + +static int +ppc64_adjust_section (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) +{ + if (dso->shdr[n].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[n].sh_name), ".got")) + { + Elf64_Addr data; + + /* .got[0]-0x8000 points to .got, it needs to be adjusted. */ + data = read_ube64 (dso, dso->shdr[n].sh_addr); + if (addr_to_sec (dso, data - 0x8000) == n + && data - 0x8000 == dso->shdr[n].sh_addr) + write_be64 (dso, dso->shdr[n].sh_addr, data + adjust); + } + return 0; +} + +static int +ppc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PPC64_GLINK && dyn->d_un.d_ptr >= start) + { + dyn->d_un.d_ptr += adjust; + return 1; + } + + return 0; +} + +static int +ppc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: PowerPC64 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +ppc64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_PPC64_IRELATIVE) + { + GElf_Addr val = read_ube64 (dso, rela->r_offset); + + if (val == rela->r_addend && val >= start) + write_be64 (dso, rela->r_offset, val + adjust); + if (rela->r_addend >= start) + rela->r_addend += adjust; + } + else if (GELF_R_TYPE (rela->r_info) == R_PPC64_JMP_IREL) + { + if (rela->r_addend >= start) + rela->r_addend += adjust; + } + return 0; +} + +static int +ppc64_prelink_rel (struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: PowerPC64 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +ppc64_fixup_plt (struct prelink_info *info, GElf_Rela *rela, GElf_Addr value) +{ + DSO *dso = info->dso; + int sec, i; + size_t n; + struct opd_rec rec; + + if (value == 0) + { + rec.fn = 0; + rec.toc = 0; + rec.chain = 0; + } + else if ((sec = addr_to_sec (dso, value)) != -1) + { + rec.fn = read_ube64 (dso, value); + rec.toc = read_ube64 (dso, value + 8); + rec.chain = read_ube64 (dso, value + 16); + } + else + { + for (i = 0; i < info->ent->ndepends; ++i) + if (info->ent->depends[i]->opd + && info->ent->depends[i]->opd->start <= value + && (info->ent->depends[i]->opd->start + + info->ent->depends[i]->opd->size) > value) + break; + + if (i == info->ent->ndepends) + { + error (0, 0, "%s: R_PPC64_JMP_SLOT doesn't resolve to an .opd address", + dso->filename); + return 1; + } + if ((value - info->ent->depends[i]->opd->start) % 8) + { + error (0, 0, "%s: R_PPC64_JMP_SLOT doesn't resolve to valid .opd section location", + dso->filename); + return 1; + } + n = (value - info->ent->depends[i]->opd->start) / 8; + rec.fn = info->ent->depends[i]->opd->table[n]; + rec.toc = info->ent->depends[i]->opd->table[n + 1]; + rec.chain = info->ent->depends[i]->opd->table[n + 2]; + } + write_be64 (dso, rela->r_offset, rec.fn); + write_be64 (dso, rela->r_offset + 8, rec.toc); + write_be64 (dso, rela->r_offset + 16, rec.chain); + return 0; +} + +static int +ppc64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso = info->dso; + GElf_Addr value; + + if (GELF_R_TYPE (rela->r_info) == R_PPC64_NONE + || GELF_R_TYPE (rela->r_info) == R_PPC64_IRELATIVE + || GELF_R_TYPE (rela->r_info) == R_PPC64_JMP_IREL) + return 0; + else if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE) + { + write_be64 (dso, rela->r_offset, rela->r_addend); + return 0; + } + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC64_GLOB_DAT: + case R_PPC64_ADDR64: + case R_PPC64_UADDR64: + write_be64 (dso, rela->r_offset, value); + break; + case R_PPC64_DTPREL64: + write_be64 (dso, rela->r_offset, value - 0x8000); + break; + case R_PPC64_ADDR32: + case R_PPC64_UADDR32: + write_be32 (dso, rela->r_offset, value); + break; + case R_PPC64_JMP_SLOT: + return ppc64_fixup_plt (info, rela, value); + case R_PPC64_ADDR16: + case R_PPC64_UADDR16: + case R_PPC64_ADDR16_LO: + write_be16 (dso, rela->r_offset, value); + break; + case R_PPC64_DTPREL16: + case R_PPC64_DTPREL16_LO: + write_be16 (dso, rela->r_offset, value - 0x8000); + break; + case R_PPC64_ADDR16_HI: + case R_PPC64_DTPREL16_HA: + write_be16 (dso, rela->r_offset, value >> 16); + break; + case R_PPC64_DTPREL16_HI: + write_be16 (dso, rela->r_offset, (value - 0x8000) >> 16); + break; + case R_PPC64_ADDR16_HA: + write_be16 (dso, rela->r_offset, (value + 0x8000) >> 16); + break; + case R_PPC64_ADDR16_HIGHER: + write_be16 (dso, rela->r_offset, value >> 32); + break; + case R_PPC64_ADDR16_HIGHERA: + write_be16 (dso, rela->r_offset, (value + 0x8000) >> 32); + break; + case R_PPC64_ADDR16_HIGHEST: + write_be16 (dso, rela->r_offset, value >> 48); + break; + case R_PPC64_ADDR16_HIGHESTA: + write_be16 (dso, rela->r_offset, (value + 0x8000) >> 48); + break; + case R_PPC64_ADDR16_LO_DS: + case R_PPC64_ADDR16_DS: + write_be16 (dso, rela->r_offset, + (value & 0xfffc) | read_ube16 (dso, rela->r_offset & 3)); + break; + case R_PPC64_ADDR24: + write_be32 (dso, rela->r_offset, + (value & 0x03fffffc) + | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); + break; + case R_PPC64_ADDR14: + write_be32 (dso, rela->r_offset, + (value & 0xfffc) + | (read_ube32 (dso, rela->r_offset) & 0xffff0003)); + break; + case R_PPC64_ADDR14_BRTAKEN: + case R_PPC64_ADDR14_BRNTAKEN: + write_be32 (dso, rela->r_offset, + (value & 0xfffc) + | (read_ube32 (dso, rela->r_offset) & 0xffdf0003) + | ((((GELF_R_TYPE (rela->r_info) == R_PPC64_ADDR14_BRTAKEN) + << 21) + ^ (value >> 42)) & 0x00200000)); + break; + case R_PPC64_REL24: + write_be32 (dso, rela->r_offset, + ((value - rela->r_offset) & 0x03fffffc) + | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); + break; + case R_PPC64_REL32: + write_be32 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_PPC64_REL64: + write_be64 (dso, rela->r_offset, value - rela->r_offset); + break; + /* DTPMOD64 and TPREL* is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_PPC64_DTPMOD64: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_PPC64_DTPMOD64 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_PPC64_TPREL64: + case R_PPC64_TPREL16: + case R_PPC64_TPREL16_LO: + case R_PPC64_TPREL16_HI: + case R_PPC64_TPREL16_HA: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + { + value += info->resolvetls->offset - 0x7000; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC64_TPREL64: + write_be64 (dso, rela->r_offset, value); + break; + case R_PPC64_TPREL16: + case R_PPC64_TPREL16_LO: + write_be16 (dso, rela->r_offset, value); + break; + case R_PPC64_TPREL16_HI: + write_be16 (dso, rela->r_offset, value >> 16); + break; + case R_PPC64_TPREL16_HA: + write_be16 (dso, rela->r_offset, (value + 0x8000) >> 16); + break; + } + } + break; + case R_PPC64_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_PPC64_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown ppc relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +ppc64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + GElf_Rela *ret; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC64_ADDR64: + case R_PPC64_UADDR64: + buf_write_be64 (buf, rela->r_addend); + break; + case R_PPC64_ADDR32: + case R_PPC64_UADDR32: + buf_write_be32 (buf, rela->r_addend); + break; + case R_PPC64_ADDR16: + case R_PPC64_UADDR16: + buf_write_be16 (buf, rela->r_addend); + break; + case R_PPC64_IRELATIVE: + if (dest_addr == 0) + return 5; + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = dest_addr; + ret->r_info = GELF_R_INFO (0, R_PPC64_IRELATIVE); + ret->r_addend = rela->r_addend; + break; + default: + abort (); + } + return 0; +} + +static int +ppc64_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: PowerPC64 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +ppc64_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC64_NONE: + break; + case R_PPC64_GLOB_DAT: + case R_PPC64_ADDR64: + case R_PPC64_UADDR64: + buf_write_be64 (buf, value); + break; + case R_PPC64_ADDR32: + case R_PPC64_UADDR32: + buf_write_be32 (buf, value); + break; + case R_PPC64_ADDR16_HA: + value += 0x8000; + /* FALLTHROUGH */ + case R_PPC64_ADDR16_HI: + value = value >> 16; + /* FALLTHROUGH */ + case R_PPC64_ADDR16: + case R_PPC64_UADDR16: + case R_PPC64_ADDR16_LO: + buf_write_be16 (buf, value); + break; + case R_PPC64_ADDR16_HIGHERA: + value += 0x8000; + /* FALLTHROUGH */ + case R_PPC64_ADDR16_HIGHER: + buf_write_be16 (buf, value >> 32); + break; + case R_PPC64_ADDR16_HIGHESTA: + value += 0x8000; + /* FALLTHROUGH */ + case R_PPC64_ADDR16_HIGHEST: + buf_write_be16 (buf, value >> 48); + break; + case R_PPC64_ADDR16_LO_DS: + case R_PPC64_ADDR16_DS: + buf_write_be16 (buf, (value & 0xfffc) + | (buf_read_ube16 (buf) & 3)); + break; + case R_PPC64_ADDR24: + buf_write_be32 (buf, (value & 0x03fffffc) + | (buf_read_ube32 (buf) & 0xfc000003)); + break; + case R_PPC64_ADDR14: + buf_write_be32 (buf, (value & 0xfffc) + | (buf_read_ube32 (buf) & 0xffff0003)); + break; + case R_PPC64_ADDR14_BRTAKEN: + case R_PPC64_ADDR14_BRNTAKEN: + buf_write_be32 (buf, (value & 0xfffc) + | (buf_read_ube32 (buf) & 0xffdf0003) + | ((((GELF_R_TYPE (rela->r_info) + == R_PPC64_ADDR14_BRTAKEN) << 21) + ^ (value >> 42)) & 0x00200000)); + break; + case R_PPC64_REL24: + buf_write_be32 (buf, ((value - rela->r_offset) & 0x03fffffc) + | (buf_read_ube32 (buf) & 0xfc000003)); + break; + case R_PPC64_REL32: + buf_write_be32 (buf, value - rela->r_offset); + break; + case R_PPC64_REL64: + buf_write_be64 (buf, value - rela->r_offset); + break; + case R_PPC64_RELATIVE: + error (0, 0, "%s: R_PPC64_RELATIVE in ET_EXEC object?", + info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +ppc64_prelink_conflict_rel (DSO *dso, struct prelink_info *info, + GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: PowerPC64 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +ppc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + int r_type; + + if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_PPC64_NONE) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + { + switch (GELF_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD and TPREL relocs need conflicts. */ + case R_PPC64_DTPMOD64: + case R_PPC64_TPREL64: + case R_PPC64_TPREL16: + case R_PPC64_TPREL16_LO: + case R_PPC64_TPREL16_HI: + case R_PPC64_TPREL16_HA: + if (info->curtls == NULL || info->dso == dso) + return 0; + break; + /* Similarly IRELATIVE relocations always need conflicts. */ + case R_PPC64_IRELATIVE: + case R_PPC64_JMP_IREL: + break; + default: + return 0; + } + value = 0; + } + else if (info->dso == dso && !conflict->ifunc) + return 0; + else + { + /* DTPREL wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC64_DTPREL64: + case R_PPC64_DTPREL16: + case R_PPC64_DTPREL16_LO: + case R_PPC64_DTPREL16_HI: + case R_PPC64_DTPREL16_HA: + return 0; + } + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + value += rela->r_addend; + r_type = GELF_R_TYPE (rela->r_info); + switch (r_type) + { + case R_PPC64_GLOB_DAT: + r_type = R_PPC64_ADDR64; + case R_PPC64_ADDR64: + case R_PPC64_UADDR64: + if (conflict != NULL && conflict->ifunc) + r_type = R_PPC64_IRELATIVE; + break; + case R_PPC64_IRELATIVE: + case R_PPC64_JMP_IREL: + break; + case R_PPC64_JMP_SLOT: + if (conflict != NULL && conflict->ifunc) + r_type = R_PPC64_JMP_IREL; + break; + case R_PPC64_ADDR32: + case R_PPC64_UADDR32: + value = (Elf32_Sword) value; + break; + case R_PPC64_ADDR16_HA: + value += 0x8000; + /* FALLTHROUGH */ + case R_PPC64_ADDR16_HI: + value = value >> 16; + /* FALLTHROUGH */ + case R_PPC64_ADDR16: + case R_PPC64_UADDR16: + case R_PPC64_ADDR16_LO: + if (r_type != R_PPC64_UADDR16) + r_type = R_PPC64_ADDR16; + value = ((value & 0xffff) ^ 0x8000) - 0x8000; + break; + case R_PPC64_ADDR16_HIGHERA: + value += 0x8000; + /* FALLTHROUGH */ + case R_PPC64_ADDR16_HIGHER: + r_type = R_PPC64_ADDR16; + value = (((value >> 32) & 0xffff) ^ 0x8000) - 0x8000; + break; + case R_PPC64_ADDR16_HIGHESTA: + value += 0x8000; + /* FALLTHROUGH */ + case R_PPC64_ADDR16_HIGHEST: + r_type = R_PPC64_ADDR16; + value = ((Elf64_Sxword) value) >> 48; + break; + case R_PPC64_ADDR16_LO_DS: + case R_PPC64_ADDR16_DS: + r_type = R_PPC64_ADDR16; + value = ((value & 0xffff) ^ 0x8000) - 0x8000; + value |= read_ube16 (dso, rela->r_offset) & 3; + break; + case R_PPC64_ADDR24: + r_type = R_PPC64_ADDR32; + value = (value & 0x03fffffc) + | (read_ube32 (dso, rela->r_offset) & 0xfc000003); + value = (Elf32_Sword) value; + break; + case R_PPC64_ADDR14: + r_type = R_PPC64_ADDR32; + value = (value & 0xfffc) + | (read_ube32 (dso, rela->r_offset) & 0xffff0003); + value = (Elf32_Sword) value; + break; + case R_PPC64_ADDR14_BRTAKEN: + case R_PPC64_ADDR14_BRNTAKEN: + r_type = R_PPC64_ADDR32; + value = (value & 0xfffc) + | (read_ube32 (dso, rela->r_offset) & 0xffdf0003) + | ((((r_type == R_PPC64_ADDR14_BRTAKEN) << 21) + ^ (value >> 42)) & 0x00200000); + value = (Elf32_Sword) value; + break; + case R_PPC64_REL24: + r_type = R_PPC64_ADDR32; + value = ((value - rela->r_offset) & 0x03fffffc) + | (read_ube32 (dso, rela->r_offset) & 0xfc000003); + value = (Elf32_Sword) value; + break; + case R_PPC64_REL32: + r_type = R_PPC64_ADDR32; + value -= rela->r_offset; + value = (Elf32_Sword) value; + break; + case R_PPC64_REL64: + r_type = R_PPC64_ADDR64; + value -= rela->r_offset; + break; + case R_PPC64_DTPMOD64: + case R_PPC64_DTPREL64: + case R_PPC64_DTPREL16: + case R_PPC64_DTPREL16_LO: + case R_PPC64_DTPREL16_HI: + case R_PPC64_DTPREL16_HA: + case R_PPC64_TPREL64: + case R_PPC64_TPREL16: + case R_PPC64_TPREL16_LO: + case R_PPC64_TPREL16_HI: + case R_PPC64_TPREL16_HA: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + r_type = R_PPC64_ADDR16; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC64_DTPMOD64: + r_type = R_PPC64_ADDR64; + value = tls->modid; + break; + case R_PPC64_DTPREL64: + r_type = R_PPC64_ADDR64; + value -= 0x8000; + break; + case R_PPC64_DTPREL16: + case R_PPC64_DTPREL16_LO: + value -= 0x8000; + break; + case R_PPC64_DTPREL16_HI: + value = (value - 0x8000) >> 16; + break; + case R_PPC64_DTPREL16_HA: + value >>= 16; + break; + case R_PPC64_TPREL64: + r_type = R_PPC64_ADDR64; + value += tls->offset - 0x7000; + break; + case R_PPC64_TPREL16: + case R_PPC64_TPREL16_LO: + value += tls->offset - 0x7000; + break; + case R_PPC64_TPREL16_HI: + value = (value + tls->offset - 0x7000) >> 16; + break; + case R_PPC64_TPREL16_HA: + value = (value + tls->offset - 0x7000 + 0x8000) >> 16; + break; + } + if (r_type == R_PPC64_ADDR16) + value = ((value & 0xffff) ^ 0x8000) - 0x8000; + break; + default: + error (0, 0, "%s: Unknown PowerPC64 relocation type %d", dso->filename, + r_type); + return 1; + } + if (conflict != NULL && conflict->ifunc + && r_type != R_PPC64_IRELATIVE && r_type != R_PPC64_JMP_IREL) + { + error (0, 0, "%s: relocation %d against IFUNC symbol", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + ret->r_info = GELF_R_INFO (0, r_type); + ret->r_addend = value; + return 0; +} + +static int +ppc64_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: PowerPC64 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +ppc64_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +ppc64_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + switch (GELF_R_TYPE (rela->r_info)) + { + case R_PPC64_NONE: + return 0; + case R_PPC64_JMP_SLOT: + /* .plt section will become SHT_NOBITS. */ + return 0; + case R_PPC64_JMP_IREL: + /* .iplt section will become SHT_NOBITS. */ + return 0; + case R_PPC64_RELATIVE: + case R_PPC64_ADDR64: + case R_PPC64_IRELATIVE: + write_be64 (dso, rela->r_offset, rela->r_addend); + break; + case R_PPC64_GLOB_DAT: + case R_PPC64_UADDR64: + case R_PPC64_DTPREL64: + case R_PPC64_TPREL64: + case R_PPC64_DTPMOD64: + case R_PPC64_REL64: + write_be64 (dso, rela->r_offset, 0); + break; + case R_PPC64_ADDR32: + case R_PPC64_UADDR32: + case R_PPC64_REL32: + write_be32 (dso, rela->r_offset, 0); + break; + case R_PPC64_ADDR16_HA: + case R_PPC64_DTPREL16_HA: + case R_PPC64_TPREL16_HA: + case R_PPC64_ADDR16_HI: + case R_PPC64_DTPREL16_HI: + case R_PPC64_TPREL16_HI: + case R_PPC64_ADDR16: + case R_PPC64_UADDR16: + case R_PPC64_ADDR16_LO: + case R_PPC64_DTPREL16: + case R_PPC64_TPREL16: + case R_PPC64_DTPREL16_LO: + case R_PPC64_TPREL16_LO: + case R_PPC64_ADDR16_HIGHERA: + case R_PPC64_ADDR16_HIGHER: + case R_PPC64_ADDR16_HIGHESTA: + case R_PPC64_ADDR16_HIGHEST: + case R_PPC64_ADDR16_LO_DS: + case R_PPC64_ADDR16_DS: + write_be16 (dso, rela->r_offset, 0); + break; + case R_PPC64_ADDR24: + case R_PPC64_REL24: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xfc000003); + break; + case R_PPC64_ADDR14: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffff0003); + break; + case R_PPC64_ADDR14_BRTAKEN: + case R_PPC64_ADDR14_BRNTAKEN: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffdf0003); + break; + case R_PPC64_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_PPC64_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown ppc relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +ppc64_reloc_size (int reloc_type) +{ + switch (reloc_type) + { + case R_PPC64_ADDR16: + case R_PPC64_UADDR16: + case R_PPC64_ADDR16_LO: + case R_PPC64_ADDR16_HA: + case R_PPC64_ADDR16_HI: + case R_PPC64_ADDR16_LO_DS: + case R_PPC64_ADDR16_DS: + case R_PPC64_ADDR16_HIGHER: + case R_PPC64_ADDR16_HIGHERA: + case R_PPC64_ADDR16_HIGHEST: + case R_PPC64_ADDR16_HIGHESTA: + case R_PPC64_DTPREL16: + case R_PPC64_DTPREL16_LO: + case R_PPC64_DTPREL16_HI: + case R_PPC64_DTPREL16_HA: + case R_PPC64_TPREL16: + case R_PPC64_TPREL16_LO: + case R_PPC64_TPREL16_HI: + case R_PPC64_TPREL16_HA: + return 2; + case R_PPC64_GLOB_DAT: + case R_PPC64_ADDR64: + case R_PPC64_UADDR64: + case R_PPC64_REL64: + case R_PPC64_DTPMOD64: + case R_PPC64_DTPREL64: + case R_PPC64_TPREL64: + case R_PPC64_IRELATIVE: + return 8; + default: + break; + } + return 4; +} + +static int +ppc64_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_PPC64_COPY: return RTYPE_CLASS_COPY | RTYPE_CLASS_PLT; + default: + if (reloc_type >= R_PPC64_DTPMOD64 + && reloc_type <= R_PPC64_TPREL16_HIGHESTA) + return RTYPE_CLASS_TLS; + return RTYPE_CLASS_PLT; + } +} + +static int +ppc64_read_opd (DSO *dso, struct prelink_entry *ent) +{ + int opd; + GElf_Addr n, s; + + free (ent->opd); + ent->opd = NULL; + for (opd = 1; opd < dso->ehdr.e_shnum; ++opd) + if (dso->shdr[opd].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[opd].sh_name), ".opd")) + break; + if (opd == dso->ehdr.e_shnum) + return 0; + ent->opd = malloc (sizeof (struct opd_lib) + dso->shdr[opd].sh_size); + /* The error will happen only when we'll need the opd. */ + if (ent->opd == NULL) + return 0; + s = dso->shdr[opd].sh_addr; + for (n = 0; n < dso->shdr[opd].sh_size / 8; ++n, s += 8) + ent->opd->table[n] = read_ube64 (dso, s); + ent->opd->start = dso->shdr[opd].sh_addr; + ent->opd->size = dso->shdr[opd].sh_size; + return 0; +} + +static int +ppc64_free_opd (struct prelink_entry *ent) +{ + free (ent->opd); + ent->opd = NULL; + return 0; +} + +PL_ARCH(ppc64) = { + .name = "PowerPC", + .class = ELFCLASS64, + .machine = EM_PPC64, + .alternate_machine = { EM_NONE }, + .R_JMP_SLOT = R_PPC64_JMP_SLOT, + .R_COPY = R_PPC64_COPY, + .R_RELATIVE = R_PPC64_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_PLT, + .dynamic_linker = "/lib64/ld64.so.1", + .adjust_section = ppc64_adjust_section, + .adjust_dyn = ppc64_adjust_dyn, + .adjust_rel = ppc64_adjust_rel, + .adjust_rela = ppc64_adjust_rela, + .prelink_rel = ppc64_prelink_rel, + .prelink_rela = ppc64_prelink_rela, + .prelink_conflict_rel = ppc64_prelink_conflict_rel, + .prelink_conflict_rela = ppc64_prelink_conflict_rela, + .apply_conflict_rela = ppc64_apply_conflict_rela, + .apply_rel = ppc64_apply_rel, + .apply_rela = ppc64_apply_rela, + .rel_to_rela = ppc64_rel_to_rela, + .need_rel_to_rela = ppc64_need_rel_to_rela, + .reloc_size = ppc64_reloc_size, + .reloc_class = ppc64_reloc_class, + .read_opd = ppc64_read_opd, + .free_opd = ppc64_free_opd, + .max_reloc_size = 8, + .undo_prelink_rela = ppc64_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0x8000000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x8001000000LL, + .mmap_end = 0x8100000000LL, + .max_page_size = 0x10000, + .page_size = 0x1000 +}; diff --git a/src/arch-s390.c b/src/arch-s390.c new file mode 100644 index 0000000..e5fe130 --- /dev/null +++ b/src/arch-s390.c @@ -0,0 +1,632 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2009, 2010, 2013 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" + +static int +s390_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PLTGOT) + { + int sec = addr_to_sec (dso, dyn->d_un.d_ptr); + Elf64_Addr data; + + if (sec == -1) + return 0; + + data = read_ube32 (dso, dyn->d_un.d_ptr); + /* If .got.plt[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_be32 (dso, dyn->d_un.d_ptr, data + adjust); + + data = read_ube32 (dso, dyn->d_un.d_ptr + 4); + /* If .got.plt[1] points to .plt + 0x2c, it needs to be adjusted. */ + if (data && data >= start) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (data == dso->shdr[i].sh_addr + 0x2c + && dso->shdr[i].sh_type == SHT_PROGBITS + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt") == 0) + { + write_be32 (dso, dyn->d_un.d_ptr + 4, data + adjust); + break; + } + } + } + return 0; +} + +static int +s390_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +s390_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + Elf32_Addr addr; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_RELATIVE: + if ((Elf32_Addr) rela->r_addend >= start) + { + addr = read_ube32 (dso, rela->r_offset); + if (addr == rela->r_addend) + write_be32 (dso, rela->r_offset, addr + adjust); + rela->r_addend += (Elf32_Sword) adjust; + } + break; + case R_390_IRELATIVE: + if (rela->r_addend >= start) + /* Adjust the resolver function address. */ + rela->r_addend += adjust; + /* FALLTHROUGH */ + case R_390_JMP_SLOT: + /* Adjust the address in the GOT slot. */ + addr = read_ube32 (dso, rela->r_offset); + if (addr >= start) + write_be32 (dso, rela->r_offset, addr + adjust); + break; + } + return 0; +} + +static int +s390_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +s390_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso = info->dso; + GElf_Addr value; + + if (GELF_R_TYPE (rela->r_info) == R_390_NONE + || GELF_R_TYPE (rela->r_info) == R_390_IRELATIVE) + return 0; + else if (GELF_R_TYPE (rela->r_info) == R_390_RELATIVE) + { + write_be32 (dso, rela->r_offset, rela->r_addend); + return 0; + } + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + write_be32 (dso, rela->r_offset, value - rela->r_addend); + break; + case R_390_32: + write_be32 (dso, rela->r_offset, value); + break; + case R_390_PC32: + write_be32 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_390_PC32DBL: + case R_390_PLT32DBL: + write_be32 (dso, rela->r_offset, + ((Elf32_Sword) (value - rela->r_offset)) >> 1); + break; + case R_390_16: + write_be16 (dso, rela->r_offset, value); + break; + case R_390_PC16: + write_be16 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_390_PC16DBL: + case R_390_PLT16DBL: + write_be16 (dso, rela->r_offset, + ((int16_t) (value - rela->r_offset)) >> 1); + break; + case R_390_8: + write_8 (dso, rela->r_offset, value); + break; + case R_390_TLS_DTPOFF: + write_be32 (dso, rela->r_offset, value); + break; + /* DTPMOD and TPOFF is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_390_TLS_DTPMOD: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_390_TLS_DTPMOD reloc in executable?", + dso->filename); + return 1; + } + break; + case R_390_TLS_TPOFF: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_be32 (dso, rela->r_offset, + value - info->resolvetls->offset); + break; + case R_390_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_390_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown S390 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +s390_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + GElf_Rela *ret; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_32: + buf_write_be32 (buf, rela->r_addend); + break; + case R_390_16: + buf_write_be16 (buf, rela->r_addend); + break; + case R_390_8: + buf_write_8 (buf, rela->r_addend); + break; + case R_390_IRELATIVE: + if (dest_addr == 0) + return 5; + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = dest_addr; + ret->r_info = GELF_R_INFO (0, R_390_IRELATIVE); + ret->r_addend = rela->r_addend; + break; + default: + abort (); + } + return 0; +} + +static int +s390_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +s390_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_NONE: + break; + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + buf_write_be32 (buf, value - rela->r_addend); + break; + case R_390_32: + buf_write_be32 (buf, value); + break; + case R_390_PC32: + buf_write_be32 (buf, value - rela->r_offset); + break; + case R_390_PC32DBL: + case R_390_PLT32DBL: + buf_write_be32 (buf, ((Elf32_Sword) (value - rela->r_offset)) >> 1); + break; + case R_390_16: + buf_write_be16 (buf, value); + break; + case R_390_PC16: + buf_write_be16 (buf, value - rela->r_offset); + break; + case R_390_PC16DBL: + case R_390_PLT16DBL: + buf_write_be16 (buf, ((int16_t) (value - rela->r_offset)) >> 1); + break; + case R_390_8: + buf_write_8 (buf, value); + break; + case R_390_COPY: + abort (); + case R_390_RELATIVE: + error (0, 0, "%s: R_390_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +s390_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +s390_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + + if (GELF_R_TYPE (rela->r_info) == R_390_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_390_NONE) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + { + switch (GELF_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD and TPOFF relocs need conflicts. */ + case R_390_TLS_DTPMOD: + case R_390_TLS_TPOFF: + if (info->curtls == NULL || info->dso == dso) + return 0; + break; + /* IRELATIVE always need conflicts. */ + case R_390_IRELATIVE: + break; + default: + return 0; + } + value = 0; + } + else if (info->dso == dso && !conflict->ifunc) + return 0; + else + { + /* DTPOFF wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rela->r_info) == R_390_TLS_DTPOFF + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + ret->r_info = GELF_R_INFO (0, R_390_32); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + ret->r_addend = (Elf32_Sword) (value - rela->r_addend); + if (conflict != NULL && conflict->ifunc) + ret->r_info = GELF_R_INFO (0, R_390_IRELATIVE); + break; + case R_390_32: + ret->r_addend = (Elf32_Sword) value; + if (conflict != NULL && conflict->ifunc) + ret->r_info = GELF_R_INFO (0, R_390_IRELATIVE); + break; + case R_390_IRELATIVE: + ret->r_addend = (Elf32_Sword) value; + ret->r_info = GELF_R_INFO (0, R_390_IRELATIVE); + break; + case R_390_PC32: + ret->r_addend = (Elf32_Sword) (value - rela->r_offset); + break; + case R_390_PC32DBL: + case R_390_PLT32DBL: + ret->r_addend = ((Elf32_Sword) (value - rela->r_offset)) >> 1; + break; + case R_390_PC16: + value -= rela->r_offset; + case R_390_16: + ret->r_addend = (Elf32_Half) value; + ret->r_info = GELF_R_INFO (0, R_390_16); + break; + case R_390_PC16DBL: + case R_390_PLT16DBL: + ret->r_addend = (Elf32_Half) (((int16_t) (value - rela->r_offset)) >> 1); + ret->r_info = GELF_R_INFO (0, R_390_16); + break; + case R_390_8: + ret->r_addend = value & 0xff; + ret->r_info = GELF_R_INFO (0, R_390_8); + break; + case R_390_COPY: + error (0, 0, "R_390_COPY should not be present in shared libraries"); + return 1; + case R_390_TLS_DTPMOD: + case R_390_TLS_DTPOFF: + case R_390_TLS_TPOFF: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_TLS_DTPMOD: + ret->r_addend = tls->modid; + break; + case R_390_TLS_DTPOFF: + ret->r_addend = value; + break; + case R_390_TLS_TPOFF: + ret->r_addend = value - tls->offset; + break; + } + break; + + default: + error (0, 0, "%s: Unknown S390 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +s390_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +s390_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +s390_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + int i; + + dso = info->dso; + if (dso->info[DT_PLTGOT]) + { + /* Write address of .plt + 0x2c into got[1]. + .plt + 0x2c is what got[3] contains unless prelinking. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf64_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = dso->shdr[i].sh_addr + 0x2c; + write_be32 (dso, dso->info[DT_PLTGOT] + 4, data); + } + + return 0; +} + +static int +s390_arch_undo_prelink (DSO *dso) +{ + int i; + + if (dso->info[DT_PLTGOT]) + { + /* Clear got[1] if it contains address of .plt + 0x2c. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = read_ube32 (dso, dso->info[DT_PLTGOT] + 4); + if (data == dso->shdr[i].sh_addr + 0x2c) + write_be32 (dso, dso->info[DT_PLTGOT] + 4, 0); + } + + return 0; +} + +static int +s390_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + int sec; + const char *name; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_NONE: + case R_390_RELATIVE: + case R_390_IRELATIVE: + break; + case R_390_JMP_SLOT: + sec = addr_to_sec (dso, rela->r_offset); + name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name); + if (sec == -1 || (strcmp (name, ".got") && strcmp (name, ".got.plt"))) + { + error (0, 0, "%s: R_390_JMP_SLOT not pointing into .got section", + dso->filename); + return 1; + } + else + { + Elf32_Addr data = read_ube32 (dso, dso->shdr[sec].sh_addr + 4); + + assert (rela->r_offset >= dso->shdr[sec].sh_addr + 12); + assert (((rela->r_offset - dso->shdr[sec].sh_addr) & 3) == 0); + write_be32 (dso, rela->r_offset, + 8 * (rela->r_offset - dso->shdr[sec].sh_addr - 12) + + data); + } + break; + case R_390_GLOB_DAT: + case R_390_32: + case R_390_PC32: + case R_390_PC32DBL: + case R_390_PLT32DBL: + case R_390_TLS_DTPMOD: + case R_390_TLS_DTPOFF: + case R_390_TLS_TPOFF: + write_be32 (dso, rela->r_offset, 0); + break; + case R_390_16: + case R_390_PC16: + case R_390_PC16DBL: + case R_390_PLT16DBL: + write_be16 (dso, rela->r_offset, 0); + break; + case R_390_8: + write_8 (dso, rela->r_offset, 0); + break; + case R_390_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_390_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown s390 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +s390_reloc_size (int reloc_type) +{ + switch (reloc_type) + { + case R_390_16: + case R_390_PC16: + case R_390_PC16DBL: + case R_390_PLT16DBL: + return 2; + case R_390_8: + return 1; + default: + return 4; + } +} + +static int +s390_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_390_COPY: return RTYPE_CLASS_COPY; + case R_390_JMP_SLOT: return RTYPE_CLASS_PLT; + case R_390_TLS_DTPMOD: + case R_390_TLS_DTPOFF: + case R_390_TLS_TPOFF: + return RTYPE_CLASS_TLS; + default: return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(s390) = { + .name = "S390", + .class = ELFCLASS32, + .machine = EM_S390, + .alternate_machine = { 0xA390 }, + .R_JMP_SLOT = R_390_JMP_SLOT, + .R_COPY = R_390_COPY, + .R_RELATIVE = R_390_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld.so.1", + .adjust_dyn = s390_adjust_dyn, + .adjust_rel = s390_adjust_rel, + .adjust_rela = s390_adjust_rela, + .prelink_rel = s390_prelink_rel, + .prelink_rela = s390_prelink_rela, + .prelink_conflict_rel = s390_prelink_conflict_rel, + .prelink_conflict_rela = s390_prelink_conflict_rela, + .apply_conflict_rela = s390_apply_conflict_rela, + .apply_rel = s390_apply_rel, + .apply_rela = s390_apply_rela, + .rel_to_rela = s390_rel_to_rela, + .need_rel_to_rela = s390_need_rel_to_rela, + .reloc_size = s390_reloc_size, + .reloc_class = s390_reloc_class, + .max_reloc_size = 4, + .arch_prelink = s390_arch_prelink, + .arch_undo_prelink = s390_arch_undo_prelink, + .undo_prelink_rela = s390_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0x40000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x41000000, + .mmap_end = 0x50000000, + .max_page_size = 0x1000, + .page_size = 0x1000 +}; diff --git a/src/arch-s390x.c b/src/arch-s390x.c new file mode 100644 index 0000000..e4d82f7 --- /dev/null +++ b/src/arch-s390x.c @@ -0,0 +1,658 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2009, 2013 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" + +static int +s390x_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PLTGOT) + { + int sec = addr_to_sec (dso, dyn->d_un.d_ptr); + Elf64_Addr data; + + if (sec == -1) + return 0; + + data = read_ube64 (dso, dyn->d_un.d_ptr); + /* If .got.plt[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_be64 (dso, dyn->d_un.d_ptr, data + adjust); + + data = read_ube64 (dso, dyn->d_un.d_ptr + 8); + /* If .got.plt[1] points to .plt + 0x2e, it needs to be adjusted. */ + if (data && data >= start) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (data == dso->shdr[i].sh_addr + 0x2e + && dso->shdr[i].sh_type == SHT_PROGBITS + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt") == 0) + { + write_be64 (dso, dyn->d_un.d_ptr + 8, data + adjust); + break; + } + } + } + return 0; +} + +static int +s390x_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +s390x_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + Elf64_Addr addr; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_RELATIVE: + if (rela->r_addend >= start) + { + addr = read_ube64 (dso, rela->r_offset); + if (addr == rela->r_addend) + write_be64 (dso, rela->r_offset, addr + adjust); + rela->r_addend += adjust; + } + break; + case R_390_IRELATIVE: + if (rela->r_addend >= start) + /* Adjust the resolver function address. */ + rela->r_addend += adjust; + /* FALLTHROUGH */ + case R_390_JMP_SLOT: + /* Adjust the address in the GOT slot. */ + addr = read_ube64 (dso, rela->r_offset); + if (addr >= start) + write_be64 (dso, rela->r_offset, addr + adjust); + break; + } + return 0; +} + +static int +s390x_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +s390x_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso = info->dso; + GElf_Addr value; + + if (GELF_R_TYPE (rela->r_info) == R_390_NONE + || GELF_R_TYPE (rela->r_info) == R_390_IRELATIVE) + return 0; + else if (GELF_R_TYPE (rela->r_info) == R_390_RELATIVE) + { + write_be64 (dso, rela->r_offset, rela->r_addend); + return 0; + } + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + case R_390_64: + write_be64 (dso, rela->r_offset, value); + break; + case R_390_PC64: + write_be64 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_390_32: + write_be32 (dso, rela->r_offset, value); + break; + case R_390_PC32: + write_be32 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_390_PC32DBL: + case R_390_PLT32DBL: + write_be32 (dso, rela->r_offset, + ((Elf32_Sword) (value - rela->r_offset)) >> 1); + break; + case R_390_16: + write_be16 (dso, rela->r_offset, value); + break; + case R_390_PC16: + write_be16 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_390_PC16DBL: + case R_390_PLT16DBL: + write_be16 (dso, rela->r_offset, + ((int16_t) (value - rela->r_offset)) >> 1); + break; + case R_390_8: + write_8 (dso, rela->r_offset, value); + break; + case R_390_TLS_DTPOFF: + write_be64 (dso, rela->r_offset, value); + break; + /* DTPMOD and TPOFF is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_390_TLS_DTPMOD: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_390_TLS_DTPMOD reloc in executable?", + dso->filename); + return 1; + } + break; + case R_390_TLS_TPOFF: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_be64 (dso, rela->r_offset, value - info->resolvetls->offset); + break; + case R_390_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_390_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown S390 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +s390x_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + GElf_Rela *ret; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_64: + buf_write_be64 (buf, rela->r_addend); + break; + case R_390_32: + buf_write_be32 (buf, rela->r_addend); + break; + case R_390_16: + buf_write_be16 (buf, rela->r_addend); + break; + case R_390_8: + buf_write_8 (buf, rela->r_addend); + break; + case R_390_IRELATIVE: + if (dest_addr == 0) + return 5; + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = dest_addr; + ret->r_info = GELF_R_INFO (0, R_390_IRELATIVE); + ret->r_addend = rela->r_addend; + break; + default: + abort (); + } + return 0; +} + +static int +s390x_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +s390x_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_NONE: + break; + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + case R_390_64: + buf_write_be64 (buf, value); + break; + case R_390_PC64: + buf_write_be64 (buf, value - rela->r_offset); + break; + case R_390_32: + buf_write_be32 (buf, value); + break; + case R_390_PC32: + buf_write_be32 (buf, value - rela->r_offset); + break; + case R_390_PC32DBL: + case R_390_PLT32DBL: + buf_write_be32 (buf, ((Elf32_Sword) (value - rela->r_offset)) >> 1); + break; + case R_390_16: + buf_write_be16 (buf, value); + break; + case R_390_PC16: + buf_write_be16 (buf, value - rela->r_offset); + break; + case R_390_PC16DBL: + case R_390_PLT16DBL: + buf_write_be16 (buf, ((int16_t) (value - rela->r_offset)) >> 1); + break; + case R_390_8: + buf_write_8 (buf, value); + break; + case R_390_COPY: + abort (); + case R_390_RELATIVE: + error (0, 0, "%s: R_390_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +s390x_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +s390x_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + int r_type; + + if (GELF_R_TYPE (rela->r_info) == R_390_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_390_NONE) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + { + switch (GELF_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD and TPOFF relocs need conflicts. */ + case R_390_TLS_DTPMOD: + case R_390_TLS_TPOFF: + if (info->curtls == NULL || info->dso == dso) + return 0; + break; + /* IRELATIVE always need conflicts. */ + case R_390_IRELATIVE: + break; + default: + return 0; + } + value = 0; + } + else if (info->dso == dso && !conflict->ifunc) + return 0; + else + { + /* DTPOFF wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rela->r_info) == R_390_TLS_DTPOFF + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + r_type = GELF_R_TYPE (rela->r_info); + value += rela->r_addend; + switch (r_type) + { + case R_390_PC64: + value -= rela->r_offset; + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + r_type = R_390_64; + case R_390_64: + case R_390_IRELATIVE: + ret->r_addend = value; + if (conflict != NULL && conflict->ifunc) + r_type = R_390_IRELATIVE; + break; + case R_390_PC32: + value -= rela->r_offset; + case R_390_32: + ret->r_addend = (Elf32_Addr) value; + r_type = R_390_32; + break; + case R_390_PC32DBL: + case R_390_PLT32DBL: + ret->r_addend + = (Elf32_Addr) (((Elf32_Sword) (value - rela->r_offset)) >> 1); + r_type = R_390_32; + break; + case R_390_PC16: + value -= rela->r_offset; + case R_390_16: + ret->r_addend = (Elf32_Half) value; + r_type = R_390_16; + break; + case R_390_PC16DBL: + case R_390_PLT16DBL: + ret->r_addend = (Elf32_Half) (((int16_t) (value - rela->r_offset)) >> 1); + r_type = R_390_16; + break; + case R_390_8: + ret->r_addend = value & 0xff; + break; + case R_390_COPY: + error (0, 0, "R_390_COPY should not be present in shared libraries"); + return 1; + case R_390_TLS_DTPMOD: + case R_390_TLS_DTPOFF: + case R_390_TLS_TPOFF: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + switch (r_type) + { + case R_390_TLS_DTPMOD: + ret->r_addend = tls->modid; + break; + case R_390_TLS_DTPOFF: + ret->r_addend = value; + break; + case R_390_TLS_TPOFF: + ret->r_addend = value - tls->offset; + break; + } + r_type = R_390_64; + break; + default: + error (0, 0, "%s: Unknown S390 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + ret->r_info = GELF_R_INFO (0, r_type); + return 0; +} + +static int +s390x_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +s390x_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +s390x_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + int i; + + dso = info->dso; + if (dso->info[DT_PLTGOT]) + { + /* Write address of .plt + 0x2e into got[1]. + .plt + 0x2e is what got[3] contains unless prelinking. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf64_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = dso->shdr[i].sh_addr + 0x2e; + write_be64 (dso, dso->info[DT_PLTGOT] + 8, data); + } + + return 0; +} + +static int +s390x_arch_undo_prelink (DSO *dso) +{ + int i; + + if (dso->info[DT_PLTGOT]) + { + /* Clear got[1] if it contains address of .plt + 0x2e. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf64_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = read_ube64 (dso, dso->info[DT_PLTGOT] + 8); + if (data == dso->shdr[i].sh_addr + 0x2e) + write_be64 (dso, dso->info[DT_PLTGOT] + 8, 0); + } + + return 0; +} + +static int +s390x_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + int sec; + const char *name; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_390_NONE: + case R_390_RELATIVE: + case R_390_IRELATIVE: + break; + case R_390_JMP_SLOT: + sec = addr_to_sec (dso, rela->r_offset); + name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name); + if (sec == -1 || (strcmp (name, ".got") && strcmp (name, ".got.plt"))) + { + error (0, 0, "%s: R_390_JMP_SLOT not pointing into .got section", + dso->filename); + return 1; + } + else + { + Elf64_Addr data = read_ube64 (dso, dso->shdr[sec].sh_addr + 8); + + assert (rela->r_offset >= dso->shdr[sec].sh_addr + 24); + assert (((rela->r_offset - dso->shdr[sec].sh_addr) & 7) == 0); + write_be64 (dso, rela->r_offset, + 4 * (rela->r_offset - dso->shdr[sec].sh_addr - 24) + + data); + } + break; + case R_390_GLOB_DAT: + case R_390_64: + case R_390_PC64: + case R_390_TLS_DTPMOD: + case R_390_TLS_DTPOFF: + case R_390_TLS_TPOFF: + write_be64 (dso, rela->r_offset, 0); + break; + case R_390_32: + case R_390_PC32: + case R_390_PC32DBL: + case R_390_PLT32DBL: + write_be32 (dso, rela->r_offset, 0); + break; + case R_390_16: + case R_390_PC16: + case R_390_PC16DBL: + case R_390_PLT16DBL: + write_be16 (dso, rela->r_offset, 0); + break; + case R_390_8: + write_8 (dso, rela->r_offset, 0); + break; + case R_390_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_390_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown s390x relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +s390x_reloc_size (int reloc_type) +{ + switch (reloc_type) + { + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + case R_390_64: + case R_390_PC64: + case R_390_IRELATIVE: + return 8; + case R_390_32: + case R_390_PC32: + case R_390_PC32DBL: + case R_390_PLT32DBL: + default: + return 4; + case R_390_16: + case R_390_PC16: + case R_390_PC16DBL: + case R_390_PLT16DBL: + return 2; + case R_390_8: + return 1; + } +} + +static int +s390x_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_390_COPY: return RTYPE_CLASS_COPY; + case R_390_JMP_SLOT: return RTYPE_CLASS_PLT; + case R_390_TLS_DTPMOD: + case R_390_TLS_DTPOFF: + case R_390_TLS_TPOFF: + return RTYPE_CLASS_TLS; + default: return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(s390x) = { + .name = "S390", + .class = ELFCLASS64, + .machine = EM_S390, + .alternate_machine = { 0xA390 }, + .R_JMP_SLOT = R_390_JMP_SLOT, + .R_COPY = R_390_COPY, + .R_RELATIVE = R_390_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld64.so.1", + .adjust_dyn = s390x_adjust_dyn, + .adjust_rel = s390x_adjust_rel, + .adjust_rela = s390x_adjust_rela, + .prelink_rel = s390x_prelink_rel, + .prelink_rela = s390x_prelink_rela, + .prelink_conflict_rel = s390x_prelink_conflict_rel, + .prelink_conflict_rela = s390x_prelink_conflict_rela, + .apply_conflict_rela = s390x_apply_conflict_rela, + .apply_rel = s390x_apply_rel, + .apply_rela = s390x_apply_rela, + .rel_to_rela = s390x_rel_to_rela, + .need_rel_to_rela = s390x_need_rel_to_rela, + .reloc_size = s390x_reloc_size, + .reloc_class = s390x_reloc_class, + .max_reloc_size = 8, + .arch_prelink = s390x_arch_prelink, + .arch_undo_prelink = s390x_arch_undo_prelink, + .undo_prelink_rela = s390x_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0x4000000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x4010000000LL, + .mmap_end = 0x5000000000LL, + .max_page_size = 0x1000, + .page_size = 0x1000 +}; diff --git a/src/arch-sh.c b/src/arch-sh.c new file mode 100644 index 0000000..1b11312 --- /dev/null +++ b/src/arch-sh.c @@ -0,0 +1,453 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" + +static int +sh_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PLTGOT) + { + int sec = addr_to_sec (dso, dyn->d_un.d_ptr); + Elf32_Addr data; + + if (sec == -1) + return 0; + + data = read_une32 (dso, dyn->d_un.d_ptr); + /* If .got.plt[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_ne32 (dso, dyn->d_un.d_ptr, data + adjust); + + data = read_une32 (dso, dyn->d_un.d_ptr + 4); + /* If .got.plt[1] points to .plt + 36, it needs to be adjusted. */ + if (data && data >= start) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (data == dso->shdr[i].sh_addr + 36 + && dso->shdr[i].sh_type == SHT_PROGBITS + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt") == 0) + { + write_ne32 (dso, dyn->d_un.d_ptr + 4, data + adjust); + break; + } + } + } + return 0; +} + +static int +sh_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: SH doesn't support REL relocs", dso->filename); + return 1; +} + +static int +sh_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + Elf32_Addr data; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SH_RELATIVE: + if (rela->r_addend && (Elf32_Addr) rela->r_addend >= start) + { + rela->r_addend += (Elf32_Sword) adjust; + break; + } + /* FALLTHROUGH */ + case R_SH_JMP_SLOT: + data = read_une32 (dso, rela->r_offset); + if (data >= start) + write_ne32 (dso, rela->r_offset, data + adjust); + break; + break; + } + return 0; +} + +static int +sh_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: SH doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +sh_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso; + GElf_Addr value; + + dso = info->dso; + if (GELF_R_TYPE (rela->r_info) == R_SH_NONE) + /* Fast path: nothing to do. */ + return 0; + else if (GELF_R_TYPE (rela->r_info) == R_SH_RELATIVE) + { + if (rela->r_addend) + write_ne32 (dso, rela->r_offset, rela->r_addend); + return 0; + } + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SH_GLOB_DAT: + case R_SH_JMP_SLOT: + case R_SH_DIR32: + write_ne32 (dso, rela->r_offset, value); + break; + case R_SH_REL32: + write_ne32 (dso, rela->r_offset, value - rela->r_addend); + break; + case R_SH_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_SH_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown sh relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +sh_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SH_GLOB_DAT: + case R_SH_JMP_SLOT: + case R_SH_DIR32: + buf_write_ne32 (info->dso, buf, rela->r_addend); + break; + default: + abort (); + } + return 0; +} + +static int +sh_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: SH doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +sh_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SH_NONE: + break; + case R_SH_GLOB_DAT: + case R_SH_JMP_SLOT: + case R_SH_DIR32: + buf_write_ne32 (info->dso, buf, value); + break; + case R_SH_REL32: + buf_write_ne32 (info->dso, buf, value - rela->r_offset); + break; + case R_SH_COPY: + abort (); + case R_SH_RELATIVE: + error (0, 0, "%s: R_SH_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +sh_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: SH doesn't support REL relocs", dso->filename); + return 1; +} + +static int +sh_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + GElf_Rela *ret; + + if (GELF_R_TYPE (rela->r_info) == R_SH_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_SH_NONE + || info->dso == dso) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + return 0; + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on SuperH yet", + dso->filename); + return 1; + } + value = conflict_lookup_value (conflict); + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SH_REL32: + value -= rela->r_offset; + ret->r_info = GELF_R_INFO (0, R_SH_DIR32); + /* FALLTHROUGH */ + case R_SH_DIR32: + if ((rela->r_offset & 3) == 0) + ret->r_info = GELF_R_INFO (0, R_SH_GLOB_DAT); + /* FALLTHROUGH */ + case R_SH_GLOB_DAT: + case R_SH_JMP_SLOT: + ret->r_addend = (Elf32_Sword) (value + rela->r_addend); + break; + case R_SH_COPY: + error (0, 0, "R_SH_COPY should not be present in shared libraries"); + return 1; + default: + error (0, 0, "%s: Unknown sh relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +sh_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + return 0; +} + +static int +sh_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +sh_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + int i; + + dso = info->dso; + if (dso->info[DT_PLTGOT]) + { + /* Write address of .plt + 36 into got[1]. + .plt + 36 is what got[3] contains unless prelinking. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = dso->shdr[i].sh_addr + 36; + write_ne32 (dso, dso->info[DT_PLTGOT] + 4, data); + } + + return 0; +} + +static int +sh_arch_undo_prelink (DSO *dso) +{ + int i; + + if (dso->info[DT_PLTGOT]) + { + /* Clear got[1] if it contains address of .plt + 36. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf32_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = read_une32 (dso, dso->info[DT_PLTGOT] + 4); + if (data == dso->shdr[i].sh_addr + 36) + write_ne32 (dso, dso->info[DT_PLTGOT] + 4, 0); + } + + return 0; +} + +static int +sh_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + int sec; + const char *name; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SH_NONE: + break; + case R_SH_RELATIVE: + if (rela->r_addend) + write_ne32 (dso, rela->r_offset, 0); + break; + case R_SH_JMP_SLOT: + sec = addr_to_sec (dso, rela->r_offset); + name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name); + if (sec == -1 || (strcmp (name, ".got") && strcmp (name, ".got.plt"))) + { + error (0, 0, "%s: R_SH_JMP_SLOT not pointing into .got section", + dso->filename); + return 1; + } + else + { + Elf32_Addr data = read_une32 (dso, dso->shdr[sec].sh_addr + 4); + + assert (rela->r_offset >= dso->shdr[sec].sh_addr + 12); + assert (((rela->r_offset - dso->shdr[sec].sh_addr) & 3) == 0); + write_ne32 (dso, rela->r_offset, + 7 * (rela->r_offset - dso->shdr[sec].sh_addr - 12) + + data); + } + break; + case R_SH_GLOB_DAT: + case R_SH_DIR32: + case R_SH_REL32: + write_ne32 (dso, rela->r_offset, 0); + break; + case R_SH_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_SH_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown sh relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +sh_reloc_size (int reloc_type) +{ + return 4; +} + +static int +sh_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_SH_COPY: return RTYPE_CLASS_COPY; + case R_SH_JMP_SLOT: return RTYPE_CLASS_PLT; + default: return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(sh) = { + .name = "SuperH", + .class = ELFCLASS32, + .machine = EM_SH, + .alternate_machine = { EM_NONE }, + .R_JMP_SLOT = R_SH_JMP_SLOT, + .R_COPY = R_SH_COPY, + .R_RELATIVE = R_SH_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld-linux.so.2", + .adjust_dyn = sh_adjust_dyn, + .adjust_rel = sh_adjust_rel, + .adjust_rela = sh_adjust_rela, + .prelink_rel = sh_prelink_rel, + .prelink_rela = sh_prelink_rela, + .prelink_conflict_rel = sh_prelink_conflict_rel, + .prelink_conflict_rela = sh_prelink_conflict_rela, + .apply_conflict_rela = sh_apply_conflict_rela, + .apply_rel = sh_apply_rel, + .apply_rela = sh_apply_rela, + .rel_to_rela = sh_rel_to_rela, + .need_rel_to_rela = sh_need_rel_to_rela, + .reloc_size = sh_reloc_size, + .reloc_class = sh_reloc_class, + .max_reloc_size = 4, + .arch_prelink = sh_arch_prelink, + .arch_undo_prelink = sh_arch_undo_prelink, + .undo_prelink_rela = sh_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0x29555000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x30000000, + .mmap_end = 0x40000000, + .max_page_size = 0x10000, + .page_size = 0x1000 +}; diff --git a/src/arch-sparc.c b/src/arch-sparc.c new file mode 100644 index 0000000..e016a79 --- /dev/null +++ b/src/arch-sparc.c @@ -0,0 +1,645 @@ +/* Copyright (C) 2001, 2002, 2004, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" + +static int +sparc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PLTGOT) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".got")) + { + Elf32_Addr data; + + data = read_ube32 (dso, dso->shdr[i].sh_addr); + /* .got[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_be32 (dso, dso->shdr[i].sh_addr, data + adjust); + break; + } + } + + return 0; +} + +static int +sparc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename); + return 1; +} + +static int +sparc_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + if (GELF_R_TYPE (rela->r_info) == R_SPARC_RELATIVE) + { + if (rela->r_addend) + { + if ((Elf32_Addr) rela->r_addend >= start) + rela->r_addend += (Elf32_Sword) adjust; + } + else + { + GElf_Addr val = read_ube32 (dso, rela->r_offset); + + if (val >= start) + write_be32 (dso, rela->r_offset, val + adjust); + } + } + return 0; +} + +static int +sparc_prelink_rel (struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", info->dso->filename); + return 1; +} + +static void +sparc_fixup_plt (DSO *dso, GElf_Rela *rela, GElf_Addr value) +{ + Elf32_Sword disp = value - rela->r_offset; + + if (disp >= -0x800000 && disp < 0x800000) + { + /* b,a value + nop + nop */ + write_be32 (dso, rela->r_offset, 0x30800000 | ((disp >> 2) & 0x3fffff)); + write_be32 (dso, rela->r_offset + 4, 0x01000000); + write_be32 (dso, rela->r_offset + 8, 0x01000000); + } + else + { + /* sethi %hi(value), %g1 + jmpl %g1 + %lo(value), %g0 + nop */ + write_be32 (dso, rela->r_offset, 0x03000000 | ((value >> 10) & 0x3fffff)); + write_be32 (dso, rela->r_offset + 4, 0x81c06000 | (value & 0x3ff)); + write_be32 (dso, rela->r_offset + 8, 0x01000000); + } +} + +static int +sparc_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso = info->dso; + GElf_Addr value; + + if (GELF_R_TYPE (rela->r_info) == R_SPARC_NONE) + return 0; + else if (GELF_R_TYPE (rela->r_info) == R_SPARC_RELATIVE) + { + /* 32-bit SPARC handles RELATIVE relocs as + *(int *)rela->r_offset += l_addr + rela->r_addend. + RELATIVE relocs against .got traditionally used to have the + addend in memory pointed by r_offset and 0 r_addend, + other RELATIVE relocs and more recent .got RELATIVE relocs + too have 0 in memory and non-zero r_addend. For prelinking, + we need the value in memory to be already relocated for + l_addr == 0 case, so we have to make sure r_addend will be 0. */ + if (rela->r_addend == 0) + return 0; + value = read_ube32 (dso, rela->r_offset); + value += rela->r_addend; + rela->r_addend = 0; + write_be32 (dso, rela->r_offset, value); + /* Tell prelink_rela routine it should update the relocation. */ + return 2; + } + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SPARC_GLOB_DAT: + case R_SPARC_32: + case R_SPARC_UA32: + write_be32 (dso, rela->r_offset, value); + break; + case R_SPARC_JMP_SLOT: + sparc_fixup_plt (dso, rela, value); + break; + case R_SPARC_8: + write_8 (dso, rela->r_offset, value); + break; + case R_SPARC_16: + case R_SPARC_UA16: + write_be16 (dso, rela->r_offset, value); + break; + case R_SPARC_LO10: + write_be32 (dso, rela->r_offset, + (value & 0x3ff) | (read_ube32 (dso, rela->r_offset) & ~0x3ff)); + break; + case R_SPARC_HI22: + write_be32 (dso, rela->r_offset, + ((value >> 10) & 0x3fffff) + | (read_ube32 (dso, rela->r_offset) & 0xffc00000)); + break; + case R_SPARC_DISP8: + write_8 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_SPARC_DISP16: + write_be16 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_SPARC_DISP32: + write_be32 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_SPARC_WDISP30: + write_be32 (dso, rela->r_offset, + (((value - rela->r_offset) >> 2) & 0x3fffffff) + | (read_ube32 (dso, rela->r_offset) & 0xc0000000)); + break; + case R_SPARC_TLS_DTPOFF32: + write_be32 (dso, rela->r_offset, value + rela->r_addend); + break; + /* DTPMOD32 and TPOFF32 is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_SPARC_TLS_DTPMOD32: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_SPARC_TLS_DTPMOD32 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_SPARC_TLS_TPOFF32: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_be32 (dso, rela->r_offset, + value + rela->r_addend - info->resolvetls->offset); + break; + case R_SPARC_TLS_LE_HIX22: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_be32 (dso, rela->r_offset, + (read_ube32 (dso, rela->r_offset) & 0xffc00000) + | (((~(value + rela->r_addend - info->resolvetls->offset)) + >> 10) & 0x3fffff)); + break; + case R_SPARC_TLS_LE_LOX10: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_be32 (dso, rela->r_offset, + (read_ube32 (dso, rela->r_offset) & 0xffffe000) | 0x1c00 + | ((value + rela->r_addend - info->resolvetls->offset) + & 0x3ff)); + break; + case R_SPARC_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_SPARC_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown sparc relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +sparc_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SPARC_32: + case R_SPARC_UA32: + buf_write_be32 (buf, rela->r_addend); + break; + case R_SPARC_16: + case R_SPARC_UA16: + buf_write_be16 (buf, rela->r_addend); + break; + case R_SPARC_8: + buf_write_8 (buf, rela->r_addend); + break; + default: + abort (); + } + return 0; +} + +static int +sparc_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +sparc_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SPARC_NONE: + break; + case R_SPARC_DISP32: + value -= rela->r_offset; + case R_SPARC_GLOB_DAT: + case R_SPARC_32: + case R_SPARC_UA32: + buf_write_be32 (buf, value); + break; + case R_SPARC_DISP16: + value -= rela->r_offset; + case R_SPARC_16: + case R_SPARC_UA16: + buf_write_be16 (buf, value); + break; + case R_SPARC_DISP8: + value -= rela->r_offset; + case R_SPARC_8: + buf_write_8 (buf, value); + break; + case R_SPARC_LO10: + buf_write_be32 (buf, (buf_read_ube32 (buf) & ~0x3ff) | (value & 0x3ff)); + break; + case R_SPARC_HI22: + buf_write_be32 (buf, (buf_read_ube32 (buf) & 0xffc00000) + | ((value >> 10) & 0x3fffff)); + break; + case R_SPARC_WDISP30: + buf_write_be32 (buf, (buf_read_ube32 (buf) & 0xc0000000) + | (((value - rela->r_offset) >> 2) & 0x3fffffff)); + break; + case R_SPARC_RELATIVE: + error (0, 0, "%s: R_SPARC_RELATIVE in ET_EXEC object?", + info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +sparc_prelink_conflict_rel (DSO *dso, struct prelink_info *info, + GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename); + return 1; +} + +static int +sparc_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + int r_type; + + if (GELF_R_TYPE (rela->r_info) == R_SPARC_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_SPARC_NONE + || info->dso == dso) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + { + if (info->curtls == NULL) + return 0; + switch (GELF_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD32 and TPOFF32 relocs need conflicts. */ + case R_SPARC_TLS_DTPMOD32: + case R_SPARC_TLS_TPOFF32: + case R_SPARC_TLS_LE_HIX22: + case R_SPARC_TLS_LE_LOX10: + break; + default: + return 0; + } + value = 0; + } + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on SPARC yet", + dso->filename); + return 1; + } + else + { + /* DTPOFF32 wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rela->r_info) == R_SPARC_TLS_DTPOFF32 + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + value += rela->r_addend; + r_type = GELF_R_TYPE (rela->r_info); + switch (r_type) + { + case R_SPARC_DISP32: + value -= rela->r_offset; + case R_SPARC_GLOB_DAT: + case R_SPARC_32: + r_type = R_SPARC_32; + break; + case R_SPARC_DISP16: + value -= rela->r_offset; + case R_SPARC_16: + r_type = R_SPARC_16; + break; + case R_SPARC_DISP8: + value -= rela->r_offset; + case R_SPARC_8: + r_type = R_SPARC_8; + break; + /* Attempt to transform all reloc which read-modify-write into + simple writes. */ + case R_SPARC_LO10: + value = (read_ube32 (dso, rela->r_offset) & ~0x3ff) | (value & 0x3ff); + r_type = R_SPARC_32; + break; + case R_SPARC_HI22: + value = (read_ube32 (dso, rela->r_offset) & 0xffc00000) + | ((value >> 10) & 0x3fffff); + r_type = R_SPARC_32; + break; + case R_SPARC_WDISP30: + value = (read_ube32 (dso, rela->r_offset) & 0xc0000000) + | (((value - rela->r_offset) >> 2) & 0x3fffffff); + r_type = R_SPARC_32; + break; + case R_SPARC_UA16: + case R_SPARC_UA32: + case R_SPARC_JMP_SLOT: + break; + case R_SPARC_TLS_DTPMOD32: + case R_SPARC_TLS_DTPOFF32: + case R_SPARC_TLS_TPOFF32: + case R_SPARC_TLS_LE_HIX22: + case R_SPARC_TLS_LE_LOX10: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + r_type = R_SPARC_32; + tls = conflict ? conflict->lookup.tls : info->curtls; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SPARC_TLS_DTPMOD32: + value = tls->modid; + break; + case R_SPARC_TLS_DTPOFF32: + break; + case R_SPARC_TLS_TPOFF32: + value -= tls->offset; + break; + case R_SPARC_TLS_LE_HIX22: + value -= tls->offset; + value = (read_ube32 (dso, rela->r_offset) & 0xffc00000) + | (((~value) >> 10) & 0x3fffff); + break; + case R_SPARC_TLS_LE_LOX10: + value -= tls->offset; + value = (read_ube32 (dso, rela->r_offset) & 0xffffe000) | 0x1c00 + | (value & 0x3ff); + break; + } + break; + default: + error (0, 0, "%s: Unknown Sparc relocation type %d", dso->filename, + r_type); + return 1; + } + ret->r_info = GELF_R_INFO (0, r_type); + ret->r_addend = (Elf32_Sword) value; + return 0; +} + +static int +sparc_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename); + return 1; +} + +static int +sparc_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +sparc_arch_prelink (struct prelink_info *info) +{ + return 0; +} + +static int +sparc_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + int sec; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SPARC_NONE: + return 0; + case R_SPARC_RELATIVE: + /* 32-bit SPARC handles RELATIVE relocs as + *(int *)rela->r_offset += l_addr + rela->r_addend. + RELATIVE relocs against .got traditionally used to have the + addend in memory pointed by r_offset and 0 r_addend, + other RELATIVE relocs and more recent RELATIVE relocs have 0 + in memory and non-zero r_addend. + Always store 0 to memory when doing undo. */ + assert (rela->r_addend == 0); + rela->r_addend = (Elf32_Sword) read_ube32 (dso, rela->r_offset); + write_be32 (dso, rela->r_offset, 0); + /* Tell undo_prelink_rela routine it should update the + relocation. */ + return 2; + case R_SPARC_GLOB_DAT: + case R_SPARC_32: + case R_SPARC_UA32: + case R_SPARC_DISP32: + case R_SPARC_TLS_DTPMOD32: + case R_SPARC_TLS_DTPOFF32: + case R_SPARC_TLS_TPOFF32: + write_be32 (dso, rela->r_offset, 0); + break; + case R_SPARC_JMP_SLOT: + sec = addr_to_sec (dso, rela->r_offset); + if (sec != -1) + { + /* sethi .-.plt, %g1 + b,a .plt+0 */ + write_be32 (dso, rela->r_offset, + 0x03000000 + | ((rela->r_offset - dso->shdr[sec].sh_addr) + & 0x3fffff)); + write_be32 (dso, rela->r_offset + 4, + 0x30800000 + | (((dso->shdr[sec].sh_addr - rela->r_offset - 4) >> 2) + & 0x3fffff)); + } + break; + case R_SPARC_8: + case R_SPARC_DISP8: + write_8 (dso, rela->r_offset, 0); + break; + case R_SPARC_16: + case R_SPARC_UA16: + case R_SPARC_DISP16: + write_be16 (dso, rela->r_offset, 0); + break; + case R_SPARC_LO10: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & ~0x3ff); + break; + case R_SPARC_TLS_LE_LOX10: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffffe000); + break; + case R_SPARC_HI22: + case R_SPARC_TLS_LE_HIX22: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffc00000); + break; + case R_SPARC_WDISP30: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xc0000000); + break; + case R_SPARC_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_SPARC_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown sparc relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +sparc_reloc_size (int reloc_type) +{ + switch (reloc_type) + { + case R_SPARC_8: + case R_SPARC_DISP8: + return 1; + case R_SPARC_16: + case R_SPARC_DISP16: + case R_SPARC_UA16: + return 2; + default: + break; + } + return 4; +} + +static int +sparc_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_SPARC_COPY: return RTYPE_CLASS_COPY; + case R_SPARC_JMP_SLOT: return RTYPE_CLASS_PLT; + case R_SPARC_TLS_DTPMOD32: + case R_SPARC_TLS_DTPOFF32: + case R_SPARC_TLS_TPOFF32: + case R_SPARC_TLS_LE_HIX22: + case R_SPARC_TLS_LE_LOX10: + return RTYPE_CLASS_TLS; + default: return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(sparc) = { + .name = "SPARC", + .class = ELFCLASS32, + .machine = EM_SPARC, + .alternate_machine = { EM_SPARC32PLUS }, + .R_JMP_SLOT = R_SPARC_JMP_SLOT, + .R_COPY = R_SPARC_COPY, + .R_RELATIVE = R_SPARC_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib/ld-linux.so.2", + .adjust_dyn = sparc_adjust_dyn, + .adjust_rel = sparc_adjust_rel, + .adjust_rela = sparc_adjust_rela, + .prelink_rel = sparc_prelink_rel, + .prelink_rela = sparc_prelink_rela, + .prelink_conflict_rel = sparc_prelink_conflict_rel, + .prelink_conflict_rela = sparc_prelink_conflict_rela, + .apply_conflict_rela = sparc_apply_conflict_rela, + .apply_rel = sparc_apply_rel, + .apply_rela = sparc_apply_rela, + .rel_to_rela = sparc_rel_to_rela, + .need_rel_to_rela = sparc_need_rel_to_rela, + .reloc_size = sparc_reloc_size, + .reloc_class = sparc_reloc_class, + .max_reloc_size = 4, + .arch_prelink = sparc_arch_prelink, + .undo_prelink_rela = sparc_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0x70000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x71000000LL, + .mmap_end = 0x80000000LL, + .max_page_size = 0x10000, + .page_size = 0x1000 +}; diff --git a/src/arch-sparc64.c b/src/arch-sparc64.c new file mode 100644 index 0000000..aee4601 --- /dev/null +++ b/src/arch-sparc64.c @@ -0,0 +1,849 @@ +/* Copyright (C) 2001, 2002, 2004, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" + +#define SPARC64_R_TYPE(info) (GELF_R_TYPE (info) & 0xff) + +static int +sparc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PLTGOT) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".got")) + { + Elf64_Addr data; + + data = read_ube64 (dso, dso->shdr[i].sh_addr); + /* .got[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_be64 (dso, dso->shdr[i].sh_addr, data + adjust); + break; + } + } + + return 0; +} + +static int +sparc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename); + return 1; +} + +static int +sparc64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_RELATIVE) + { + if (rela->r_addend >= start) + rela->r_addend += adjust; + } + else if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_JMP_SLOT + && rela->r_addend) + { + /* .plt[32768+] r_addends are -some_address_in_plt_section. */ + if ((- rela->r_addend) >= start) + rela->r_addend -= adjust; + } + return 0; +} + +static int +sparc64_prelink_rel (struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", info->dso->filename); + return 1; +} + +static void +sparc64_fixup_plt (DSO *dso, GElf_Rela *rela, GElf_Addr value) +{ + Elf64_Sxword disp = value - rela->r_offset; + + if (rela->r_addend) + { + /* .plt[32768+] */ + write_be64 (dso, rela->r_offset, value); + } + else if (disp >= -0x800000 && disp < 0x800000) + { + /* b,a value + nop + nop */ + write_be32 (dso, rela->r_offset, 0x30800000 | ((disp >> 2) & 0x3fffff)); + write_be32 (dso, rela->r_offset + 4, 0x01000000); + write_be32 (dso, rela->r_offset + 8, 0x01000000); + } + else if (! (value >> 32)) + { + /* sethi %hi(value), %g1 + jmpl %g1 + %lo(value), %g0 + nop */ + write_be32 (dso, rela->r_offset, 0x03000000 | ((value >> 10) & 0x3fffff)); + write_be32 (dso, rela->r_offset + 4, 0x81c06000 | (value & 0x3ff)); + write_be32 (dso, rela->r_offset + 8, 0x01000000); + } + else if ((rela->r_offset + 4 > value + && ((rela->r_offset - value) >> 31) == 0) + || (value > rela->r_offset + 4 + && ((value - rela->r_offset - 4) >> 31) == 0)) + { + /* mov %o7, %g1 + call value + mov %g1, %o7 */ + write_be32 (dso, rela->r_offset, 0x8210000f); + write_be32 (dso, rela->r_offset + 4, 0x40000000 + | (((value - rela->r_offset - 4) >> 2) & 0x3fffffff)); + write_be32 (dso, rela->r_offset + 8, 0x9e100001); + } + else + { + unsigned int csts[4]; + int i = 0; + + /* sethi %hh(value), %g1 + sethi %lm(value), %g5 + or %g1, %hm(value), %g1 + or %g5, %lo(value), %g5 + sllx %g1, 32, %g1 + jmpl %g1 + %g5, %g0 + nop */ + + csts[0] = value >> 42; + csts[1] = (value >> 32) & 0x3ff; + csts[2] = (value >> 10) & 0x3fffff; + csts[3] = value & 0x3ff; + write_be32 (dso, rela->r_offset, 0x03000000 | csts[0]); + write_be32 (dso, rela->r_offset + 4, 0x0b000000 | csts[2]); + /* Sparc64 shared libs are often 0xfffff800XXXXXXXX, so optimize + for this common case. */ + if (csts[1] == 0) + write_be32 (dso, rela->r_offset + 8, 0x83287020); + else + write_be32 (dso, rela->r_offset + 8, 0x82106000 | csts[1]); + write_be32 (dso, rela->r_offset + 12, 0x8a116000 | csts[3]); + if (csts[1] != 0) + write_be32 (dso, rela->r_offset + 16, 0x83287020), i = 4; + write_be32 (dso, rela->r_offset + 16 + i, 0x81c04005); + write_be32 (dso, rela->r_offset + 20 + i, 0x01000000); + } +} + +static int +sparc64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso = info->dso; + GElf_Addr value; + + if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_NONE) + return 0; + else if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_RELATIVE) + { + /* 64-bit SPARC handles RELATIVE relocs as + *(long *)rela->r_offset = l_addr + rela->r_addend, + so we must update the memory. */ + write_be64 (dso, rela->r_offset, rela->r_addend); + return 0; + } + value = info->resolve (info, GELF_R_SYM (rela->r_info), + SPARC64_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (SPARC64_R_TYPE (rela->r_info)) + { + case R_SPARC_GLOB_DAT: + case R_SPARC_64: + case R_SPARC_UA64: + write_be64 (dso, rela->r_offset, value); + break; + case R_SPARC_32: + case R_SPARC_UA32: + write_be32 (dso, rela->r_offset, value); + break; + case R_SPARC_JMP_SLOT: + sparc64_fixup_plt (dso, rela, value); + break; + case R_SPARC_8: + write_8 (dso, rela->r_offset, value); + break; + case R_SPARC_16: + case R_SPARC_UA16: + write_be16 (dso, rela->r_offset, value); + break; + case R_SPARC_LO10: + write_be32 (dso, rela->r_offset, + (value & 0x3ff) | (read_ube32 (dso, rela->r_offset) & ~0x3ff)); + break; + case R_SPARC_LM22: + case R_SPARC_HI22: + write_be32 (dso, rela->r_offset, + ((value >> 10) & 0x3fffff) + | (read_ube32 (dso, rela->r_offset) & 0xffc00000)); + break; + case R_SPARC_DISP8: + write_8 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_SPARC_DISP16: + write_be16 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_SPARC_DISP32: + write_be32 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_SPARC_DISP64: + write_be64 (dso, rela->r_offset, value - rela->r_offset); + break; + case R_SPARC_WDISP30: + write_be32 (dso, rela->r_offset, + (((value - rela->r_offset) >> 2) & 0x3fffffff) + | (read_ube32 (dso, rela->r_offset) & 0xc0000000)); + break; + case R_SPARC_TLS_DTPOFF64: + write_be64 (dso, rela->r_offset, value + rela->r_addend); + break; + /* DTPMOD64 and TPOFF64 is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_SPARC_TLS_DTPMOD64: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_SPARC_TLS_DTPMOD64 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_SPARC_TLS_TPOFF64: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_be64 (dso, rela->r_offset, + value + rela->r_addend - info->resolvetls->offset); + break; + case R_SPARC_TLS_LE_HIX22: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_be32 (dso, rela->r_offset, + (read_ube32 (dso, rela->r_offset) & 0xffc00000) + | (((~(value + rela->r_addend - info->resolvetls->offset)) + >> 10) & 0x3fffff)); + break; + case R_SPARC_TLS_LE_LOX10: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_be32 (dso, rela->r_offset, + (read_ube32 (dso, rela->r_offset) & 0xffffe000) | 0x1c00 + | ((value + rela->r_addend - info->resolvetls->offset) + & 0x3ff)); + break; + case R_SPARC_H44: + write_be32 (dso, rela->r_offset, + ((value >> 22) & 0x3fffff) + | (read_ube32 (dso, rela->r_offset) & 0xffc00000)); + break; + case R_SPARC_M44: + write_be32 (dso, rela->r_offset, + ((value >> 12) & 0x3ff) + | (read_ube32 (dso, rela->r_offset) & ~0x3ff)); + break; + case R_SPARC_L44: + write_be32 (dso, rela->r_offset, + (value & 0xfff) | (read_ube32 (dso, rela->r_offset) & ~0xfff)); + break; + case R_SPARC_HH22: + write_be32 (dso, rela->r_offset, + ((value >> 42) & 0x3fffff) + | (read_ube32 (dso, rela->r_offset) & 0xffc00000)); + break; + case R_SPARC_HM10: + write_be32 (dso, rela->r_offset, + ((value >> 32) & 0x3ff) + | (read_ube32 (dso, rela->r_offset) & ~0x3ff)); + break; + case R_SPARC_OLO10: + write_be32 (dso, rela->r_offset, + (((value & 0x3ff) + (GELF_R_TYPE (rela->r_info) >> 8)) & 0x1fff) + | (read_ube32 (dso, rela->r_offset) & ~0x1fff)); + break; + case R_SPARC_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_SPARC_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown sparc relocation type %d", dso->filename, + (int) SPARC64_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +sparc64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + switch (SPARC64_R_TYPE (rela->r_info)) + { + case R_SPARC_64: + case R_SPARC_UA64: + buf_write_be64 (buf, rela->r_addend); + break; + case R_SPARC_32: + case R_SPARC_UA32: + buf_write_be32 (buf, rela->r_addend); + break; + case R_SPARC_16: + case R_SPARC_UA16: + buf_write_be16 (buf, rela->r_addend); + break; + case R_SPARC_8: + buf_write_8 (buf, rela->r_addend); + break; + default: + abort (); + } + return 0; +} + +static int +sparc64_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +sparc64_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + SPARC64_R_TYPE (rela->r_info)); + value += rela->r_addend; + switch (SPARC64_R_TYPE (rela->r_info)) + { + case R_SPARC_NONE: + break; + case R_SPARC_DISP64: + value -= rela->r_offset; + case R_SPARC_GLOB_DAT: + case R_SPARC_64: + case R_SPARC_UA64: + buf_write_be64 (buf, value); + break; + case R_SPARC_DISP32: + value -= rela->r_offset; + case R_SPARC_32: + case R_SPARC_UA32: + buf_write_be32 (buf, value); + break; + case R_SPARC_DISP16: + value -= rela->r_offset; + case R_SPARC_16: + case R_SPARC_UA16: + buf_write_be16 (buf, value); + break; + case R_SPARC_DISP8: + value -= rela->r_offset; + case R_SPARC_8: + buf_write_8 (buf, value); + break; + case R_SPARC_LO10: + buf_write_be32 (buf, (buf_read_ube32 (buf) & ~0x3ff) | (value & 0x3ff)); + break; + case R_SPARC_LM22: + case R_SPARC_HI22: + buf_write_be32 (buf, (buf_read_ube32 (buf) & 0xffc00000) + | ((value >> 10) & 0x3fffff)); + break; + case R_SPARC_WDISP30: + buf_write_be32 (buf, (buf_read_ube32 (buf) & 0xc0000000) + | (((value - rela->r_offset) >> 2) & 0x3fffffff)); + break; + case R_SPARC_H44: + buf_write_be32 (buf, (buf_read_ube32 (buf) & 0xffc00000) + | ((value >> 22) & 0x3fffff)); + break; + case R_SPARC_M44: + buf_write_be32 (buf, (buf_read_ube32 (buf) & ~0x3ff) + | ((value >> 12) & 0x3ff)); + break; + case R_SPARC_L44: + buf_write_be32 (buf, (buf_read_ube32 (buf) & ~0xfff) | (value & 0xfff)); + break; + case R_SPARC_HH22: + buf_write_be32 (buf, (buf_read_ube32 (buf) & 0xffc00000) + | ((value >> 42) & 0x3fffff)); + break; + case R_SPARC_HM10: + buf_write_be32 (buf, (buf_read_ube32 (buf) & ~0x3ff) + | ((value >> 32) & 0x3ff)); + break; + case R_SPARC_OLO10: + buf_write_be32 (buf, (buf_read_ube32 (buf) & ~0x1fff) + | (((value & 0x3ff) + + (GELF_R_TYPE (rela->r_info) >> 8)) & 0x1fff)); + break; + case R_SPARC_RELATIVE: + error (0, 0, "%s: R_SPARC_RELATIVE in ET_EXEC object?", + info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +sparc64_prelink_conflict_rel (DSO *dso, struct prelink_info *info, + GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename); + return 1; +} + +static int +sparc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + int r_type; + + if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_RELATIVE + || SPARC64_R_TYPE (rela->r_info) == R_SPARC_NONE + || info->dso == dso) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + SPARC64_R_TYPE (rela->r_info)); + if (conflict == NULL) + { + if (info->curtls == NULL) + return 0; + switch (SPARC64_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD64 and TPOFF64 relocs need conflicts. */ + case R_SPARC_TLS_DTPMOD64: + case R_SPARC_TLS_TPOFF64: + case R_SPARC_TLS_LE_HIX22: + case R_SPARC_TLS_LE_LOX10: + break; + default: + return 0; + } + value = 0; + } + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on SPARC64 yet", + dso->filename); + return 1; + } + else + { + /* DTPOFF64 wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_TLS_DTPOFF64 + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + value += rela->r_addend; + r_type = SPARC64_R_TYPE (rela->r_info); + switch (r_type) + { + case R_SPARC_DISP64: + value -= rela->r_offset; + case R_SPARC_GLOB_DAT: + case R_SPARC_64: + r_type = R_SPARC_64; + break; + case R_SPARC_DISP32: + value -= rela->r_offset; + case R_SPARC_32: + r_type = R_SPARC_32; + break; + case R_SPARC_DISP16: + value -= rela->r_offset; + case R_SPARC_16: + r_type = R_SPARC_16; + break; + case R_SPARC_DISP8: + value -= rela->r_offset; + case R_SPARC_8: + r_type = R_SPARC_8; + break; + /* Attempt to transform all reloc which read-modify-write into + simple writes. */ + case R_SPARC_LO10: + value = (read_ube32 (dso, rela->r_offset) & ~0x3ff) | (value & 0x3ff); + r_type = R_SPARC_32; + break; + case R_SPARC_LM22: + case R_SPARC_HI22: + value = (read_ube32 (dso, rela->r_offset) & 0xffc00000) + | ((value >> 10) & 0x3fffff); + r_type = R_SPARC_32; + break; + case R_SPARC_WDISP30: + value = (read_ube32 (dso, rela->r_offset) & 0xc0000000) + | (((value - rela->r_offset) >> 2) & 0x3fffffff); + r_type = R_SPARC_32; + break; + case R_SPARC_H44: + value = (read_ube32 (dso, rela->r_offset) & 0xffc00000) + | ((value >> 22) & 0x3fffff); + r_type = R_SPARC_32; + break; + case R_SPARC_M44: + value = (read_ube32 (dso, rela->r_offset) & ~0x3ff) + | ((value >> 12) & 0x3ff); + r_type = R_SPARC_32; + break; + case R_SPARC_L44: + value = (read_ube32 (dso, rela->r_offset) & ~0xfff) | (value & 0xfff); + r_type = R_SPARC_32; + break; + case R_SPARC_HH22: + value = (read_ube32 (dso, rela->r_offset) & 0xffc00000) + | ((value >> 42) & 0x3fffff); + r_type = R_SPARC_32; + break; + case R_SPARC_HM10: + value = (read_ube32 (dso, rela->r_offset) & ~0x3ff) + | ((value >> 32) & 0x3ff); + r_type = R_SPARC_32; + break; + case R_SPARC_OLO10: + value = (read_ube32 (dso, rela->r_offset) & ~0x1fff) + | (((value & 0x3ff) + (GELF_R_TYPE (rela->r_info) >> 8)) & 0x1fff); + r_type = R_SPARC_32; + break; + case R_SPARC_JMP_SLOT: + if (rela->r_addend) + r_type = R_SPARC_64; + break; + case R_SPARC_UA16: + case R_SPARC_UA32: + case R_SPARC_UA64: + break; + case R_SPARC_TLS_DTPMOD64: + case R_SPARC_TLS_DTPOFF64: + case R_SPARC_TLS_TPOFF64: + case R_SPARC_TLS_LE_HIX22: + case R_SPARC_TLS_LE_LOX10: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + switch (r_type) + { + case R_SPARC_TLS_DTPMOD64: + r_type = R_SPARC_64; + value = tls->modid; + break; + case R_SPARC_TLS_DTPOFF64: + r_type = R_SPARC_64; + break; + case R_SPARC_TLS_TPOFF64: + r_type = R_SPARC_64; + value -= tls->offset; + break; + case R_SPARC_TLS_LE_HIX22: + r_type = R_SPARC_32; + value -= tls->offset; + value = (read_ube32 (dso, rela->r_offset) & 0xffc00000) + | (((~value) >> 10) & 0x3fffff); + break; + case R_SPARC_TLS_LE_LOX10: + r_type = R_SPARC_32; + value -= tls->offset; + value = (read_ube32 (dso, rela->r_offset) & 0xffffe000) | 0x1c00 + | (value & 0x3ff); + break; + } + break; + default: + error (0, 0, "%s: Unknown Sparc relocation type %d", dso->filename, + r_type); + return 1; + } + ret->r_info = GELF_R_INFO (0, r_type); + ret->r_addend = value; + return 0; +} + +static int +sparc64_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename); + return 1; +} + +static int +sparc64_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +sparc64_arch_prelink (struct prelink_info *info) +{ + return 0; +} + +static int +sparc64_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + int sec; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_SPARC_NONE: + break; + case R_SPARC_JMP_SLOT: + sec = addr_to_sec (dso, rela->r_offset); + if (sec != -1) + { + if (rela->r_addend == 0) + { + /* sethi .-.plt, %g1 + b,a %xcc, .plt+0x20 */ + write_be32 (dso, rela->r_offset, + 0x03000000 + | ((rela->r_offset - dso->shdr[sec].sh_addr) + & 0x3fffff)); + write_be32 (dso, rela->r_offset + 4, + 0x30680000 + | (((dso->shdr[sec].sh_addr + 32 + - rela->r_offset - 4) >> 2) + & 0x7ffff)); + write_be32 (dso, rela->r_offset + 8, 0x01000000); + write_be32 (dso, rela->r_offset + 12, 0x01000000); + write_be32 (dso, rela->r_offset + 16, 0x01000000); + write_be32 (dso, rela->r_offset + 20, 0x01000000); + write_be32 (dso, rela->r_offset + 24, 0x01000000); + write_be32 (dso, rela->r_offset + 28, 0x01000000); + } + else + { + GElf_Addr slot = ((rela->r_offset + 0x400 + - dso->shdr[sec].sh_addr) + / 0x1400) * 0x1400 + + dso->shdr[sec].sh_addr - 0x400; + /* slot+12 contains: ldx [%o7 + X], %g1 */ + GElf_Addr ptr = slot + (read_ube32 (dso, slot + 12) & 0xfff) + 4; + + write_be64 (dso, rela->r_offset, + dso->shdr[sec].sh_addr + - (slot + ((rela->r_offset - ptr) / 8) * 24 + 4)); + } + } + break; + case R_SPARC_RELATIVE: + case R_SPARC_GLOB_DAT: + case R_SPARC_64: + case R_SPARC_UA64: + case R_SPARC_DISP64: + case R_SPARC_TLS_DTPMOD64: + case R_SPARC_TLS_DTPOFF64: + case R_SPARC_TLS_TPOFF64: + write_be64 (dso, rela->r_offset, 0); + break; + case R_SPARC_32: + case R_SPARC_UA32: + case R_SPARC_DISP32: + write_be32 (dso, rela->r_offset, 0); + break; + case R_SPARC_8: + case R_SPARC_DISP8: + write_8 (dso, rela->r_offset, 0); + break; + case R_SPARC_16: + case R_SPARC_UA16: + case R_SPARC_DISP16: + write_be16 (dso, rela->r_offset, 0); + break; + case R_SPARC_LO10: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & ~0x3ff); + break; + case R_SPARC_LM22: + case R_SPARC_HI22: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffc00000); + break; + case R_SPARC_WDISP30: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xc0000000); + break; + case R_SPARC_H44: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffc00000); + break; + case R_SPARC_M44: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & ~0x3ff); + break; + case R_SPARC_L44: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & ~0xfff); + break; + case R_SPARC_HH22: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffc00000); + break; + case R_SPARC_HM10: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & ~0x3ff); + break; + case R_SPARC_OLO10: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & ~0x1fff); + break; + case R_SPARC_TLS_LE_LOX10: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffffe000); + break; + case R_SPARC_TLS_LE_HIX22: + write_be32 (dso, rela->r_offset, + read_ube32 (dso, rela->r_offset) & 0xffc00000); + break; + case R_SPARC_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_SPARC_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown sparc relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +sparc64_reloc_size (int reloc_type) +{ + switch (reloc_type) + { + case R_SPARC_8: + case R_SPARC_DISP8: + return 1; + case R_SPARC_16: + case R_SPARC_DISP16: + case R_SPARC_UA16: + return 2; + case R_SPARC_RELATIVE: + case R_SPARC_64: + case R_SPARC_UA64: + case R_SPARC_GLOB_DAT: + return 8; + default: + break; + } + return 4; +} + +static int +sparc64_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_SPARC_COPY: return RTYPE_CLASS_COPY; + case R_SPARC_JMP_SLOT: return RTYPE_CLASS_PLT; + case R_SPARC_TLS_DTPMOD64: + case R_SPARC_TLS_DTPOFF64: + case R_SPARC_TLS_TPOFF64: + case R_SPARC_TLS_LE_HIX22: + case R_SPARC_TLS_LE_LOX10: + return RTYPE_CLASS_TLS; + default: return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(sparc64) = { + .name = "SPARC", + .class = ELFCLASS64, + .machine = EM_SPARCV9, + .alternate_machine = { EM_NONE }, + .R_JMP_SLOT = R_SPARC_JMP_SLOT, + .R_COPY = R_SPARC_COPY, + .R_RELATIVE = R_SPARC_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib64/ld-linux.so.2", + .adjust_dyn = sparc64_adjust_dyn, + .adjust_rel = sparc64_adjust_rel, + .adjust_rela = sparc64_adjust_rela, + .prelink_rel = sparc64_prelink_rel, + .prelink_rela = sparc64_prelink_rela, + .prelink_conflict_rel = sparc64_prelink_conflict_rel, + .prelink_conflict_rela = sparc64_prelink_conflict_rela, + .apply_conflict_rela = sparc64_apply_conflict_rela, + .apply_rel = sparc64_apply_rel, + .apply_rela = sparc64_apply_rela, + .rel_to_rela = sparc64_rel_to_rela, + .need_rel_to_rela = sparc64_need_rel_to_rela, + .reloc_size = sparc64_reloc_size, + .reloc_class = sparc64_reloc_class, + .max_reloc_size = 8, + .arch_prelink = sparc64_arch_prelink, + .undo_prelink_rela = sparc64_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0xfffff80100000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0xfffff80101000000LL, + /* If we need yet more space for shared libraries, we can of course + expand, but limiting all DSOs into 4 GB means stack overflows + jumping to shared library functions is much harder (there is + '\0' byte in the address before the bytes that matter). */ + .mmap_end = 0xfffff80200000000LL, + .max_page_size = 0x100000, + .page_size = 0x2000 +}; diff --git a/src/arch-x86_64.c b/src/arch-x86_64.c new file mode 100644 index 0000000..dae66cd --- /dev/null +++ b/src/arch-x86_64.c @@ -0,0 +1,643 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2006, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + Copyright (C) 2011 Wind River Systems, Inc. + x32 support by Mark Hatle <mark.hatle@windriver.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> + +#include "prelink.h" + +/* The x32 ABI: https://sites.google.com/site/x32abi/documents/abi.pdf + * documents a "class" value for specific reads and writes. All this + * indicates is that we should be using the ELFCLASS to determine if + * this should be a 32/64 bit read/write. (See table 4.9) + * + * We emulate this behavior below... + */ +#define read_uleclass(DSO, ADDR) \ +( gelf_getclass(DSO->elf) == ELFCLASS32 ? read_ule32(DSO, ADDR) : read_ule64(DSO, ADDR) ) + +#define write_leclass(DSO, ADDR, VAL) \ +( gelf_getclass(DSO->elf) == ELFCLASS32 ? write_le32(DSO, ADDR, VAL) : write_le64(DSO, ADDR, VAL) ) + +#define buf_write_leclass(DSO, BUF, VAL) \ +( gelf_getclass(DSO->elf) == ELFCLASS32 ? buf_write_le32(BUF, VAL) : buf_write_le64(BUF, VAL) ) + +static int +x86_64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust) +{ + if (dyn->d_tag == DT_PLTGOT) + { + int sec = addr_to_sec (dso, dyn->d_un.d_ptr); + Elf64_Addr data; + + if (sec == -1) + return 0; + + data = read_ule64 (dso, dyn->d_un.d_ptr); + /* If .got.plt[0] points to _DYNAMIC, it needs to be adjusted. */ + if (data == dso->shdr[n].sh_addr && data >= start) + write_le64 (dso, dyn->d_un.d_ptr, data + adjust); + + data = read_ule64 (dso, dyn->d_un.d_ptr + 8); + /* If .got.plt[1] points to .plt + 0x16, it needs to be adjusted. */ + if (data && data >= start) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (data == dso->shdr[i].sh_addr + 0x16 + && dso->shdr[i].sh_type == SHT_PROGBITS + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), ".plt") == 0) + { + write_le64 (dso, dyn->d_un.d_ptr + 8, data + adjust); + break; + } + } + } + return 0; +} + +static int +x86_64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust) +{ + error (0, 0, "%s: X86-64 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +x86_64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust) +{ + Elf64_Addr addr; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_X86_64_RELATIVE: + if (rela->r_addend >= start) + { + if (read_uleclass (dso, rela->r_offset) == rela->r_addend) + write_leclass (dso, rela->r_offset, rela->r_addend + adjust); + rela->r_addend += adjust; + } + break; + case R_X86_64_IRELATIVE: + if (rela->r_addend >= start) + rela->r_addend += adjust; + /* FALLTHROUGH */ + case R_X86_64_JUMP_SLOT: + addr = read_uleclass (dso, rela->r_offset); + if (addr >= start) + write_leclass (dso, rela->r_offset, addr + adjust); + break; + } + return 0; +} + +static int +x86_64_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) +{ + error (0, 0, "%s: X86-64 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +x86_64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr) +{ + DSO *dso; + GElf_Addr value; + + dso = info->dso; + if (GELF_R_TYPE (rela->r_info) == R_X86_64_NONE + || GELF_R_TYPE (rela->r_info) == R_X86_64_IRELATIVE) + return 0; + else if (GELF_R_TYPE (rela->r_info) == R_X86_64_RELATIVE) + { + write_leclass (dso, rela->r_offset, rela->r_addend); + return 0; + } + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + write_leclass (dso, rela->r_offset, value + rela->r_addend); + break; + case R_X86_64_64: + write_le64 (dso, rela->r_offset, value + rela->r_addend); + break; + case R_X86_64_32: + write_le32 (dso, rela->r_offset, value + rela->r_addend); + break; + case R_X86_64_PC32: + write_le32 (dso, rela->r_offset, value + rela->r_addend - rela->r_offset); + break; + case R_X86_64_DTPOFF64: + write_le64 (dso, rela->r_offset, value + rela->r_addend); + break; + /* DTPMOD64 and TPOFF64 is impossible to predict in shared libraries + unless prelink sets the rules. */ + case R_X86_64_DTPMOD64: + if (dso->ehdr.e_type == ET_EXEC) + { + error (0, 0, "%s: R_X86_64_DTPMOD64 reloc in executable?", + dso->filename); + return 1; + } + break; + case R_X86_64_TPOFF64: + if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) + write_le64 (dso, rela->r_offset, + value + rela->r_addend - info->resolvetls->offset); + break; + case R_X86_64_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_X86_64_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown X86-64 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +x86_64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr) +{ + GElf_Rela *ret; + switch (GELF_R_TYPE (rela->r_info)) + { + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + buf_write_leclass (info->dso, buf, rela->r_addend); + break; + case R_X86_64_64: + buf_write_le64 (buf, rela->r_addend); + break; + case R_X86_64_IRELATIVE: + if (dest_addr == 0) + return 5; + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = dest_addr; + ret->r_info = GELF_R_INFO (0, R_X86_64_IRELATIVE); + ret->r_addend = rela->r_addend; + break; + case R_X86_64_32: + buf_write_le32 (buf, rela->r_addend); + break; + default: + abort (); + } + return 0; +} + +static int +x86_64_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) +{ + error (0, 0, "%s: X86-64 doesn't support REL relocs", info->dso->filename); + return 1; +} + +static int +x86_64_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) +{ + GElf_Addr value; + + value = info->resolve (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_X86_64_NONE: + break; + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + buf_write_leclass (info->dso, buf, value + rela->r_addend); + break; + case R_X86_64_64: + buf_write_le64 (buf, value + rela->r_addend); + break; + case R_X86_64_32: + buf_write_le32 (buf, value + rela->r_addend); + break; + case R_X86_64_PC32: + buf_write_le32 (buf, value + rela->r_addend - rela->r_offset); + break; + case R_X86_64_COPY: + abort (); + case R_X86_64_RELATIVE: + error (0, 0, "%s: R_X86_64_RELATIVE in ET_EXEC object?", info->dso->filename); + return 1; + default: + return 1; + } + return 0; +} + +static int +x86_64_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr) +{ + error (0, 0, "%s: X86-64 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +x86_64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr) +{ + GElf_Addr value; + struct prelink_conflict *conflict; + struct prelink_tls *tls; + GElf_Rela *ret; + + if (GELF_R_TYPE (rela->r_info) == R_X86_64_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_X86_64_NONE) + /* Fast path: nothing to do. */ + return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), + GELF_R_TYPE (rela->r_info)); + if (conflict == NULL) + { + switch (GELF_R_TYPE (rela->r_info)) + { + /* Even local DTPMOD and TPOFF relocs need conflicts. */ + case R_X86_64_DTPMOD64: + case R_X86_64_TPOFF64: + if (info->curtls == NULL || info->dso == dso) + return 0; + break; + /* Similarly IRELATIVE relocations always need conflicts. */ + case R_X86_64_IRELATIVE: + break; + default: + return 0; + } + value = 0; + } + else if (info->dso == dso && !conflict->ifunc) + return 0; + else + { + /* DTPOFF wants to see only real conflicts, not lookups + with reloc_class RTYPE_CLASS_TLS. */ + if (GELF_R_TYPE (rela->r_info) == R_X86_64_DTPOFF64 + && conflict->lookup.tls == conflict->conflict.tls + && conflict->lookupval == conflict->conflictval) + return 0; + + value = conflict_lookup_value (conflict); + } + ret = prelink_conflict_add_rela (info); + if (ret == NULL) + return 1; + ret->r_offset = rela->r_offset; + ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info)); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_X86_64_GLOB_DAT: + ret->r_info = GELF_R_INFO (0, (gelf_getclass (dso->elf) == ELFCLASS32 ? R_X86_64_32 : R_X86_64_64)); + /* FALLTHROUGH */ + case R_X86_64_JUMP_SLOT: + case R_X86_64_64: + case R_X86_64_IRELATIVE: + ret->r_addend = value + rela->r_addend; + if (conflict != NULL && conflict->ifunc) + ret->r_info = GELF_R_INFO (0, R_X86_64_IRELATIVE); + break; + case R_X86_64_32: + value += rela->r_addend; + ret->r_addend = value; + break; + case R_X86_64_PC32: + ret->r_addend = value + rela->r_addend - rela->r_offset; + ret->r_info = GELF_R_INFO (0, R_X86_64_32); + break; + case R_X86_64_COPY: + error (0, 0, "R_X86_64_COPY should not be present in shared libraries"); + return 1; + case R_X86_64_DTPMOD64: + case R_X86_64_DTPOFF64: + case R_X86_64_TPOFF64: + if (conflict != NULL + && (conflict->reloc_class != RTYPE_CLASS_TLS + || conflict->lookup.tls == NULL)) + { + error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", + dso->filename); + return 1; + } + tls = conflict ? conflict->lookup.tls : info->curtls; + ret->r_info = GELF_R_INFO (0, R_X86_64_64); + switch (GELF_R_TYPE (rela->r_info)) + { + case R_X86_64_DTPMOD64: + ret->r_addend = tls->modid; + break; + case R_X86_64_DTPOFF64: + ret->r_addend = value + rela->r_addend; + break; + case R_X86_64_TPOFF64: + ret->r_addend = value + rela->r_addend - tls->offset; + break; + } + break; + + default: + error (0, 0, "%s: Unknown X86-64 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +x86_64_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) +{ + error (0, 0, "%s: X86-64 doesn't support REL relocs", dso->filename); + return 1; +} + +static int +x86_64_need_rel_to_rela (DSO *dso, int first, int last) +{ + return 0; +} + +static int +x86_64_arch_prelink (struct prelink_info *info) +{ + DSO *dso; + int i; + + dso = info->dso; + if (dso->info[DT_PLTGOT]) + { + /* Write address of .plt + 0x16 into got[1]. + .plt + 0x16 is what got[3] contains unless prelinking. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf64_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + assert (i < dso->ehdr.e_shnum); + data = dso->shdr[i].sh_addr + 0x16; + write_le64 (dso, dso->info[DT_PLTGOT] + 8, data); + } + + return 0; +} + +static int +x86_64_arch_undo_prelink (DSO *dso) +{ + int i; + + if (dso->info[DT_PLTGOT]) + { + /* Clear got[1] if it contains address of .plt + 0x16. */ + int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]); + Elf64_Addr data; + + if (sec == -1) + return 1; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_PROGBITS + && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".plt")) + break; + + if (i == dso->ehdr.e_shnum) + return 0; + data = read_ule64 (dso, dso->info[DT_PLTGOT] + 8); + if (data == dso->shdr[i].sh_addr + 0x16) + write_le64 (dso, dso->info[DT_PLTGOT] + 8, 0); + } + + return 0; +} + +static int +x86_64_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) +{ + int sec; + const char *name; + + switch (GELF_R_TYPE (rela->r_info)) + { + case R_X86_64_NONE: + case R_X86_64_RELATIVE: + case R_X86_64_IRELATIVE: + break; + case R_X86_64_JUMP_SLOT: + sec = addr_to_sec (dso, rela->r_offset); + name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name); + if (sec == -1 || (strcmp (name, ".got") && strcmp (name, ".got.plt"))) + { + error (0, 0, "%s: R_X86_64_JUMP_SLOT not pointing into .got section", + dso->filename); + return 1; + } + else + { + Elf64_Addr data = read_uleclass (dso, dso->shdr[sec].sh_addr + 8); + + assert (rela->r_offset >= dso->shdr[sec].sh_addr + 24); + assert (((rela->r_offset - dso->shdr[sec].sh_addr) & 7) == 0); + write_leclass (dso, rela->r_offset, + 2 * (rela->r_offset - dso->shdr[sec].sh_addr - 24) + + data); + } + break; + case R_X86_64_GLOB_DAT: + write_leclass (dso, rela->r_offset, 0); + break; + case R_X86_64_64: + case R_X86_64_DTPMOD64: + case R_X86_64_DTPOFF64: + case R_X86_64_TPOFF64: + write_le64 (dso, rela->r_offset, 0); + break; + case R_X86_64_32: + case R_X86_64_PC32: + write_le32 (dso, rela->r_offset, 0); + break; + case R_X86_64_COPY: + if (dso->ehdr.e_type == ET_EXEC) + /* COPY relocs are handled specially in generic code. */ + return 0; + error (0, 0, "%s: R_X86_64_COPY reloc in shared library?", dso->filename); + return 1; + default: + error (0, 0, "%s: Unknown X86-64 relocation type %d", dso->filename, + (int) GELF_R_TYPE (rela->r_info)); + return 1; + } + return 0; +} + +static int +x86_64_reloc_size (int reloc_type) +{ + switch (reloc_type) + { + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + case R_X86_64_64: + case R_X86_64_IRELATIVE: + return 8; + default: + return 4; + } +} + +static int +x86_64_x32_reloc_size (int reloc_type) +{ + switch (reloc_type) + { + case R_X86_64_64: + return 8; + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + case R_X86_64_IRELATIVE: + default: + return 4; + } +} + +static int +x86_64_reloc_class (int reloc_type) +{ + switch (reloc_type) + { + case R_X86_64_COPY: return RTYPE_CLASS_COPY; + case R_X86_64_JUMP_SLOT: return RTYPE_CLASS_PLT; + case R_X86_64_DTPMOD64: + case R_X86_64_DTPOFF64: + case R_X86_64_TPOFF64: + return RTYPE_CLASS_TLS; + default: return RTYPE_CLASS_VALID; + } +} + +PL_ARCH(x32) = { + .name = "x32", + .class = ELFCLASS32, + .machine = EM_X86_64, + .alternate_machine = { EM_NONE }, + .R_JMP_SLOT = R_X86_64_JUMP_SLOT, + .R_COPY = R_X86_64_COPY, + .R_RELATIVE = R_X86_64_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/libx32/ld-linux-x32.so.2", + .adjust_dyn = x86_64_adjust_dyn, + .adjust_rel = x86_64_adjust_rel, + .adjust_rela = x86_64_adjust_rela, + .prelink_rel = x86_64_prelink_rel, + .prelink_rela = x86_64_prelink_rela, + .prelink_conflict_rel = x86_64_prelink_conflict_rel, + .prelink_conflict_rela = x86_64_prelink_conflict_rela, + .apply_conflict_rela = x86_64_apply_conflict_rela, + .apply_rel = x86_64_apply_rel, + .apply_rela = x86_64_apply_rela, + .rel_to_rela = x86_64_rel_to_rela, + .need_rel_to_rela = x86_64_need_rel_to_rela, + .reloc_size = x86_64_x32_reloc_size, + .reloc_class = x86_64_reloc_class, + .max_reloc_size = 8, + .arch_prelink = x86_64_arch_prelink, + .arch_undo_prelink = x86_64_arch_undo_prelink, + .undo_prelink_rela = x86_64_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0x40000000, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x41000000, + .mmap_end = 0x50000000, + .max_page_size = 0x200000, + .page_size = 0x1000 +}; + +PL_ARCH(x86_64) = { + .name = "x86-64", + .class = ELFCLASS64, + .machine = EM_X86_64, + .alternate_machine = { EM_NONE }, + .R_JMP_SLOT = R_X86_64_JUMP_SLOT, + .R_COPY = R_X86_64_COPY, + .R_RELATIVE = R_X86_64_RELATIVE, + .rtype_class_valid = RTYPE_CLASS_VALID, + .dynamic_linker = "/lib64/ld-linux-x86-64.so.2", + .adjust_dyn = x86_64_adjust_dyn, + .adjust_rel = x86_64_adjust_rel, + .adjust_rela = x86_64_adjust_rela, + .prelink_rel = x86_64_prelink_rel, + .prelink_rela = x86_64_prelink_rela, + .prelink_conflict_rel = x86_64_prelink_conflict_rel, + .prelink_conflict_rela = x86_64_prelink_conflict_rela, + .apply_conflict_rela = x86_64_apply_conflict_rela, + .apply_rel = x86_64_apply_rel, + .apply_rela = x86_64_apply_rela, + .rel_to_rela = x86_64_rel_to_rela, + .need_rel_to_rela = x86_64_need_rel_to_rela, + .reloc_size = x86_64_reloc_size, + .reloc_class = x86_64_reloc_class, + .max_reloc_size = 8, + .arch_prelink = x86_64_arch_prelink, + .arch_undo_prelink = x86_64_arch_undo_prelink, + .undo_prelink_rela = x86_64_undo_prelink_rela, + /* Although TASK_UNMAPPED_BASE is 0x2a95555555, we leave some + area so that mmap of /etc/ld.so.cache and ld.so's malloc + does not take some library's VA slot. + Also, if this guard area isn't too small, typically + even dlopened libraries will get the slots they desire. */ + .mmap_base = 0x3000000000LL, + .mmap_end = 0x4000000000LL, + .max_page_size = 0x200000, + .page_size = 0x1000 +}; diff --git a/src/cache.c b/src/cache.c new file mode 100644 index 0000000..abcec27 --- /dev/null +++ b/src/cache.c @@ -0,0 +1,861 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2006, 2013 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include "prelinktab.h" + +htab_t prelink_devino_htab, prelink_filename_htab; + +int prelink_entry_count; + +static hashval_t +devino_hash (const void *p) +{ + struct prelink_entry *e = (struct prelink_entry *)p; + + return (e->dev << 2) ^ (e->ino) ^ (e->ino >> 20); +} + +static int +devino_eq (const void *p, const void *q) +{ + struct prelink_entry *e = (struct prelink_entry *)p; + struct prelink_entry *f = (struct prelink_entry *)q; + + return e->ino == f->ino && e->dev == f->dev; +} + +static hashval_t +filename_hash (const void *p) +{ + struct prelink_entry *e = (struct prelink_entry *)p; + const unsigned char *s = (const unsigned char *)e->filename; + hashval_t h = 0; + unsigned char c; + size_t len = 0; + + while ((c = *s++) != '\0') + { + h += c + (c << 17); + h ^= h >> 2; + ++len; + } + return h + len + (len << 17); +} + +static int +filename_eq (const void *p, const void *q) +{ + struct prelink_entry *e = (struct prelink_entry *)p; + struct prelink_entry *f = (struct prelink_entry *)q; + + return strcmp (e->filename, f->filename) == 0; +} + +int +prelink_init_cache (void) +{ + prelink_devino_htab = htab_try_create (100, devino_hash, devino_eq, NULL); + prelink_filename_htab = htab_try_create (100, filename_hash, filename_eq, + NULL); + if (prelink_devino_htab == NULL || prelink_filename_htab == NULL) + error (EXIT_FAILURE, ENOMEM, "Could not create hash table"); + return 0; +} + +struct prelink_entry * +prelink_find_entry (const char *filename, const struct stat64 *stp, + int insert) +{ + struct prelink_entry e, *ent = NULL; + void **filename_slot, *dummy = NULL; + void **devino_slot = NULL; + struct stat64 st; + char *canon_filename = NULL; + + e.filename = filename; + filename_slot = htab_find_slot (prelink_filename_htab, &e, + insert ? INSERT : NO_INSERT); + if (filename_slot == NULL) + { + if (insert) + goto error_out; + filename_slot = &dummy; + } + + if (*filename_slot != NULL) + return (struct prelink_entry *) *filename_slot; + + if (! stp) + { + canon_filename = prelink_canonicalize (filename, &st); + if (canon_filename == NULL && wrap_stat64 (filename, &st) < 0) + { + error (0, errno, "Could not stat %s", filename); + if (insert) + { + *filename_slot = &dummy; + htab_clear_slot (prelink_filename_htab, filename_slot); + } + return NULL; + } + stp = &st; + } + + e.dev = stp->st_dev; + e.ino = stp->st_ino; + devino_slot = htab_find_slot (prelink_devino_htab, &e, + insert ? INSERT : NO_INSERT); + if (devino_slot == NULL) + { + if (insert) + goto error_out; + free (canon_filename); + return NULL; + } + + if (*devino_slot != NULL) + { + ent = (struct prelink_entry *) *devino_slot; + if (canon_filename == NULL) + canon_filename = prelink_canonicalize (filename, NULL); + if (canon_filename == NULL) + { + error (0, 0, "Could not canonicalize filename %s", filename); + goto error_out2; + } + + if (strcmp (canon_filename, ent->canon_filename) != 0) + { + struct prelink_link *hardlink; + + hardlink = (struct prelink_link *) + malloc (sizeof (struct prelink_link)); + if (hardlink == NULL) + { + error (0, ENOMEM, "Could not record hardlink %s to %s", + canon_filename, ent->canon_filename); + goto error_out2; + } + + hardlink->canon_filename = canon_filename; + hardlink->next = ent->hardlink; + ent->hardlink = hardlink; + } + else + free (canon_filename); + return ent; + } + + if (! insert) + { + if (canon_filename != NULL) + free (canon_filename); + return NULL; + } + + ent = (struct prelink_entry *) calloc (sizeof (struct prelink_entry), 1); + if (ent == NULL) + goto error_out; + + ent->filename = strdup (filename); + if (ent->filename == NULL) + goto error_out; + + if (canon_filename != NULL) + ent->canon_filename = canon_filename; + else + ent->canon_filename = prelink_canonicalize (filename, NULL); + if (ent->canon_filename == NULL) + { + error (0, 0, "Could not canonicalize filename %s", filename); + free ((char *) ent->filename); + free (ent); + goto error_out2; + } + + ent->dev = stp->st_dev; + ent->ino = stp->st_ino; + ent->ctime = stp->st_ctime; + ent->mtime = stp->st_mtime; + + *filename_slot = ent; + *devino_slot = ent; + ++prelink_entry_count; + return ent; + +error_out: + free (ent); + error (0, ENOMEM, "Could not insert %s into hash table", filename); +error_out2: + if (insert) + { + if (filename_slot != NULL) + { + assert (*filename_slot == NULL); + *filename_slot = &dummy; + htab_clear_slot (prelink_filename_htab, filename_slot); + } + if (devino_slot != NULL && *devino_slot == NULL) + { + *devino_slot = &dummy; + htab_clear_slot (prelink_devino_htab, devino_slot); + } + } + free (canon_filename); + return NULL; +} + +static struct prelink_entry * +prelink_load_entry (const char *filename) +{ + struct prelink_entry e, *ent = NULL; + void **filename_slot, *dummy = NULL; + void **devino_slot = &dummy; + struct stat64 st; + uint32_t ctime = 0, mtime = 0; + char *canon_filename = NULL; + + e.filename = filename; + filename_slot = htab_find_slot (prelink_filename_htab, &e, INSERT); + if (filename_slot == NULL) + goto error_out; + + if (*filename_slot != NULL) + return (struct prelink_entry *) *filename_slot; + + canon_filename = prelink_canonicalize (filename, &st); + if (canon_filename == NULL) + goto error_out2; + if (strcmp (canon_filename, filename) != 0) + { + *filename_slot = &dummy; + htab_clear_slot (prelink_filename_htab, filename_slot); + + e.filename = canon_filename; + filename_slot = htab_find_slot (prelink_filename_htab, &e, INSERT); + if (filename_slot == NULL) + goto error_out; + + if (*filename_slot != NULL) + { + free (canon_filename); + return (struct prelink_entry *) *filename_slot; + } + } + + if (! S_ISREG (st.st_mode)) + { + free (canon_filename); + *filename_slot = &dummy; + htab_clear_slot (prelink_filename_htab, filename_slot); + return NULL; + } + else + { + e.dev = st.st_dev; + e.ino = st.st_ino; + ctime = (uint32_t) st.st_ctime; + mtime = (uint32_t) st.st_mtime; + devino_slot = htab_find_slot (prelink_devino_htab, &e, INSERT); + if (devino_slot == NULL) + goto error_out; + } + + if (*devino_slot != NULL) + { + free (canon_filename); + *filename_slot = &dummy; + htab_clear_slot (prelink_filename_htab, filename_slot); + return (struct prelink_entry *) *devino_slot; + } + + ent = (struct prelink_entry *) calloc (sizeof (struct prelink_entry), 1); + if (ent == NULL) + goto error_out; + + ent->filename = strdup (filename); + if (ent->filename == NULL) + goto error_out; + + ent->canon_filename = canon_filename; + ent->dev = e.dev; + ent->ino = e.ino; + ent->ctime = ctime; + ent->mtime = mtime; + *filename_slot = ent; + *devino_slot = ent; + ++prelink_entry_count; + return ent; + +error_out: + free (ent); + error (0, ENOMEM, "Could not insert %s into hash table", filename); +error_out2: + if (filename_slot != NULL) + { + *filename_slot = &dummy; + htab_clear_slot (prelink_filename_htab, filename_slot); + } + if (devino_slot != NULL && devino_slot != &dummy) + { + *devino_slot = &dummy; + htab_clear_slot (prelink_devino_htab, devino_slot); + } + free (canon_filename); + return NULL; +} + +static int +deps_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + + if (a == NULL) + return (b != NULL); + if (a != NULL && b == NULL) + return -1; + + if (a->type == ET_NONE && b->type != ET_NONE) + return 1; + if (a->type != ET_NONE && b->type == ET_NONE) + return -1; + + /* Libraries with fewest dependencies first. */ + if (a->ndepends < b->ndepends) + return -1; + if (a->ndepends > b->ndepends) + return 1; + return 0; +} + +int +prelink_load_cache (void) +{ + int fd, i, j; + struct stat64 st; + struct prelink_cache *cache; + struct prelink_entry **ents; + size_t cache_size; + uint32_t string_start, *dep; + + fd = wrap_open (prelink_cache, O_RDONLY); + if (fd < 0) + return 0; /* The cache does not exist yet. */ + + if (fstat64 (fd, &st) < 0 + || st.st_size == 0) + { + close (fd); + return 0; + } + + cache = mmap (0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (cache == MAP_FAILED) + error (EXIT_FAILURE, errno, "mmap of prelink cache file failed."); + cache_size = st.st_size; + if (memcmp (cache->magic, PRELINK_CACHE_MAGIC, + sizeof (PRELINK_CACHE_MAGIC) - 1)) + { + if (memcmp (cache->magic, PRELINK_CACHE_NAME, + sizeof (PRELINK_CACHE_NAME) - 1)) + error (EXIT_FAILURE, 0, "%s: is not prelink cache file", + prelink_cache); + munmap (cache, cache_size); + return 0; + } + dep = (uint32_t *) & cache->entry[cache->nlibs]; + string_start = ((long) dep) - ((long) cache) + + cache->ndeps * sizeof (uint32_t); + ents = (struct prelink_entry **) + alloca (cache->nlibs * sizeof (struct prelink_entry *)); + memset (ents, 0, cache->nlibs * sizeof (struct prelink_entry *)); + for (i = 0; i < cache->nlibs; i++) + { + /* Sanity checks. */ + if (cache->entry[i].filename < string_start + || cache->entry[i].filename >= string_start + cache->len_strings + || cache->entry[i].depends >= cache->ndeps) + error (EXIT_FAILURE, 0, "%s: bogus prelink cache file", + prelink_cache); + + ents[i] = prelink_load_entry (((char *) cache) + + cache->entry[i].filename); + } + + for (i = 0; i < cache->nlibs; i++) + { + if (ents[i] == NULL) + continue; + + if (ents[i]->type != ET_NONE) + continue; + + ents[i]->checksum = cache->entry[i].checksum; + ents[i]->base = cache->entry[i].base; + ents[i]->end = cache->entry[i].end; + ents[i]->type = (ents[i]->base == 0 && ents[i]->end == 0) + ? ET_CACHE_EXEC : ET_CACHE_DYN; + ents[i]->flags = cache->entry[i].flags; + + if (ents[i]->flags == PCF_UNPRELINKABLE) + ents[i]->type = (quick || print_cache) ? ET_UNPRELINKABLE : ET_NONE; + + /* If mtime is equal to ctime, assume the filesystem does not store + ctime. */ + if (quick + && ((ents[i]->ctime == ents[i]->mtime + && ents[i]->type != ET_UNPRELINKABLE) + || ents[i]->ctime != cache->entry[i].ctime + || ents[i]->mtime != cache->entry[i].mtime)) + ents[i]->type = ET_NONE; + + for (j = cache->entry[i].depends; dep[j] != i; ++j) + if (dep[j] >= cache->nlibs) + error (EXIT_FAILURE, 0, "%s: bogus prelink cache file", + prelink_cache); + else if (ents[dep[j]] == NULL) + ents[i]->type = ET_NONE; + + if (ents[i]->type == ET_NONE) + continue; + + ents[i]->ndepends = j - cache->entry[i].depends; + if (ents[i]->ndepends) + { + ents[i]->depends = + (struct prelink_entry **) + malloc (ents[i]->ndepends * sizeof (struct prelink_entry *)); + if (ents[i]->depends == NULL) + error (EXIT_FAILURE, ENOMEM, "Cannot read cache file %s", + prelink_cache); + + for (j = 0; j < ents[i]->ndepends; ++j) + ents[i]->depends[j] = ents[dep[cache->entry[i].depends + j]]; + } + } + + if (quick) + { + qsort (ents, cache->nlibs, sizeof (struct prelink_entry *), deps_cmp); + for (i = 0; i < cache->nlibs; ++i) + { + if (ents[i] == NULL || ents[i]->type == ET_NONE) + continue; + + for (j = 0; j < ents[i]->ndepends; ++j) + if (ents[i]->depends[j]->type == ET_NONE) + { + ents[i]->type = ET_NONE; + free (ents[i]->depends); + ents[i]->depends = NULL; + ents[i]->ndepends = 0; + break; + } + } + } + + munmap (cache, cache_size); + close (fd); + return 0; +} + +static int +prelink_print_cache_size (void **p, void *info) +{ + struct prelink_entry *e = * (struct prelink_entry **) p; + int *psize = (int *) info; + + if ((e->base & 0xffffffff) != e->base + || (e->end & 0xffffffff) != e->end) + { + *psize = 16; + return 0; + } + + return 1; +} + +static int +prelink_print_cache_object (void **p, void *info) +{ + struct prelink_entry *e = * (struct prelink_entry **) p; + int *psize = (int *) info, i; + + if (e->type == ET_UNPRELINKABLE) + { + printf ("%s (not prelinkable)%s\n", e->filename, e->ndepends ? ":" : ""); + for (i = 0; i < e->ndepends; i++) + if (e->depends[i]->type == ET_UNPRELINKABLE) + printf (" %s (not prelinkable)\n", e->depends[i]->filename); + else + printf (" %s [0x%08x]\n", e->depends[i]->filename, + e->depends[i]->checksum); + return 1; + } + + if (e->type == ET_CACHE_DYN) + printf ("%s [0x%08x] 0x%0*llx-0x%0*llx%s\n", e->filename, e->checksum, + *psize, (long long) e->base, *psize, (long long) e->end, + e->ndepends ? ":" : ""); + else + printf ("%s%s\n", e->filename, e->ndepends ? ":" : ""); + for (i = 0; i < e->ndepends; i++) + printf (" %s [0x%08x]\n", e->depends[i]->filename, + e->depends[i]->checksum); + return 1; +} + +int +prelink_print_cache (void) +{ + int size = 8; + + printf ("%d objects found in prelink cache `%s'\n", prelink_entry_count, + prelink_cache); + + htab_traverse (prelink_filename_htab, prelink_print_cache_size, &size); + htab_traverse (prelink_filename_htab, prelink_print_cache_object, &size); + return 0; +} + +struct collect_ents +{ + struct prelink_entry **ents; + size_t len_strings; + int nents; + int ndeps; +}; + +static int +prelink_save_cache_check (struct prelink_entry *ent) +{ + int i; + + for (i = 0; i < ent->ndepends; ++i) + switch (ent->depends[i]->type) + { + case ET_DYN: + if (ent->depends[i]->done < 2 + || (quick && (ent->depends[i]->flags & PCF_PRELINKED))) + return 1; + break; + case ET_CACHE_DYN: + if (prelink_save_cache_check (ent->depends[i])) + return 1; + break; + case ET_UNPRELINKABLE: + if (ent->type != ET_UNPRELINKABLE) + return 1; + if (prelink_save_cache_check (ent->depends[i])) + return 1; + break; + default: + return 1; + } + + return 0; +} + +static int +find_ents (void **p, void *info) +{ + struct collect_ents *l = (struct collect_ents *) info; + struct prelink_entry *e = * (struct prelink_entry **) p; + + if (((e->type == ET_DYN || e->type == ET_EXEC) && e->done == 2) + || ((e->type == ET_CACHE_DYN || e->type == ET_CACHE_EXEC + || e->type == ET_UNPRELINKABLE) + && ! prelink_save_cache_check (e))) + { + l->ents[l->nents++] = e; + l->ndeps += e->ndepends + 1; + l->len_strings += strlen (e->canon_filename) + 1; + } + return 1; +} + +int +prelink_save_cache (int do_warn) +{ + struct prelink_cache cache; + struct collect_ents l; + struct prelink_cache_entry *data; + uint32_t *deps, ndeps = 0, i, j, k; + char *strings; + int fd, len; + struct prelink_entry *ents_array[prelink_entry_count]; + + memset (&cache, 0, sizeof (cache)); + memcpy ((char *) & cache, PRELINK_CACHE_MAGIC, + sizeof (PRELINK_CACHE_MAGIC) - 1); + l.ents = ents_array; + l.nents = 0; + l.ndeps = 0; + l.len_strings = 0; + htab_traverse (prelink_filename_htab, find_ents, &l); + cache.nlibs = l.nents; + cache.ndeps = l.ndeps; + cache.len_strings = l.len_strings; + + len = cache.nlibs * sizeof (struct prelink_cache_entry) + + cache.ndeps * sizeof (uint32_t) + cache.len_strings; + char data_buf[len]; + data = (struct prelink_cache_entry *) data_buf; + deps = (uint32_t *) & data[cache.nlibs]; + strings = (char *) & deps[cache.ndeps]; + + for (i = 0; i < l.nents; ++i) + { + data[i].filename = (strings - (char *) data) + sizeof (cache); + strings = stpcpy (strings, l.ents[i]->canon_filename) + 1; + data[i].checksum = l.ents[i]->checksum; + data[i].flags = l.ents[i]->flags & ~PCF_PRELINKED; + data[i].ctime = l.ents[i]->ctime; + data[i].mtime = l.ents[i]->mtime; + if (l.ents[i]->type == ET_EXEC || l.ents[i]->type == ET_CACHE_EXEC) + { + data[i].base = 0; + data[i].end = 0; + } + else if (l.ents[i]->type == ET_UNPRELINKABLE) + { + data[i].base = 0; + data[i].end = 0; + data[i].checksum = 0; + data[i].flags = PCF_UNPRELINKABLE; + } + else + { + data[i].base = l.ents[i]->base; + data[i].end = l.ents[i]->end; + } + } + + for (i = 0; i < cache.nlibs; i++) + { + data[i].depends = ndeps; + for (j = 0; j < l.ents[i]->ndepends; j++) + { + for (k = 0; k < cache.nlibs; k++) + if (l.ents[k] == l.ents[i]->depends[j]) + break; + if (k == cache.nlibs) + abort (); + deps[ndeps++] = k; + } + deps[ndeps++] = i; + } + + size_t prelink_cache_len = strlen (prelink_cache); + char prelink_cache_tmp [prelink_cache_len + sizeof (".XXXXXX")]; + memcpy (mempcpy (prelink_cache_tmp, prelink_cache, prelink_cache_len), + ".XXXXXX", sizeof (".XXXXXX")); + fd = wrap_mkstemp (prelink_cache_tmp); + if (fd < 0) + { + error (0, errno, "Could not write prelink cache"); + return 1; + } + + if (write (fd, &cache, sizeof (cache)) != sizeof (cache) + || write (fd, data, len) != len + || fchmod (fd, 0644) + || fsync (fd) + || close (fd) + || wrap_rename (prelink_cache_tmp, prelink_cache)) + { + error (0, errno, "Could not write prelink cache"); + wrap_unlink (prelink_cache_tmp); + return 1; + } + return 0; +} + +#ifndef NDEBUG +static void +prelink_entry_dumpfn (FILE *f, const void *ptr) +{ + struct prelink_entry *e = (struct prelink_entry *) ptr; + struct prelink_link *l; + int i; + + fprintf (f, "%s|%s|%s|%x|%x|%llx|%llx|%llx|%llx|%llx|%d|%d|%d|%d|%d|%d|%d|", + e->filename, + strcmp (e->canon_filename, e->filename) ? e->canon_filename : "", + e->soname && strcmp (e->soname, e->filename) ? e->soname : "", + e->timestamp, e->checksum, + (long long) e->base, (long long) e->end, (long long) e->pltgot, + (long long) e->dev, (long long) e->ino, + e->type, e->done, e->ndepends, e->refs, e->flags, + e->prev ? e->prev->u.tmp : -1, e->next ? e->next->u.tmp : -1); + for (i = 0; i < e->ndepends; ++i) + fprintf (f, "%d-", e->depends [i]->u.tmp); + fputc ('|', f); + for (l = e->hardlink; l; l = l->next) + fprintf (f, "%s|", l->canon_filename); + fputs ("\n", f); +} + +void +prelink_entry_dump (htab_t htab, const char *filename) +{ + size_t i; + + for (i = 0; i < htab->size; ++i) + if (htab->entries [i] && htab->entries [i] != (void *) 1) + ((struct prelink_entry *) htab->entries [i])->u.tmp = i; + htab_dump (htab, filename, prelink_entry_dumpfn); +} + +static char *restore_line; +static size_t restore_size; + +static void * +prelink_entry_restorefn (FILE *f) +{ + struct prelink_entry *e; + struct prelink_link **plink; + char *p, *q, *s; + long long ll[5]; + int ii[5]; + int i; + + if (getline (&restore_line, &restore_size, f) < 0) + abort (); + e = (struct prelink_entry *) calloc (1, sizeof (struct prelink_entry)); + if (e == NULL) + abort (); + p = restore_line; + q = strchr (p, '|'); + s = malloc (q - p + 1); + memcpy (s, p, q - p); + s [q - p] = '\0'; + e->filename = s; + ++q; + p = q; + if (*p == '|') + e->canon_filename = strdup (e->filename); + else + { + q = strchr (p, '|'); + s = malloc (q - p + 1); + memcpy (s, p, q - p); + s [q - p] = '\0'; + e->canon_filename = s; + } + ++q; + p = q; + if (*p == '|') + e->soname = strdup (e->filename); + else + { + q = strchr (p, '|'); + s = malloc (q - p + 1); + memcpy (s, p, q - p); + s [q - p] = '\0'; + e->soname = s; + } + p = q + 1; + if (sscanf (p, "%x|%x|%llx|%llx|%llx|%llx|%llx|%d|%d|%d|%d|%d|%d|%d|%n", + ii, ii + 1, ll, ll + 1, ll + 2, ll + 3, ll + 4, + &e->type, &e->done, &e->ndepends, &e->refs, &e->flags, + ii + 2, ii + 3, ii + 4) < 14) + abort (); + e->timestamp = ii[0]; + e->checksum = ii[1]; + e->base = ll[0]; + e->end = ll[1]; + e->pltgot = ll[2]; + e->dev = ll[3]; + e->ino = ll[4]; + e->prev = (void *) (long) ii[2]; + e->next = (void *) (long) ii[3]; + e->depends = (struct prelink_entry **) + malloc (e->ndepends * sizeof (struct prelink_entry *)); + p += ii[4]; + for (i = 0; i < e->ndepends; ++i) + { + e->depends [i] = (void *) strtol (p, &q, 0); + if (p == q || *q != '-') + abort (); + p = q + 1; + } + if (*p++ != '|') + abort (); + plink = &e->hardlink; + while (*p != '\n') + { + struct prelink_link *link = (struct prelink_link *) + malloc (sizeof (struct prelink_link)); + q = strchr (p, '|'); + *plink = link; + plink = &link->next; + s = malloc (q - p + 1); + memcpy (s, p, q - p); + s [q - p] = '\0'; + e->soname = s; + link->canon_filename = s; + p = q + 1; + } + *plink = NULL; + ++prelink_entry_count; + return e; +} + +void +prelink_entry_restore (htab_t htab, const char *filename) +{ + size_t i, j; + struct prelink_entry *e; + + prelink_entry_count = 0; + htab_restore (htab, filename, prelink_entry_restorefn); + free (restore_line); + for (i = 0; i < htab->size; ++i) + if (htab->entries [i] && htab->entries [i] != (void *) 1) + { + e = (struct prelink_entry *) htab->entries [i]; + if (e->prev == (void *) -1) + e->prev = NULL; + else + e->prev = (struct prelink_entry *) + htab->entries [(long) e->prev]; + if (e->next == (void *) -1) + e->next = NULL; + else + e->next = (struct prelink_entry *) + htab->entries [(long) e->next]; + for (j = 0; j < e->ndepends; ++j) + { + e->depends [j] = (struct prelink_entry *) + htab->entries [(long) e->depends [j]]; + } + } +} +#endif diff --git a/src/canonicalize.c b/src/canonicalize.c new file mode 100644 index 0000000..717e991 --- /dev/null +++ b/src/canonicalize.c @@ -0,0 +1,374 @@ +/* Return the canonical absolute name of a given file. + Copyright (C) 1996-2002, 2004, 2005, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <errno.h> +#include <stddef.h> + +#include "hashtab.h" +#include "prelink.h" + +htab_t prelink_dirname_htab; + +struct dirname_entry +{ + const char *dirname; + size_t dirname_len; + const char *canon_dirname; + size_t canon_dirname_len; +}; + +static hashval_t +dirname_hash (const void *p) +{ + struct dirname_entry *e = (struct dirname_entry *)p; + const unsigned char *s = (const unsigned char *)e->dirname; + hashval_t h = 0; + unsigned char c; + size_t len = e->dirname_len; + + while (len--) + { + c = *s++; + h += c + (c << 17); + h ^= h >> 2; + } + return h + e->dirname_len + (e->dirname_len << 17); +} + +static int +dirname_eq (const void *p, const void *q) +{ + struct dirname_entry *e = (struct dirname_entry *)p; + struct dirname_entry *f = (struct dirname_entry *)q; + + return (e->dirname_len == f->dirname_len + && memcmp (e->dirname, f->dirname, e->dirname_len) == 0); +} + +/* Return the canonical absolute name of file NAME. A canonical name + does not contain any `.', `..' components nor any repeated path + separators ('/') or symlinks. All path components must exist. + The result is malloc'd. */ + +char * +canon_filename (const char *name, int nested, struct stat64 *stp, + const char *chroot, int allow_last_link, + int allow_missing) +{ + char *rpath, *dest, *extra_buf = NULL, *rpath_root; + const char *start, *end, *rpath_limit; + long int path_max; + int num_links = 0; + int stp_initialized = 0; + int chroot_len; + + if (name == NULL) + { + errno = EINVAL; + return NULL; + } + + if (name[0] == '\0') + { + errno = ENOENT; + return NULL; + } + chroot_len = strlen (chroot); + if (chroot_len > 0 && chroot[chroot_len - 1] == '/') + chroot_len--; + +#ifdef PATH_MAX + path_max = PATH_MAX; +#else + path_max = pathconf (name, _PC_PATH_MAX); + if (path_max <= 0) + path_max = 1024; +#endif + + rpath = malloc (path_max + chroot_len + 1); + if (rpath == NULL) + return NULL; + rpath_limit = rpath + path_max; + + if (name[0] != '/') + { + if (!getcwd (rpath, path_max)) + { + rpath[0] = '\0'; + goto error; + } + if (chroot_len > 0) + { + struct stat64 st; + char *cwd = canon_filename (rpath, 1, &st, chroot, 0, 0); + if (cwd == NULL) + goto error; + if (memcmp (cwd, chroot, chroot_len) != 0) + goto error; + strcpy (rpath, cwd); + free (cwd); + rpath_root = rpath + chroot_len; + } + else + rpath_root = rpath; + + dest = strchr (rpath_root, '\0'); + } + else + { + if (chroot_len > 0) + rpath_root = (char *) mempcpy (rpath, chroot, chroot_len); + else + rpath_root = rpath; + + rpath_root[0] = '/'; + dest = rpath_root + 1; + + if (!nested) + { + if (prelink_dirname_htab == NULL) + prelink_dirname_htab = htab_try_create (100, dirname_hash, + dirname_eq, NULL); + if (prelink_dirname_htab == NULL) + nested = 1; + } + if (!nested) + { + struct dirname_entry e; + void **dirname_slot; + + end = strrchr (name, '/'); + + e.dirname = name; + e.dirname_len = end - name; + dirname_slot = htab_find_slot (prelink_dirname_htab, &e, INSERT); + if (*dirname_slot == NULL) + { + struct dirname_entry *ep = malloc (sizeof (struct dirname_entry) + + e.dirname_len + 1); + if (ep != NULL) + { + char *dirname = (char *) (ep + 1); + struct stat64 st; + + ep->dirname = (const char *) dirname; + ep->dirname_len = e.dirname_len; + memcpy (dirname, name, ep->dirname_len); + dirname[ep->dirname_len] = '\0'; + ep->canon_dirname = canon_filename (ep->dirname, 1, &st, + chroot, 0, 0); + if (ep->canon_dirname == NULL || !S_ISDIR (st.st_mode)) + free (ep); + else + { + ep->canon_dirname_len = strlen (ep->canon_dirname); + *dirname_slot = ep; + } + } + } + + if (*dirname_slot != NULL) + { + struct dirname_entry *ep = *dirname_slot; + + if (rpath + ep->canon_dirname_len + 1 >= rpath_limit) + { + size_t new_size, root_size; + char *new_rpath; + + new_size = rpath_limit - rpath; + root_size = rpath_root - rpath; + if (ep->canon_dirname_len + 1 > path_max) + new_size += ep->canon_dirname_len + 1; + else + new_size += path_max; + new_rpath = (char *) realloc (rpath, new_size); + if (new_rpath == NULL) + goto error; + rpath = new_rpath; + rpath_limit = rpath + new_size; + rpath_root = rpath + root_size; + } + dest = mempcpy (rpath, ep->canon_dirname, ep->canon_dirname_len); + *dest = '\0'; + name = end + 1; + } + } + } + + for (start = end = name; *start; start = end) + { + int n; + + /* Skip sequence of multiple path-separators. */ + while (*start == '/') + ++start; + + /* Find end of path component. */ + for (end = start; *end && *end != '/'; ++end) + /* Nothing. */; + + if (end - start == 0) + break; + else if (end - start == 1 && start[0] == '.') + /* nothing */; + else if (end - start == 2 && start[0] == '.' && start[1] == '.') + { + /* Back up to previous component, ignore if at root already. */ + if (dest > rpath_root + 1) + while ((--dest)[-1] != '/'); + stp_initialized = 0; + } + else + { + size_t new_size; + + if (dest[-1] != '/') + *dest++ = '/'; + + if (dest + (end - start) >= rpath_limit) + { + ptrdiff_t dest_offset = dest - rpath; + size_t root_size = rpath_root - rpath; + char *new_rpath; + + new_size = rpath_limit - rpath; + if (end - start + 1 > path_max) + new_size += end - start + 1; + else + new_size += path_max; + new_rpath = (char *) realloc (rpath, new_size); + if (new_rpath == NULL) + goto error; + rpath = new_rpath; + rpath_limit = rpath + new_size; + rpath_root = rpath + root_size; + + dest = rpath + dest_offset; + } + + dest = mempcpy (dest, start, end - start); + *dest = '\0'; + + if (allow_last_link && *end == '\0') + break; + + if (lstat64 (rpath, stp) < 0) + { + if (allow_missing && *end == '\0') + break; + goto error; + } + + stp_initialized = 1; + + if (S_ISLNK (stp->st_mode)) + { + char *buf = alloca (path_max); + size_t len; + + if (++num_links > MAXSYMLINKS) + { + errno = ELOOP; + goto error; + } + + n = readlink (rpath, buf, path_max); + if (n < 0) + { + if (allow_missing && *end == '\0') + break; + goto error; + } + buf[n] = '\0'; + + if (!extra_buf) + extra_buf = alloca (path_max); + + len = strlen (end); + if ((long int) (n + len) >= path_max) + { + errno = ENAMETOOLONG; + goto error; + } + + /* Careful here, end may be a pointer into extra_buf... */ + memmove (&extra_buf[n], end, len + 1); + name = end = memcpy (extra_buf, buf, n); + + if (buf[0] == '/') + dest = rpath_root + 1; /* It's an absolute symlink */ + else + /* Back up to previous component, ignore if at root already: */ + if (dest > rpath_root + 1) + while ((--dest)[-1] != '/'); + } + else if (!S_ISDIR (stp->st_mode) && *end != '\0') + { + errno = ENOTDIR; + goto error; + } + } + } + if (dest > rpath + 1 && dest[-1] == '/') + --dest; + *dest = '\0'; + + if (!stp_initialized && !allow_missing && !allow_last_link + && lstat64 (rpath, stp) < 0) + goto error; + + if (dest + 1 - rpath <= (rpath_limit - rpath) / 2) + { + char *new_rpath = realloc (rpath, dest + 1 - rpath); + + if (new_rpath != NULL) + return new_rpath; + } + return rpath; + +error: + free (rpath); + return NULL; +} + +char *unsysroot_file_name (const char *name); + +char * +prelink_canonicalize (const char *name, struct stat64 *stp) +{ + struct stat64 st; + char *canon, *final; + + canon = canon_filename (name, 0, stp ? stp : &st, + sysroot ? sysroot : "", 0, 0); + if (canon == NULL) + return NULL; + final = unsysroot_file_name (canon); + if (final != canon) + free (canon); + return final; +} diff --git a/src/checksum.c b/src/checksum.c new file mode 100644 index 0000000..20a23a0 --- /dev/null +++ b/src/checksum.c @@ -0,0 +1,89 @@ +/* Copyright (C) 2001, 2002, 2003 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <endian.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include "prelink.h" + +int +prelink_set_checksum (DSO *dso) +{ + extern uint32_t crc32 (uint32_t crc, unsigned char *buf, size_t len); + uint32_t crc; + int i, cvt; + + if (set_dynamic (dso, DT_CHECKSUM, 0, 1)) + return 1; + + if (dso->info_DT_GNU_PRELINKED + && set_dynamic (dso, DT_GNU_PRELINKED, 0, 1)) + return 1; + + /* Ensure any pending .mdebug/.dynsym/.dynstr etc. modifications + write_dso would do happen before checksumming. */ + if (prepare_write_dso (dso)) + return 1; + + cvt = ! ((__BYTE_ORDER == __LITTLE_ENDIAN + && dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) + || (__BYTE_ORDER == __BIG_ENDIAN + && dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)); + crc = 0; + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + if (! (dso->shdr[i].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR))) + continue; + if (dso->shdr[i].sh_type != SHT_NOBITS && dso->shdr[i].sh_size) + { + Elf_Scn *scn = dso->scn[i]; + Elf_Data *d = NULL; + + /* Cannot use elf_rawdata here, since the image is not written + yet. */ + while ((d = elf_getdata (scn, d)) != NULL) + { + if (cvt && d->d_type != ELF_T_BYTE) + { + gelf_xlatetof (dso->elf, d, d, + dso->ehdr.e_ident[EI_DATA]); + crc = crc32 (crc, d->d_buf, d->d_size); + gelf_xlatetom (dso->elf, d, d, + dso->ehdr.e_ident[EI_DATA]); + } + else + crc = crc32 (crc, d->d_buf, d->d_size); + } + } + } + + if (set_dynamic (dso, DT_CHECKSUM, crc, 1)) + abort (); + if (dso->info_DT_GNU_PRELINKED + && set_dynamic (dso, DT_GNU_PRELINKED, dso->info_DT_GNU_PRELINKED, 1)) + abort (); + dso->info_DT_CHECKSUM = crc; + + return 0; +} diff --git a/src/conflict.c b/src/conflict.c new file mode 100644 index 0000000..9ae2ddb --- /dev/null +++ b/src/conflict.c @@ -0,0 +1,832 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2007, 2009 Red Hat, Inc. + Copyright (C) 2008 CodeSourcery. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + Updated by Maciej W. Rozycki <macro@codesourcery.com>, 2008. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "prelink.h" +#include "reloc.h" +#include "reloc-info.h" + +struct prelink_conflict * +prelink_conflict (struct prelink_info *info, GElf_Word r_sym, + int reloc_type) +{ + GElf_Word symoff = info->symtab_start + r_sym * info->symtab_entsize; + struct prelink_conflict *conflict; + int reloc_class = info->dso->arch->reloc_class (reloc_type); + size_t idx = 0; + + if (info->curconflicts->hash != &info->curconflicts->first) + idx = symoff % 251; + for (conflict = info->curconflicts->hash[idx]; conflict; + conflict = conflict->next) + if (conflict->symoff == symoff && conflict->reloc_class == reloc_class) + { + conflict->used = 1; + return conflict; + } + + return NULL; +} + +GElf_Rela * +prelink_conflict_add_rela (struct prelink_info *info) +{ + GElf_Rela *ret; + + if (info->conflict_rela_alloced == info->conflict_rela_size) + { + info->conflict_rela_alloced += 10; + info->conflict_rela = realloc (info->conflict_rela, + info->conflict_rela_alloced + * sizeof (GElf_Rela)); + if (info->conflict_rela == NULL) + { + error (0, ENOMEM, "Could not build .gnu.conflict section memory image"); + return NULL; + } + } + ret = info->conflict_rela + info->conflict_rela_size++; + ret->r_offset = 0; + ret->r_info = 0; + ret->r_addend = 0; + return ret; +} + +static int +prelink_conflict_rel (DSO *dso, int n, struct prelink_info *info) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Rel rel; + int sec, ndx, maxndx; + + while ((data = elf_getdata (scn, data)) != NULL) + { + GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off; + + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; + ++ndx, addr += dso->shdr[n].sh_entsize) + { + gelfx_getrel (dso->elf, data, ndx, &rel); + sec = addr_to_sec (dso, rel.r_offset); + if (sec == -1) + continue; + + if (dso->arch->prelink_conflict_rel (dso, info, &rel, addr)) + return 1; + } + } + return 0; +} + +static int +prelink_conflict_rela (DSO *dso, int n, struct prelink_info *info) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Rela rela; + int sec, ndx, maxndx; + + while ((data = elf_getdata (scn, data)) != NULL) + { + GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off; + + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; + ++ndx, addr += dso->shdr[n].sh_entsize) + { + gelfx_getrela (dso->elf, data, ndx, &rela); + sec = addr_to_sec (dso, rela.r_offset); + if (sec == -1) + continue; + + if (dso->arch->prelink_conflict_rela (dso, info, &rela, addr)) + return 1; + } + } + return 0; +} + +struct copy_relocs +{ + GElf_Rela *rela; + int alloced; + int count; +}; + +static int +prelink_add_copy_rel (DSO *dso, int n, GElf_Rel *rel, struct copy_relocs *cr) +{ + Elf_Data *data = NULL; + int symsec = dso->shdr[n].sh_link; + Elf_Scn *scn = dso->scn[symsec]; + GElf_Sym sym; + size_t entsize = dso->shdr[symsec].sh_entsize; + off_t off = reloc_r_sym (dso, rel->r_info) * entsize; + + while ((data = elf_getdata (scn, data)) != NULL) + { + if (data->d_off <= off && + data->d_off + data->d_size >= off + entsize) + { + gelfx_getsym (dso->elf, data, (off - data->d_off) / entsize, &sym); + if (sym.st_size == 0) + { + error (0, 0, "%s: Copy reloc against symbol with zero size", + dso->filename); + return 1; + } + + if (cr->alloced == cr->count) + { + cr->alloced += 10; + cr->rela = realloc (cr->rela, cr->alloced * sizeof (GElf_Rela)); + if (cr->rela == NULL) + { + error (0, ENOMEM, "%s: Could not build list of COPY relocs", + dso->filename); + return 1; + } + } + cr->rela[cr->count].r_offset = rel->r_offset; + cr->rela[cr->count].r_info = rel->r_info; + cr->rela[cr->count].r_addend = sym.st_size; + ++cr->count; + return 0; + } + } + + error (0, 0, "%s: Copy reloc against unknown symbol", dso->filename); + return 1; +} + +static int +prelink_find_copy_rel (DSO *dso, int n, struct copy_relocs *cr) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Rel rel; + int sec, ndx, maxndx; + + while ((data = elf_getdata (scn, data)) != NULL) + { + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getrel (dso->elf, data, ndx, &rel); + sec = addr_to_sec (dso, rel.r_offset); + if (sec == -1) + continue; + + if (reloc_r_type (dso, rel.r_info) == dso->arch->R_COPY + && prelink_add_copy_rel (dso, n, &rel, cr)) + return 1; + } + } + return 0; +} + +static int +prelink_find_copy_rela (DSO *dso, int n, struct copy_relocs *cr) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + union { + GElf_Rel rel; + GElf_Rela rela; + } u; + int sec, ndx, maxndx; + + while ((data = elf_getdata (scn, data)) != NULL) + { + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getrela (dso->elf, data, ndx, &u.rela); + sec = addr_to_sec (dso, u.rela.r_offset); + if (sec == -1) + continue; + + if (reloc_r_type (dso, u.rela.r_info) == dso->arch->R_COPY) + { + if (u.rela.r_addend != 0) + { + error (0, 0, "%s: COPY reloc with non-zero addend?", + dso->filename); + return 1; + } + if (prelink_add_copy_rel (dso, n, &u.rel, cr)) + return 1; + } + } + } + return 0; +} + +static int +rela_cmp (const void *A, const void *B) +{ + GElf_Rela *a = (GElf_Rela *)A; + GElf_Rela *b = (GElf_Rela *)B; + + if (a->r_offset < b->r_offset) + return -1; + if (a->r_offset > b->r_offset) + return 1; + return 0; +} + +static DSO *conflict_rela_cmp_dso; + +static int +conflict_rela_cmp (const void *A, const void *B) +{ + DSO *dso = conflict_rela_cmp_dso; + GElf_Rela *a = (GElf_Rela *)A; + GElf_Rela *b = (GElf_Rela *)B; + + if (reloc_r_sym (dso, a->r_info) < reloc_r_sym (dso, b->r_info)) + return -1; + if (reloc_r_sym (dso, a->r_info) > reloc_r_sym (dso, b->r_info)) + return 1; + if (a->r_offset < b->r_offset) + return -1; + if (a->r_offset > b->r_offset) + return 1; + return 0; +} + +int +get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr, + char *buf, GElf_Word size, GElf_Addr dest_addr) +{ + int sec = addr_to_sec (dso, addr), j; + Elf_Scn *scn; + Elf_Data *data; + off_t off; + + if (sec == -1) + return 1; + + memset (buf, 0, size); + if (dso->shdr[sec].sh_type != SHT_NOBITS) + { + scn = dso->scn[sec]; + data = NULL; + off = addr - dso->shdr[sec].sh_addr; + while ((data = elf_rawdata (scn, data)) != NULL) + { + if (data->d_off < off + size + && data->d_off + data->d_size > off) + { + off_t off2 = off - data->d_off; + size_t len = size; + + if (off2 < 0) + { + len += off2; + off2 = 0; + } + if (off2 + len > data->d_size) + len = data->d_size - off2; + assert (off2 + len <= data->d_size); + assert (len <= size); + memcpy (buf + off2 - off, data->d_buf + off2, len); + } + } + } + + if (info->dso != dso) + { + /* This is tricky. We need to apply any conflicts + against memory area which we've copied to the COPY + reloc offset. */ + for (j = 0; j < info->conflict_rela_size; ++j) + { + int reloc_type, reloc_size, ret; + off_t off; + + if (info->conflict_rela[j].r_offset >= addr + size) + continue; + if (info->conflict_rela[j].r_offset + dso->arch->max_reloc_size + <= addr) + continue; + + reloc_type = reloc_r_type (dso, info->conflict_rela[j].r_info); + reloc_size = dso->arch->reloc_size (reloc_type); + if (info->conflict_rela[j].r_offset + reloc_size <= addr) + continue; + + off = info->conflict_rela[j].r_offset - addr; + + /* Check if whole relocation fits into the area. + Punt if not. */ + if (off < 0 || size - off < reloc_size) + return 2; + /* Note that apply_conflict_rela shouldn't rely on R_SYM + field of conflict to be 0. */ + ret + = dso->arch->apply_conflict_rela (info, info->conflict_rela + j, + buf + off, + dest_addr ? dest_addr + off : 0); + if (ret) + return ret; + } + } + else + { + int i, ndx, maxndx; + int reloc_type, reloc_size; + union { GElf_Rel rel; GElf_Rela rela; } u; + off_t off; + + if (addr + size > info->dynbss_base + && addr < info->dynbss_base + info->dynbss_size) + { + if (addr < info->dynbss_base + || addr + size > info->dynbss_base + info->dynbss_size) + return 4; + + memcpy (buf, info->dynbss + (addr - info->dynbss_base), size); + return 0; + } + + if (addr + size > info->sdynbss_base + && addr < info->sdynbss_base + info->sdynbss_size) + { + if (addr < info->sdynbss_base + || addr + size > info->sdynbss_base + info->sdynbss_size) + return 4; + + memcpy (buf, info->sdynbss + (addr - info->sdynbss_base), size); + return 0; + } + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + + if (! (dso->shdr[i].sh_flags & SHF_ALLOC)) + continue; + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".gnu.conflict")) + continue; + switch (dso->shdr[i].sh_type) + { + case SHT_REL: + case SHT_RELA: + break; + default: + continue; + } + scn = dso->scn[i]; + data = NULL; + while ((data = elf_getdata (scn, data)) != NULL) + { + maxndx = data->d_size / dso->shdr[i].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + if (dso->shdr[i].sh_type == SHT_REL) + gelfx_getrel (dso->elf, data, ndx, &u.rel); + else + gelfx_getrela (dso->elf, data, ndx, &u.rela); + + if (u.rel.r_offset >= addr + size) + continue; + if (u.rel.r_offset + dso->arch->max_reloc_size <= addr) + continue; + + reloc_type = reloc_r_type (dso, u.rel.r_info); + reloc_size = dso->arch->reloc_size (reloc_type); + if (u.rel.r_offset + reloc_size <= addr) + continue; + + if (reloc_type == dso->arch->R_COPY) + return 3; + + off = u.rel.r_offset - addr; + + /* Check if whole relocation fits into the area. + Punt if not. */ + if (off < 0 || size - off < reloc_size) + return 2; + + if (dso->shdr[i].sh_type == SHT_REL) + dso->arch->apply_rel (info, &u.rel, buf + off); + else + dso->arch->apply_rela (info, &u.rela, buf + off); + } + } + } + } + + return 0; +} + +int +prelink_build_conflicts (struct prelink_info *info) +{ + int i, ndeps = info->ent->ndepends + 1; + struct prelink_entry *ent; + int ret = 0; + DSO *dso; + struct copy_relocs cr; + + info->dsos = alloca (sizeof (struct DSO *) * ndeps); + memset (info->dsos, 0, sizeof (struct DSO *) * ndeps); + memset (&cr, 0, sizeof (cr)); + info->dsos[0] = info->dso; + for (i = 1; i < ndeps; ++i) + { + ent = info->ent->depends[i - 1]; + if ((dso = open_dso (ent->filename)) == NULL) + goto error_out; + info->dsos[i] = dso; + /* Now check that the DSO matches what we recorded about it. */ + if (ent->timestamp != dso->info_DT_GNU_PRELINKED + || ent->checksum != dso->info_DT_CHECKSUM + || ent->base != dso->base) + { + error (0, 0, "%s: Library %s has changed since it has been prelinked", + info->dso->filename, ent->filename); + goto error_out; + } + } + + for (i = 0; i < ndeps; ++i) + { + int j, sec, first_conflict, maxidx; + struct prelink_conflict *conflict; + + dso = info->dsos[i]; + ent = i ? info->ent->depends[i - 1] : info->ent; + + /* Verify .gnu.liblist sections of all dependent libraries. */ + if (i && ent->ndepends > 0) + { + const char *name; + int nliblist; + Elf32_Lib *liblist; + Elf_Scn *scn; + Elf_Data *data; + + for (j = 1; j < dso->ehdr.e_shnum; ++j) + if (dso->shdr[j].sh_type == SHT_GNU_LIBLIST + && (name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[j].sh_name)) + && ! strcmp (name, ".gnu.liblist") + && (dso->shdr[j].sh_size % sizeof (Elf32_Lib)) == 0) + break; + + if (j == dso->ehdr.e_shnum) + { + error (0, 0, "%s: Library %s has dependencies, but doesn't contain .gnu.liblist section", + info->dso->filename, ent->filename); + goto error_out; + } + + nliblist = dso->shdr[j].sh_size / sizeof (Elf32_Lib); + scn = dso->scn[j]; + data = elf_getdata (scn, NULL); + if (data == NULL || elf_getdata (scn, data) + || data->d_buf == NULL || data->d_off + || data->d_size != dso->shdr[j].sh_size) + { + error (0, 0, "%s: Could not read .gnu.liblist section from %s", + info->dso->filename, ent->filename); + goto error_out; + } + + if (nliblist != ent->ndepends) + { + error (0, 0, "%s: Library %s has different number of libs in .gnu.liblist than expected", + info->dso->filename, ent->filename); + goto error_out; + } + liblist = (Elf32_Lib *) data->d_buf; + for (j = 0; j < nliblist; ++j) + if (liblist[j].l_time_stamp != ent->depends[j]->timestamp + || liblist[j].l_checksum != ent->depends[j]->checksum) + { + error (0, 0, "%s: .gnu.liblist in library %s is inconsistent with recorded dependencies", + info->dso->filename, ent->filename); + goto error_out; + } + + /* Extra check, maybe not needed. */ + for (j = 0; j < nliblist; ++j) + { + int k; + for (k = 0; k < info->ent->ndepends; ++k) + if (liblist[j].l_time_stamp == info->ent->depends[k]->timestamp + && liblist[j].l_checksum == info->ent->depends[k]->checksum) + break; + + if (k == info->ent->ndepends) + abort (); + } + } + + info->curconflicts = &info->conflicts[i]; + info->curtls = info->tls[i].modid ? info->tls + i : NULL; + first_conflict = info->conflict_rela_size; + sec = addr_to_sec (dso, dso->info[DT_SYMTAB]); + /* DT_SYMTAB should be found and should point to + start of .dynsym section. */ + if (sec == -1 || dso->info[DT_SYMTAB] != dso->shdr[sec].sh_addr) + { + error (0, 0, "Bad symtab"); + goto error_out; + } + info->symtab_start = dso->shdr[sec].sh_addr - dso->base; + info->symtab_end = info->symtab_start + dso->shdr[sec].sh_size; + for (j = 0; j < dso->ehdr.e_shnum; ++j) + { + if (! (dso->shdr[j].sh_flags & SHF_ALLOC)) + continue; + switch (dso->shdr[j].sh_type) + { + case SHT_REL: + if (i == 0 + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[j].sh_name), + ".gnu.conflict") == 0) + break; + if (prelink_conflict_rel (dso, j, info)) + goto error_out; + break; + case SHT_RELA: + if (i == 0 + && strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[j].sh_name), + ".gnu.conflict") == 0) + break; + if (prelink_conflict_rela (dso, j, info)) + goto error_out; + break; + } + } + + if (dso->arch->arch_prelink_conflict + && dso->arch->arch_prelink_conflict (dso, info)) + goto error_out; + + maxidx = 1; + if (info->curconflicts->hash != &info->curconflicts->first) + maxidx = 251; + for (j = 0; j < maxidx; j++) + for (conflict = info->curconflicts->hash[j]; conflict; + conflict = conflict->next) + if (! conflict->used && (i || conflict->ifunc)) + { + error (0, 0, "%s: Conflict %08llx (%s) not found in any relocation", + dso->filename, (unsigned long long) conflict->symoff, conflict->symname); + ret = 1; + } + + /* Record library's position in search scope into R_SYM field. */ + for (j = first_conflict; j < info->conflict_rela_size; ++j) + info->conflict_rela[j].r_info + = reloc_r_info (dso, i, + reloc_r_type (dso, info->conflict_rela[j].r_info)); + + if (dynamic_info_is_set (dso, DT_TEXTREL) + && info->conflict_rela_size > first_conflict) + { + /* We allow prelinking against non-PIC libraries, as long as + no conflict is against read-only segment. */ + int k; + + for (j = first_conflict; j < info->conflict_rela_size; ++j) + for (k = 0; k < dso->ehdr.e_phnum; ++k) + if (dso->phdr[k].p_type == PT_LOAD + && (dso->phdr[k].p_flags & PF_W) == 0 + && dso->phdr[k].p_vaddr + <= info->conflict_rela[j].r_offset + && dso->phdr[k].p_vaddr + dso->phdr[k].p_memsz + > info->conflict_rela[j].r_offset) + { + error (0, 0, "%s: Cannot prelink against non-PIC shared library %s", + info->dso->filename, dso->filename); + goto error_out; + } + } + } + + dso = info->dso; + for (i = 0; i < dso->ehdr.e_shnum; ++i) + { + if (! (dso->shdr[i].sh_flags & SHF_ALLOC)) + continue; + switch (dso->shdr[i].sh_type) + { + case SHT_REL: + if (prelink_find_copy_rel (dso, i, &cr)) + goto error_out; + break; + case SHT_RELA: + if (prelink_find_copy_rela (dso, i, &cr)) + goto error_out; + break; + } + } + + if (cr.count) + { + int bss1, bss2, firstbss2 = 0; + const char *name; + + qsort (cr.rela, cr.count, sizeof (GElf_Rela), rela_cmp); + bss1 = addr_to_sec (dso, cr.rela[0].r_offset); + bss2 = addr_to_sec (dso, cr.rela[cr.count - 1].r_offset); + if (bss1 != bss2) + { + for (i = 1; i < cr.count; ++i) + if (cr.rela[i].r_offset + >= dso->shdr[bss1].sh_addr + dso->shdr[bss1].sh_size) + break; + if (cr.rela[i].r_offset < dso->shdr[bss2].sh_addr) + { + error (0, 0, "%s: Copy relocs against 3 or more sections", + dso->filename); + goto error_out; + } + firstbss2 = i; + info->sdynbss_size = cr.rela[i - 1].r_offset - cr.rela[0].r_offset; + info->sdynbss_size += cr.rela[i - 1].r_addend; + info->sdynbss = calloc (info->sdynbss_size, 1); + info->sdynbss_base = cr.rela[0].r_offset; + if (info->sdynbss == NULL) + { + error (0, ENOMEM, "%s: Cannot build .sdynbss", dso->filename); + goto error_out; + } + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_LOAD + && dso->shdr[bss1].sh_addr >= dso->phdr[i].p_vaddr + && dso->shdr[bss1].sh_addr + < dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz) + break; + if (i == dso->ehdr.e_phnum + || dso->shdr[bss2].sh_addr + dso->shdr[bss2].sh_size + > dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz) + { + error (0, 0, "%s: Copy relocs against more than one segment", + dso->filename); + goto error_out; + } + } + + info->dynbss_size = cr.rela[cr.count - 1].r_offset + - cr.rela[firstbss2].r_offset; + info->dynbss_size += cr.rela[cr.count - 1].r_addend; + info->dynbss = calloc (info->dynbss_size, 1); + info->dynbss_base = cr.rela[firstbss2].r_offset; + if (info->dynbss == NULL) + { + error (0, ENOMEM, "%s: Cannot build .dynbss", dso->filename); + goto error_out; + } + + /* emacs apparently has .rel.bss relocations against .data section, + crap. */ + if (dso->shdr[bss1].sh_type != SHT_NOBITS + && strcmp (name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[bss1].sh_name), + ".dynbss") != 0 + && strcmp (name, ".sdynbss") != 0) + { + error (0, 0, "%s: COPY relocations don't point into .bss or .sbss section", + dso->filename); + goto error_out; + } + if (bss1 != bss2 + && dso->shdr[bss2].sh_type != SHT_NOBITS + && strcmp (name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[bss2].sh_name), + ".dynbss") != 0 + && strcmp (name, ".sdynbss") != 0) + { + error (0, 0, "%s: COPY relocations don't point into .bss or .sbss section", + dso->filename); + goto error_out; + } + + for (i = 0; i < cr.count; ++i) + { + struct prelink_symbol *s; + DSO *ndso = NULL; + int j, reloc_class; + + reloc_class + = dso->arch->reloc_class (reloc_r_type (dso, cr.rela[i].r_info)); + + assert (reloc_class != RTYPE_CLASS_TLS); + + for (s = & info->symbols[reloc_r_sym (dso, cr.rela[i].r_info)]; s; + s = s->next) + if (s->reloc_class == reloc_class) + break; + + if (s == NULL || s->u.ent == NULL) + { + error (0, 0, "%s: Could not find symbol copy reloc is against", + dso->filename); + goto error_out; + } + + for (j = 1; j < ndeps; ++j) + if (info->ent->depends[j - 1] == s->u.ent) + { + ndso = info->dsos[j]; + break; + } + + assert (j < ndeps); + if (i < firstbss2) + j = get_relocated_mem (info, ndso, s->u.ent->base + s->value, + info->sdynbss + cr.rela[i].r_offset + - info->sdynbss_base, cr.rela[i].r_addend, + cr.rela[i].r_offset); + else + j = get_relocated_mem (info, ndso, s->u.ent->base + s->value, + info->dynbss + cr.rela[i].r_offset + - info->dynbss_base, cr.rela[i].r_addend, + cr.rela[i].r_offset); + + switch (j) + { + case 1: + error (0, 0, "%s: Could not find variable copy reloc is against", + dso->filename); + goto error_out; + case 2: + error (0, 0, "%s: Conflict partly overlaps with %08llx-%08llx area", + dso->filename, + (long long) cr.rela[i].r_offset, + (long long) (cr.rela[i].r_offset + cr.rela[i].r_addend)); + goto error_out; + } + } + } + + if (info->conflict_rela_size) + { + conflict_rela_cmp_dso = dso; + qsort (info->conflict_rela, info->conflict_rela_size, sizeof (GElf_Rela), + conflict_rela_cmp); + + /* Now make sure all conflict RELA's are against absolute 0 symbol. */ + for (i = 0; i < info->conflict_rela_size; ++i) + info->conflict_rela[i].r_info + = reloc_r_info (dso, 0, + reloc_r_type (dso, info->conflict_rela[i].r_info)); + + if (enable_cxx_optimizations && remove_redundant_cxx_conflicts (info)) + goto error_out; + } + + for (i = 1; i < ndeps; ++i) + if (info->dsos[i]) + close_dso (info->dsos[i]); + + info->dsos = NULL; + free (cr.rela); + return ret; + +error_out: + free (cr.rela); + free (info->dynbss); + free (info->sdynbss); + info->dynbss = NULL; + info->sdynbss = NULL; + for (i = 1; i < ndeps; ++i) + if (info->dsos[i]) + close_dso (info->dsos[i]); + return 1; +} diff --git a/src/crc32.c b/src/crc32.c new file mode 100644 index 0000000..f36abb5 --- /dev/null +++ b/src/crc32.c @@ -0,0 +1,87 @@ +/* Copyright (C) 2001 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <stdint.h> +#include <sys/types.h> + +/* Table computed with Mark Adler's makecrc.c utility. */ +static const uint32_t crc32_table[256] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d +}; + +uint32_t crc32 (uint32_t crc, unsigned char *buf, size_t len) +{ + unsigned char *end; + + crc = ~crc; + for (end = buf + len; buf < end; ++buf) + crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); + return ~crc; +} diff --git a/src/cxx.c b/src/cxx.c new file mode 100644 index 0000000..4391ebe --- /dev/null +++ b/src/cxx.c @@ -0,0 +1,643 @@ +/* Copyright (C) 2001, 2002, 2003, 2007, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <alloca.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include "prelink.h" +#include "reloc-info.h" + +static struct + { + const char *prefix; + unsigned char prefix_len, st_info, check_pltref; + } +specials[] = + { + /* G++ 3.0 ABI. */ + /* Virtual table. */ + { "_ZTV", 4, GELF_ST_INFO (STB_WEAK, STT_OBJECT), 1 }, + /* Typeinfo. */ + { "_ZTI", 4, GELF_ST_INFO (STB_WEAK, STT_OBJECT), 0 }, + /* G++ 2.96-RH ABI. */ + /* Virtual table. */ + { "__vt_", 5, GELF_ST_INFO (STB_WEAK, STT_OBJECT), 0 }, + { NULL, 0, 0, 0 } + }; + +struct find_cxx_sym_valsize +{ + GElf_Addr start; + GElf_Addr end; + unsigned int idx; + unsigned char mark; +}; + +struct find_cxx_sym_cache +{ + Elf_Data *symtab, *strtab; + int symsec, strsec, count; + struct find_cxx_sym_valsize vals[]; +}; + +struct find_cxx_sym +{ + DSO *dso; + int n; + struct find_cxx_sym_cache *cache; + struct prelink_entry *ent; + Elf_Data *symtab, *strtab; + int symsec, strsec; + int lastndx; + GElf_Sym sym; +}; + +static int +cachecmp (const void *a, const void *b) +{ + GElf_Addr va = ((const struct find_cxx_sym_valsize *) a)->start; + GElf_Addr vb = ((const struct find_cxx_sym_valsize *) b)->start; + + if (va < vb) + return -1; + if (va > vb) + return 1; + + va = ((const struct find_cxx_sym_valsize *) a)->end; + vb = ((const struct find_cxx_sym_valsize *) b)->end; + + if (va < vb) + return -1; + + return va > vb; +} + +static struct find_cxx_sym_cache * +create_cache (DSO *dso, int plt) +{ + Elf_Data *symtab, *strtab; + Elf_Scn *scn; + int symsec, strsec, ndx, dndx, maxndx; + struct find_cxx_sym_cache *cache; + GElf_Addr top; + + symsec = addr_to_sec (dso, dso->info[DT_SYMTAB]); + if (symsec == -1) + return (struct find_cxx_sym_cache *) -1UL; + scn = dso->scn[symsec]; + symtab = elf_getdata (scn, NULL); + assert (elf_getdata (scn, symtab) == NULL); + strsec = addr_to_sec (dso, dso->info[DT_STRTAB]); + if (strsec == -1) + return (struct find_cxx_sym_cache *) -1UL; + scn = dso->scn[strsec]; + strtab = elf_getdata (scn, NULL); + assert (elf_getdata (scn, strtab) == NULL); + maxndx = symtab->d_size / dso->shdr[symsec].sh_entsize; + + cache = malloc (sizeof (*cache) + sizeof (cache->vals[0]) * maxndx); + if (cache == NULL) + { + error (0, ENOMEM, "%s: Could load symbol table", dso->filename); + return NULL; + } + + cache->symsec = symsec; + cache->strsec = strsec; + cache->symtab = symtab; + cache->strtab = strtab; + for (ndx = 0, dndx = 0; ndx < maxndx; ++ndx) + { + GElf_Sym sym; + const char *name; + int k; + + gelfx_getsym (dso->elf, symtab, ndx, &sym); + if (plt) + { + if (sym.st_shndx != SHN_UNDEF || sym.st_value == 0) + continue; + } + else if (sym.st_shndx == SHN_UNDEF) + continue; + cache->vals[dndx].start = sym.st_value; + cache->vals[dndx].end = sym.st_value + sym.st_size; + cache->vals[dndx].idx = ndx; + cache->vals[dndx].mark = 0; + name = (const char *) strtab->d_buf + sym.st_name; + if (!plt && ELF32_ST_VISIBILITY (sym.st_other) == STV_DEFAULT) + for (k = 0; specials[k].prefix; ++k) + if (sym.st_info == specials[k].st_info + && strncmp (name, specials[k].prefix, + specials[k].prefix_len) == 0) + { + cache->vals[dndx].mark = 1; + break; + } + ++dndx; + } + + maxndx = dndx; + qsort (cache->vals, maxndx, sizeof (cache->vals[0]), cachecmp); + + if (!plt) + { + for (top = 0, ndx = 0; ndx < maxndx; ++ndx) + { + if (cache->vals[ndx].start < top + || (ndx < maxndx - 1 + && cache->vals[ndx].end > cache->vals[ndx + 1].start)) + cache->vals[ndx].mark = 0; + if (cache->vals[ndx].end > top) + top = cache->vals[ndx].end; + } + + for (ndx = dndx = 0; ndx < maxndx; ++ndx) + if (cache->vals[ndx].mark) + cache->vals[dndx++] = cache->vals[ndx]; + } + cache->count = dndx; + return cache; +} + +static int +find_cxx_sym (struct prelink_info *info, GElf_Addr addr, + struct find_cxx_sym *fcs, int reloc_size, + struct find_cxx_sym_cache **cache) +{ + int n, ndeps = info->ent->ndepends + 1; + unsigned int hi, lo, mid; + DSO *dso = NULL; + struct find_cxx_sym_cache *c; + + if (fcs->dso == NULL + || addr < fcs->dso->base + || addr >= fcs->dso->end) + { + for (n = 1; n < ndeps; ++n) + { + dso = info->dsos[n]; + if (addr >= dso->base + && addr < dso->end) + break; + } + + if (n == ndeps + && addr >= info->dso->base + && addr < info->dso->end) + { + n = 0; + dso = info->dso; + } + + assert (n < ndeps); + + if (cache[n] == NULL) + { + cache[n] = create_cache (dso, 0); + if (cache[n] == NULL) + return -2; + } + if (cache[n] == (struct find_cxx_sym_cache *) -1UL) + return -1; + + fcs->n = n; + fcs->ent = n ? info->ent->depends[n - 1] : info->ent; + fcs->dso = dso; + fcs->cache = cache[n]; + fcs->symsec = fcs->cache->symsec; + fcs->symtab = fcs->cache->symtab; + fcs->strsec = fcs->cache->strsec; + fcs->strtab = fcs->cache->strtab; + fcs->lastndx = -1; + } + else + dso = fcs->dso; + + c = fcs->cache; + lo = 0; + hi = c->count; + if (fcs->lastndx != -1) + { + if (c->vals[fcs->lastndx].start <= addr) + { + lo = fcs->lastndx; + if (hi - lo >= 16) + { + if (c->vals[lo + 2].start > addr) + hi = lo + 2; + else if (c->vals[lo + 15].start > addr) + hi = lo + 15; + } + } + else + { + hi = fcs->lastndx; + if (hi >= 15) + { + if (c->vals[hi - 2].start <= addr) + lo = hi - 2; + else if (c->vals[hi - 15].start <= addr) + lo = hi - 15; + } + } + } + while (lo < hi) + { + mid = (lo + hi) / 2; + if (c->vals[mid].start <= addr) + { + if (c->vals[mid].end >= addr + reloc_size) + { + gelfx_getsym (dso->elf, fcs->symtab, c->vals[mid].idx, + &fcs->sym); + fcs->lastndx = mid; + return c->vals[mid].idx; + } + lo = mid + 1; + } + else + hi = mid; + } + + return -1; +} + +/* The idea here is that C++ virtual tables are always emitted + in .gnu.linkonce.d.* sections as WEAK symbols and they + need to be the same. + We check if they are and if yes, remove conflicts against + virtual tables which will not be used. */ + +int +remove_redundant_cxx_conflicts (struct prelink_info *info) +{ + int i, j, k, n, o, state, removed = 0; + int ndx, sec; + unsigned int hi, lo, mid; + int reloc_type, reloc_size; + struct find_cxx_sym fcs1, fcs2; + char *mem1, *mem2; + const char *name = NULL, *secname = NULL; + GElf_Addr symtab_start; + GElf_Word symoff; + Elf_Data *binsymtab = NULL; + int binsymtabsec; + struct prelink_conflict *conflict; + struct find_cxx_sym_cache **cache; + struct find_cxx_sym_cache *binsymcache = NULL; + int ret = 0; + int rtype_class_valid; + + /* Don't bother doing this for non-C++ programs. */ + for (i = 0; i < info->ent->ndepends; ++i) + if (strstr (info->ent->depends[i]->canon_filename, "libstdc++")) + break; + if (i == info->ent->ndepends) + return 0; + + binsymtabsec = addr_to_sec (info->dso, info->dso->info[DT_SYMTAB]); + if (binsymtabsec != -1) + { + Elf_Scn *scn = info->dso->scn[binsymtabsec]; + + binsymtab = elf_getdata (scn, NULL); + assert (elf_getdata (scn, binsymtab) == NULL); + } + + rtype_class_valid = info->dso->arch->rtype_class_valid; + + state = 0; + memset (&fcs1, 0, sizeof (fcs1)); + memset (&fcs2, 0, sizeof (fcs2)); + cache = alloca (sizeof (struct find_cxx_sym_cache *) + * (info->ent->ndepends + 1)); + memset (cache, '\0', sizeof (struct find_cxx_sym_cache *) + * (info->ent->ndepends + 1)); + for (i = 0; i < info->conflict_rela_size; ++i) + { + size_t cidx; + + reloc_type = reloc_r_type (info->dso, info->conflict_rela[i].r_info); + reloc_size = info->dso->arch->reloc_size (reloc_type); + + if (reloc_r_sym (info->dso, info->conflict_rela[i].r_info) != 0) + continue; + + if (state + && fcs1.sym.st_value <= info->conflict_rela[i].r_offset + && fcs1.sym.st_value + fcs1.sym.st_size + >= info->conflict_rela[i].r_offset + reloc_size) + { + if (state == 3) + goto remove_noref; + if (state == 2) + goto check_pltref; + continue; + } + + n = find_cxx_sym (info, info->conflict_rela[i].r_offset, + &fcs1, reloc_size, cache); + + state = 0; + if (n == -1) + continue; + if (n == -2) + { + ret = 1; + goto out_free_cache; + } + state = 1; + sec = addr_to_sec (fcs1.dso, fcs1.sym.st_value); + if (sec == -1) + continue; + secname = strptr (fcs1.dso, fcs1.dso->ehdr.e_shstrndx, + fcs1.dso->shdr[sec].sh_name); + if (secname == NULL) + continue; + + name = (const char *) fcs1.strtab->d_buf + fcs1.sym.st_name; + + for (k = 0; specials[k].prefix; ++k) + if (ELF32_ST_VISIBILITY (fcs1.sym.st_other) == STV_DEFAULT + && fcs1.sym.st_info == specials[k].st_info + && strncmp (name, specials[k].prefix, specials[k].prefix_len) == 0) + break; + + if (specials[k].prefix == NULL) + continue; + + if (strcmp (secname, ".data") != 0 + && strcmp (secname, ".data.rel.ro") != 0 + && strcmp (secname, ".sdata") != 0) + continue; + + if (specials[k].check_pltref) + state = 2; + + symtab_start = fcs1.dso->shdr[fcs1.symsec].sh_addr - fcs1.dso->base; + symoff = symtab_start + n * fcs1.dso->shdr[fcs1.symsec].sh_entsize; + + cidx = 0; + if (info->conflicts[fcs1.n].hash != &info->conflicts[fcs1.n].first) + cidx = symoff % 251; + for (conflict = info->conflicts[fcs1.n].hash[cidx]; conflict; + conflict = conflict->next) + if (conflict->symoff == symoff + && conflict->reloc_class == rtype_class_valid) + break; + + if (conflict == NULL) + goto check_pltref; + + if (conflict->conflict.ent != fcs1.ent + || fcs1.dso->base + conflict->conflictval != fcs1.sym.st_value) + goto check_pltref; + + if (verbose > 4) + error (0, 0, "Possible C++ conflict removal from unreferenced table at %s:%s+%d", + fcs1.dso->filename, name, + (int) (info->conflict_rela[i].r_offset - fcs1.sym.st_value)); + + /* Limit size slightly. */ + if (fcs1.sym.st_size > 16384) + goto check_pltref; + + o = find_cxx_sym (info, conflict->lookup.ent->base + conflict->lookupval, + &fcs2, fcs1.sym.st_size, cache); + + if (o == -2) + { + ret = 1; + goto out_free_cache; + } + + if (o == -1 + || fcs1.sym.st_size != fcs2.sym.st_size + || fcs1.sym.st_info != fcs2.sym.st_info + || ELF32_ST_VISIBILITY (fcs2.sym.st_other) != STV_DEFAULT + || strcmp (name, (char *) fcs2.strtab->d_buf + fcs2.sym.st_name) != 0) + goto check_pltref; + + mem1 = malloc (fcs1.sym.st_size * 2); + if (mem1 == NULL) + { + error (0, ENOMEM, "%s: Could not compare %s arrays", + info->dso->filename, name); + ret = 1; + goto out_free_cache; + } + + mem2 = mem1 + fcs1.sym.st_size; + + if (get_relocated_mem (info, fcs1.dso, fcs1.sym.st_value, mem1, + fcs1.sym.st_size, 0) + || get_relocated_mem (info, fcs2.dso, fcs2.sym.st_value, mem2, + fcs1.sym.st_size, 0) + || memcmp (mem1, mem2, fcs1.sym.st_size) != 0) + { + free (mem1); + goto check_pltref; + } + + free (mem1); + + state = 3; + +remove_noref: + if (verbose > 3) + error (0, 0, "Removing C++ conflict from unreferenced table at %s:%s+%d", + fcs1.dso->filename, name, + (int) (info->conflict_rela[i].r_offset - fcs1.sym.st_value)); + + info->conflict_rela[i].r_info = + reloc_r_info (info->dso, 1, + reloc_r_type (info->dso, info->conflict_rela[i].r_info)); + ++removed; + continue; + +check_pltref: + /* If the binary calls directly (or takes its address) one of the + methods in a virtual table, but doesn't define it, there is no + need to leave conflicts in the virtual table which will only + slow down the code (as it has to hop through binary's .plt + back to the method). */ + if (state != 2 + || info->conflict_rela[i].r_addend < info->dso->base + || info->conflict_rela[i].r_addend >= info->dso->end + || binsymtab == NULL) + continue; + + if (binsymcache == NULL) + { + binsymcache = create_cache (info->dso, 1); + if (binsymcache == NULL) + { + ret = 1; + goto out_free_cache; + } + } + if (binsymcache == (struct find_cxx_sym_cache *) -1UL) + continue; + + lo = 0; + mid = 0; + hi = binsymcache->count; + while (lo < hi) + { + mid = (lo + hi) / 2; + if (binsymcache->vals[mid].start < info->conflict_rela[i].r_addend) + lo = mid + 1; + else if (binsymcache->vals[mid].start + > info->conflict_rela[i].r_addend) + hi = mid; + else + break; + } + if (lo >= hi) + continue; + + while (mid > 0 && binsymcache->vals[mid - 1].start + == info->conflict_rela[i].r_addend) + --mid; + + while (mid < binsymcache->count + && binsymcache->vals[mid].start + == info->conflict_rela[i].r_addend) + { + GElf_Sym sym; + + ndx = binsymcache->vals[mid].idx; + mid++; + gelfx_getsym (info->dso->elf, binsymtab, ndx, &sym); + assert (sym.st_value == info->conflict_rela[i].r_addend); + if (sym.st_shndx == SHN_UNDEF && sym.st_value) + { + struct prelink_symbol *s; + size_t maxidx, l; + + if (verbose > 4) + error (0, 0, "Possible C++ conflict removal due to reference to binary's .plt at %s:%s+%d", + fcs1.dso->filename, name, + (int) (info->conflict_rela[i].r_offset + - fcs1.sym.st_value)); + + for (s = &info->symbols[ndx]; s; s = s->next) + if (s->reloc_class == RTYPE_CLASS_PLT) + break; + + if (s == NULL) + break; + + maxidx = 1; + if (info->conflicts[fcs1.n].hash + != &info->conflicts[fcs1.n].first) + { + if (info->conflicts[fcs1.n].hash2 == NULL) + { + info->conflicts[fcs1.n].hash2 + = calloc (sizeof (struct prelink_conflict *), 251); + if (info->conflicts[fcs1.n].hash2 != NULL) + { + for (l = 0; l < 251; l++) + for (conflict = info->conflicts[fcs1.n].hash[l]; + conflict; conflict = conflict->next) + if (conflict->reloc_class == rtype_class_valid + && conflict->conflict.ent) + { + size_t ccidx + = (conflict->lookup.ent->base + + conflict->lookupval) % 251; + conflict->next2 + = info->conflicts[fcs1.n].hash2[ccidx]; + info->conflicts[fcs1.n].hash2[ccidx] + = conflict; + } + } + } + if (info->conflicts[fcs1.n].hash2 != NULL) + { + size_t ccidx = info->conflict_rela[i].r_addend % 251; + for (conflict = info->conflicts[fcs1.n].hash2[ccidx]; + conflict; conflict = conflict->next2) + if (conflict->lookup.ent->base + conflict->lookupval + == info->conflict_rela[i].r_addend + && (conflict->conflict.ent->base + + conflict->conflictval + == s->u.ent->base + s->value)) + goto pltref_remove; + break; + } + maxidx = 251; + } + + for (l = 0; l < maxidx; l++) + for (conflict = info->conflicts[fcs1.n].hash[l]; + conflict; conflict = conflict->next) + if (conflict->lookup.ent->base + conflict->lookupval + == info->conflict_rela[i].r_addend + && conflict->conflict.ent + && (conflict->conflict.ent->base + + conflict->conflictval == s->u.ent->base + s->value) + && conflict->reloc_class == rtype_class_valid) + { +pltref_remove: + if (verbose > 3) + error (0, 0, "Removing C++ conflict due to reference to binary's .plt at %s:%s+%d", + fcs1.dso->filename, name, + (int) (info->conflict_rela[i].r_offset + - fcs1.sym.st_value)); + + info->conflict_rela[i].r_info = + reloc_r_info (info->dso, 1, reloc_r_type (info->dso, info->conflict_rela[i].r_info)); + ++removed; + goto pltref_check_done; + } + +pltref_check_done: + break; + } + } + } + + if (removed) + { + for (i = 0, j = 0; i < info->conflict_rela_size; ++i) + if (reloc_r_sym (info->dso, info->conflict_rela[i].r_info) == 0) + { + if (i != j) + info->conflict_rela[j] = info->conflict_rela[i]; + ++j; + } + info->conflict_rela_size = j; + } + +out_free_cache: + for (i = 0; i < info->ent->ndepends + 1; i++) + if (cache[i] && cache[i] != (struct find_cxx_sym_cache *) -1UL) + free (cache[i]); + if (binsymcache && binsymcache != (struct find_cxx_sym_cache *) -1UL) + free (binsymcache); + return ret; +} diff --git a/src/data.c b/src/data.c new file mode 100644 index 0000000..751f96f --- /dev/null +++ b/src/data.c @@ -0,0 +1,339 @@ +/* Copyright (C) 2001, 2002 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include "prelink.h" + +#define UREAD(le,nn) \ +uint##nn##_t \ +read_u##le##nn (DSO *dso, GElf_Addr addr) \ +{ \ + Elf_Type type; \ + unsigned char *data = get_data (dso, addr, NULL, &type); \ + \ + if (data == NULL) \ + return 0; \ + \ + if (type == ELF_T_BYTE) \ + return buf_read_u##le##nn (data); \ + else \ + return *(uint##nn##_t *)data; \ +} + +#define WRITE(le,nn) \ +int \ +write_##le##nn (DSO *dso, GElf_Addr addr, uint##nn##_t val) \ +{ \ + int sec; \ + Elf_Type type; \ + unsigned char *data = get_data (dso, addr, &sec, &type); \ + \ + if (data == NULL) \ + return -1; \ + \ + if (type == ELF_T_BYTE) \ + buf_write_##le##nn (data, val); \ + else \ + *(uint##nn##_t *)data = val; \ + elf_flagscn (dso->scn[sec], ELF_C_SET, ELF_F_DIRTY); \ + return 0; \ +} + +#define BUFREADUNE(nn) \ +uint##nn##_t \ +buf_read_une##nn (DSO *dso, unsigned char *buf) \ +{ \ + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) \ + return buf_read_ule##nn (buf); \ + else \ + return buf_read_ube##nn (buf); \ +} + +#define READUNE(nn) \ +uint##nn##_t \ +read_une##nn (DSO *dso, GElf_Addr addr) \ +{ \ + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) \ + return read_ule##nn (dso, addr); \ + else \ + return read_ube##nn (dso, addr); \ +} + +#define WRITENE(nn) \ +void \ +write_ne##nn (DSO *dso, GElf_Addr addr, uint##nn##_t val) \ +{ \ + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) \ + write_le##nn (dso, addr, val); \ + else \ + write_be##nn (dso, addr, val); \ +} + +#define BUFWRITENE(nn) \ +void \ +buf_write_ne##nn (DSO *dso, unsigned char *buf, \ + uint##nn##_t val) \ +{ \ + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) \ + buf_write_le##nn (buf, val); \ + else \ + buf_write_be##nn (buf, val); \ +} + +#define READWRITE(le,nn) UREAD(le,nn) WRITE(le,nn) +#define READWRITESIZE(nn) \ + READWRITE(le,nn) READWRITE(be,nn) \ + BUFREADUNE(nn) READUNE(nn) \ + WRITENE(nn) BUFWRITENE(nn) + +unsigned char * +get_data (DSO *dso, GElf_Addr addr, int *secp, Elf_Type *typep) +{ + int sec = addr_to_sec (dso, addr); + Elf_Data *data = NULL; + + if (sec == -1) + return NULL; + + if (secp) + *secp = sec; + + addr -= dso->shdr[sec].sh_addr; + while ((data = elf_getdata (dso->scn[sec], data)) != NULL) + if (data->d_off <= addr && data->d_off + data->d_size > addr) + { + if (typep) *typep = data->d_type; + return (unsigned char *) data->d_buf + (addr - data->d_off); + } + return NULL; +} + +/* Initialize IT so that the first byte it provides is address ADDR + of DSO. */ + +void +init_data_iterator (struct data_iterator *it, DSO *dso, GElf_Addr addr) +{ + it->dso = dso; + it->data = NULL; + it->addr = addr; +} + +/* Return a pointer to the next SIZE bytes pointed to by IT, and move + IT to the end of the returned block. Return null if the data could + not be read for some reason. */ + +unsigned char * +get_data_from_iterator (struct data_iterator *it, GElf_Addr size) +{ + unsigned char *ptr; + + /* If we're at the end of a data block, move onto the next. */ + if (it->data && it->data->d_off + it->data->d_size == it->sec_offset) + it->data = elf_getdata (it->dso->scn[it->sec], it->data); + + if (it->data == NULL) + { + /* Find out which section contains the next byte. */ + it->sec = addr_to_sec (it->dso, it->addr); + if (it->sec < 0) + return NULL; + + /* Fast-forward to the block that contains ADDR, if any. */ + it->sec_offset = it->addr - it->dso->shdr[it->sec].sh_addr; + do + it->data = elf_getdata (it->dso->scn[it->sec], it->data); + while (it->data && it->data->d_off + it->data->d_size <= it->sec_offset); + } + + /* Make sure that all the data we want is included in this block. */ + if (it->data == NULL + || it->data->d_off > it->sec_offset + || it->data->d_off + it->data->d_size < it->sec_offset + size) + return NULL; + + ptr = (unsigned char *) it->data->d_buf + (it->sec_offset - it->data->d_off); + it->sec_offset += size; + it->addr += size; + return ptr; +} + +/* Read the symbol pointed to by IT into SYM and move IT onto the + next symbol. Return true on success. */ + +int +get_sym_from_iterator (struct data_iterator *it, GElf_Sym *sym) +{ + GElf_Addr offset, size; + unsigned char *ptr; + + size = gelf_fsize (it->dso->elf, ELF_T_SYM, 1, EV_CURRENT); + ptr = get_data_from_iterator (it, size); + if (ptr != NULL) + { + offset = ptr - (unsigned char *) it->data->d_buf; + if (offset % size == 0) + { + gelfx_getsym (it->dso->elf, it->data, offset / size, sym); + return 1; + } + } + return 0; +} + +inline uint8_t +buf_read_u8 (unsigned char *data) +{ + return *data; +} + +inline uint16_t +buf_read_ule16 (unsigned char *data) +{ + return data[0] | (data[1] << 8); +} + +inline uint16_t +buf_read_ube16 (unsigned char *data) +{ + return data[1] | (data[0] << 8); +} + +inline uint32_t +buf_read_ule32 (unsigned char *data) +{ + return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +} + +inline uint32_t +buf_read_ube32 (unsigned char *data) +{ + return data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24); +} + +inline uint64_t +buf_read_ule64 (unsigned char *data) +{ + return (data[0] | (data[1] << 8) | (data[2] << 16)) + | (((uint64_t)data[3]) << 24) + | (((uint64_t)data[4]) << 32) + | (((uint64_t)data[5]) << 40) + | (((uint64_t)data[6]) << 48) + | (((uint64_t)data[7]) << 56); +} + +inline uint64_t +buf_read_ube64 (unsigned char *data) +{ + return (data[7] | (data[6] << 8) | (data[5] << 16)) + | (((uint64_t)data[4]) << 24) + | (((uint64_t)data[3]) << 32) + | (((uint64_t)data[2]) << 40) + | (((uint64_t)data[1]) << 48) + | (((uint64_t)data[0]) << 56); +} + +inline void +buf_write_8 (unsigned char *data, uint8_t val) +{ + *data = val; +} + +inline void +buf_write_le16 (unsigned char *data, uint16_t val) +{ + data[0] = val; + data[1] = val >> 8; +} + +inline void +buf_write_be16 (unsigned char *data, uint16_t val) +{ + data[1] = val; + data[0] = val >> 8; +} + +inline void +buf_write_le32 (unsigned char *data, uint32_t val) +{ + data[0] = val; + data[1] = val >> 8; + data[2] = val >> 16; + data[3] = val >> 24; +} + +inline void +buf_write_be32 (unsigned char *data, uint32_t val) +{ + data[3] = val; + data[2] = val >> 8; + data[1] = val >> 16; + data[0] = val >> 24; +} + +inline void +buf_write_le64 (unsigned char *data, uint64_t val) +{ + data[0] = val; + data[1] = val >> 8; + data[2] = val >> 16; + data[3] = val >> 24; + data[4] = val >> 32; + data[5] = val >> 40; + data[6] = val >> 48; + data[7] = val >> 56; +} + +inline void +buf_write_be64 (unsigned char *data, uint64_t val) +{ + data[7] = val; + data[6] = val >> 8; + data[5] = val >> 16; + data[4] = val >> 24; + data[3] = val >> 32; + data[2] = val >> 40; + data[1] = val >> 48; + data[0] = val >> 56; +} + +READWRITE(,8) +READWRITESIZE(16) +READWRITESIZE(32) +READWRITESIZE(64) + +const char * +strptr (DSO *dso, int sec, off_t offset) +{ + Elf_Scn *scn; + Elf_Data *data; + + scn = dso->scn[sec]; + if (offset >= 0 && offset < dso->shdr[sec].sh_size) + { + data = NULL; + while ((data = elf_getdata (scn, data)) != NULL) + { + if (data->d_buf + && offset >= data->d_off + && offset < data->d_off + data->d_size) + return (const char *) data->d_buf + (offset - data->d_off); + } + } + + return NULL; +} diff --git a/src/doit.c b/src/doit.c new file mode 100644 index 0000000..6b07afb --- /dev/null +++ b/src/doit.c @@ -0,0 +1,256 @@ +/* Copyright (C) 2001, 2003, 2004, 2005, 2007 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <alloca.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include "prelinktab.h" + +struct collect_ents + { + struct prelink_entry **ents; + int nents; + }; + +static int +find_ents (void **p, void *info) +{ + struct collect_ents *l = (struct collect_ents *) info; + struct prelink_entry *e = * (struct prelink_entry **) p; + + e->u.tmp = 0; + if ((e->type == ET_DYN && e->done == 1) + || (e->type == ET_EXEC && e->done == 0 && ! libs_only)) + l->ents[l->nents++] = e; + + return 1; +} + +static void +prelink_ent (struct prelink_entry *ent) +{ + int i, j; + DSO *dso; + struct stat64 st; + struct prelink_link *hardlink; + char *move = NULL, *move_temp; + size_t movelen = 0; + + for (i = 0; i < ent->ndepends; ++i) + if (ent->depends[i]->done == 1) + prelink_ent (ent->depends[i]); + + for (i = 0; i < ent->ndepends; ++i) + if (ent->depends[i]->done != 2) + { + ent->done = 0; + if (! undo) + ent->type = ET_UNPRELINKABLE; + if (verbose) + error (0, 0, "Could not prelink %s because its dependency %s could not be prelinked", + ent->filename, ent->depends[i]->filename); + return; + } + + ent->u.tmp = 1; + for (i = 0; i < ent->ndepends; ++i) + ent->depends[i]->u.tmp = 1; + for (i = 0; i < ent->ndepends; ++i) + { + struct prelink_entry *dent = ent->depends[i]; + for (j = 0; j < dent->ndepends; ++j) + if (dent->depends[j]->u.tmp == 0) + { + ent->done = 0; + if (! undo) + ent->type = ET_UNPRELINKABLE; + if (verbose) + error (0, 0, "Could not prelink %s because it doesn't use %s, but one of its dependencies has been prelinked against it", + ent->filename, dent->depends[j]->filename); + ent->u.tmp = 0; + for (i = 0; i < ent->ndepends; ++i) + ent->depends[i]->u.tmp = 0; + return; + } + } + ent->u.tmp = 0; + for (i = 0; i < ent->ndepends; ++i) + ent->depends[i]->u.tmp = 0; + + if (verbose) + { + if (dry_run) + printf ("Would prelink %s\n", ent->canon_filename); + else + printf ("Prelinking %s\n", ent->canon_filename); + } + + dso = open_dso (ent->canon_filename); + if (dso == NULL) + goto error_out; + + if (fstat64 (dso->fd, &st) < 0) + { + error (0, errno, "%s changed during prelinking", ent->filename); + goto error_out; + } + + if (st.st_dev != ent->dev || st.st_ino != ent->ino) + { + error (0, 0, "%s changed during prelinking", ent->filename); + goto error_out; + } + + if (dry_run) + close_dso (dso); + else + { + if (prelink_prepare (dso)) + goto make_unprelinkable; + if (ent->type == ET_DYN && relocate_dso (dso, ent->base)) + goto make_unprelinkable; + if (prelink (dso, ent)) + goto make_unprelinkable; + if (update_dso (dso, NULL)) + { + dso = NULL; + goto error_out; + } + } + ent->done = 2; + ent->flags |= PCF_PRELINKED; + + /* Redo hardlinks. */ + for (hardlink = ent->hardlink; hardlink; hardlink = hardlink->next) + { + size_t len; + + if (wrap_lstat64 (hardlink->canon_filename, &st) < 0) + { + error (0, 0, "Could not stat %s (former hardlink to %s)", + hardlink->canon_filename, ent->canon_filename); + continue; + } + + if (st.st_dev != ent->dev || st.st_ino != ent->ino) + { + error (0, 0, "%s is no longer hardlink to %s", + hardlink->canon_filename, ent->canon_filename); + continue; + } + + if (verbose) + { + if (dry_run) + printf ("Would link %s to %s\n", hardlink->canon_filename, + ent->canon_filename); + else + printf ("Linking %s to %s\n", hardlink->canon_filename, + ent->canon_filename); + } + + if (dry_run) + continue; + + len = strlen (hardlink->canon_filename); + if (len + sizeof (".#prelink#") > movelen) + { + movelen = len + sizeof (".#prelink#"); + move_temp = move; + move = realloc (move, movelen); + if (move == NULL) + { + free(move_temp); + error (0, ENOMEM, "Could not hardlink %s to %s", + hardlink->canon_filename, ent->canon_filename); + movelen = 0; + continue; + } + } + + memcpy (mempcpy (move, hardlink->canon_filename, len), ".#prelink#", + sizeof (".#prelink#")); + if (wrap_rename (hardlink->canon_filename, move) < 0) + { + error (0, errno, "Could not hardlink %s to %s", + hardlink->canon_filename, ent->canon_filename); + continue; + } + + if (wrap_link (ent->canon_filename, hardlink->canon_filename) < 0) + { + error (0, errno, "Could not hardlink %s to %s", + hardlink->canon_filename, ent->canon_filename); + + if (wrap_rename (move, hardlink->canon_filename) < 0) + { + error (0, errno, "Could not rename %s back to %s", + move, hardlink->canon_filename); + } + continue; + } + + if (wrap_unlink (move) < 0) + { + error (0, errno, "Could not unlink %s", move); + continue; + } + } + free (move); + + if (! dry_run && wrap_stat64 (ent->canon_filename, &st) >= 0) + { + ent->dev = st.st_dev; + ent->ino = st.st_ino; + ent->ctime = st.st_ctime; + ent->mtime = st.st_mtime; + } + return; + +make_unprelinkable: + if (! undo) + ent->type = ET_UNPRELINKABLE; +error_out: + ent->done = 0; + if (dso) + close_dso (dso); + return; +} + +void +prelink_all (void) +{ + struct collect_ents l; + int i; + + l.ents = + (struct prelink_entry **) alloca (prelink_entry_count + * sizeof (struct prelink_entry *)); + l.nents = 0; + htab_traverse (prelink_filename_htab, find_ents, &l); + + for (i = 0; i < l.nents; ++i) + if (l.ents[i]->done == 1 + || (l.ents[i]->done == 0 && l.ents[i]->type == ET_EXEC)) + prelink_ent (l.ents[i]); +} diff --git a/src/dso.c b/src/dso.c new file mode 100644 index 0000000..983f1d8 --- /dev/null +++ b/src/dso.c @@ -0,0 +1,2013 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2010 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <time.h> +#include <unistd.h> +#include <utime.h> +#include "prelink.h" + +#if defined HAVE_LIBSELINUX && defined HAVE_SELINUX_SELINUX_H +#include <selinux/selinux.h> +#define USE_SELINUX +#endif + +#include <sys/xattr.h> + +#define RELOCATE_SCN(shf) \ + ((shf) & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR)) + +#ifndef ELF_F_PERMISSIVE +# define ELF_F_PERMISSIVE 0 +#endif + +void +read_dynamic (DSO *dso) +{ + int i; + + memset (dso->info, 0, sizeof(dso->info)); + dso->info_set_mask = 0; + for (i = 0; i < dso->ehdr.e_shnum; i++) + if (dso->shdr[i].sh_type == SHT_DYNAMIC) + { + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[i]; + GElf_Dyn dyn; + + dso->dynamic = i; + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + + maxndx = data->d_size / dso->shdr[i].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getdyn (dso->elf, data, ndx, &dyn); + if (dyn.d_tag == DT_NULL) + break; + else if ((GElf_Xword) dyn.d_tag < DT_NUM) + { + dso->info[dyn.d_tag] = dyn.d_un.d_val; + if (dyn.d_tag < 50) + dso->info_set_mask |= (1ULL << dyn.d_tag); + } + else if (dyn.d_tag == DT_CHECKSUM) + { + dso->info_DT_CHECKSUM = dyn.d_un.d_val; + dso->info_set_mask |= (1ULL << DT_CHECKSUM_BIT); + } + else if (dyn.d_tag == DT_GNU_PRELINKED) + { + dso->info_DT_GNU_PRELINKED = dyn.d_un.d_val; + dso->info_set_mask |= (1ULL << DT_GNU_PRELINKED_BIT); + } + else if (dyn.d_tag == DT_VERDEF) + { + dso->info_DT_VERDEF = dyn.d_un.d_val; + dso->info_set_mask |= (1ULL << DT_VERDEF_BIT); + } + else if (dyn.d_tag == DT_VERNEED) + { + dso->info_DT_VERNEED = dyn.d_un.d_val; + dso->info_set_mask |= (1ULL << DT_VERNEED_BIT); + } + else if (dyn.d_tag == DT_VERSYM) + { + dso->info_DT_VERSYM = dyn.d_un.d_val; + dso->info_set_mask |= (1ULL << DT_VERSYM_BIT); + } + else if (dyn.d_tag == DT_FILTER) + dso->info_set_mask |= (1ULL << DT_FILTER_BIT); + else if (dyn.d_tag == DT_AUXILIARY) + dso->info_set_mask |= (1ULL << DT_AUXILIARY_BIT); + else if (dyn.d_tag == DT_LOPROC) + dso->info_set_mask |= (1ULL << DT_LOPROC_BIT); + else if (dyn.d_tag == DT_GNU_HASH) + { + dso->info_DT_GNU_HASH = dyn.d_un.d_val; + dso->info_set_mask |= (1ULL << DT_GNU_HASH_BIT); + } + else if (dyn.d_tag == DT_TLSDESC_PLT) + { + dso->info_DT_TLSDESC_PLT = dyn.d_un.d_val; + dso->info_set_mask |= (1ULL << DT_TLSDESC_PLT_BIT); + } + if (dso->ehdr.e_machine == EM_MIPS) + { + if (dyn.d_tag == DT_MIPS_LOCAL_GOTNO) + dso->info_DT_MIPS_LOCAL_GOTNO = dyn.d_un.d_val; + else if (dyn.d_tag == DT_MIPS_GOTSYM) + dso->info_DT_MIPS_GOTSYM = dyn.d_un.d_val; + else if (dyn.d_tag == DT_MIPS_SYMTABNO) + dso->info_DT_MIPS_SYMTABNO = dyn.d_un.d_val; + else if (dyn.d_tag == DT_MIPS_PLTGOT) + dso->info_DT_MIPS_PLTGOT = dyn.d_un.d_val; + } + } + if (ndx < maxndx) + break; + } + } +} + +int +set_dynamic (DSO *dso, GElf_Word tag, GElf_Addr value, int fatal) +{ + Elf_Data *data; + Elf_Scn *scn; + GElf_Dyn dyn; + int ndx, maxndx; + uint64_t mask = dso->info_set_mask; + + assert (dso->shdr[dso->dynamic].sh_type == SHT_DYNAMIC); + + scn = dso->scn[dso->dynamic]; + + data = elf_getdata (scn, NULL); + assert (elf_getdata (scn, data) == NULL); + + switch (tag) + { + case DT_CHECKSUM: + mask |= (1ULL << DT_CHECKSUM_BIT); break; + case DT_GNU_PRELINKED: + mask |= (1ULL << DT_GNU_PRELINKED_BIT); break; + case DT_VERDEF: + mask |= (1ULL << DT_VERDEF_BIT); break; + case DT_VERNEED: + mask |= (1ULL << DT_VERNEED_BIT); break; + case DT_VERSYM: + mask |= (1ULL << DT_VERSYM_BIT); break; + default: + if (tag < DT_NUM && tag < 50) + mask |= (1ULL << tag); + break; + } + + maxndx = data->d_size / dso->shdr[dso->dynamic].sh_entsize; + for (ndx = 0; ndx < maxndx; ndx++) + { + gelfx_getdyn (dso->elf, data, ndx, &dyn); + if (dyn.d_tag == DT_NULL) + break; + else if (dyn.d_tag == tag) + { + if (dyn.d_un.d_ptr != value) + { + dyn.d_un.d_ptr = value; + gelfx_update_dyn (dso->elf, data, ndx, &dyn); + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + } + + return 0; + } + } + assert (ndx < maxndx); + + if (ndx + 1 < maxndx) + { + /* DT_NULL is not the last dynamic entry. */ + gelfx_update_dyn (dso->elf, data, ndx + 1, &dyn); + dyn.d_tag = tag; + dyn.d_un.d_ptr = value; + gelfx_update_dyn (dso->elf, data, ndx, &dyn); + dso->info_set_mask = mask; + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + return 0; + } + + if (fatal) + error (0, 0, "%s: Not enough room to add .dynamic entry", + dso->filename); + return 1; +} + +int +check_dso (DSO *dso) +{ + int i, last = 1; + + /* FIXME: Several routines in prelink and in libelf-0.7.0 too + rely on sh_offset's monotonically increasing. */ + for (i = 2; i < dso->ehdr.e_shnum; ++i) + { + if (dso->shdr[last].sh_offset + + (dso->shdr[last].sh_type == SHT_NOBITS + ? 0 : dso->shdr[last].sh_size) > dso->shdr[i].sh_offset) + { + if (!dso->permissive + || RELOCATE_SCN (dso->shdr[last].sh_flags) + || RELOCATE_SCN (dso->shdr[i].sh_flags)) + { + error (0, 0, "%s: section file offsets not monotonically increasing", + dso->filename); + return 1; + } + } + if (!dso->permissive + || (dso->shdr[i].sh_type != SHT_NOBITS && dso->shdr[i].sh_size != 0)) + last = i; + } + +#ifndef DSO_READONLY + if (dso_has_bad_textrel (dso)) + { + error (0, 0, "%s has text relocations", dso->filename); + return 1; + } +#endif + + return 0; +} + +DSO * +open_dso (const char *name) +{ + int fd; + + fd = wrap_open (name, O_RDONLY); + if (fd == -1) + { + error (0, errno, "cannot open \"%s\"", name); + return NULL; + } + return fdopen_dso (fd, name); +} + +/* WARNING: If prelink is ever multi-threaded, this will not work + Other alternatives are: + 1) make section_cmp nested function - trampolines + vs. non-exec stack needs to be resolved for it though + 2) make the variable __thread + 3) use locking around the qsort + */ +static DSO *section_cmp_dso; + +static int +section_cmp (const void *A, const void *B) +{ + int *a = (int *) A; + int *b = (int *) B; + DSO *dso = section_cmp_dso; + + if (dso->shdr[*a].sh_offset < dso->shdr[*b].sh_offset) + return -1; + if (dso->shdr[*a].sh_offset > dso->shdr[*b].sh_offset) + return 1; + if (*a < *b) + return -1; + return *a > *b; +} + +DSO * +fdopen_dso (int fd, const char *name) +{ + Elf *elf = NULL; + GElf_Ehdr ehdr; + GElf_Addr last_off; + int i, j, k, last, *sections, *invsections; + DSO *dso = NULL; +#ifndef DSO_READONLY + struct PLArch *plarch; + extern struct PLArch __start_pl_arch[], __stop_pl_arch[]; +#endif /* DSO_READONLY */ + + elf = elf_begin (fd, ELF_C_READ, NULL); + if (elf == NULL) + { + error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1)); + goto error_out; + } + + if (elf_kind (elf) != ELF_K_ELF) + { + error (0, 0, "\"%s\" is not an ELF file", name); + goto error_out; + } + + if (gelf_getehdr (elf, &ehdr) == NULL) + { + error (0, 0, "cannot get the ELF header: %s", + elf_errmsg (-1)); + goto error_out; + } + + if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC) + { + error (0, 0, "\"%s\" is not a shared library", name); + goto error_out; + } + + if (ehdr.e_shnum == 0) + { + GElf_Phdr phdr; + + /* Check for UPX compressed executables. */ + if (ehdr.e_type == ET_EXEC + && ehdr.e_phnum > 0 + && (gelf_getphdr (elf, 0, &phdr), phdr.p_type == PT_LOAD) + && phdr.p_filesz >= 256 + && phdr.p_filesz <= 4096 + && phdr.p_offset == 0 + && ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize < phdr.p_filesz) + { + char *buf = alloca (phdr.p_filesz); + size_t start = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; + + if (pread (fd, buf, phdr.p_filesz, 0) == phdr.p_filesz + && memmem (buf + start, phdr.p_filesz - start, + "UPX!", 4) != NULL) + { + error (0, 0, "\"%s\" is UPX compressed executable", name); + goto error_out; + } + } + error (0, 0, "\"%s\" has no section headers", name); + goto error_out; + } + + /* Allocate DSO structure. Leave place for additional 20 new section + headers. */ + dso = (DSO *) + malloc (sizeof(DSO) + (ehdr.e_shnum + 20) * sizeof(GElf_Shdr) + + (ehdr.e_phnum + 1) * sizeof(GElf_Phdr) + + (ehdr.e_shnum + 20) * sizeof(Elf_Scn *)); + if (!dso) + { + error (0, ENOMEM, "Could not open DSO"); + goto error_out; + } + + elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE); + + memset (dso, 0, sizeof(DSO)); + dso->elf = elf; + dso->ehdr = ehdr; + dso->phdr = (GElf_Phdr *) &dso->shdr[ehdr.e_shnum + 20]; + dso->scn = (Elf_Scn **) &dso->phdr[ehdr.e_phnum + 1]; + switch (ehdr.e_ident[EI_CLASS]) + { + case ELFCLASS32: + dso->mask = 0xffffffff; break; + case ELFCLASS64: + dso->mask = 0xffffffffffffffffULL; break; + } + for (i = 0; i < ehdr.e_phnum; ++i) + gelf_getphdr (elf, i, dso->phdr + i); + dso->fd = fd; + + for (i = 0, j = 0; i < ehdr.e_shnum; ++i) + { + dso->scn[i] = elf_getscn (elf, i); + gelfx_getshdr (elf, dso->scn[i], dso->shdr + i); + if ((dso->shdr[i].sh_flags & SHF_ALLOC) && dso->shdr[i].sh_type != SHT_NOBITS) + j = 1; + } + if (j == 0) + { + /* If all ALLOC sections are SHT_NOBITS, then this is a + stripped-to-file debuginfo. Skip it silently. */ + goto error_out; + } + + sections = (int *) alloca (dso->ehdr.e_shnum * sizeof (int) * 2); + sections[0] = 0; + for (i = 1, j = 1, k = dso->ehdr.e_shnum, last = -1; + i < dso->ehdr.e_shnum; ++i) + if (RELOCATE_SCN (dso->shdr[i].sh_flags)) + { + last = i; + sections[j++] = i; + } + else + sections[--k] = i; + assert (j == k); + + section_cmp_dso = dso; + qsort (sections + k, dso->ehdr.e_shnum - k, sizeof (*sections), section_cmp); + invsections = sections + dso->ehdr.e_shnum; + invsections[0] = 0; + for (i = 1, j = 0; i < ehdr.e_shnum; ++i) + { + if (i != sections[i]) + { + j = 1; + dso->scn[i] = elf_getscn (elf, sections[i]); + gelfx_getshdr (elf, dso->scn[i], dso->shdr + i); + } + invsections[sections[i]] = i; + } + +#ifndef DSO_READONLY + if (j) + { + dso->move = init_section_move (dso); + if (dso->move == NULL) + goto error_out; + memcpy (dso->move->old_to_new, invsections, dso->ehdr.e_shnum * sizeof (int)); + memcpy (dso->move->new_to_old, sections, dso->ehdr.e_shnum * sizeof (int)); + } +#endif /* DSO_READONLY */ + + last_off = 0; + for (i = 1; i < ehdr.e_shnum; ++i) + { + if (dso->shdr[i].sh_link >= ehdr.e_shnum) + { + error (0, 0, "%s: bogus sh_link value %d", name, + dso->shdr[i].sh_link); + goto error_out; + } + dso->shdr[i].sh_link = invsections[dso->shdr[i].sh_link]; + if (dso->shdr[i].sh_type == SHT_REL + || dso->shdr[i].sh_type == SHT_RELA + || (dso->shdr[i].sh_flags & SHF_INFO_LINK)) + { + if (dso->shdr[i].sh_info >= ehdr.e_shnum) + { + error (0, 0, "%s: bogus sh_info value %d", name, + dso->shdr[i].sh_info); + goto error_out; + } + dso->shdr[i].sh_info = invsections[dso->shdr[i].sh_info]; + } + + /* Some linkers mess up sh_offset fields for empty or nobits + sections. */ + if (RELOCATE_SCN (dso->shdr[i].sh_flags) + && (dso->shdr[i].sh_size == 0 + || dso->shdr[i].sh_type == SHT_NOBITS)) + { + for (j = i + 1; j < ehdr.e_shnum; ++j) + if (! RELOCATE_SCN (dso->shdr[j].sh_flags)) + break; + else if (dso->shdr[j].sh_size != 0 + && dso->shdr[j].sh_type != SHT_NOBITS) + break; + dso->shdr[i].sh_offset = (last_off + dso->shdr[i].sh_addralign - 1) + & ~(dso->shdr[i].sh_addralign - 1); + if (j < ehdr.e_shnum + && dso->shdr[i].sh_offset > dso->shdr[j].sh_offset) + { + GElf_Addr k; + + for (k = dso->shdr[i].sh_addralign - 1; k; ) + { + k >>= 1; + dso->shdr[i].sh_offset = (last_off + k) & ~k; + if (dso->shdr[i].sh_offset <= dso->shdr[j].sh_offset) + break; + } + } + last_off = dso->shdr[i].sh_offset; + } + else + last_off = dso->shdr[i].sh_offset + dso->shdr[i].sh_size; + } + dso->ehdr.e_shstrndx = invsections[dso->ehdr.e_shstrndx]; + +#ifndef DSO_READONLY + for (plarch = __start_pl_arch; plarch < __stop_pl_arch; plarch++) + if (plarch->class == ehdr.e_ident[EI_CLASS] + && (plarch->machine == ehdr.e_machine + || plarch->alternate_machine[0] == ehdr.e_machine + || plarch->alternate_machine[1] == ehdr.e_machine + || plarch->alternate_machine[2] == ehdr.e_machine)) + break; + + if (plarch == __stop_pl_arch || ehdr.e_machine == EM_NONE) + { + error (0, 0, "\"%s\"'s architecture is not supported", name); + goto error_out; + } + + dso->arch = plarch; +#else + dso->arch = NULL; +#endif /* DSO_READONLY */ + + dso->base = ~(GElf_Addr) 0; + dso->align = 0; + dso->end = 0; + for (i = 0; i < dso->ehdr.e_phnum; i++) + if (dso->phdr[i].p_type == PT_LOAD) + { + GElf_Addr base, end; + + if (dso->phdr[i].p_align > dso->align) + dso->align = dso->phdr[i].p_align; + base = dso->phdr[i].p_vaddr & ~(dso->phdr[i].p_align - 1); + end = dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz; + if (base < dso->base) + dso->base = base; + if (end > dso->end) + dso->end = end; + } + + if (dso->base == ~(GElf_Addr) 0) + { + error (0, 0, "%s: cannot find loadable segment", name); + goto error_out; + } + + read_dynamic (dso); + + dso->filename = (const char *) strdup (name); + dso->soname = dso->filename; + if (dso->info[DT_STRTAB] && dso->info[DT_SONAME]) + { + const char *soname; + + soname = get_data (dso, dso->info[DT_STRTAB] + dso->info[DT_SONAME], + NULL, NULL); + if (soname && soname[0] != '\0') + dso->soname = (const char *) strdup (soname); + } + +#ifndef DSO_READONLY + if (dso->arch->machine == EM_ALPHA + || dso->arch->machine == EM_MIPS) + for (i = 1; i < ehdr.e_shnum; ++i) + { + if ((dso->shdr[i].sh_type == SHT_ALPHA_DEBUG + && dso->arch->machine == EM_ALPHA) + || (dso->shdr[i].sh_type == SHT_MIPS_DEBUG + && dso->arch->machine == EM_MIPS)) + { + const char *name + = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name); + if (! strcmp (name, ".mdebug")) + dso->mdebug_orig_offset = dso->shdr[i].sh_offset; + break; + } + } +#endif /* DSO_READONLY */ + + return dso; + +error_out: + if (dso) + { + free (dso->move); + if (dso->soname != dso->filename) + free ((char *) dso->soname); + free ((char *) dso->filename); + free (dso); + } + if (elf) + elf_end (elf); + if (fd != -1) + { + fsync (fd); + close (fd); + } + return NULL; +} + +#ifndef DSO_READONLY +static int +adjust_symtab_section_indices (DSO *dso, int n, int old_shnum, int *old_to_new) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Sym sym; + int changed = 0, ndx, maxndx; + + while ((data = elf_getdata (scn, data)) != NULL) + { + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getsym (dso->elf, data, ndx, &sym); + if (sym.st_shndx > SHN_UNDEF && sym.st_shndx < SHN_LORESERVE) + { + if (sym.st_shndx >= old_shnum + || old_to_new[sym.st_shndx] == -1) + { + if (! sym.st_size && + sym.st_info == ELF32_ST_INFO (STB_LOCAL, STT_SECTION)) + { + sym.st_value = 0; + sym.st_shndx = SHN_UNDEF; + gelfx_update_sym (dso->elf, data, ndx, &sym); + changed = 1; + continue; + } + else + { + if (sym.st_shndx >= old_shnum) + { + error (0, 0, "%s: Symbol section index outside of section numbers", + dso->filename); + return 1; + } + error (0, 0, "%s: Section symbol points into has been removed", + dso->filename); + return 1; + } + } + if (old_to_new[sym.st_shndx] != sym.st_shndx) + { + changed = 1; + sym.st_shndx = old_to_new[sym.st_shndx]; + gelfx_update_sym (dso->elf, data, ndx, &sym); + } + } + } + } + + if (changed) + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + + return 0; +} + +static int +set_stt_section_values (DSO *dso, int n) +{ + Elf_Data *data; + Elf_Scn *scn = dso->scn[n]; + GElf_Sym sym; + int ndx, maxndx, sec; + char seen[dso->ehdr.e_shnum]; + + memset (seen, 0, dso->ehdr.e_shnum); + data = elf_getdata (scn, NULL); + assert (data != NULL); + assert (elf_getdata (scn, data) == NULL); + assert (data->d_off == 0); + + maxndx = data->d_size / dso->shdr[n].sh_entsize; + gelfx_getsym (dso->elf, data, 0, &sym); + if (sym.st_info != ELF32_ST_INFO (STB_LOCAL, STT_NOTYPE) + || sym.st_size != 0 || sym.st_other != 0 + || sym.st_value != 0 || sym.st_shndx != SHN_UNDEF + || sym.st_name != 0) + return 0; + + for (ndx = 1; ndx < maxndx; ++ndx) + { + gelfx_getsym (dso->elf, data, ndx, &sym); + if (sym.st_info == ELF32_ST_INFO (STB_LOCAL, STT_SECTION) + && sym.st_size == 0 && sym.st_other == 0 + && sym.st_name == 0) + { + if (sym.st_shndx > SHN_UNDEF && sym.st_shndx < SHN_LORESERVE) + { + seen[sym.st_shndx] = 1; + sym.st_value = dso->shdr[sym.st_shndx].sh_addr; + gelfx_update_sym (dso->elf, data, ndx, &sym); + } + } + else + break; + } + + for (ndx = 1, sec = 1; ndx < maxndx; ++ndx) + { + gelfx_getsym (dso->elf, data, ndx, &sym); + if (sym.st_info == ELF32_ST_INFO (STB_LOCAL, STT_SECTION) + && sym.st_size == 0 && sym.st_other == 0 + && sym.st_name == 0) + { + if (sym.st_shndx == SHN_UNDEF) + { + while (sec < dso->ehdr.e_shnum && seen[sec]) + ++sec; + + if (sec >= dso->ehdr.e_shnum) + sym.st_value = 0; + else + sym.st_value = dso->shdr[sec].sh_addr; + sym.st_shndx = sec++; + gelfx_update_sym (dso->elf, data, ndx, &sym); + } + } + else + break; + } + + return 0; +} + +struct section_move * +init_section_move (DSO *dso) +{ + struct section_move *move; + int i; + + move = malloc (sizeof (struct section_move) + + (dso->ehdr.e_shnum * 2 + 20) * sizeof (int)); + if (move == NULL) + { + error (0, ENOMEM, "%s: Could not move sections", dso->filename); + return move; + } + move->old_shnum = dso->ehdr.e_shnum; + move->new_shnum = dso->ehdr.e_shnum; + move->old_to_new = (int *)(move + 1); + move->new_to_old = move->old_to_new + move->new_shnum; + for (i = 0; i < move->new_shnum; i++) + { + move->old_to_new[i] = i; + move->new_to_old[i] = i; + } + return move; +} + +void +add_section (struct section_move *move, int sec) +{ + int i; + + assert (move->new_shnum < move->old_shnum + 20); + assert (sec <= move->new_shnum); + + memmove (move->new_to_old + sec + 1, move->new_to_old + sec, + (move->new_shnum - sec) * sizeof (int)); + ++move->new_shnum; + move->new_to_old[sec] = -1; + for (i = 1; i < move->old_shnum; i++) + if (move->old_to_new[i] >= sec) + ++move->old_to_new[i]; +} + +void +remove_section (struct section_move *move, int sec) +{ + int i; + + assert (sec < move->new_shnum); + + memmove (move->new_to_old + sec, move->new_to_old + sec + 1, + (move->new_shnum - sec - 1) * sizeof (int)); + --move->new_shnum; + for (i = 1; i < move->old_shnum; i++) + if (move->old_to_new[i] == sec) + move->old_to_new[i] = -1; + else if (move->old_to_new[i] > sec) + --move->old_to_new[i]; +} + +int +reopen_dso (DSO *dso, struct section_move *move, const char *temp_base) +{ + char filename[strlen (temp_base ? temp_base : dso->filename) + + sizeof ("/dev/shm/.#prelink#.XXXXXX")]; + int adddel = 0; + int free_move = 0; + Elf *elf = NULL; + GElf_Ehdr ehdr; + char *e_ident; + int fd, i, j; + + if (move == NULL) + { + move = init_section_move (dso); + if (move == NULL) + return 1; + free_move = 1; + } + else + assert (dso->ehdr.e_shnum == move->old_shnum); + + if (temp_base == NULL) + temp_base = dso->filename; + sprintf (filename, "%s.#prelink#.XXXXXX", temp_base); + + fd = wrap_mkstemp (filename); + if (fd == -1) + { + strcpy (filename, "/tmp/#prelink#.XXXXXX"); + fd = wrap_mkstemp (filename); + if (fd == -1) + { + strcpy (filename, "/dev/shm/#prelink#.XXXXXX"); + fd = wrap_mkstemp (filename); + } + if (fd == -1) + { + error (0, errno, "Could not create temporary file %s", filename); + goto error_out; + } + } + + elf = elf_begin (fd, ELF_C_WRITE, NULL); + if (elf == NULL) + { + error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1)); + goto error_out; + + } + + /* Some gelf_newehdr implementations don't return the resulting + ElfNN_Ehdr, so we have to do it the hard way instead of: + e_ident = (char *) gelf_newehdr (elf, gelf_getclass (dso->elf)); */ + switch (gelf_getclass (dso->elf)) + { + case ELFCLASS32: + e_ident = (char *) elf32_newehdr (elf); + break; + case ELFCLASS64: + e_ident = (char *) elf64_newehdr (elf); + break; + default: + e_ident = NULL; + break; + } + + if (e_ident == NULL + /* This is here just for the gelfx wrapper, so that gelf_update_ehdr + already has the correct ELF class. */ + || memcpy (e_ident, dso->ehdr.e_ident, EI_NIDENT) == NULL + || gelf_update_ehdr (elf, &dso->ehdr) == 0 + || gelf_newphdr (elf, dso->ehdr.e_phnum) == 0) + { + error (0, 0, "Could not create new ELF headers"); + goto error_out; + } + ehdr = dso->ehdr; + elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE); + for (i = 0; i < ehdr.e_phnum; ++i) + gelf_update_phdr (elf, i, dso->phdr + i); + + for (i = 1; i < move->new_shnum; ++i) + { + Elf_Scn *scn; + Elf_Data data, *data1, *data2; + + if (move->new_to_old[i] == -1) + { + scn = elf_newscn (elf); + elf_newdata (scn); + } + else + { + j = move->new_to_old[i]; + scn = elf_newscn (elf); + gelfx_update_shdr (elf, scn, &dso->shdr[j]); + if (dso->shdr[j].sh_type == SHT_NOBITS) + { + data1 = elf_getdata (dso->scn[j], NULL); + data2 = elf_newdata (scn); + memcpy (data2, data1, sizeof (*data1)); + } + else + { + memset (&data, 0, sizeof data); + data.d_type = ELF_T_NUM; + data1 = NULL; + while ((data1 = elf_getdata (dso->scn[j], data1)) + != NULL) + { + if (data.d_type == ELF_T_NUM) + data = *data1; + else if (data.d_type != data1->d_type + || data.d_version != data1->d_version) + abort (); + else + { + if (data1->d_off < data.d_off) + { + data.d_size += data.d_off - data1->d_off; + data.d_off = data1->d_off; + } + if (data1->d_off + data1->d_size + > data.d_off + data.d_size) + data.d_size = data1->d_off + data1->d_size + - data.d_off; + if (data1->d_align > data.d_align) + data.d_align = data1->d_align; + } + } + if (data.d_type == ELF_T_NUM) + { + assert (dso->shdr[j].sh_size == 0); + continue; + } + if (data.d_size != 0) + { + data.d_buf = calloc (1, data.d_size); + if (data.d_buf == NULL) + { + error (0, ENOMEM, "%s: Could not copy section", + dso->filename); + goto error_out; + } + } + else + data.d_buf = NULL; + data1 = NULL; + while ((data1 = elf_getdata (dso->scn[j], data1)) + != NULL) + memcpy (data.d_buf + data1->d_off - data.d_off, data1->d_buf, + data1->d_size); + data2 = elf_newdata (scn); + memcpy (data2, &data, sizeof (data)); + } + } + } + + ehdr.e_shnum = move->new_shnum; + dso->temp_filename = strdup (filename); + if (dso->temp_filename == NULL) + { + error (0, ENOMEM, "%s: Could not save temporary filename", dso->filename); + goto error_out; + } + dso->elfro = dso->elf; + dso->elf = elf; + dso->fdro = dso->fd; + dso->fd = fd; + dso->ehdr = ehdr; + dso->lastscn = 0; + elf = NULL; + fd = -1; + for (i = 1; i < move->old_shnum; i++) + if (move->old_to_new[i] != i) + { + adddel = 1; + break; + } + if (! adddel) + for (i = 1; i < move->new_shnum; i++) + if (move->new_to_old[i] != i) + { + adddel = 1; + break; + } + + for (i = 1; i < move->new_shnum; i++) + { + dso->scn[i] = elf_getscn (dso->elf, i); + gelfx_getshdr (dso->elf, dso->scn[i], dso->shdr + i); + if (move->new_to_old[i] == -1) + continue; + if (dso->move + && (dso->shdr[i].sh_type == SHT_SYMTAB + || dso->shdr[i].sh_type == SHT_DYNSYM)) + { + if (adjust_symtab_section_indices (dso, i, dso->move->old_shnum, + dso->move->old_to_new)) + goto error_out; + } + if (adddel) + { + if (dso->shdr[i].sh_link) + { + if (dso->shdr[i].sh_link >= move->old_shnum) + { + error (0, 0, "%s: bogus sh_link value %d", dso->filename, + dso->shdr[i].sh_link); + goto error_out; + } + if (move->old_to_new[dso->shdr[i].sh_link] == -1) + { + error (0, 0, "Section sh_link points to has been removed"); + goto error_out; + } + dso->shdr[i].sh_link = move->old_to_new[dso->shdr[i].sh_link]; + } + /* Only some section types use sh_info for section index. */ + if (dso->shdr[i].sh_info + && (dso->shdr[i].sh_type == SHT_REL + || dso->shdr[i].sh_type == SHT_RELA + || (dso->shdr[i].sh_flags & SHF_INFO_LINK))) + { + if (dso->shdr[i].sh_info >= move->old_shnum) + { + error (0, 0, "%s: bogus sh_info value %d", dso->filename, + dso->shdr[i].sh_info); + goto error_out; + } + if (move->old_to_new[dso->shdr[i].sh_info] == -1) + { + error (0, 0, "Section sh_info points to has been removed"); + goto error_out; + } + dso->shdr[i].sh_info = move->old_to_new[dso->shdr[i].sh_info]; + } + if (dso->shdr[i].sh_type == SHT_SYMTAB + || dso->shdr[i].sh_type == SHT_DYNSYM) + { + if (adjust_symtab_section_indices (dso, i, move->old_shnum, + move->old_to_new)) + goto error_out; + } + } + } + + free (dso->move); + dso->move = NULL; + + dso->ehdr.e_shstrndx = move->old_to_new[dso->ehdr.e_shstrndx]; + gelf_update_ehdr (dso->elf, &dso->ehdr); + + read_dynamic (dso); + + /* If shoff does not point after last section, we need to adjust the sections + after it if we added or removed some sections. */ + if (move->old_shnum != move->new_shnum + && adjust_dso_nonalloc (dso, 0, dso->ehdr.e_shoff + 1, + ((long) move->new_shnum - (long) move->old_shnum) + * gelf_fsize (dso->elf, ELF_T_SHDR, 1, + EV_CURRENT))) + goto error_out; + + if (free_move) + free (move); + return 0; + +error_out: + if (free_move) + free (move); + if (elf) + elf_end (elf); + if (fd != -1) + { + wrap_unlink (filename); + fsync (fd); + close (fd); + } + return 1; +} + +/* Return true if the value of symbol SYM, which belongs to DSO, + should be treated as an address within the DSO, and should + therefore track DSO's relocations. */ + +int +adjust_symbol_p (DSO *dso, GElf_Sym *sym) +{ + if (sym->st_shndx == SHN_ABS + && sym->st_value != 0 + && (GELF_ST_TYPE (sym->st_info) <= STT_FUNC + || (dso->ehdr.e_machine == EM_ARM + && GELF_ST_TYPE (sym->st_info) == STT_ARM_TFUNC))) + /* This is problematic. How do we find out if + we should relocate this? Assume we should. */ + return 1; + + /* If a MIPS object does not define a symbol, but has a lazy binding + stub for it, st_value will point to that stub. Note that unlike + other targets, these stub addresses never participate in symbol + lookup; the stubs can only be called by the object that defines them. + st_values are only used in this way so that the associated GOT entry + can store a Quickstart value without losing the original stub + address. */ + if (dso->ehdr.e_machine == EM_MIPS + && sym->st_shndx == SHN_UNDEF + && sym->st_value != 0) + return 1; + + return (sym->st_shndx > SHN_UNDEF + && sym->st_shndx < dso->ehdr.e_shnum + && ELF32_ST_TYPE (sym->st_info) != STT_TLS + && RELOCATE_SCN (dso->shdr[sym->st_shndx].sh_flags)); +} + +static int +adjust_symtab (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Sym sym; + int ndx, maxndx; + + while ((data = elf_getdata (scn, data)) != NULL) + { + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getsym (dso->elf, data, ndx, &sym); + if (adjust_symbol_p (dso, &sym) && sym.st_value >= start) + { + sym.st_value += adjust; + gelfx_update_sym (dso->elf, data, ndx, &sym); + } + } + } + + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + return 0; +} +#endif /* DSO_READONLY */ + +int +dso_is_rdwr (DSO *dso) +{ + return dso->elfro != NULL; +} + +#ifndef DSO_READONLY +GElf_Addr +adjust_old_to_new (DSO *dso, GElf_Addr addr) +{ + int i; + + if (dso->adjust == NULL) + return addr; /* Fast path. */ + + for (i = 0; i < dso->nadjust; i++) + if (addr >= dso->adjust[i].start) + { + addr += dso->adjust[i].adjust; + assert (dso->ehdr.e_ident[EI_CLASS] != ELFCLASS32 + || addr == (Elf32_Addr) addr); + return addr; + } + + return addr; +} + +GElf_Addr +adjust_new_to_old (DSO *dso, GElf_Addr addr) +{ + int i; + + if (dso->adjust == NULL) + return addr; /* Fast path. */ + + for (i = 0; i < dso->nadjust; i++) + if (addr >= dso->adjust[i].start + dso->adjust[i].adjust) + { + addr -= dso->adjust[i].adjust; + assert (dso->ehdr.e_ident[EI_CLASS] != ELFCLASS32 + || addr == (Elf32_Addr) addr); + return addr; + } + + return addr; +} + +static int +adjust_dynamic (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Dyn dyn; + int ndx, maxndx; + + while ((data = elf_getdata (scn, data)) != NULL) + { + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getdyn (dso->elf, data, ndx, &dyn); + if (dso->arch->adjust_dyn (dso, n, &dyn, start, adjust) == 0) + switch (dyn.d_tag) + { + case DT_REL: + case DT_RELA: + /* On some arches DT_REL* may be 0 indicating no relocations + (if DT_REL*SZ is also 0). Don't adjust it in that case. */ + if (dyn.d_un.d_ptr && dyn.d_un.d_ptr >= start) + { + dyn.d_un.d_ptr += adjust; + gelfx_update_dyn (dso->elf, data, ndx, &dyn); + } + break; + default: + if (dyn.d_tag < DT_ADDRRNGLO || dyn.d_tag > DT_ADDRRNGHI) + break; + /* FALLTHROUGH */ + case DT_INIT: + case DT_FINI: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + case DT_PREINIT_ARRAY: + case DT_VERDEF: + case DT_VERNEED: + case DT_VERSYM: + case DT_PLTGOT: + if (dyn.d_un.d_ptr >= start) + { + dyn.d_un.d_ptr += adjust; + gelfx_update_dyn (dso->elf, data, ndx, &dyn); + } + break; + } + else + gelfx_update_dyn (dso->elf, data, ndx, &dyn); + } + } + + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + + /* Update the cached dynamic info as well. */ + read_dynamic (dso); + return 0; +} +#endif /* DSO_READONLY */ + +int +addr_to_sec (DSO *dso, GElf_Addr addr) +{ + GElf_Shdr *shdr; + int i; + + shdr = &dso->shdr[dso->lastscn]; + for (i = -1; i < dso->ehdr.e_shnum; shdr = &dso->shdr[++i]) + if (RELOCATE_SCN (shdr->sh_flags) + && shdr->sh_addr <= addr && shdr->sh_addr + shdr->sh_size > addr + && (shdr->sh_type != SHT_NOBITS || (shdr->sh_flags & SHF_TLS) == 0)) + { + if (i != -1) + dso->lastscn = i; + return dso->lastscn; + } + + return -1; +} + +#ifndef DSO_READONLY +static int +adjust_rel (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Rel rel; + int sec, ndx, maxndx; + + while ((data = elf_getdata (scn, data)) != NULL) + { + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getrel (dso->elf, data, ndx, &rel); + sec = addr_to_sec (dso, rel.r_offset); + if (sec == -1) + continue; + + dso->arch->adjust_rel (dso, &rel, start, adjust); + addr_adjust (rel.r_offset, start, adjust); + gelfx_update_rel (dso->elf, data, ndx, &rel); + } + } + + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + return 0; +} + +static int +adjust_rela (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Rela rela; + int sec, ndx, maxndx; + + while ((data = elf_getdata (scn, data)) != NULL) + { + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + { + gelfx_getrela (dso->elf, data, ndx, &rela); + sec = addr_to_sec (dso, rela.r_offset); + if (sec == -1) + continue; + + dso->arch->adjust_rela (dso, &rela, start, adjust); + addr_adjust (rela.r_offset, start, adjust); + gelfx_update_rela (dso->elf, data, ndx, &rela); + } + } + + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + return 0; +} + +int +adjust_nonalloc (DSO *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int first, + GElf_Addr start, GElf_Addr adjust) +{ + int i; + + for (i = 1; i < ehdr->e_shnum; i++) + { + if (RELOCATE_SCN (shdr[i].sh_flags) || shdr[i].sh_type == SHT_NULL) + continue; + + if ((shdr[i].sh_offset > start + || (shdr[i].sh_offset == start && i >= first)) + && (adjust & (shdr[i].sh_addralign - 1))) + adjust = (adjust + shdr[i].sh_addralign - 1) + & ~(shdr[i].sh_addralign - 1); + } + + if (ehdr->e_shoff >= start) + { + GElf_Addr shdralign = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); + + if (adjust & (shdralign - 1)) + adjust = (adjust + shdralign - 1) & ~(shdralign - 1); + ehdr->e_shoff += adjust; + } + + for (i = 1; i < ehdr->e_shnum; i++) + { + if (RELOCATE_SCN (shdr[i].sh_flags) || shdr[i].sh_type == SHT_NULL) + continue; + + if (shdr[i].sh_offset > start + || (shdr[i].sh_offset == start && i >= first)) + shdr[i].sh_offset += adjust; + } + return 0; +} + +int +adjust_dso_nonalloc (DSO *dso, int first, GElf_Addr start, GElf_Addr adjust) +{ + return adjust_nonalloc (dso, &dso->ehdr, dso->shdr, first, start, adjust); +} + +/* Add ADJUST to all addresses above START. */ +int +adjust_dso (DSO *dso, GElf_Addr start, GElf_Addr adjust) +{ + int i; + + if (dso->arch->arch_adjust + && dso->arch->arch_adjust (dso, start, adjust)) + return 1; + + if (dso->ehdr.e_entry >= start) + { + dso->ehdr.e_entry += adjust; + gelf_update_ehdr (dso->elf, &dso->ehdr); + elf_flagehdr (dso->elf, ELF_C_SET, ELF_F_DIRTY); + } + + for (i = 0; i < dso->ehdr.e_phnum; i++) + { + /* Leave STACK segment alone, it has + p_vaddr == p_paddr == p_offset == p_filesz == p_memsz == 0. */ + if (dso->phdr[i].p_type == PT_GNU_STACK) + continue; + if (! start) + { + dso->phdr[i].p_vaddr += adjust; + dso->phdr[i].p_paddr += adjust; + } + else if (start <= dso->phdr[i].p_vaddr) + { + dso->phdr[i].p_vaddr += adjust; + dso->phdr[i].p_paddr += adjust; + dso->phdr[i].p_offset += adjust; + } + else if (start < dso->phdr[i].p_vaddr + dso->phdr[i].p_filesz) + { + dso->phdr[i].p_filesz += adjust; + dso->phdr[i].p_memsz += adjust; + } + else if (start < dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz) + dso->phdr[i].p_memsz += adjust; + else + continue; + if (dso->phdr[i].p_type == PT_LOAD + && (dso->phdr[i].p_vaddr - dso->phdr[i].p_offset) + % dso->phdr[i].p_align) + { + error (0, 0, "%s: PT_LOAD %08llx %08llx 0x%x would be not properly aligned", + dso->filename, (long long) dso->phdr[i].p_offset, + (long long) dso->phdr[i].p_vaddr, (int) dso->phdr[i].p_align); + return 1; + } + gelf_update_phdr (dso->elf, i, dso->phdr + i); + } + elf_flagphdr (dso->elf, ELF_C_SET, ELF_F_DIRTY); + + for (i = 1; i < dso->ehdr.e_shnum; i++) + { + const char *name; + + if (dso->arch->adjust_section) + { + int ret = dso->arch->adjust_section (dso, i, start, adjust); + + if (ret == 1) + return 1; + else if (ret) + continue; + } + switch (dso->shdr[i].sh_type) + { + case SHT_PROGBITS: + case SHT_MIPS_DWARF: + name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name); + if (strcmp (name, ".stab") == 0 + && adjust_stabs (dso, i, start, adjust)) + return 1; + if (strcmp (name, ".debug_info") == 0 + && adjust_dwarf2 (dso, i, start, adjust)) + return 1; + break; + case SHT_HASH: + case SHT_GNU_HASH: + case SHT_NOBITS: + case SHT_STRTAB: + break; + case SHT_SYMTAB: + case SHT_DYNSYM: + if (adjust_symtab (dso, i, start, adjust)) + return 1; + break; + case SHT_DYNAMIC: + if (adjust_dynamic (dso, i, start, adjust)) + return 1; + break; + case SHT_REL: + /* Don't adjust reloc sections for debug info. */ + if (dso->shdr[i].sh_flags & SHF_ALLOC) + if (adjust_rel (dso, i, start, adjust)) + return 1; + break; + case SHT_RELA: + if (dso->shdr[i].sh_flags & SHF_ALLOC) + if (adjust_rela (dso, i, start, adjust)) + return 1; + break; + } + if ((dso->arch->machine == EM_ALPHA + && dso->shdr[i].sh_type == SHT_ALPHA_DEBUG) + || (dso->arch->machine == EM_MIPS + && dso->shdr[i].sh_type == SHT_MIPS_DEBUG)) + if (adjust_mdebug (dso, i, start, adjust)) + return 1; + } + + for (i = 0; i < dso->ehdr.e_shnum; i++) + { + if (RELOCATE_SCN (dso->shdr[i].sh_flags)) + { + if (dso->shdr[i].sh_addr >= start) + { + Elf_Scn *scn = dso->scn[i]; + + dso->shdr[i].sh_addr += adjust; + if (start) + dso->shdr[i].sh_offset += adjust; + gelfx_update_shdr (dso->elf, scn, dso->shdr + i); + elf_flagshdr (scn, ELF_C_SET, ELF_F_DIRTY); + } + } + } + + addr_adjust (dso->base, start, adjust); + addr_adjust (dso->end, start, adjust); + + if (start) + { + start = adjust_new_to_old (dso, start); + for (i = 0; i < dso->nadjust; i++) + if (start < dso->adjust[i].start) + dso->adjust[i].adjust += adjust; + else + break; + if (i < dso->nadjust && start == dso->adjust[i].start) + dso->adjust[i].adjust += adjust; + else + { + dso->adjust = + realloc (dso->adjust, (dso->nadjust + 1) * sizeof (*dso->adjust)); + if (dso->adjust == NULL) + { + error (0, ENOMEM, "Cannot record the list of adjustements being made"); + return 1; + } + memmove (dso->adjust + i + 1, dso->adjust + i, dso->nadjust - i); + dso->adjust[i].start = start; + dso->adjust[i].adjust = adjust; + ++dso->nadjust; + } + } + + return start ? adjust_dso_nonalloc (dso, 0, 0, adjust) : 0; +} + +int +recompute_nonalloc_offsets (DSO *dso) +{ + int i, first_nonalloc, sec_before_shoff = 0; + GElf_Addr last_offset = 0; + GElf_Addr shdralign = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); + GElf_Addr shdrsize = gelf_fsize (dso->elf, ELF_T_SHDR, 1, EV_CURRENT) + * dso->ehdr.e_shnum; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (RELOCATE_SCN (dso->shdr[i].sh_flags)) + { + if (dso->shdr[i].sh_type == SHT_NOBITS) + last_offset = dso->shdr[i].sh_offset; + else + last_offset = dso->shdr[i].sh_offset + dso->shdr[i].sh_size; + } + else + break; + + first_nonalloc = i; + if (dso->ehdr.e_shoff < dso->shdr[i].sh_offset) + { + dso->ehdr.e_shoff = (last_offset + shdralign - 1) & ~(shdralign - 1); + last_offset = dso->ehdr.e_shoff + shdrsize; + } + else + for (; i < dso->ehdr.e_shnum; ++i) + if (dso->shdr[i].sh_offset < dso->ehdr.e_shoff + && (i == dso->ehdr.e_shnum - 1 + || dso->shdr[i + 1].sh_offset > dso->ehdr.e_shoff)) + { + sec_before_shoff = i; + break; + } + + for (i = first_nonalloc; i < dso->ehdr.e_shnum; ++i) + { + assert (!RELOCATE_SCN (dso->shdr[i].sh_flags)); + assert (dso->shdr[i].sh_type != SHT_NOBITS); + dso->shdr[i].sh_offset = (last_offset + dso->shdr[i].sh_addralign - 1) + & ~(dso->shdr[i].sh_addralign - 1); + last_offset = dso->shdr[i].sh_offset + dso->shdr[i].sh_size; + if (i == sec_before_shoff) + { + dso->ehdr.e_shoff = (last_offset + shdralign - 1) & ~(shdralign - 1); + last_offset = dso->ehdr.e_shoff + shdrsize; + } + } + + return 0; +} + +int +strtabfind (DSO *dso, int strndx, const char *name) +{ + Elf_Scn *scn; + Elf_Data *data; + const char *p, *q, *r; + size_t len = strlen (name); + + if (dso->shdr[strndx].sh_type != SHT_STRTAB) + return 0; + + scn = dso->scn[strndx]; + data = elf_getdata (scn, NULL); + assert (elf_getdata (scn, data) == NULL); + assert (data->d_off == 0); + assert (data->d_size == dso->shdr[strndx].sh_size); + q = data->d_buf + data->d_size; + for (p = data->d_buf; p < q; p = r + 1) + { + r = strchr (p, '\0'); + if (r - p >= len && memcmp (r - len, name, len) == 0) + return (r - (const char *) data->d_buf) - len; + } + + return 0; +} + +int +shstrtabadd (DSO *dso, const char *name) +{ + Elf_Scn *scn; + Elf_Data *data; + GElf_Addr adjust; + const char *p, *q, *r; + size_t len = strlen (name), align; + int ret; + + scn = dso->scn[dso->ehdr.e_shstrndx]; + data = elf_getdata (scn, NULL); + assert (elf_getdata (scn, data) == NULL); + assert (data->d_off == 0); + assert (data->d_size == dso->shdr[dso->ehdr.e_shstrndx].sh_size); + q = data->d_buf + data->d_size; + for (p = data->d_buf; p < q; p = r + 1) + { + r = strchr (p, '\0'); + if (r - p >= len && memcmp (r - len, name, len) == 0) + return (r - (const char *) data->d_buf) - len; + } + + data->d_buf = realloc (data->d_buf, data->d_size + len + 1); + if (data->d_buf == NULL) + { + error (0, ENOMEM, "Cannot add new section name %s", name); + return 0; + } + + memcpy (data->d_buf + data->d_size, name, len + 1); + ret = data->d_size; + data->d_size += len + 1; + align = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); + adjust = (len + 1 + align - 1) & ~(align - 1); + if (adjust_dso_nonalloc (dso, 0, + dso->shdr[dso->ehdr.e_shstrndx].sh_offset + + dso->shdr[dso->ehdr.e_shstrndx].sh_size, + adjust)) + return 0; + dso->shdr[dso->ehdr.e_shstrndx].sh_size += len + 1; + return ret; +} + +int +relocate_dso (DSO *dso, GElf_Addr base) +{ + /* Check if it is already relocated. */ + if (dso->base == base) + return 0; + + if (! dso_is_rdwr (dso)) + { + if (reopen_dso (dso, NULL, NULL)) + return 1; + } + + return adjust_dso (dso, 0, base - dso->base); +} +#endif /* DSO_READONLY */ + +static int +close_dso_1 (DSO *dso) +{ + if (dso_is_rdwr (dso)) + { + int i; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + Elf_Scn *scn = dso->scn[i]; + Elf_Data *data = NULL; + + while ((data = elf_getdata (scn, data)) != NULL) + { + free (data->d_buf); + data->d_buf = NULL; + } + } + } + + elf_end (dso->elf); + fsync (dso->fd); + close (dso->fd); + if (dso->elfro) + { + elf_end (dso->elfro); + fsync (dso->fdro); + close (dso->fdro); + } + if (dso->filename != dso->soname) + free ((char *) dso->soname); + free ((char *) dso->filename); + free ((char *) dso->temp_filename); + free (dso->move); + free (dso->adjust); + free (dso->undo.d_buf); + free (dso); + return 0; +} + +int +close_dso (DSO *dso) +{ + int rdwr = dso_is_rdwr (dso); + + if (rdwr && dso->temp_filename != NULL) + wrap_unlink (dso->temp_filename); + close_dso_1 (dso); + return 0; +} + +#ifndef DSO_READONLY +int +prepare_write_dso (DSO *dso) +{ + int i; + + if (check_dso (dso) + || (dso->mdebug_orig_offset && finalize_mdebug (dso))) + return 1; + + gelf_update_ehdr (dso->elf, &dso->ehdr); + for (i = 0; i < dso->ehdr.e_phnum; ++i) + gelf_update_phdr (dso->elf, i, dso->phdr + i); + for (i = 0; i < dso->ehdr.e_shnum; ++i) + { + gelfx_update_shdr (dso->elf, dso->scn[i], dso->shdr + i); + if (dso->shdr[i].sh_type == SHT_SYMTAB + || dso->shdr[i].sh_type == SHT_DYNSYM) + set_stt_section_values (dso, i); + } + return 0; +} + +int +write_dso (DSO *dso) +{ + if (prepare_write_dso (dso)) + return 1; + + if (! dso->permissive && ELF_F_PERMISSIVE) + elf_flagelf (dso->elf, ELF_C_CLR, ELF_F_PERMISSIVE); + + if (elf_update (dso->elf, ELF_C_WRITE) == -1) + return 2; + return 0; +} + +static int +copy_xattrs (const char *temp_name, const char *name, int ignore_errors) +{ + ssize_t sz = wrap_listxattr (name, NULL, 0), valsz = 0; + char *list = NULL, *end, *p, *val = NULL, *newval; + + if (sz < 0) + { + if (errno == ENOSYS || errno == ENOTSUP) + return 0; + goto read_err; + } + list = malloc (sz + 1); + if (list == NULL) + goto read_err; + sz = wrap_listxattr (name, list, sz); + if (sz < 0) + goto read_err; + end = list + sz; + *end = '\0'; + for (p = list; p != end; p = strchr (p, '\0') + 1) + if (*p == '\0' || strcmp (p, "security.selinux") == 0) + continue; + else + { + sz = wrap_getxattr (name, p, val, valsz); + if (sz < 0) + { + if (errno != ERANGE) + goto read_err; + sz = wrap_getxattr (name, p, NULL, 0); + if (sz < 0) + goto read_err; + } + if (sz > valsz) + { + valsz = sz * 2; + if (valsz < 64) + valsz = 64; + newval = realloc (val, valsz); + if (newval == NULL) + goto read_err; + val = newval; + sz = wrap_getxattr (name, p, val, valsz); + if (sz < 0) + goto read_err; + } + if (wrap_setxattr (temp_name, p, val, sz, 0) < 0) + { + if (errno == ENOSYS || errno == ENOTSUP) + continue; + if (!ignore_errors) + { + int err = errno; + ssize_t newsz; + + newval = malloc (sz); + if (newval == NULL + || (newsz = wrap_getxattr (temp_name, p, newval, sz)) != sz + || memcmp (val, newval, sz) != 0) + { + error (0, err, "Could not set extended attributes for %s", + name); + free (newval); + free (val); + free (list); + return 1; + } + free (newval); + } + } + } + free (val); + free (list); + return 0; + +read_err: + error (0, errno, "Could not get extended attributes for %s", name); + free (val); + free (list); + return 1; +} + +static int +set_security_context (const char *temp_name, const char *name, + int ignore_errors) +{ +#ifdef USE_SELINUX + static int selinux_enabled = -1; + if (selinux_enabled == -1) + selinux_enabled = is_selinux_enabled (); + if (selinux_enabled > 0) + { + security_context_t scontext; + if (getfilecon (name, &scontext) < 0) + { + /* If the filesystem doesn't support extended attributes, + the original had no special security context and the + target cannot have one either. */ + if (errno == EOPNOTSUPP) + return 0; + + error (0, errno, "Could not get security context for %s", + name); + return 1; + } + if (setfilecon (temp_name, scontext) < 0 && !ignore_errors) + { + error (0, errno, "Could not set security context for %s", + name); + freecon (scontext); + return 1; + } + freecon (scontext); + } +#endif /* USE_SELINUX */ + return copy_xattrs (temp_name, name, ignore_errors); +} + +int +copy_fd_to_file (int fdin, const char *name, struct stat64 *st) +{ + struct stat64 stt; + off_t off = 0; + int err, fdout; + struct utimbuf u; + + if (strcmp (name, "-") == 0) + fdout = 1; + else + fdout = wrap_open (name, O_WRONLY | O_CREAT, 0600); + if (fdout != -1 + && fstat64 (fdin, &stt) >= 0 + && send_file (fdout, fdin, &off, stt.st_size) == stt.st_size) + { + if (fchown (fdout, st->st_uid, st->st_gid) >= 0) + fchmod (fdout, st->st_mode & 07777); + if (strcmp (name, "-") != 0) + { + set_security_context (name, name, 1); + u.actime = time (NULL); + u.modtime = st->st_mtime; + wrap_utime (name, &u); + close (fdout); + } + return 0; + } + else if (fdout != -1) + { + err = errno; + if (strcmp (name, "-") == 0) + close (fdout); + } + else + err = errno; + return err; +} + +int +update_dso (DSO *dso, const char *orig_name) +{ + int rdwr = dso_is_rdwr (dso); + + if (rdwr) + { + char *name1, *name2; + struct utimbuf u; + struct stat64 st; + int fdin; + + switch (write_dso (dso)) + { + case 2: + error (0, 0, "Could not write %s: %s", dso->filename, + elf_errmsg (-1)); + /* FALLTHROUGH */ + case 1: + close_dso (dso); + return 1; + case 0: + break; + } + + name1 = strdupa (dso->filename); + name2 = strdupa (dso->temp_filename); + if (fstat64 (dso->fdro, &st) < 0) + { + error (0, errno, "Could not stat %s", dso->filename); + close_dso (dso); + return 1; + } + if ((fchown (dso->fd, st.st_uid, st.st_gid) < 0 + || fchmod (dso->fd, st.st_mode & 07777) < 0) + && orig_name == NULL) + { + error (0, errno, "Could not set %s owner or mode", dso->filename); + close_dso (dso); + return 1; + } + if (orig_name != NULL) + fdin = dup (dso->fd); + else + fdin = -1; + close_dso_1 (dso); + u.actime = time (NULL); + u.modtime = st.st_mtime; + wrap_utime (name2, &u); + + if (set_security_context (name2, orig_name ? orig_name : name1, + orig_name != NULL)) + { + if (fdin != -1) + close (fdin); + wrap_unlink (name2); + return 1; + } + + if ((orig_name != NULL && strcmp (name1, "-") == 0) + || wrap_rename (name2, name1)) + { + if (fdin != -1) + { + int err = copy_fd_to_file (fdin, name1, &st); + + close (fdin); + wrap_unlink (name2); + if (err == 0) + return 0; + error (0, err, "Could not rename nor copy temporary to %s", + name1); + return 1; + } + wrap_unlink (name2); + error (0, errno, "Could not rename temporary to %s", name1); + return 1; + } + if (fdin != -1) + close (fdin); + } + else + close_dso_1 (dso); + + return 0; +} + +int allow_bad_textrel; + +int +dso_has_bad_textrel (DSO *dso) +{ + if (allow_bad_textrel) + return 0; + + switch (dso->arch->machine) + { + case EM_IA_64: + case EM_PPC: + case EM_PPC64: + case EM_X86_64: + case EM_ALPHA: + case EM_S390: + case EM_MIPS: + case EM_ARM: + return dynamic_info_is_set (dso, DT_TEXTREL); + + default: + return 0; + } +} +#endif /* DSO_READONLY */ diff --git a/src/dwarf2.c b/src/dwarf2.c new file mode 100644 index 0000000..b9588b1 --- /dev/null +++ b/src/dwarf2.c @@ -0,0 +1,1388 @@ +/* Copyright (C) 2001, 2002, 2003, 2005, 2006, 2009, 2010, 2011, 2012 + Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <byteswap.h> +#include <endian.h> +#include <errno.h> +#include <error.h> +#include <limits.h> +#include <string.h> +#include <sys/types.h> + +#include "dwarf2.h" +#include "hashtab.h" +#include "prelink.h" + +#define read_uleb128(ptr) ({ \ + unsigned int ret = 0; \ + unsigned int c; \ + int shift = 0; \ + do \ + { \ + c = *ptr++; \ + ret |= (c & 0x7f) << shift; \ + shift += 7; \ + } while (c & 0x80); \ + \ + if (shift >= 35) \ + ret = UINT_MAX; \ + ret; \ +}) + +static uint16_t (*do_read_16) (unsigned char *ptr); +static uint32_t (*do_read_32) (unsigned char *ptr); +static uint64_t (*do_read_32_64) (unsigned char *ptr); +static uint64_t (*do_read_64) (unsigned char *ptr); +static uint64_t (*do_read_ptr) (unsigned char *ptr); +static void (*write_32) (unsigned char *ptr, GElf_Addr val); +static void (*write_64) (unsigned char *ptr, GElf_Addr val); +static void (*write_ptr) (unsigned char *ptr, GElf_Addr val); + +static int ptr_size; + +#define read_1(ptr) *ptr++ + +#define read_16(ptr) ({ \ + uint16_t ret = do_read_16 (ptr); \ + ptr += 2; \ + ret; \ +}) + +#define read_32(ptr) ({ \ + uint32_t ret = do_read_32 (ptr); \ + ptr += 4; \ + ret; \ +}) + +#define read_64(ptr) ({ \ + uint64_t ret = do_read_64 (ptr); \ + ptr += 8; \ + ret; \ +}) + +#define read_ptr(ptr) ({ \ + uint64_t ret = do_read_ptr (ptr); \ + ptr += ptr_size; \ + ret; \ +}) + +static uint64_t +buf_read_ule32_64 (unsigned char *p) +{ + return buf_read_ule32 (p); +} + +static uint64_t +buf_read_ube32_64 (unsigned char *p) +{ + return buf_read_ube32 (p); +} + +static void +dwarf2_write_le32 (unsigned char *p, GElf_Addr val) +{ + uint32_t v = (uint32_t) val; + + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; +} + +static void +dwarf2_write_le64 (unsigned char *p, GElf_Addr val) +{ + p[0] = val; + p[1] = val >> 8; + p[2] = val >> 16; + p[3] = val >> 24; + p[4] = val >> 32; + p[5] = val >> 40; + p[6] = val >> 48; + p[7] = val >> 56; +} + +static void +dwarf2_write_be32 (unsigned char *p, GElf_Addr val) +{ + uint32_t v = (uint32_t) val; + + p[3] = v; + p[2] = v >> 8; + p[1] = v >> 16; + p[0] = v >> 24; +} + +static void +dwarf2_write_be64 (unsigned char *p, GElf_Addr val) +{ + p[7] = val; + p[6] = val >> 8; + p[5] = val >> 16; + p[4] = val >> 24; + p[3] = val >> 32; + p[2] = val >> 40; + p[1] = val >> 48; + p[0] = val >> 56; +} + +static struct + { + const char *name; + unsigned char *data; + size_t size; + int sec; + } debug_sections[] = + { +#define DEBUG_INFO 0 +#define DEBUG_ABBREV 1 +#define DEBUG_LINE 2 +#define DEBUG_ARANGES 3 +#define DEBUG_PUBNAMES 4 +#define DEBUG_PUBTYPES 5 +#define DEBUG_MACINFO 6 +#define DEBUG_LOC 7 +#define DEBUG_STR 8 +#define DEBUG_FRAME 9 +#define DEBUG_RANGES 10 +#define DEBUG_TYPES 11 +#define DEBUG_MACRO 12 + { ".debug_info", NULL, 0, 0 }, + { ".debug_abbrev", NULL, 0, 0 }, + { ".debug_line", NULL, 0, 0 }, + { ".debug_aranges", NULL, 0, 0 }, + { ".debug_pubnames", NULL, 0, 0 }, + { ".debug_pubtypes", NULL, 0, 0 }, + { ".debug_macinfo", NULL, 0, 0 }, + { ".debug_loc", NULL, 0, 0 }, + { ".debug_str", NULL, 0, 0 }, + { ".debug_frame", NULL, 0, 0 }, + { ".debug_ranges", NULL, 0, 0 }, + { ".debug_types", NULL, 0, 0 }, + { ".debug_macro", NULL, 0, 0 }, + { NULL, NULL, 0 } + }; + +struct abbrev_attr + { + unsigned int attr; + unsigned int form; + }; + +struct abbrev_tag + { + unsigned int entry; + unsigned int tag; + int nattr; + struct abbrev_attr attr[0]; + }; + +struct cu_data + { + GElf_Addr cu_entry_pc; + GElf_Addr cu_low_pc; + unsigned char cu_version; + }; + +static hashval_t +abbrev_hash (const void *p) +{ + struct abbrev_tag *t = (struct abbrev_tag *)p; + + return t->entry; +} + +static int +abbrev_eq (const void *p, const void *q) +{ + struct abbrev_tag *t1 = (struct abbrev_tag *)p; + struct abbrev_tag *t2 = (struct abbrev_tag *)q; + + return t1->entry == t2->entry; +} + +static void +abbrev_del (void *p) +{ + free (p); +} + +static htab_t +read_abbrev (DSO *dso, unsigned char *ptr) +{ + htab_t h = htab_try_create (50, abbrev_hash, abbrev_eq, abbrev_del); + unsigned int attr, form; + struct abbrev_tag *t; + int size; + void **slot; + + if (h == NULL) + { +no_memory: + error (0, ENOMEM, "%s: Could not read .debug_abbrev", dso->filename); + if (h) + htab_delete (h); + return NULL; + } + + while ((attr = read_uleb128 (ptr)) != 0) + { + size = 10; + t = malloc (sizeof (*t) + size * sizeof (struct abbrev_attr)); + if (t == NULL) + goto no_memory; + t->entry = attr; + t->nattr = 0; + slot = htab_find_slot (h, t, INSERT); + if (slot == NULL) + { + free (t); + goto no_memory; + } + if (*slot != NULL) + { + error (0, 0, "%s: Duplicate DWARF abbreviation %d", dso->filename, + t->entry); + free (t); + htab_delete (h); + return NULL; + } + t->tag = read_uleb128 (ptr); + ++ptr; /* skip children flag. */ + while ((attr = read_uleb128 (ptr)) != 0) + { + if (t->nattr == size) + { + size += 10; + t = realloc (t, sizeof (*t) + size * sizeof (struct abbrev_attr)); + if (t == NULL) + goto no_memory; + } + form = read_uleb128 (ptr); + if (form == 2 + || (form > DW_FORM_flag_present + && form != DW_FORM_ref_sig8 + && form != DW_FORM_GNU_ref_alt + && form != DW_FORM_GNU_strp_alt)) + { + error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, form); + htab_delete (h); + return NULL; + } + + t->attr[t->nattr].attr = attr; + t->attr[t->nattr++].form = form; + } + if (read_uleb128 (ptr) != 0) + { + error (0, 0, "%s: DWARF abbreviation does not end with 2 zeros", + dso->filename); + htab_delete (h); + return NULL; + } + *slot = t; + } + + return h; +} + +static int +adjust_location_list (DSO *dso, struct cu_data *cu, unsigned char *ptr, + size_t len, GElf_Addr start, GElf_Addr adjust) +{ + unsigned char *end = ptr + len; + unsigned char op; + GElf_Addr addr; + + while (ptr < end) + { + op = *ptr++; + switch (op) + { + case DW_OP_addr: + addr = read_ptr (ptr); + if (addr >= start && addr_to_sec (dso, addr) != -1) + write_ptr (ptr - ptr_size, addr + adjust); + break; + case DW_OP_deref: + case DW_OP_dup: + case DW_OP_drop: + case DW_OP_over: + case DW_OP_swap: + case DW_OP_rot: + case DW_OP_xderef: + case DW_OP_abs: + case DW_OP_and: + case DW_OP_div: + case DW_OP_minus: + case DW_OP_mod: + case DW_OP_mul: + case DW_OP_neg: + case DW_OP_not: + case DW_OP_or: + case DW_OP_plus: + case DW_OP_shl: + case DW_OP_shr: + case DW_OP_shra: + case DW_OP_xor: + case DW_OP_eq: + case DW_OP_ge: + case DW_OP_gt: + case DW_OP_le: + case DW_OP_lt: + case DW_OP_ne: + case DW_OP_lit0 ... DW_OP_lit31: + case DW_OP_reg0 ... DW_OP_reg31: + case DW_OP_nop: + case DW_OP_push_object_address: + case DW_OP_form_tls_address: + case DW_OP_call_frame_cfa: + case DW_OP_stack_value: + case DW_OP_GNU_push_tls_address: + case DW_OP_GNU_uninit: + break; + case DW_OP_const1u: + case DW_OP_pick: + case DW_OP_deref_size: + case DW_OP_xderef_size: + case DW_OP_const1s: + ++ptr; + break; + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_skip: + case DW_OP_bra: + case DW_OP_call2: + ptr += 2; + break; + case DW_OP_const4u: + case DW_OP_const4s: + case DW_OP_call4: + case DW_OP_GNU_parameter_ref: + ptr += 4; + break; + case DW_OP_call_ref: + if (cu == NULL) + { + error (0, 0, "%s: DWARF DW_OP_call_ref shouldn't appear" + " in .debug_frame", dso->filename); + return 1; + } + if (cu->cu_version == 2) + ptr += ptr_size; + else + ptr += 4; + break; + case DW_OP_const8u: + case DW_OP_const8s: + ptr += 8; + break; + case DW_OP_constu: + case DW_OP_plus_uconst: + case DW_OP_regx: + case DW_OP_piece: + case DW_OP_consts: + case DW_OP_breg0 ... DW_OP_breg31: + case DW_OP_fbreg: + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + read_uleb128 (ptr); + break; + case DW_OP_bregx: + case DW_OP_bit_piece: + case DW_OP_GNU_regval_type: + read_uleb128 (ptr); + read_uleb128 (ptr); + break; + case DW_OP_implicit_value: + { + uint32_t leni = read_uleb128 (ptr); + ptr += leni; + } + break; + case DW_OP_GNU_implicit_pointer: + if (cu == NULL) + { + error (0, 0, "%s: DWARF DW_OP_GNU_implicit_pointer shouldn't" + " appear in .debug_frame", dso->filename); + return 1; + } + if (cu->cu_version == 2) + ptr += ptr_size; + else + ptr += 4; + read_uleb128 (ptr); + break; + case DW_OP_GNU_entry_value: + { + uint32_t leni = read_uleb128 (ptr); + if ((end - ptr) < leni) + { + error (0, 0, "%s: DWARF DW_OP_GNU_entry_value with too large" + " length", dso->filename); + return 1; + } + if (adjust_location_list (dso, cu, ptr, leni, start, adjust)) + return 1; + ptr += leni; + } + break; + case DW_OP_GNU_const_type: + read_uleb128 (ptr); + ptr += *ptr + 1; + break; + case DW_OP_GNU_deref_type: + ++ptr; + read_uleb128 (ptr); + break; + default: + error (0, 0, "%s: Unknown DWARF DW_OP_%d", dso->filename, op); + return 1; + } + } + return 0; +} + +static int +adjust_dwarf2_ranges (DSO *dso, GElf_Addr offset, GElf_Addr base, + GElf_Addr start, GElf_Addr adjust) +{ + unsigned char *ptr, *endsec; + GElf_Addr low, high; + int adjusted_base; + + ptr = debug_sections[DEBUG_RANGES].data; + if (ptr == NULL) + { + error (0, 0, "%s: DW_AT_ranges attribute, yet no .debug_ranges section", + dso->filename); + return 1; + } + if (offset >= debug_sections[DEBUG_RANGES].size) + { + error (0, 0, + "%s: DW_AT_ranges offset %Ld outside of .debug_ranges section", + dso->filename, (long long) offset); + return 1; + } + endsec = ptr + debug_sections[DEBUG_RANGES].size; + ptr += offset; + adjusted_base = (base && base >= start && addr_to_sec (dso, base) != -1); + while (ptr < endsec) + { + low = read_ptr (ptr); + high = read_ptr (ptr); + if (low == 0 && high == 0) + break; + + if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff)) + { + base = high; + adjusted_base = (base && base >= start + && addr_to_sec (dso, base) != -1); + if (adjusted_base) + write_ptr (ptr - ptr_size, base + adjust); + } + else if (! adjusted_base) + { + if (base + low >= start && addr_to_sec (dso, base + low) != -1) + { + write_ptr (ptr - 2 * ptr_size, low + adjust); + if (high == low) + write_ptr (ptr - ptr_size, high + adjust); + } + if (low != high && base + high >= start + && addr_to_sec (dso, base + high - 1) != -1) + write_ptr (ptr - ptr_size, high + adjust); + } + } + + elf_flagscn (dso->scn[debug_sections[DEBUG_RANGES].sec], ELF_C_SET, + ELF_F_DIRTY); + return 0; +} + +static int +adjust_dwarf2_loc (DSO *dso, struct cu_data *cu, GElf_Addr offset, + GElf_Addr base, GElf_Addr start, GElf_Addr adjust) +{ + unsigned char *ptr, *endsec; + GElf_Addr low, high; + int adjusted_base; + size_t len; + + ptr = debug_sections[DEBUG_LOC].data; + if (ptr == NULL) + { + error (0, 0, "%s: loclistptr attribute, yet no .debug_loc section", + dso->filename); + return 1; + } + if (offset >= debug_sections[DEBUG_LOC].size) + { + error (0, 0, + "%s: loclistptr offset %Ld outside of .debug_loc section", + dso->filename, (long long) offset); + return 1; + } + endsec = ptr + debug_sections[DEBUG_LOC].size; + ptr += offset; + adjusted_base = (base && base >= start && addr_to_sec (dso, base) != -1); + while (ptr < endsec) + { + low = read_ptr (ptr); + high = read_ptr (ptr); + if (low == 0 && high == 0) + break; + + if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff)) + { + base = high; + adjusted_base = (base && base >= start + && addr_to_sec (dso, base) != -1); + if (adjusted_base) + write_ptr (ptr - ptr_size, base + adjust); + continue; + } + len = read_16 (ptr); + assert (ptr + len <= endsec); + + if (adjust_location_list (dso, cu, ptr, len, start, adjust)) + return 1; + + ptr += len; + } + + elf_flagscn (dso->scn[debug_sections[DEBUG_LOC].sec], ELF_C_SET, + ELF_F_DIRTY); + return 0; +} + +static unsigned char * +adjust_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, + struct cu_data *cu, + GElf_Addr start, GElf_Addr adjust, htab_t offset_hash) +{ + int i; + GElf_Addr addr; + + for (i = 0; i < t->nattr; ++i) + { + uint32_t form = t->attr[i].form; + uint32_t len = 0; + + while (1) + { + switch (t->attr[i].attr) + { + case DW_AT_data_member_location: + /* In DWARF4+ DW_AT_data_member_location + with DW_FORM_data[48] is just very high + constant, rather than loclistptr. */ + if (cu->cu_version >= 4 && form != DW_FORM_sec_offset) + break; + /* FALLTHRU */ + case DW_AT_location: + case DW_AT_string_length: + case DW_AT_return_addr: + case DW_AT_frame_base: + case DW_AT_segment: + case DW_AT_static_link: + case DW_AT_use_location: + case DW_AT_vtable_elem_location: + case DW_AT_ranges: + if (form == DW_FORM_data4 || form == DW_FORM_sec_offset) + addr = read_32 (ptr), ptr -= 4; + else if (form == DW_FORM_data8) + addr = read_64 (ptr), ptr -= 8; + else + break; + { + GElf_Addr base; + + if (cu->cu_entry_pc != ~ (GElf_Addr) 0) + base = cu->cu_entry_pc; + else if (cu->cu_low_pc != ~ (GElf_Addr) 0) + base = cu->cu_low_pc; + else + base = 0; + if (t->attr[i].attr == DW_AT_ranges) + { + if (adjust_dwarf2_ranges (dso, addr, base, start, adjust)) + return NULL; + } + else + { + GElf_Addr *offsetp = malloc (sizeof (addr)); + void **slot; + if (offsetp == NULL) + return NULL; + *offsetp = addr; + slot = htab_find_slot (offset_hash, offsetp, INSERT); + if (slot == NULL) + { + free (offsetp); + return NULL; + } + if (*slot == NULL) + { + *slot = offsetp; + if (adjust_dwarf2_loc (dso, cu, addr, base, + start, adjust)) + return NULL; + } + else + free (offsetp); + } + } + break; + } + switch (form) + { + case DW_FORM_addr: + addr = read_ptr (ptr); + if (t->tag == DW_TAG_compile_unit + || t->tag == DW_TAG_partial_unit) + { + if (t->attr[i].attr == DW_AT_entry_pc) + cu->cu_entry_pc = addr; + else if (t->attr[i].attr == DW_AT_low_pc) + cu->cu_low_pc = addr; + if (addr == 0) + break; + } + if (addr >= start + && addr_to_sec (dso, + ((t->attr[i].attr == DW_AT_high_pc + && addr > start) + ? addr - 1 + : addr)) != -1) + write_ptr (ptr - ptr_size, addr + adjust); + break; + case DW_FORM_flag_present: + break; + case DW_FORM_ref1: + case DW_FORM_flag: + case DW_FORM_data1: + ++ptr; + break; + case DW_FORM_ref2: + case DW_FORM_data2: + ptr += 2; + break; + case DW_FORM_ref4: + case DW_FORM_GNU_ref_alt: + case DW_FORM_data4: + case DW_FORM_sec_offset: + ptr += 4; + break; + case DW_FORM_ref8: + case DW_FORM_data8: + case DW_FORM_ref_sig8: + ptr += 8; + break; + case DW_FORM_sdata: + case DW_FORM_ref_udata: + case DW_FORM_udata: + read_uleb128 (ptr); + break; + case DW_FORM_ref_addr: + if (cu->cu_version == 2) + ptr += ptr_size; + else + ptr += 4; + break; + case DW_FORM_strp: + case DW_FORM_GNU_strp_alt: + ptr += 4; + break; + case DW_FORM_string: + ptr = strchr (ptr, '\0') + 1; + break; + case DW_FORM_indirect: + form = read_uleb128 (ptr); + continue; + case DW_FORM_block1: + len = *ptr++; + break; + case DW_FORM_block2: + len = read_16 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block4: + len = read_32 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block: + len = read_uleb128 (ptr); + form = DW_FORM_block1; + assert (len < UINT_MAX); + break; + case DW_FORM_exprloc: + len = read_uleb128 (ptr); + assert (len < UINT_MAX); + break; + default: + error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, + form); + return NULL; + } + + if (form == DW_FORM_block1) + { + switch (t->attr[i].attr) + { + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_byte_size: + case DW_AT_bit_offset: + case DW_AT_bit_size: + case DW_AT_string_length: + case DW_AT_lower_bound: + case DW_AT_return_addr: + case DW_AT_bit_stride: + case DW_AT_upper_bound: + case DW_AT_count: + case DW_AT_segment: + case DW_AT_static_link: + case DW_AT_use_location: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_byte_stride: + case DW_AT_GNU_call_site_value: + case DW_AT_GNU_call_site_data_value: + case DW_AT_GNU_call_site_target: + case DW_AT_GNU_call_site_target_clobbered: + if (adjust_location_list (dso, cu, ptr, len, start, adjust)) + return NULL; + break; + default: + if (t->attr[i].attr <= DW_AT_linkage_name + || (t->attr[i].attr >= DW_AT_MIPS_fde + && t->attr[i].attr <= DW_AT_MIPS_has_inlines) + || (t->attr[i].attr >= DW_AT_sf_names + && t->attr[i].attr <= DW_AT_body_end)) + break; + error (0, 0, "%s: Unknown DWARF DW_AT_%d with block DW_FORM", + dso->filename, t->attr[i].attr); + return NULL; + } + ptr += len; + } + else if (form == DW_FORM_exprloc) + { + if (adjust_location_list (dso, cu, ptr, len, start, adjust)) + return NULL; + ptr += len; + } + + break; + } + } + + return ptr; +} + +static int +adjust_dwarf2_line (DSO *dso, GElf_Addr start, GElf_Addr adjust) +{ + unsigned char *ptr = debug_sections[DEBUG_LINE].data; + unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size; + unsigned char *endcu, *endprol; + unsigned char opcode_base, *opcode_lengths, op; + uint32_t value; + GElf_Addr addr; + int i; + + while (ptr < endsec) + { + endcu = ptr + 4; + endcu += read_32 (ptr); + if (endcu == ptr + 0xffffffff) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); + return 1; + } + + if (endcu > endsec) + { + error (0, 0, "%s: .debug_line CU does not fit into section", + dso->filename); + return 1; + } + + value = read_16 (ptr); + if (value != 2 && value != 3 && value != 4) + { + error (0, 0, "%s: DWARF version %d unhandled", dso->filename, + value); + return 1; + } + + endprol = ptr + 4; + endprol += read_32 (ptr); + if (endprol > endcu) + { + error (0, 0, "%s: .debug_line CU prologue does not fit into CU", + dso->filename); + return 1; + } + + opcode_base = ptr[4 + (value >= 4)]; + opcode_lengths = ptr + 4 + (value >= 4); + + ptr = endprol; + while (ptr < endcu) + { + op = *ptr++; + if (op >= opcode_base) + continue; + if (op == DW_LNS_extended_op) + { + unsigned int len = read_uleb128 (ptr); + + assert (len < UINT_MAX); + op = *ptr++; + switch (op) + { + case DW_LNE_set_address: + addr = read_ptr (ptr); + if (addr >= start && addr_to_sec (dso, addr) != -1) + write_ptr (ptr - ptr_size, addr + adjust); + break; + case DW_LNE_end_sequence: + case DW_LNE_define_file: + case DW_LNE_set_discriminator: + default: + ptr += len - 1; + break; + } + } + else if (op == DW_LNS_fixed_advance_pc) + ptr += 2; + else + for (i = 0; i < opcode_lengths[op]; ++i) + read_uleb128 (ptr); + } + } + + elf_flagscn (dso->scn[debug_sections[DEBUG_LINE].sec], ELF_C_SET, + ELF_F_DIRTY); + return 0; +} + +static int +adjust_dwarf2_aranges (DSO *dso, GElf_Addr start, GElf_Addr adjust) +{ + unsigned char *ptr = debug_sections[DEBUG_ARANGES].data; + unsigned char *endsec = ptr + debug_sections[DEBUG_ARANGES].size; + unsigned char *endcu; + GElf_Addr addr, len; + uint32_t value; + + while (ptr < endsec) + { + endcu = ptr + 4; + endcu += read_32 (ptr); + if (endcu == ptr + 0xffffffff) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); + return 1; + } + + if (endcu > endsec) + { + error (0, 0, "%s: .debug_line CU does not fit into section", + dso->filename); + return 1; + } + + value = read_16 (ptr); + if (value != 2) + { + error (0, 0, "%s: DWARF version %d unhandled", dso->filename, + value); + return 1; + } + + ptr += 4; + if (ptr[0] != ptr_size || ptr[1]) + { + error (0, 0, "%s: Unsupported .debug_aranges address size %d or segment size %d", + dso->filename, ptr[0], ptr[1]); + return 1; + } + + ptr += 6; + while (ptr < endcu) + { + addr = read_ptr (ptr); + len = read_ptr (ptr); + if (addr == 0 && len == 0) + break; + if (addr >= start && addr_to_sec (dso, addr) != -1) + write_ptr (ptr - 2 * ptr_size, addr + adjust); + } + assert (ptr == endcu); + } + + elf_flagscn (dso->scn[debug_sections[DEBUG_LINE].sec], ELF_C_SET, + ELF_F_DIRTY); + return 0; +} + +static int +adjust_dwarf2_frame (DSO *dso, GElf_Addr start, GElf_Addr adjust) +{ + unsigned char *ptr = debug_sections[DEBUG_FRAME].data; + unsigned char *endsec = ptr + debug_sections[DEBUG_FRAME].size; + unsigned char *endie; + GElf_Addr addr, len; + uint32_t value; + + while (ptr < endsec) + { + endie = ptr + 4; + endie += read_32 (ptr); + if (endie == ptr + 0xffffffff) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); + return 1; + } + + if (endie > endsec) + { + error (0, 0, "%s: .debug_frame CIE/FDE does not fit into section", + dso->filename); + return 1; + } + + value = read_32 (ptr); + if (value == 0xffffffff) + { + /* CIE. */ + uint32_t version = *ptr++; + if (version != 1 && version != 3 && version != 4) + { + error (0, 0, "%s: unhandled .debug_frame version %d", + dso->filename, version); + return 1; + } + if (*ptr == 'S') + { + /* This is a signal frame. We don't care. */ + ptr++; + } + if (*ptr != '\0') + { + error (0, 0, "%s: .debug_frame unhandled augmentation \"%s\"", + dso->filename, ptr); + return 1; + } + ptr++; /* Skip augmentation. */ + if (version >= 4) + { + if (ptr[0] != ptr_size) + { + error (0, 0, "%s: .debug_frame unhandled pointer size %d", + dso->filename, ptr[0]); + return 1; + } + if (ptr[1] != 0) + { + error (0, 0, "%s: .debug_frame unhandled non-zero segment size", + dso->filename); + return 1; + } + ptr += 2; + } + read_uleb128 (ptr); /* Skip code_alignment factor. */ + read_uleb128 (ptr); /* Skip data_alignment factor. */ + if (version >= 3) + read_uleb128 (ptr); /* Skip return_address_register. */ + else + ptr++; + } + else + { + addr = read_ptr (ptr); + if (addr >= start && addr_to_sec (dso, addr) != -1) + write_ptr (ptr - ptr_size, addr + adjust); + read_ptr (ptr); /* Skip address range. */ + } + + while (ptr < endie) + { + unsigned char insn = *ptr++; + + if ((insn & 0xc0) == DW_CFA_advance_loc + || (insn & 0xc0) == DW_CFA_restore) + continue; + else if ((insn & 0xc0) == DW_CFA_offset) + { + read_uleb128 (ptr); + continue; + } + switch (insn) + { + case DW_CFA_nop: + case DW_CFA_remember_state: + case DW_CFA_restore_state: + case DW_CFA_GNU_window_save: + break; + case DW_CFA_offset_extended: + case DW_CFA_register: + case DW_CFA_def_cfa: + case DW_CFA_offset_extended_sf: + case DW_CFA_def_cfa_sf: + case DW_CFA_GNU_negative_offset_extended: + case DW_CFA_val_offset: + case DW_CFA_val_offset_sf: + read_uleb128 (ptr); + /* FALLTHROUGH */ + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + case DW_CFA_def_cfa_offset: + case DW_CFA_def_cfa_offset_sf: + case DW_CFA_GNU_args_size: + read_uleb128 (ptr); + break; + case DW_CFA_set_loc: + addr = read_ptr (ptr); + if (addr >= start && addr_to_sec (dso, addr) != -1) + write_ptr (ptr - ptr_size, addr + adjust); + break; + case DW_CFA_advance_loc1: + ptr++; + break; + case DW_CFA_advance_loc2: + ptr += 2; + break; + case DW_CFA_advance_loc4: + ptr += 4; + break; + case DW_CFA_expression: + case DW_CFA_val_expression: + read_uleb128 (ptr); + /* FALLTHROUGH */ + case DW_CFA_def_cfa_expression: + len = read_uleb128 (ptr); + if (adjust_location_list (dso, NULL, ptr, len, start, adjust)) + return 1; + ptr += len; + break; + default: + error (0, 0, "%s: Unhandled DW_CFA_%02x operation", + dso->filename, insn); + return 1; + } + } + } + + elf_flagscn (dso->scn[debug_sections[DEBUG_FRAME].sec], ELF_C_SET, + ELF_F_DIRTY); + return 0; +} + +static hashval_t +loclistoffset_hash (const void *p) +{ + GElf_Addr *offset = (GElf_Addr *)p; + + return *offset; +} + +static int +loclistoffset_eq (const void *p, const void *q) +{ + GElf_Addr *offset1 = (GElf_Addr *)p; + GElf_Addr *offset2 = (GElf_Addr *)q; + + return *offset1 = *offset2; +} + +static void +loclistoffset_del (void *p) +{ + free (p); +} + +static int +adjust_dwarf2_info (DSO *dso, GElf_Addr start, GElf_Addr adjust, int type) +{ + unsigned char *ptr, *endcu, *endsec; + uint32_t value; + htab_t abbrev; + struct abbrev_tag tag, *t; + struct cu_data cu; + htab_t offset_hash = htab_try_create (50, loclistoffset_hash, + loclistoffset_eq, loclistoffset_del); + + if (offset_hash == NULL) + { + error (0, ENOMEM, "%s: Could not create hash for attributes", + dso->filename); + return 1; + } + + memset (&cu, 0, sizeof(cu)); + ptr = debug_sections[type].data; + endsec = ptr + debug_sections[type].size; + while (ptr < endsec) + { + if (ptr + 11 > endsec) + { + error (0, 0, "%s: .debug_info CU header too small", dso->filename); + htab_delete (offset_hash); + return 1; + } + + endcu = ptr + 4; + endcu += read_32 (ptr); + if (endcu == ptr + 0xffffffff) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); + htab_delete (offset_hash); + return 1; + } + + if (endcu > endsec) + { + error (0, 0, "%s: .debug_info too small", dso->filename); + htab_delete (offset_hash); + return 1; + } + + value = read_16 (ptr); + if (value != 2 && value != 3 && value != 4) + { + error (0, 0, "%s: DWARF version %d unhandled", dso->filename, value); + htab_delete (offset_hash); + return 1; + } + cu.cu_version = value; + + value = read_32 (ptr); + if (value >= debug_sections[DEBUG_ABBREV].size) + { + if (debug_sections[DEBUG_ABBREV].data == NULL) + error (0, 0, "%s: .debug_abbrev not present", dso->filename); + else + error (0, 0, "%s: DWARF CU abbrev offset too large", + dso->filename); + htab_delete (offset_hash); + return 1; + } + + if (ptr_size == 0) + { + ptr_size = read_1 (ptr); + if (ptr_size == 4) + { + do_read_ptr = do_read_32_64; + write_ptr = write_32; + } + else if (ptr_size == 8) + { + do_read_ptr = do_read_64; + write_ptr = write_64; + } + else + { + error (0, 0, "%s: Invalid DWARF pointer size %d", + dso->filename, ptr_size); + htab_delete (offset_hash); + return 1; + } + } + else if (read_1 (ptr) != ptr_size) + { + error (0, 0, "%s: DWARF pointer size differs between CUs", + dso->filename); + htab_delete (offset_hash); + return 1; + } + + abbrev = read_abbrev (dso, debug_sections[DEBUG_ABBREV].data + value); + if (abbrev == NULL) + { + htab_delete (offset_hash); + return 1; + } + + cu.cu_entry_pc = ~ (GElf_Addr) 0; + cu.cu_low_pc = ~ (GElf_Addr) 0; + + if (type == DEBUG_TYPES) + { + ptr += 8; /* Skip type_signature. */ + ptr += 4; /* Skip type_offset. */ + } + + while (ptr < endcu) + { + tag.entry = read_uleb128 (ptr); + if (tag.entry == 0) + continue; + t = htab_find_with_hash (abbrev, &tag, tag.entry); + if (t == NULL) + { + error (0, 0, "%s: Could not find DWARF abbreviation %d", + dso->filename, tag.entry); + htab_delete (abbrev); + htab_delete (offset_hash); + return 1; + } + + ptr = adjust_attributes (dso, ptr, t, &cu, start, adjust, + offset_hash); + if (ptr == NULL) + { + htab_delete (abbrev); + htab_delete (offset_hash); + return 1; + } + } + + htab_delete (abbrev); + } + htab_delete (offset_hash); + return 0; +} + +int +adjust_dwarf2 (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) +{ + Elf_Data *data; + Elf_Scn *scn; + int i, j; + + for (i = 0; debug_sections[i].name; ++i) + { + debug_sections[i].data = NULL; + debug_sections[i].size = 0; + debug_sections[i].sec = 0; + } + ptr_size = 0; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (! (dso->shdr[i].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) + && dso->shdr[i].sh_size) + { + const char *name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name); + + if (strncmp (name, ".debug_", sizeof (".debug_") - 1) == 0) + { + for (j = 0; debug_sections[j].name; ++j) + if (strcmp (name, debug_sections[j].name) == 0) + { + if (debug_sections[j].data) + { + error (0, 0, "%s: Found two copies of %s section", + dso->filename, name); + return 1; + } + + scn = dso->scn[i]; + data = elf_getdata (scn, NULL); + assert (data != NULL && data->d_buf != NULL); + assert (elf_getdata (scn, data) == NULL); + assert (data->d_off == 0); + assert (data->d_size == dso->shdr[i].sh_size); + debug_sections[j].data = data->d_buf; + debug_sections[j].size = data->d_size; + debug_sections[j].sec = i; + break; + } + + if (debug_sections[j].name == NULL) + { + error (0, 0, "%s: Unknown debugging section %s", + dso->filename, name); + return 1; + } + } + } + + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) + { + do_read_16 = buf_read_ule16; + do_read_32 = buf_read_ule32; + do_read_32_64 = buf_read_ule32_64; + do_read_64 = buf_read_ule64; + write_32 = dwarf2_write_le32; + write_64 = dwarf2_write_le64; + } + else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) + { + do_read_16 = buf_read_ube16; + do_read_32 = buf_read_ube32; + do_read_32_64 = buf_read_ube32_64; + do_read_64 = buf_read_ube64; + write_32 = dwarf2_write_be32; + write_64 = dwarf2_write_be64; + } + else + { + error (0, 0, "%s: Wrong ELF data enconding", dso->filename); + return 1; + } + + if (debug_sections[DEBUG_INFO].data != NULL + && adjust_dwarf2_info (dso, start, adjust, DEBUG_INFO)) + return 1; + + if (debug_sections[DEBUG_TYPES].data != NULL + && adjust_dwarf2_info (dso, start, adjust, DEBUG_TYPES)) + return 1; + + if (ptr_size == 0) + /* Should not happen. */ + ptr_size = dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64 ? 8 : 4; + + if (debug_sections[DEBUG_LINE].data != NULL + && adjust_dwarf2_line (dso, start, adjust)) + return 1; + + if (debug_sections[DEBUG_ARANGES].data != NULL + && adjust_dwarf2_aranges (dso, start, adjust)) + return 1; + + if (debug_sections[DEBUG_FRAME].data != NULL + && adjust_dwarf2_frame (dso, start, adjust)) + return 1; + + /* .debug_abbrev requires no adjustement. */ + /* .debug_pubnames requires no adjustement. */ + /* .debug_pubtypes requires no adjustement. */ + /* .debug_macinfo requires no adjustement. */ + /* .debug_str requires no adjustement. */ + /* .debug_ranges adjusted for each DW_AT_ranges pointing into it. */ + /* .debug_loc adjusted for each loclistptr pointing into it. */ + + elf_flagscn (dso->scn[n], ELF_C_SET, ELF_F_DIRTY); + return 0; +} diff --git a/src/dwarf2.h b/src/dwarf2.h new file mode 100644 index 0000000..b0c80b6 --- /dev/null +++ b/src/dwarf2.h @@ -0,0 +1,570 @@ +/* Copyright (C) 2001, 2002, 2009, 2010, 2011, 2012 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define DW_TAG_padding 0x00 +#define DW_TAG_array_type 0x01 +#define DW_TAG_class_type 0x02 +#define DW_TAG_entry_point 0x03 +#define DW_TAG_enumeration_type 0x04 +#define DW_TAG_formal_parameter 0x05 +#define DW_TAG_imported_declaration 0x08 +#define DW_TAG_label 0x0a +#define DW_TAG_lexical_block 0x0b +#define DW_TAG_member 0x0d +#define DW_TAG_pointer_type 0x0f +#define DW_TAG_reference_type 0x10 +#define DW_TAG_compile_unit 0x11 +#define DW_TAG_string_type 0x12 +#define DW_TAG_structure_type 0x13 +#define DW_TAG_subroutine_type 0x15 +#define DW_TAG_typedef 0x16 +#define DW_TAG_union_type 0x17 +#define DW_TAG_unspecified_parameters 0x18 +#define DW_TAG_variant 0x19 +#define DW_TAG_common_block 0x1a +#define DW_TAG_common_inclusion 0x1b +#define DW_TAG_inheritance 0x1c +#define DW_TAG_inlined_subroutine 0x1d +#define DW_TAG_module 0x1e +#define DW_TAG_ptr_to_member_type 0x1f +#define DW_TAG_set_type 0x20 +#define DW_TAG_subrange_type 0x21 +#define DW_TAG_with_stmt 0x22 +#define DW_TAG_access_declaration 0x23 +#define DW_TAG_base_type 0x24 +#define DW_TAG_catch_block 0x25 +#define DW_TAG_const_type 0x26 +#define DW_TAG_constant 0x27 +#define DW_TAG_enumerator 0x28 +#define DW_TAG_file_type 0x29 +#define DW_TAG_friend 0x2a +#define DW_TAG_namelist 0x2b +#define DW_TAG_namelist_item 0x2c +#define DW_TAG_packed_type 0x2d +#define DW_TAG_subprogram 0x2e +#define DW_TAG_template_type_param 0x2f +#define DW_TAG_template_value_param 0x30 +#define DW_TAG_thrown_type 0x31 +#define DW_TAG_try_block 0x32 +#define DW_TAG_variant_part 0x33 +#define DW_TAG_variable 0x34 +#define DW_TAG_volatile_type 0x35 +#define DW_TAG_dwarf_procedure 0x36 +#define DW_TAG_restrict_type 0x37 +#define DW_TAG_interface_type 0x38 +#define DW_TAG_namespace 0x39 +#define DW_TAG_imported_module 0x3a +#define DW_TAG_unspecified_type 0x3b +#define DW_TAG_partial_unit 0x3c +#define DW_TAG_imported_unit 0x3d +#define DW_TAG_condition 0x3f +#define DW_TAG_shared_type 0x40 +#define DW_TAG_type_unit 0x41 +#define DW_TAG_rvalue_reference_type 0x42 +#define DW_TAG_template_alias 0x43 +#define DW_TAG_MIPS_loop 0x4081 +#define DW_TAG_format_label 0x4101 +#define DW_TAG_function_template 0x4102 +#define DW_TAG_class_template 0x4103 +#define DW_TAG_GNU_BINCL 0x4104 +#define DW_TAG_GNU_EINCL 0x4105 +#define DW_TAG_GNU_template_template_param 0x4106 +#define DW_TAG_GNU_template_parameter_pack 0x4107 +#define DW_TAG_GNU_formal_parameter_pack 0x4108 +#define DW_TAG_GNU_call_site 0x4109 +#define DW_TAG_GNU_call_site_parameter 0x410a +#define DW_TAG_lo_user 0x4080 +#define DW_TAG_hi_user 0xffff + +#define DW_children_no 0x0 +#define DW_children_yes 0x1 + +#define DW_FORM_addr 0x01 +#define DW_FORM_block2 0x03 +#define DW_FORM_block4 0x04 +#define DW_FORM_data2 0x05 +#define DW_FORM_data4 0x06 +#define DW_FORM_data8 0x07 +#define DW_FORM_string 0x08 +#define DW_FORM_block 0x09 +#define DW_FORM_block1 0x0a +#define DW_FORM_data1 0x0b +#define DW_FORM_flag 0x0c +#define DW_FORM_sdata 0x0d +#define DW_FORM_strp 0x0e +#define DW_FORM_udata 0x0f +#define DW_FORM_ref_addr 0x10 +#define DW_FORM_ref1 0x11 +#define DW_FORM_ref2 0x12 +#define DW_FORM_ref4 0x13 +#define DW_FORM_ref8 0x14 +#define DW_FORM_ref_udata 0x15 +#define DW_FORM_indirect 0x16 +#define DW_FORM_sec_offset 0x17 +#define DW_FORM_exprloc 0x18 +#define DW_FORM_flag_present 0x19 +#define DW_FORM_ref_sig8 0x20 +#define DW_FORM_GNU_ref_alt 0x1f20 +#define DW_FORM_GNU_strp_alt 0x1f21 + +#define DW_AT_sibling 0x01 +#define DW_AT_location 0x02 +#define DW_AT_name 0x03 +#define DW_AT_ordering 0x09 +#define DW_AT_subscr_data 0x0a +#define DW_AT_byte_size 0x0b +#define DW_AT_bit_offset 0x0c +#define DW_AT_bit_size 0x0d +#define DW_AT_element_list 0x0f +#define DW_AT_stmt_list 0x10 +#define DW_AT_low_pc 0x11 +#define DW_AT_high_pc 0x12 +#define DW_AT_language 0x13 +#define DW_AT_member 0x14 +#define DW_AT_discr 0x15 +#define DW_AT_discr_value 0x16 +#define DW_AT_visibility 0x17 +#define DW_AT_import 0x18 +#define DW_AT_string_length 0x19 +#define DW_AT_common_reference 0x1a +#define DW_AT_comp_dir 0x1b +#define DW_AT_const_value 0x1c +#define DW_AT_containing_type 0x1d +#define DW_AT_default_value 0x1e +#define DW_AT_inline 0x20 +#define DW_AT_is_optional 0x21 +#define DW_AT_lower_bound 0x22 +#define DW_AT_producer 0x25 +#define DW_AT_prototyped 0x27 +#define DW_AT_return_addr 0x2a +#define DW_AT_start_scope 0x2c +#define DW_AT_stride_size 0x2e +#define DW_AT_bit_stride 0x2e +#define DW_AT_upper_bound 0x2f +#define DW_AT_abstract_origin 0x31 +#define DW_AT_accessibility 0x32 +#define DW_AT_address_class 0x33 +#define DW_AT_artificial 0x34 +#define DW_AT_base_types 0x35 +#define DW_AT_calling_convention 0x36 +#define DW_AT_count 0x37 +#define DW_AT_data_member_location 0x38 +#define DW_AT_decl_column 0x39 +#define DW_AT_decl_file 0x3a +#define DW_AT_decl_line 0x3b +#define DW_AT_declaration 0x3c +#define DW_AT_discr_list 0x3d +#define DW_AT_encoding 0x3e +#define DW_AT_external 0x3f +#define DW_AT_frame_base 0x40 +#define DW_AT_friend 0x41 +#define DW_AT_identifier_case 0x42 +#define DW_AT_macro_info 0x43 +#define DW_AT_namelist_items 0x44 +#define DW_AT_priority 0x45 +#define DW_AT_segment 0x46 +#define DW_AT_specification 0x47 +#define DW_AT_static_link 0x48 +#define DW_AT_type 0x49 +#define DW_AT_use_location 0x4a +#define DW_AT_variable_parameter 0x4b +#define DW_AT_virtuality 0x4c +#define DW_AT_vtable_elem_location 0x4d +#define DW_AT_allocated 0x4e +#define DW_AT_associated 0x4f +#define DW_AT_data_location 0x50 +#define DW_AT_stride 0x51 +#define DW_AT_byte_stride 0x51 +#define DW_AT_entry_pc 0x52 +#define DW_AT_use_UTF8 0x53 +#define DW_AT_extension 0x54 +#define DW_AT_ranges 0x55 +#define DW_AT_trampoline 0x56 +#define DW_AT_call_column 0x57 +#define DW_AT_call_file 0x58 +#define DW_AT_call_line 0x59 +#define DW_AT_description 0x5a +#define DW_AT_binary_scale 0x5b +#define DW_AT_decimal_scale 0x5c +#define DW_AT_small 0x5d +#define DW_AT_decimal_sign 0x5e +#define DW_AT_digit_count 0x5f +#define DW_AT_picture_string 0x60 +#define DW_AT_mutable 0x61 +#define DW_AT_threads_scaled 0x62 +#define DW_AT_explicit 0x63 +#define DW_AT_object_pointer 0x64 +#define DW_AT_endianity 0x65 +#define DW_AT_elemental 0x66 +#define DW_AT_pure 0x67 +#define DW_AT_recursive 0x68 +#define DW_AT_signature 0x69 +#define DW_AT_main_subprogram 0x6a +#define DW_AT_data_bit_offset 0x6b +#define DW_AT_const_expr 0x6c +#define DW_AT_enum_class 0x6d +#define DW_AT_linkage_name 0x6e +#define DW_AT_MIPS_fde 0x2001 +#define DW_AT_MIPS_loop_begin 0x2002 +#define DW_AT_MIPS_tail_loop_begin 0x2003 +#define DW_AT_MIPS_epilog_begin 0x2004 +#define DW_AT_MIPS_loop_unroll_factor 0x2005 +#define DW_AT_MIPS_software_pipeline_depth 0x2006 +#define DW_AT_MIPS_linkage_name 0x2007 +#define DW_AT_MIPS_stride 0x2008 +#define DW_AT_MIPS_abstract_name 0x2009 +#define DW_AT_MIPS_clone_origin 0x200a +#define DW_AT_MIPS_has_inlines 0x200b +#define DW_AT_sf_names 0x2101 +#define DW_AT_src_info 0x2102 +#define DW_AT_mac_info 0x2103 +#define DW_AT_src_coords 0x2104 +#define DW_AT_body_begin 0x2105 +#define DW_AT_body_end 0x2106 +#define DW_AT_GNU_vector 0x2107 +#define DW_AT_GNU_guarded_by 0x2108 +#define DW_AT_GNU_pt_guarded_by 0x2109 +#define DW_AT_GNU_guarded 0x210a +#define DW_AT_GNU_pt_guarded 0x210b +#define DW_AT_GNU_locks_excluded 0x210c +#define DW_AT_GNU_exclusive_locks_required 0x210d +#define DW_AT_GNU_shared_locks_required 0x210e +#define DW_AT_GNU_odr_signature 0x210f +#define DW_AT_GNU_template_name 0x2110 +#define DW_AT_GNU_call_site_value 0x2111 +#define DW_AT_GNU_call_site_data_value 0x2112 +#define DW_AT_GNU_call_site_target 0x2113 +#define DW_AT_GNU_call_site_target_clobbered 0x2114 +#define DW_AT_GNU_tail_call 0x2115 +#define DW_AT_GNU_all_tail_call_sites 0x2116 +#define DW_AT_GNU_all_call_sites 0x2117 +#define DW_AT_GNU_all_source_call_sites 0x2118 +#define DW_AT_lo_user 0x2000 +#define DW_AT_hi_user 0x3ff0 + +#define DW_OP_addr 0x03 +#define DW_OP_deref 0x06 +#define DW_OP_const1u 0x08 +#define DW_OP_const1s 0x09 +#define DW_OP_const2u 0x0a +#define DW_OP_const2s 0x0b +#define DW_OP_const4u 0x0c +#define DW_OP_const4s 0x0d +#define DW_OP_const8u 0x0e +#define DW_OP_const8s 0x0f +#define DW_OP_constu 0x10 +#define DW_OP_consts 0x11 +#define DW_OP_dup 0x12 +#define DW_OP_drop 0x13 +#define DW_OP_over 0x14 +#define DW_OP_pick 0x15 +#define DW_OP_swap 0x16 +#define DW_OP_rot 0x17 +#define DW_OP_xderef 0x18 +#define DW_OP_abs 0x19 +#define DW_OP_and 0x1a +#define DW_OP_div 0x1b +#define DW_OP_minus 0x1c +#define DW_OP_mod 0x1d +#define DW_OP_mul 0x1e +#define DW_OP_neg 0x1f +#define DW_OP_not 0x20 +#define DW_OP_or 0x21 +#define DW_OP_plus 0x22 +#define DW_OP_plus_uconst 0x23 +#define DW_OP_shl 0x24 +#define DW_OP_shr 0x25 +#define DW_OP_shra 0x26 +#define DW_OP_xor 0x27 +#define DW_OP_bra 0x28 +#define DW_OP_eq 0x29 +#define DW_OP_ge 0x2a +#define DW_OP_gt 0x2b +#define DW_OP_le 0x2c +#define DW_OP_lt 0x2d +#define DW_OP_ne 0x2e +#define DW_OP_skip 0x2f +#define DW_OP_lit0 0x30 +#define DW_OP_lit1 0x31 +#define DW_OP_lit2 0x32 +#define DW_OP_lit3 0x33 +#define DW_OP_lit4 0x34 +#define DW_OP_lit5 0x35 +#define DW_OP_lit6 0x36 +#define DW_OP_lit7 0x37 +#define DW_OP_lit8 0x38 +#define DW_OP_lit9 0x39 +#define DW_OP_lit10 0x3a +#define DW_OP_lit11 0x3b +#define DW_OP_lit12 0x3c +#define DW_OP_lit13 0x3d +#define DW_OP_lit14 0x3e +#define DW_OP_lit15 0x3f +#define DW_OP_lit16 0x40 +#define DW_OP_lit17 0x41 +#define DW_OP_lit18 0x42 +#define DW_OP_lit19 0x43 +#define DW_OP_lit20 0x44 +#define DW_OP_lit21 0x45 +#define DW_OP_lit22 0x46 +#define DW_OP_lit23 0x47 +#define DW_OP_lit24 0x48 +#define DW_OP_lit25 0x49 +#define DW_OP_lit26 0x4a +#define DW_OP_lit27 0x4b +#define DW_OP_lit28 0x4c +#define DW_OP_lit29 0x4d +#define DW_OP_lit30 0x4e +#define DW_OP_lit31 0x4f +#define DW_OP_reg0 0x50 +#define DW_OP_reg1 0x51 +#define DW_OP_reg2 0x52 +#define DW_OP_reg3 0x53 +#define DW_OP_reg4 0x54 +#define DW_OP_reg5 0x55 +#define DW_OP_reg6 0x56 +#define DW_OP_reg7 0x57 +#define DW_OP_reg8 0x58 +#define DW_OP_reg9 0x59 +#define DW_OP_reg10 0x5a +#define DW_OP_reg11 0x5b +#define DW_OP_reg12 0x5c +#define DW_OP_reg13 0x5d +#define DW_OP_reg14 0x5e +#define DW_OP_reg15 0x5f +#define DW_OP_reg16 0x60 +#define DW_OP_reg17 0x61 +#define DW_OP_reg18 0x62 +#define DW_OP_reg19 0x63 +#define DW_OP_reg20 0x64 +#define DW_OP_reg21 0x65 +#define DW_OP_reg22 0x66 +#define DW_OP_reg23 0x67 +#define DW_OP_reg24 0x68 +#define DW_OP_reg25 0x69 +#define DW_OP_reg26 0x6a +#define DW_OP_reg27 0x6b +#define DW_OP_reg28 0x6c +#define DW_OP_reg29 0x6d +#define DW_OP_reg30 0x6e +#define DW_OP_reg31 0x6f +#define DW_OP_breg0 0x70 +#define DW_OP_breg1 0x71 +#define DW_OP_breg2 0x72 +#define DW_OP_breg3 0x73 +#define DW_OP_breg4 0x74 +#define DW_OP_breg5 0x75 +#define DW_OP_breg6 0x76 +#define DW_OP_breg7 0x77 +#define DW_OP_breg8 0x78 +#define DW_OP_breg9 0x79 +#define DW_OP_breg10 0x7a +#define DW_OP_breg11 0x7b +#define DW_OP_breg12 0x7c +#define DW_OP_breg13 0x7d +#define DW_OP_breg14 0x7e +#define DW_OP_breg15 0x7f +#define DW_OP_breg16 0x80 +#define DW_OP_breg17 0x81 +#define DW_OP_breg18 0x82 +#define DW_OP_breg19 0x83 +#define DW_OP_breg20 0x84 +#define DW_OP_breg21 0x85 +#define DW_OP_breg22 0x86 +#define DW_OP_breg23 0x87 +#define DW_OP_breg24 0x88 +#define DW_OP_breg25 0x89 +#define DW_OP_breg26 0x8a +#define DW_OP_breg27 0x8b +#define DW_OP_breg28 0x8c +#define DW_OP_breg29 0x8d +#define DW_OP_breg30 0x8e +#define DW_OP_breg31 0x8f +#define DW_OP_regx 0x90 +#define DW_OP_fbreg 0x91 +#define DW_OP_bregx 0x92 +#define DW_OP_piece 0x93 +#define DW_OP_deref_size 0x94 +#define DW_OP_xderef_size 0x95 +#define DW_OP_nop 0x96 +#define DW_OP_push_object_address 0x97 +#define DW_OP_call2 0x98 +#define DW_OP_call4 0x99 +#define DW_OP_call_ref 0x9a +#define DW_OP_form_tls_address 0x9b +#define DW_OP_call_frame_cfa 0x9c +#define DW_OP_bit_piece 0x9d +#define DW_OP_implicit_value 0x9e +#define DW_OP_stack_value 0x9f +#define DW_OP_GNU_push_tls_address 0xe0 +#define DW_OP_GNU_uninit 0xf0 +#define DW_OP_GNU_encoded_addr 0xf1 +#define DW_OP_GNU_implicit_pointer 0xf2 +#define DW_OP_GNU_entry_value 0xf3 +#define DW_OP_GNU_const_type 0xf4 +#define DW_OP_GNU_regval_type 0xf5 +#define DW_OP_GNU_deref_type 0xf6 +#define DW_OP_GNU_convert 0xf7 +#define DW_OP_GNU_reinterpret 0xf9 +#define DW_OP_GNU_parameter_ref 0xfa +#define DW_OP_lo_user 0xe0 +#define DW_OP_hi_user 0xff + +#define DW_ATE_void 0x0 +#define DW_ATE_address 0x1 +#define DW_ATE_boolean 0x2 +#define DW_ATE_complex_float 0x3 +#define DW_ATE_float 0x4 +#define DW_ATE_signed 0x5 +#define DW_ATE_signed_char 0x6 +#define DW_ATE_unsigned 0x7 +#define DW_ATE_unsigned_char 0x8 +#define DW_ATE_imaginary_float 0x9 +#define DW_ATE_packed_decimal 0xa +#define DW_ATE_numeric_string 0xb +#define DW_ATE_edited 0xc +#define DW_ATE_signed_fixed 0xd +#define DW_ATE_unsigned_fixed 0xe +#define DW_ATE_decimal_float 0xf +#define DW_ATE_lo_user 0x80 +#define DW_ATE_hi_user 0xff + +#define DW_ORD_row_major 0x0 +#define DW_ORD_col_major 0x1 + +#define DW_ACCESS_public 0x1 +#define DW_ACCESS_protected 0x2 +#define DW_ACCESS_private 0x3 + +#define DW_VIS_local 0x1 +#define DW_VIS_exported 0x2 +#define DW_VIS_qualified 0x3 + +#define DW_VIRTUALITY_none 0x0 +#define DW_VIRTUALITY_virtual 0x1 +#define DW_VIRTUALITY_pure_virtual 0x2 + +#define DW_ID_case_sensitive 0x0 +#define DW_ID_up_case 0x1 +#define DW_ID_down_case 0x2 +#define DW_ID_case_insensitive 0x3 + +#define DW_CC_normal 0x1 +#define DW_CC_program 0x2 +#define DW_CC_nocall 0x3 +#define DW_CC_lo_user 0x40 +#define DW_CC_hi_user 0xff + +#define DW_INL_not_inlined 0x0 +#define DW_INL_inlined 0x1 +#define DW_INL_declared_not_inlined 0x2 +#define DW_INL_declared_inlined 0x3 + +#define DW_DSC_label 0x0 +#define DW_DSC_range 0x1 + +#define DW_LNS_extended_op 0x0 +#define DW_LNS_copy 0x1 +#define DW_LNS_advance_pc 0x2 +#define DW_LNS_advance_line 0x3 +#define DW_LNS_set_file 0x4 +#define DW_LNS_set_column 0x5 +#define DW_LNS_negate_stmt 0x6 +#define DW_LNS_set_basic_block 0x7 +#define DW_LNS_const_add_pc 0x8 +#define DW_LNS_fixed_advance_pc 0x9 +#define DW_LNS_set_prologue_end 0xa +#define DW_LNS_set_epilogue_begin 0xb +#define DW_LNS_set_isa 0xc + +#define DW_LNE_end_sequence 0x1 +#define DW_LNE_set_address 0x2 +#define DW_LNE_define_file 0x3 +#define DW_LNE_set_discriminator 0x4 + +#define DW_CFA_advance_loc 0x40 +#define DW_CFA_offset 0x80 +#define DW_CFA_restore 0xc0 +#define DW_CFA_nop 0x00 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_def_cfa_expression 0x0f +#define DW_CFA_expression 0x10 +#define DW_CFA_offset_extended_sf 0x11 +#define DW_CFA_def_cfa_sf 0x12 +#define DW_CFA_def_cfa_offset_sf 0x13 +#define DW_CFA_val_offset 0x14 +#define DW_CFA_val_offset_sf 0x15 +#define DW_CFA_val_expression 0x16 +#define DW_CFA_MIPS_advance_loc8 0x1d +#define DW_CFA_GNU_window_save 0x2d +#define DW_CFA_GNU_args_size 0x2e +#define DW_CFA_GNU_negative_offset_extended 0x2f + +#define DW_CIE_ID 0xffffffff +#define DW_CIE_VERSION 1 + +#define DW_CFA_extended 0 +#define DW_CFA_low_user 0x1c +#define DW_CFA_high_user 0x3f + +#define DW_CHILDREN_no 0x00 +#define DW_CHILDREN_yes 0x01 + +#define DW_ADDR_none 0 + +#define DW_LANG_C89 0x0001 +#define DW_LANG_C 0x0002 +#define DW_LANG_Ada83 0x0003 +#define DW_LANG_C_plus_plus 0x0004 +#define DW_LANG_Cobol74 0x0005 +#define DW_LANG_Cobol85 0x0006 +#define DW_LANG_Fortran77 0x0007 +#define DW_LANG_Fortran90 0x0008 +#define DW_LANG_Pascal83 0x0009 +#define DW_LANG_Modula2 0x000a +#define DW_LANG_Java 0x000b +#define DW_LANG_C99 0x000c +#define DW_LANG_Ada95 0x000d +#define DW_LANG_Fortran95 0x000e +#define DW_LANG_PLI 0x000f +#define DW_LANG_ObjC 0x0010 +#define DW_LANG_ObjC_plus_plus 0x0011 +#define DW_LANG_UPC 0x0012 +#define DW_LANG_D 0x0013 +#define DW_LANG_Python 0x0014 +#define DW_LANG_Mips_Assembler 0x8001 +#define DW_LANG_lo_user 0x8000 +#define DW_LANG_hi_user 0xffff + +#define DW_MACINFO_define 1 +#define DW_MACINFO_undef 2 +#define DW_MACINFO_start_file 3 +#define DW_MACINFO_end_file 4 +#define DW_MACINFO_vendor_ext 255 diff --git a/src/elf.h b/src/elf.h new file mode 100644 index 0000000..fbadda4 --- /dev/null +++ b/src/elf.h @@ -0,0 +1,3562 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _ELF_H +#define _ELF_H 1 + +#include <features.h> + +__BEGIN_DECLS + +/* Standard ELF types. */ + +#include <stdint.h> + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ +#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II */ +#define EM_AARCH64 183 /* ARM AARCH64 */ +#define EM_TILEPRO 188 /* Tilera TILEPro */ +#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */ +#define EM_TILEGX 191 /* Tilera TILE-Gx */ +#define EM_NUM 192 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_BEFORE 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific. */ +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_ORDERED (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE (1U << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section compression header. Used when SHF_COMPRESSED is set. */ + +typedef struct +{ + Elf32_Word ch_type; /* Compression format. */ + Elf32_Word ch_size; /* Uncompressed data size. */ + Elf32_Word ch_addralign; /* Uncompressed data alignment. */ +} Elf32_Chdr; + +typedef struct +{ + Elf64_Word ch_type; /* Compression format. */ + Elf64_Word ch_reserved; + Elf64_Xword ch_size; /* Uncompressed data size. */ + Elf64_Xword ch_addralign; /* Uncompressed data alignment. */ +} Elf64_Chdr; + +/* Legal values for ch_type (compression algorithm). */ +#define ELFCOMPRESS_ZLIB 1 /* ZLIB/DEFLATE algorithm. */ +#define ELFCOMPRESS_LOOS 0x60000000 /* Start of OS-specific. */ +#define ELFCOMPRESS_HIOS 0x6fffffff /* End of OS-specific. */ +#define ELFCOMPRESS_LOPROC 0x70000000 /* Start of processor-specific. */ +#define ELFCOMPRESS_HIPROC 0x7fffffff /* End of processor-specific. */ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_GNU_UNIQUE 10 /* Unique symbol. */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ + +#define PN_XNUM 0xffff + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ +#define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, + size might increase */ +#define NT_FILE 0x46494c45 /* Contains information about mapped + files */ +#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ +#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ +#define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ +#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ +#define NT_S390_CTRS 0x304 /* s390 control registers */ +#define NT_S390_PREFIX 0x305 /* s390 prefix register */ +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ +#define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 11 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ +#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */ +#define DF_1_IGNMULDEF 0x00040000 +#define DF_1_NOKSYMS 0x00080000 +#define DF_1_NOHDR 0x00100000 +#define DF_1_EDITED 0x00200000 /* Object is modified after built. */ +#define DF_1_NORELOC 0x00400000 +#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */ +#define DF_1_GLOBAUDIT 0x01000000 /* Global auditing required. */ +#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */ + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxialiary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard <elf.h> file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + uint32_t a_type; /* Entry type */ + union + { + uint32_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + uint64_t a_type; /* Entry type */ + union + { + uint64_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine-dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ + +#define AT_RANDOM 25 /* Address of 16 random bytes. */ + +#define AT_HWCAP2 26 /* More machine-dependent hints about + processor capabilities. */ + +#define AT_EXECFN 31 /* Filename of executable. */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + +/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains + log2 of line size; mask those to get cache size. */ +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ + +/* Known OSes. These values can appear in word 0 of an + NT_GNU_ABI_TAG note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + +/* Synthetic hwcap information. The descriptor begins with two words: + word 0: number of entries + word 1: bitmask of enabled entries + Then follow variable-length entries, one byte followed by a + '\0'-terminated hwcap name string. The byte gives the bit + number to test if enabled, (1U << bit) & bitmask. */ +#define NT_GNU_HWCAP 2 + +/* Build ID bits as generated by ld --build-id. + The descriptor consists of any nonzero number of bytes. */ +#define NT_GNU_BUILD_ID 3 + +/* Version note generated by GNU gold containing a version string. */ +#define NT_GNU_GOLD_VERSION 4 + + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ +#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ +#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ +#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ +#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ +#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ +#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ +#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ +#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ +#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ +#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ +#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ +#define R_68K_TLS_LE32 37 /* 32 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE16 38 /* 16 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE8 39 /* 8 bit offset relative to + static TLS block */ +#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ +#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ +#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ +/* Keep this the last entry. */ +#define R_68K_NUM 43 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +#define R_386_SIZE32 38 /* 32-bit symbol size */ +#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ +#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS + descriptor for + relaxation. */ +#define R_386_TLS_DESC 41 /* TLS descriptor containing + pointer to code and to + argument, returning the TLS + offset for the symbol. */ +#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ +/* Keep this the last entry. */ +#define R_386_NUM 43 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +#define R_SPARC_GOTDATA_HIX22 80 +#define R_SPARC_GOTDATA_LOX10 81 +#define R_SPARC_GOTDATA_OP_HIX22 82 +#define R_SPARC_GOTDATA_OP_LOX10 83 +#define R_SPARC_GOTDATA_OP 84 +#define R_SPARC_H34 85 +#define R_SPARC_SIZE32 86 +#define R_SPARC_SIZE64 87 +#define R_SPARC_WDISP10 88 +#define R_SPARC_JMP_IREL 248 +#define R_SPARC_IRELATIVE 249 +#define R_SPARC_GNU_VTINHERIT 250 +#define R_SPARC_GNU_VTENTRY 251 +#define R_SPARC_REV32 252 +/* Keep this the last entry. */ +#define R_SPARC_NUM 253 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */ +#define EF_MIPS_PIC 2 /* Contains PIC code. */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */ +#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */ +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ +#define EF_MIPS_ARCH_32R2 0x70000000 /* MIPS32r2 code. */ +#define EF_MIPS_ARCH_64R2 0x80000000 /* MIPS64r2 code. */ + +/* The following are unofficial names and should not be used. */ + +#define E_MIPS_ARCH_1 EF_MIPS_ARCH_1 +#define E_MIPS_ARCH_2 EF_MIPS_ARCH_2 +#define E_MIPS_ARCH_3 EF_MIPS_ARCH_3 +#define E_MIPS_ARCH_4 EF_MIPS_ARCH_4 +#define E_MIPS_ARCH_5 EF_MIPS_ARCH_5 +#define E_MIPS_ARCH_32 EF_MIPS_ARCH_32 +#define E_MIPS_ARCH_64 EF_MIPS_ARCH_64 + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols. */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols. */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link. */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols. */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes. */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging info. */ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information. */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be in global data area. */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_PLT 0x8 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation. */ + Elf32_Word gt_unused; /* Not used. */ + } gt_header; /* First entry in section. */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G. */ + Elf32_Word gt_bytes; /* This many bytes would be used. */ + } gt_entry; /* Subsequent entries in section. */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used. */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used. */ + Elf32_Sword ri_gp_value; /* $gp register value. */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ +#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ +#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ +#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ +#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ +#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ +#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ +#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ +#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ +#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ +#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ +#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ +#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ +#define R_MIPS_GLOB_DAT 51 +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 +/* Keep this the last entry. */ +#define R_MIPS_NUM 128 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information. */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 +#define PT_MIPS_ABIFLAGS 0x70000003 /* FP mode requirement. */ + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +/* The address of .got.plt in an executable using the new non-PIC ABI. */ +#define DT_MIPS_PLTGOT 0x70000032 +/* The base of the PLT in an executable using the new non-PIC ABI if that + PLT is writable. For a non-writable PLT, this is omitted or has a zero + value. */ +#define DT_MIPS_RWPLT 0x70000034 +/* An alternative description of the classic MIPS RLD_MAP that is usable + in a PIE as it stores a relative offset from the address of the tag + rather than an absolute address. */ +#define DT_MIPS_RLD_MAP_REL 0x70000035 +#define DT_MIPS_NUM 0x36 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + +typedef struct +{ + /* Version of flags structure. */ + Elf32_Half version; + /* The level of the ISA: 1-5, 32, 64. */ + unsigned char isa_level; + /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ + unsigned char isa_rev; + /* The size of general purpose registers. */ + unsigned char gpr_size; + /* The size of co-processor 1 registers. */ + unsigned char cpr1_size; + /* The size of co-processor 2 registers. */ + unsigned char cpr2_size; + /* The floating-point ABI. */ + unsigned char fp_abi; + /* Processor-specific extension. */ + Elf32_Word isa_ext; + /* Mask of ASEs used. */ + Elf32_Word ases; + /* Mask of general flags. */ + Elf32_Word flags1; + Elf32_Word flags2; +} Elf_MIPS_ABIFlags_v0; + +/* Values for the register size bytes of an abi flags structure. */ + +#define MIPS_AFL_REG_NONE 0x00 /* No registers. */ +#define MIPS_AFL_REG_32 0x01 /* 32-bit registers. */ +#define MIPS_AFL_REG_64 0x02 /* 64-bit registers. */ +#define MIPS_AFL_REG_128 0x03 /* 128-bit registers. */ + +/* Masks for the ases word of an ABI flags structure. */ + +#define MIPS_AFL_ASE_DSP 0x00000001 /* DSP ASE. */ +#define MIPS_AFL_ASE_DSPR2 0x00000002 /* DSP R2 ASE. */ +#define MIPS_AFL_ASE_EVA 0x00000004 /* Enhanced VA Scheme. */ +#define MIPS_AFL_ASE_MCU 0x00000008 /* MCU (MicroController) ASE. */ +#define MIPS_AFL_ASE_MDMX 0x00000010 /* MDMX ASE. */ +#define MIPS_AFL_ASE_MIPS3D 0x00000020 /* MIPS-3D ASE. */ +#define MIPS_AFL_ASE_MT 0x00000040 /* MT ASE. */ +#define MIPS_AFL_ASE_SMARTMIPS 0x00000080 /* SmartMIPS ASE. */ +#define MIPS_AFL_ASE_VIRT 0x00000100 /* VZ ASE. */ +#define MIPS_AFL_ASE_MSA 0x00000200 /* MSA ASE. */ +#define MIPS_AFL_ASE_MIPS16 0x00000400 /* MIPS16 ASE. */ +#define MIPS_AFL_ASE_MICROMIPS 0x00000800 /* MICROMIPS ASE. */ +#define MIPS_AFL_ASE_XPA 0x00001000 /* XPA ASE. */ +#define MIPS_AFL_ASE_MASK 0x00001fff /* All ASEs. */ + +/* Values for the isa_ext word of an ABI flags structure. */ + +#define MIPS_AFL_EXT_XLR 1 /* RMI Xlr instruction. */ +#define MIPS_AFL_EXT_OCTEON2 2 /* Cavium Networks Octeon2. */ +#define MIPS_AFL_EXT_OCTEONP 3 /* Cavium Networks OcteonP. */ +#define MIPS_AFL_EXT_LOONGSON_3A 4 /* Loongson 3A. */ +#define MIPS_AFL_EXT_OCTEON 5 /* Cavium Networks Octeon. */ +#define MIPS_AFL_EXT_5900 6 /* MIPS R5900 instruction. */ +#define MIPS_AFL_EXT_4650 7 /* MIPS R4650 instruction. */ +#define MIPS_AFL_EXT_4010 8 /* LSI R4010 instruction. */ +#define MIPS_AFL_EXT_4100 9 /* NEC VR4100 instruction. */ +#define MIPS_AFL_EXT_3900 10 /* Toshiba R3900 instruction. */ +#define MIPS_AFL_EXT_10000 11 /* MIPS R10000 instruction. */ +#define MIPS_AFL_EXT_SB1 12 /* Broadcom SB-1 instruction. */ +#define MIPS_AFL_EXT_4111 13 /* NEC VR4111/VR4181 instruction. */ +#define MIPS_AFL_EXT_4120 14 /* NEC VR4120 instruction. */ +#define MIPS_AFL_EXT_5400 15 /* NEC VR5400 instruction. */ +#define MIPS_AFL_EXT_5500 16 /* NEC VR5500 instruction. */ +#define MIPS_AFL_EXT_LOONGSON_2E 17 /* ST Microelectronics Loongson 2E. */ +#define MIPS_AFL_EXT_LOONGSON_2F 18 /* ST Microelectronics Loongson 2F. */ + +/* Masks for the flags1 word of an ABI flags structure. */ +#define MIPS_AFL_FLAGS1_ODDSPREG 1 /* Uses odd single-precision registers. */ + +/* Object attribute values. */ +enum +{ + /* Not tagged or not using any ABIs affected by the differences. */ + Val_GNU_MIPS_ABI_FP_ANY = 0, + /* Using hard-float -mdouble-float. */ + Val_GNU_MIPS_ABI_FP_DOUBLE = 1, + /* Using hard-float -msingle-float. */ + Val_GNU_MIPS_ABI_FP_SINGLE = 2, + /* Using soft-float. */ + Val_GNU_MIPS_ABI_FP_SOFT = 3, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_OLD_64 = 4, + /* Using -mfpxx. */ + Val_GNU_MIPS_ABI_FP_XX = 5, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_64 = 6, + /* Using -mips32r2 -mfp64 -mno-odd-spreg. */ + Val_GNU_MIPS_ABI_FP_64A = 7, + /* Maximum allocated FP ABI value. */ + Val_GNU_MIPS_ABI_FP_MAX = 7 +}; + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indeces. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ +#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_GNU_VTENTRY 232 +#define R_PARISC_GNU_VTINHERIT 233 +#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ +#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ +#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ +#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ +#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ +#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ +#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ +#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ +#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ +#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ +#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L +#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R +#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L +#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R +#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 +#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + +/* Legal values for d_tag of Elf64_Dyn. */ +#define DT_ALPHA_PLTRO (DT_LOPROC + 0) +#define DT_ALPHA_NUM 1 + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ +#define R_PPC_TLSGD 95 /* none (sym+add)@tlsgd */ +#define R_PPC_TLSLD 96 /* none (sym+add)@tlsld */ + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* GNU extension to support local ifunc. */ +#define R_PPC_IRELATIVE 248 + +/* GNU relocs used in PIC code sequences. */ +#define R_PPC_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +/* PowerPC specific values for the Dyn d_tag field. */ +#define DT_PPC_GOT (DT_LOPROC + 0) +#define DT_PPC_OPT (DT_LOPROC + 1) +#define DT_PPC_NUM 2 + +/* PowerPC specific values for the DT_PPC_OPT Dyn entry. */ +#define PPC_OPT_TLS 1 + +/* PowerPC64 relocations defined by the ABIs */ +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ +#define R_PPC64_TLSGD 107 /* none (sym+add)@tlsgd */ +#define R_PPC64_TLSLD 108 /* none (sym+add)@tlsld */ +#define R_PPC64_TOCSAVE 109 /* none */ + +/* Added when HA and HI relocs were changed to report overflows. */ +#define R_PPC64_ADDR16_HIGH 110 +#define R_PPC64_ADDR16_HIGHA 111 +#define R_PPC64_TPREL16_HIGH 112 +#define R_PPC64_TPREL16_HIGHA 113 +#define R_PPC64_DTPREL16_HIGH 114 +#define R_PPC64_DTPREL16_HIGHA 115 + +/* GNU extension to support local ifunc. */ +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* e_flags bits specifying ABI. + 1 for original function descriptor using ABI, + 2 for revised ABI without function descriptors, + 0 for unspecified or not using any features affected by the differences. */ +#define EF_PPC64_ABI 3 + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_OPT (DT_LOPROC + 3) +#define DT_PPC64_NUM 4 + +/* PowerPC64 specific values for the DT_PPC64_OPT Dyn entry. */ +#define PPC64_OPT_TLS 1 +#define PPC64_OPT_MULTI_TOC 2 + +/* PowerPC64 specific values for the Elf64_Sym st_other field. */ +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ + (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */ +#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */ + + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +/* Constants defined in AAELF. */ +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step. */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base. */ +#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ +#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ + + +/* AArch64 relocs. */ + +#define R_AARCH64_NONE 0 /* No relocation. */ + +/* ILP32 AArch64 relocs. */ +#define R_AARCH64_P32_ABS32 1 /* Direct 32 bit. */ +#define R_AARCH64_P32_COPY 180 /* Copy symbol at runtime. */ +#define R_AARCH64_P32_GLOB_DAT 181 /* Create GOT entry. */ +#define R_AARCH64_P32_JUMP_SLOT 182 /* Create PLT entry. */ +#define R_AARCH64_P32_RELATIVE 183 /* Adjust by program base. */ +#define R_AARCH64_P32_TLS_DTPMOD 184 /* Module number, 32 bit. */ +#define R_AARCH64_P32_TLS_DTPREL 185 /* Module-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLS_TPREL 186 /* TP-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLSDESC 187 /* TLS Descriptor. */ +#define R_AARCH64_P32_IRELATIVE 188 /* STT_GNU_IFUNC relocation. */ + +/* LP64 AArch64 relocs. */ +#define R_AARCH64_ABS64 257 /* Direct 64 bit. */ +#define R_AARCH64_ABS32 258 /* Direct 32 bit. */ +#define R_AARCH64_ABS16 259 /* Direct 16-bit. */ +#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */ +#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */ +#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */ +#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */ +#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */ +#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */ +#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */ +#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */ +#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */ +#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */ +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */ +#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */ +#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */ +#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */ +#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */ +#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */ +#define R_AARCH64_CALL26 283 /* Likewise for CALL. */ +#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */ +#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */ +#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */ +#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */ +#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */ +#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */ +#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */ +#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */ +#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */ +#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */ +#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */ +#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */ +#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */ +#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */ +#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */ +#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */ +#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */ +#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */ +#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */ +#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */ +#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */ +#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */ +#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */ +#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */ +#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */ +#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */ +#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */ +#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */ +#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */ +#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */ +#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */ +#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */ +#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */ +#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */ +#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ +#define R_AARCH64_TLS_DTPMOD 1028 /* Module number, 64 bit. */ +#define R_AARCH64_TLS_DTPREL 1029 /* Module-relative offset, 64 bit. */ +#define R_AARCH64_TLS_TPREL 1030 /* TP-relative offset, 64 bit. */ +#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ +#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */ + +/* ARM relocs. */ + +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* Deprecated PC relative 26 + bit branch. */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 /* Direct & 0x7C (LDR, STR). */ +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 /* PC relative 24 bit (Thumb32 BL). */ +#define R_ARM_THM_PC8 11 /* PC relative & 0x3FC + (Thumb16 LDR, ADD, ADR). */ +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 /* Obsolete static relocation. */ +#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ +#define R_ARM_THM_SWI8 14 /* Reserved. */ +#define R_ARM_XPC25 15 /* Reserved. */ +#define R_ARM_THM_XPC22 16 /* Reserved. */ +#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ +#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ +#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* Deprecated, 32 bit PLT address. */ +#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */ +#define R_ARM_JUMP24 29 /* PC relative 24 bit + (B, BL<cond>). */ +#define R_ARM_THM_JUMP24 30 /* PC relative 24 bit (Thumb32 B.W). */ +#define R_ARM_BASE_ABS 31 /* Adjust by program base. */ +#define R_ARM_ALU_PCREL_7_0 32 /* Obsolete. */ +#define R_ARM_ALU_PCREL_15_8 33 /* Obsolete. */ +#define R_ARM_ALU_PCREL_23_15 34 /* Obsolete. */ +#define R_ARM_LDR_SBREL_11_0 35 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_19_12 36 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_27_20 37 /* Deprecated, prog. base relative. */ +#define R_ARM_TARGET1 38 +#define R_ARM_SBREL31 39 /* Program base relative. */ +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 /* 32 bit PC relative. */ +#define R_ARM_MOVW_ABS_NC 43 /* Direct 16-bit (MOVW). */ +#define R_ARM_MOVT_ABS 44 /* Direct high 16-bit (MOVT). */ +#define R_ARM_MOVW_PREL_NC 45 /* PC relative 16-bit (MOVW). */ +#define R_ARM_MOVT_PREL 46 /* PC relative (MOVT). */ +#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_PREL_NC 49 /* PC relative 16 bit + (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_PREL 50 /* PC relative high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_JUMP19 51 /* PC relative 20 bit + (Thumb32 B<cond>.W). */ +#define R_ARM_THM_JUMP6 52 /* PC relative X & 0x7E + (Thumb16 CBZ, CBNZ). */ +#define R_ARM_THM_ALU_PREL_11_0 53 /* PC relative 12 bit + (Thumb32 ADR.W). */ +#define R_ARM_THM_PC12 54 /* PC relative 12 bit + (Thumb32 LDR{D,SB,H,SH}). */ +#define R_ARM_ABS32_NOI 55 /* Direct 32-bit. */ +#define R_ARM_REL32_NOI 56 /* PC relative 32-bit. */ +#define R_ARM_ALU_PC_G0_NC 57 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G0 58 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1_NC 59 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1 60 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G2 61 /* PC relative (ADD, SUB). */ +#define R_ARM_LDR_PC_G1 62 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDR_PC_G2 63 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDRS_PC_G0 64 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G1 65 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G2 66 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDC_PC_G0 67 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G1 68 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G2 69 /* PC relative (LDC, STC). */ +#define R_ARM_ALU_SB_G0_NC 70 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G0 71 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1_NC 72 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1 73 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G2 74 /* Program base relative (ADD,SUB). */ +#define R_ARM_LDR_SB_G0 75 /* Program base relative (LDR, + STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G1 76 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G2 77 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G0 78 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G1 79 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G2 80 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDC_SB_G0 81 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G1 82 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G2 83 /* Program base relative (LDC,STC). */ +#define R_ARM_MOVW_BREL_NC 84 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_MOVT_BREL 85 /* Program base relative high + 16 bit (MOVT). */ +#define R_ARM_MOVW_BREL 86 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_THM_MOVW_BREL_NC 87 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_BREL 88 /* Program base relative high + 16 bit (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_BREL 89 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_TLS_GOTDESC 90 +#define R_ARM_TLS_CALL 91 +#define R_ARM_TLS_DESCSEQ 92 /* TLS relaxation. */ +#define R_ARM_THM_TLS_CALL 93 +#define R_ARM_PLT32_ABS 94 +#define R_ARM_GOT_ABS 95 /* GOT entry. */ +#define R_ARM_GOT_PREL 96 /* PC relative GOT entry. */ +#define R_ARM_GOT_BREL12 97 /* GOT entry relative to GOT + origin (LDR). */ +#define R_ARM_GOTOFF12 98 /* 12 bit, GOT entry relative + to GOT origin (LDR, STR). */ +#define R_ARM_GOTRELAX 99 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* PC relative & 0xFFE (Thumb16 B). */ +#define R_ARM_THM_PC9 103 /* PC relative & 0x1FE + (Thumb16 B/B<cond>). */ +#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic + thread local data */ +#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic + thread local data */ +#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS + block */ +#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of + static TLS block offset */ +#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static + TLS block */ +#define R_ARM_TLS_LDO12 109 /* 12 bit relative to TLS + block (LDR, STR). */ +#define R_ARM_TLS_LE12 110 /* 12 bit relative to static + TLS block (LDR, STR). */ +#define R_ARM_TLS_IE12GP 111 /* 12 bit GOT entry relative + to GOT origin (LDR). */ +#define R_ARM_ME_TOO 128 /* Obsolete. */ +#define R_ARM_THM_TLS_DESCSEQ 129 +#define R_ARM_THM_TLS_DESCSEQ16 129 +#define R_ARM_THM_TLS_DESCSEQ32 130 +#define R_ARM_THM_GOT_BREL12 131 /* GOT entry relative to GOT + origin, 12 bit (Thumb32 LDR). */ +#define R_ARM_IRELATIVE 160 +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_SH_MACH_MASK 0x1f +#define EF_SH_UNKNOWN 0x0 +#define EF_SH1 0x1 +#define EF_SH2 0x2 +#define EF_SH3 0x3 +#define EF_SH_DSP 0x4 +#define EF_SH3_DSP 0x5 +#define EF_SH4AL_DSP 0x6 +#define EF_SH3E 0x8 +#define EF_SH4 0x9 +#define EF_SH2E 0xb +#define EF_SH4A 0xc +#define EF_SH2A 0xd +#define EF_SH4_NOFPU 0x10 +#define EF_SH4A_NOFPU 0x11 +#define EF_SH4_NOMMU_NOFPU 0x12 +#define EF_SH2A_NOFPU 0x13 +#define EF_SH3_NOMMU 0x14 +#define EF_SH2A_SH4_NOFPU 0x15 +#define EF_SH2A_SH3_NOFPU 0x16 +#define EF_SH2A_SH4 0x17 +#define EF_SH2A_SH3E 0x18 + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* S/390 specific definitions. */ + +/* Valid values for the e_flags field. */ + +#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ +#define R_390_20 57 /* Direct 20 bit. */ +#define R_390_GOT20 58 /* 20 bit GOT offset. */ +#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ +#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS + block offset. */ +#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ +/* Keep this the last entry. */ +#define R_390_NUM 62 + + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ +#define R_X86_64_PC64 24 /* PC relative 64 bit */ +#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ +#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative + offset to GOT */ +#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ +#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset + to GOT entry */ +#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ +#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ +#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset + to PLT entry */ +#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ +#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ +#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ +#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS + descriptor. */ +#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ +#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ +#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ + +#define R_X86_64_NUM 39 + + +/* AM33 relocations. */ +#define R_MN10300_NONE 0 /* No reloc. */ +#define R_MN10300_32 1 /* Direct 32 bit. */ +#define R_MN10300_16 2 /* Direct 16 bit. */ +#define R_MN10300_8 3 /* Direct 8 bit. */ +#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ +#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ +#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ +#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ +#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ +#define R_MN10300_24 9 /* Direct 24 bit. */ +#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ +#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ +#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ +#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ +#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ +#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ +#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ +#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ +#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ +#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ +#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ +#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ +#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ +#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ +#define R_MN10300_TLS_GD 24 /* 32-bit offset for global dynamic. */ +#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */ +#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */ +#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block + offset. */ +#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block + offset. */ +#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS + block. */ +#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */ +#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */ +#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */ +#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed + by linker relaxation. */ +#define R_MN10300_ALIGN 34 /* Alignment requirement for linker + relaxation. */ +#define R_MN10300_NUM 35 + + +/* M32R relocs. */ +#define R_M32R_NONE 0 /* No reloc. */ +#define R_M32R_16 1 /* Direct 16 bit. */ +#define R_M32R_32 2 /* Direct 32 bit. */ +#define R_M32R_24 3 /* Direct 24 bit. */ +#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ +#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ +#define R_M32R_LO16 9 /* Low 16 bit. */ +#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 +/* M32R relocs use SHT_RELA. */ +#define R_M32R_16_RELA 33 /* Direct 16 bit. */ +#define R_M32R_32_RELA 34 /* Direct 32 bit. */ +#define R_M32R_24_RELA 35 /* Direct 24 bit. */ +#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ +#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ +#define R_M32R_LO16_RELA 41 /* Low 16 bit */ +#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ +#define R_M32R_RELA_GNU_VTINHERIT 43 +#define R_M32R_RELA_GNU_VTENTRY 44 +#define R_M32R_REL32 45 /* PC relative 32 bit. */ + +#define R_M32R_GOT24 48 /* 24 bit GOT entry */ +#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ +#define R_M32R_COPY 50 /* Copy symbol at runtime */ +#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ +#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ +#define R_M32R_RELATIVE 53 /* Adjust by program base */ +#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ +#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ +#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned + low */ +#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed + low */ +#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ +#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to + GOT with unsigned low */ +#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to + GOT with signed low */ +#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to + GOT */ +#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT + with unsigned low */ +#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT + with signed low */ +#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ +#define R_M32R_NUM 256 /* Keep this the last entry. */ + +/* MicroBlaze relocations */ +#define R_MICROBLAZE_NONE 0 /* No reloc. */ +#define R_MICROBLAZE_32 1 /* Direct 32 bit. */ +#define R_MICROBLAZE_32_PCREL 2 /* PC relative 32 bit. */ +#define R_MICROBLAZE_64_PCREL 3 /* PC relative 64 bit. */ +#define R_MICROBLAZE_32_PCREL_LO 4 /* Low 16 bits of PCREL32. */ +#define R_MICROBLAZE_64 5 /* Direct 64 bit. */ +#define R_MICROBLAZE_32_LO 6 /* Low 16 bit. */ +#define R_MICROBLAZE_SRO32 7 /* Read-only small data area. */ +#define R_MICROBLAZE_SRW32 8 /* Read-write small data area. */ +#define R_MICROBLAZE_64_NONE 9 /* No reloc. */ +#define R_MICROBLAZE_32_SYM_OP_SYM 10 /* Symbol Op Symbol relocation. */ +#define R_MICROBLAZE_GNU_VTINHERIT 11 /* GNU C++ vtable hierarchy. */ +#define R_MICROBLAZE_GNU_VTENTRY 12 /* GNU C++ vtable member usage. */ +#define R_MICROBLAZE_GOTPC_64 13 /* PC-relative GOT offset. */ +#define R_MICROBLAZE_GOT_64 14 /* GOT entry offset. */ +#define R_MICROBLAZE_PLT_64 15 /* PLT offset (PC-relative). */ +#define R_MICROBLAZE_REL 16 /* Adjust by program base. */ +#define R_MICROBLAZE_JUMP_SLOT 17 /* Create PLT entry. */ +#define R_MICROBLAZE_GLOB_DAT 18 /* Create GOT entry. */ +#define R_MICROBLAZE_GOTOFF_64 19 /* 64 bit offset to GOT. */ +#define R_MICROBLAZE_GOTOFF_32 20 /* 32 bit offset to GOT. */ +#define R_MICROBLAZE_COPY 21 /* Runtime copy. */ +#define R_MICROBLAZE_TLS 22 /* TLS Reloc. */ +#define R_MICROBLAZE_TLSGD 23 /* TLS General Dynamic. */ +#define R_MICROBLAZE_TLSLD 24 /* TLS Local Dynamic. */ +#define R_MICROBLAZE_TLSDTPMOD32 25 /* TLS Module ID. */ +#define R_MICROBLAZE_TLSDTPREL32 26 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSDTPREL64 27 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSGOTTPREL32 28 /* TLS Offset From Thread Pointer. */ +#define R_MICROBLAZE_TLSTPREL32 29 /* TLS Offset From Thread Pointer. */ + +/* Legal values for d_tag (dynamic entry type). */ +#define DT_NIOS2_GP 0x70000002 /* Address of _gp. */ + +/* Nios II relocations. */ +#define R_NIOS2_NONE 0 /* No reloc. */ +#define R_NIOS2_S16 1 /* Direct signed 16 bit. */ +#define R_NIOS2_U16 2 /* Direct unsigned 16 bit. */ +#define R_NIOS2_PCREL16 3 /* PC relative 16 bit. */ +#define R_NIOS2_CALL26 4 /* Direct call. */ +#define R_NIOS2_IMM5 5 /* 5 bit constant expression. */ +#define R_NIOS2_CACHE_OPX 6 /* 5 bit expression, shift 22. */ +#define R_NIOS2_IMM6 7 /* 6 bit constant expression. */ +#define R_NIOS2_IMM8 8 /* 8 bit constant expression. */ +#define R_NIOS2_HI16 9 /* High 16 bit. */ +#define R_NIOS2_LO16 10 /* Low 16 bit. */ +#define R_NIOS2_HIADJ16 11 /* High 16 bit, adjusted. */ +#define R_NIOS2_BFD_RELOC_32 12 /* 32 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_16 13 /* 16 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_8 14 /* 8 bit symbol value + addend. */ +#define R_NIOS2_GPREL 15 /* 16 bit GP pointer offset. */ +#define R_NIOS2_GNU_VTINHERIT 16 /* GNU C++ vtable hierarchy. */ +#define R_NIOS2_GNU_VTENTRY 17 /* GNU C++ vtable member usage. */ +#define R_NIOS2_UJMP 18 /* Unconditional branch. */ +#define R_NIOS2_CJMP 19 /* Conditional branch. */ +#define R_NIOS2_CALLR 20 /* Indirect call through register. */ +#define R_NIOS2_ALIGN 21 /* Alignment requirement for + linker relaxation. */ +#define R_NIOS2_GOT16 22 /* 16 bit GOT entry. */ +#define R_NIOS2_CALL16 23 /* 16 bit GOT entry for function. */ +#define R_NIOS2_GOTOFF_LO 24 /* %lo of offset to GOT pointer. */ +#define R_NIOS2_GOTOFF_HA 25 /* %hiadj of offset to GOT pointer. */ +#define R_NIOS2_PCREL_LO 26 /* %lo of PC relative offset. */ +#define R_NIOS2_PCREL_HA 27 /* %hiadj of PC relative offset. */ +#define R_NIOS2_TLS_GD16 28 /* 16 bit GOT offset for TLS GD. */ +#define R_NIOS2_TLS_LDM16 29 /* 16 bit GOT offset for TLS LDM. */ +#define R_NIOS2_TLS_LDO16 30 /* 16 bit module relative offset. */ +#define R_NIOS2_TLS_IE16 31 /* 16 bit GOT offset for TLS IE. */ +#define R_NIOS2_TLS_LE16 32 /* 16 bit LE TP-relative offset. */ +#define R_NIOS2_TLS_DTPMOD 33 /* Module number. */ +#define R_NIOS2_TLS_DTPREL 34 /* Module-relative offset. */ +#define R_NIOS2_TLS_TPREL 35 /* TP-relative offset. */ +#define R_NIOS2_COPY 36 /* Copy symbol at runtime. */ +#define R_NIOS2_GLOB_DAT 37 /* Create GOT entry. */ +#define R_NIOS2_JUMP_SLOT 38 /* Create PLT entry. */ +#define R_NIOS2_RELATIVE 39 /* Adjust by program base. */ +#define R_NIOS2_GOTOFF 40 /* 16 bit offset to GOT pointer. */ +#define R_NIOS2_CALL26_NOAT 41 /* Direct call in .noat section. */ +#define R_NIOS2_GOT_LO 42 /* %lo() of GOT entry. */ +#define R_NIOS2_GOT_HA 43 /* %hiadj() of GOT entry. */ +#define R_NIOS2_CALL_LO 44 /* %lo() of function GOT entry. */ +#define R_NIOS2_CALL_HA 45 /* %hiadj() of function GOT entry. */ + +/* TILEPro relocations. */ +#define R_TILEPRO_NONE 0 /* No reloc */ +#define R_TILEPRO_32 1 /* Direct 32 bit */ +#define R_TILEPRO_16 2 /* Direct 16 bit */ +#define R_TILEPRO_8 3 /* Direct 8 bit */ +#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ +#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ +#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ +#define R_TILEPRO_LO16 7 /* Low 16 bit */ +#define R_TILEPRO_HI16 8 /* High 16 bit */ +#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ +#define R_TILEPRO_COPY 10 /* Copy relocation */ +#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ +#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ +#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ +#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ +#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ +#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ +#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ +#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ +#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ +#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ +#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ +#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ +#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ +#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ +#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ +#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ +#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ +#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ +#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ +/* Relocs 56-59 are currently not defined. */ +#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ +#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ +#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ +#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ +#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ +#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ + +#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEPRO_NUM 130 + + +/* TILE-Gx relocations. */ +#define R_TILEGX_NONE 0 /* No reloc */ +#define R_TILEGX_64 1 /* Direct 64 bit */ +#define R_TILEGX_32 2 /* Direct 32 bit */ +#define R_TILEGX_16 3 /* Direct 16 bit */ +#define R_TILEGX_8 4 /* Direct 8 bit */ +#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ +#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ +#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ +#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ +#define R_TILEGX_HW0 9 /* hword 0 16-bit */ +#define R_TILEGX_HW1 10 /* hword 1 16-bit */ +#define R_TILEGX_HW2 11 /* hword 2 16-bit */ +#define R_TILEGX_HW3 12 /* hword 3 16-bit */ +#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ +#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ +#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ +#define R_TILEGX_COPY 16 /* Copy relocation */ +#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ +#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ +#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ +#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ +#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ +#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ +#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ +#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ +#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ +#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ +#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ +#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ +#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ +#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ +#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ +#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ +#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ +#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ +#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ +#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ +#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ +#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ +#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ +#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ +#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ +#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ +#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ +/* Relocs 90-91 are currently not defined. */ +#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ +/* Relocs 104-105 are currently not defined. */ +#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ +#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ +#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ +#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ +#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ + +#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEGX_NUM 130 + + +__END_DECLS + +#endif /* elf.h */ diff --git a/src/exec.c b/src/exec.c new file mode 100644 index 0000000..22870c2 --- /dev/null +++ b/src/exec.c @@ -0,0 +1,1074 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "prelink.h" +#include "reloc.h" +#include "space.h" + +int +update_dynamic_tags (DSO *dso, GElf_Shdr *shdr, GElf_Shdr *old_shdr, + struct section_move *move) +{ + int i, j; + + for (i = 1; i < move->new_shnum; ++i) + { + j = move->new_to_old[i]; + if (j == -1) + continue; + if ((dynamic_info_is_set (dso, DT_HASH) + && dso->info[DT_HASH] == old_shdr[j].sh_addr + && old_shdr[j].sh_type == SHT_HASH + && set_dynamic (dso, DT_HASH, shdr[i].sh_addr, 1)) + || (dynamic_info_is_set (dso, DT_SYMTAB) + && dso->info[DT_SYMTAB] == old_shdr[j].sh_addr + && old_shdr[j].sh_type == SHT_DYNSYM + && set_dynamic (dso, DT_SYMTAB, shdr[i].sh_addr, 1)) + || (dynamic_info_is_set (dso, DT_STRTAB) + && dso->info[DT_STRTAB] == old_shdr[j].sh_addr + && old_shdr[j].sh_type == SHT_STRTAB + && set_dynamic (dso, DT_STRTAB, shdr[i].sh_addr, 1)) + || (dynamic_info_is_set (dso, DT_VERDEF_BIT) + && dso->info_DT_VERDEF == old_shdr[j].sh_addr + && old_shdr[j].sh_type == SHT_GNU_verdef + && set_dynamic (dso, DT_VERDEF, shdr[i].sh_addr, 1)) + || (dynamic_info_is_set (dso, DT_VERNEED_BIT) + && dso->info_DT_VERNEED == old_shdr[j].sh_addr + && old_shdr[j].sh_type == SHT_GNU_verneed + && set_dynamic (dso, DT_VERNEED, shdr[i].sh_addr, 1)) + || (dynamic_info_is_set (dso, DT_VERSYM_BIT) + && dso->info_DT_VERSYM == old_shdr[j].sh_addr + && old_shdr[j].sh_type == SHT_GNU_versym + && set_dynamic (dso, DT_VERSYM, shdr[i].sh_addr, 1)) + || (dynamic_info_is_set (dso, DT_GNU_HASH_BIT) + && dso->info_DT_GNU_HASH == old_shdr[j].sh_addr + && old_shdr[j].sh_type == SHT_GNU_HASH + && set_dynamic (dso, DT_GNU_HASH, shdr[i].sh_addr, 1))) + return 1; + } + + return 0; +} + +int +prelink_exec (struct prelink_info *info) +{ + int i, j, ndeps = info->ent->ndepends + 1; + int dynstrndx, dynstrndxnew, growdynstr = 0, shstrndxnew; + int old_conflict = 0, old_liblist = 0; + int new_conflict = -1, new_liblist = -1; + int new_reloc = -1, new_plt = -1, new_dynstr = -1; + int old_dynbss = -1, old_bss = -1, new_dynbss = -1; + int old_sdynbss = -1, old_sbss = -1, new_sdynbss = -1; + int addcnt, undo, shnum_after_undo; + struct reloc_info rinfo, rinfonew; + DSO *dso = info->dso; + GElf_Ehdr ehdr; + GElf_Phdr phdr[dso->ehdr.e_phnum + 1]; + GElf_Shdr old_shdr[dso->ehdr.e_shnum], new_shdr[dso->ehdr.e_shnum + 20]; + GElf_Shdr shdr_after_undo[dso->ehdr.e_shnum + 20]; + GElf_Shdr *shdr; + Elf32_Lib *liblist = NULL; + struct readonly_adjust adjust; + struct section_move *move = NULL; + + if (prelink_build_conflicts (info)) + return 1; + + if (find_reloc_sections (dso, &rinfo)) + return 1; + + move = init_section_move (dso); + if (move == NULL) + return 1; + + ehdr = dso->ehdr; + memcpy (phdr, dso->phdr, dso->ehdr.e_phnum * sizeof (GElf_Phdr)); + memcpy (old_shdr, dso->shdr, dso->ehdr.e_shnum * sizeof (GElf_Shdr)); + shdr = new_shdr; + memcpy (shdr, dso->shdr, dso->ehdr.e_shnum * sizeof (GElf_Shdr)); + + for (undo = 1; undo < dso->ehdr.e_shnum; ++undo) + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[undo].sh_name), + ".gnu.prelink_undo")) + break; + + if (undo < dso->ehdr.e_shnum) + { + Elf_Data *data; + + if (undo_sections (dso, undo, move, &rinfo, &ehdr, phdr, shdr)) + { +error_out: + free (liblist); + free (move); + return 1; + } + + data = elf_getdata (dso->scn[undo], NULL); + assert (data->d_buf != NULL); + assert (data->d_off == 0); + assert (data->d_size == dso->shdr[undo].sh_size); + dso->undo = *data; + dso->undo.d_buf = malloc (dso->undo.d_size); + if (dso->undo.d_buf == NULL) + { + error (0, ENOMEM, "%s: Could not create .gnu.prelink_undo section", + dso->filename); + goto error_out; + } + memcpy (dso->undo.d_buf, data->d_buf, data->d_size); + ehdr.e_shstrndx = dso->ehdr.e_shstrndx; + } + undo = 0; + + memcpy (shdr_after_undo, shdr, ehdr.e_shnum * sizeof (GElf_Shdr)); + + for (dynstrndx = 1; dynstrndx < dso->ehdr.e_shnum; ++dynstrndx) + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[dynstrndx].sh_name), + ".dynstr")) + break; + + if (dynstrndx == dso->ehdr.e_shnum) + { + error (0, 0, "%s: Could not find .dynstr section", dso->filename); + goto error_out; + } + + dynstrndxnew = move->old_to_new[dynstrndx]; + shstrndxnew = move->old_to_new[dso->ehdr.e_shstrndx]; + shnum_after_undo = move->new_shnum; + + if (ndeps > 1) + { + liblist = calloc (ndeps - 1, sizeof (Elf32_Lib)); + if (liblist == NULL) + { + error (0, ENOMEM, "%s: Cannot build .gnu.liblist section", + dso->filename); + goto error_out; + } + } + else + liblist = NULL; + + for (i = 0; i < ndeps - 1; ++i) + { + struct prelink_entry *ent = info->ent->depends[i]; + + liblist[i].l_name = strtabfind (dso, dynstrndx, info->sonames[i + 1]); + if (liblist[i].l_name >= shdr[dynstrndxnew].sh_size) + liblist[i].l_name = 0; + if (liblist[i].l_name == 0) + growdynstr += strlen (info->sonames[i + 1]) + 1; + liblist[i].l_time_stamp = ent->timestamp; + liblist[i].l_checksum = ent->checksum; + } + + if (info->dynbss) + { + old_bss = addr_to_sec (dso, info->dynbss_base); + assert (old_bss != -1); + if (move->old_to_new[old_bss] == -1) + ++old_bss; + assert (move->old_to_new[old_bss] != -1); + assert (shdr[move->old_to_new[old_bss]].sh_addr <= info->dynbss_base); + assert (shdr[move->old_to_new[old_bss]].sh_addr + + shdr[move->old_to_new[old_bss]].sh_size > info->dynbss_base); + } + if (info->sdynbss) + { + old_sbss = addr_to_sec (dso, info->sdynbss_base); + assert (old_sbss != -1); + if (move->old_to_new[old_sbss] == -1) + ++old_sbss; + assert (move->old_to_new[old_sbss] != -1); + assert (shdr[move->old_to_new[old_sbss]].sh_addr <= info->sdynbss_base); + assert (shdr[move->old_to_new[old_sbss]].sh_addr + + shdr[move->old_to_new[old_sbss]].sh_size > info->sdynbss_base); + } + + rinfonew = rinfo; + if (rinfo.first != -1) + { + rinfonew.first = move->old_to_new[rinfo.first]; + rinfonew.last = move->old_to_new[rinfo.last]; + if (shdr[rinfonew.first].sh_type == SHT_REL + && dso->shdr[rinfo.first].sh_type == SHT_RELA) + { + rinfonew.rel_to_rela = 1; + rinfonew.reldyn_rela = 0; + } + } + if (rinfo.plt != -1) + { + rinfonew.plt = move->old_to_new[rinfo.plt]; + if (shdr[rinfonew.plt].sh_type == SHT_REL + && dso->shdr[rinfo.plt].sh_type == SHT_RELA) + { + rinfonew.rel_to_rela_plt = 1; + rinfonew.plt_rela = 0; + } + } + + for (i = 1, j = 1; i < ehdr.e_shnum; ++i) + { + const char *name; + name = strptr (dso, dso->ehdr.e_shstrndx, shdr[i].sh_name); + if (! strcmp (name, ".dynbss")) + old_dynbss = move->new_to_old[j]; + else if (! strcmp (name, ".sdynbss")) + old_sdynbss = move->new_to_old[j]; + else if (! strcmp (name, ".gnu.prelink_undo")) + undo = -1; + if (! strcmp (name, ".gnu.conflict")) + { + old_conflict = move->new_to_old[j]; + remove_section (move, j); + } + else if (! strcmp (name, ".gnu.liblist")) + { + old_liblist = move->new_to_old[j]; + remove_section (move, j); + } + else if (rinfonew.rel_to_rela + && i >= rinfonew.first && i <= rinfonew.last) + remove_section (move, j); + else if (i == rinfonew.plt + && (rinfonew.rel_to_rela || rinfonew.rel_to_rela_plt)) + remove_section (move, j); + else if (i == dynstrndxnew && growdynstr) + remove_section (move, j); + else + shdr[j++] = shdr[i]; + } + assert (j == move->new_shnum); + ehdr.e_shnum = j; + + if (old_sdynbss != -1 && old_dynbss == -1) + { + old_dynbss = old_sdynbss; + old_sdynbss = -1; + } + + GElf_Shdr add[rinfo.last - rinfo.first + 5]; + int old[rinfo.last - rinfo.first + 5]; + int new[rinfo.last - rinfo.first + 5]; + memset (add, 0, sizeof (add)); + memset (old, 0, sizeof (old)); + memset (new, 0, sizeof (new)); + + i = 0; + if (rinfonew.rel_to_rela) + { + add[i] = shdr_after_undo[rinfonew.first]; + add[i].sh_size = shdr_after_undo[rinfonew.last].sh_addr + + shdr_after_undo[rinfonew.last].sh_size + - add[i].sh_addr; + assert (sizeof (Elf32_Rel) * 3 == sizeof (Elf32_Rela) * 2); + assert (sizeof (Elf64_Rel) * 3 == sizeof (Elf64_Rela) * 2); + add[i].sh_size = add[i].sh_size / 2 * 3; + old[i] = rinfo.first; + new_reloc = i++; + for (j = rinfo.first + 1; j <= rinfo.last; ++j) + { + add[i] = shdr_after_undo[rinfonew.first - rinfo.first + j]; + add[i].sh_size = add[i].sh_size / 2 * 3; + old[i++] = j; + } + if (rinfonew.plt) + { + add[i] = shdr_after_undo[rinfonew.plt]; + if (rinfonew.rel_to_rela_plt) + add[i].sh_size = add[i].sh_size / 2 * 3; + /* Temporarily merge them, so that they are allocated adjacently. */ + add[new_reloc].sh_size += add[i].sh_size; + old[i] = rinfo.plt; + new_plt = i++; + } + } + else if (rinfonew.rel_to_rela_plt) + { + add[i] = shdr_after_undo[rinfonew.plt]; + assert (sizeof (Elf32_Rel) * 3 == sizeof (Elf32_Rela) * 2); + assert (sizeof (Elf64_Rel) * 3 == sizeof (Elf64_Rela) * 2); + add[i].sh_size = add[i].sh_size / 2 * 3; + old[i] = rinfo.plt; + new_plt = i++; + } + if (growdynstr) + { + add[i] = shdr_after_undo[dynstrndxnew]; + add[i].sh_size += growdynstr; + old[i] = dynstrndx; + new_dynstr = i++; + } + add[i].sh_flags = SHF_ALLOC; + add[i].sh_type = SHT_GNU_LIBLIST; + add[i].sh_size = (ndeps - 1) * sizeof (Elf32_Lib); + add[i].sh_addralign = sizeof (GElf_Word); + add[i].sh_entsize = sizeof (Elf32_Lib); + old[i] = old_liblist; + new_liblist = i++; + if (info->conflict_rela_size) + { + add[i].sh_flags = SHF_ALLOC; + add[i].sh_type = SHT_RELA; + add[i].sh_entsize = gelf_fsize (dso->elf, ELF_T_RELA, 1, EV_CURRENT); + add[i].sh_size = info->conflict_rela_size * add[i].sh_entsize; + add[i].sh_addralign = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); + old[i] = old_conflict; + new_conflict = i++; + } + addcnt = i; + memset (&adjust, 0, sizeof (adjust)); + adjust.new = new; + adjust.move = move; + + for (i = 0; i < addcnt; ++i) + { + new[i] = find_readonly_space (dso, add + i, &ehdr, phdr, shdr, &adjust); + if (new[i] == 0) + goto error_out; + add_section (move, new[i]); + ++adjust.newcount; + if (old[i]) + { + move->old_to_new[old[i]] = new[i]; + move->new_to_old[new[i]] = old[i]; + } + if (i == new_reloc) + { + int k, l = new[new_reloc]; + + j = rinfo.last - rinfo.first + (new_plt != -1); + shdr[l].sh_size = shdr_after_undo[rinfonew.first].sh_size / 2 * 3; + for (k = 1; k <= j; ++k) + { + insert_readonly_section (&ehdr, shdr, l + k, &adjust); + shdr[l + k] = add[new_reloc + k]; + shdr[l + k].sh_addr = shdr[l + k - 1].sh_addr + + shdr[l + k - 1].sh_size; + shdr[l + k].sh_offset = shdr[l + k - 1].sh_offset + + shdr[l + k - 1].sh_size; + new[++i] = l + k; + add_section (move, l + k); + move->old_to_new[rinfo.first + k] = l + k; + move->new_to_old[l + k] = rinfo.first + k; + ++adjust.newcount; + } + } + } + + if (info->sdynbss) + { + if (old_sdynbss == -1) + { + new_sdynbss = move->old_to_new[old_sbss]; + memmove (&shdr[new_sdynbss + 1], &shdr[new_sdynbss], + (ehdr.e_shnum - new_sdynbss) * sizeof (GElf_Shdr)); + shdr[new_sdynbss].sh_size = 0; + ++ehdr.e_shnum; + add_section (move, new_sdynbss); + for (i = 0; i < addcnt; ++i) + if (new[i] >= new_sdynbss) + ++new[i]; + } + else + new_sdynbss = move->old_to_new[old_sdynbss]; + } + + if (info->dynbss) + { + if (old_dynbss == -1) + { + new_dynbss = move->old_to_new[old_bss]; + memmove (&shdr[new_dynbss + 1], &shdr[new_dynbss], + (ehdr.e_shnum - new_dynbss) * sizeof (GElf_Shdr)); + shdr[new_dynbss].sh_size = 0; + ++ehdr.e_shnum; + add_section (move, new_dynbss); + for (i = 0; i < addcnt; ++i) + if (new[i] >= new_dynbss) + ++new[i]; + } + else + new_dynbss = move->old_to_new[old_dynbss]; + } + + if (undo != -1) + { + undo = move->old_to_new[dso->ehdr.e_shstrndx]; + memmove (&shdr[undo + 1], &shdr[undo], + (ehdr.e_shnum - undo) * sizeof (GElf_Shdr)); + memset (&shdr[undo], 0, sizeof (shdr[undo])); + shdr[undo].sh_type = SHT_PROGBITS; + shdr[undo].sh_addralign = dso->undo.d_align; + ++ehdr.e_shnum; + for (i = 0; i < addcnt; ++i) + if (new[i] >= undo) + ++new[i]; + add_section (move, undo); + } + + i = ehdr.e_shnum; + ehdr.e_shnum = dso->ehdr.e_shnum; + dso->ehdr = ehdr; + memcpy (dso->phdr, phdr, ehdr.e_phnum * sizeof (GElf_Phdr)); + if (reopen_dso (dso, move, NULL)) + goto error_out; + + assert (i == dso->ehdr.e_shnum); + + if (shnum_after_undo != move->new_shnum) + adjust_nonalloc (dso, &dso->ehdr, shdr, 0, + dso->ehdr.e_shoff + 1, + ((long) move->new_shnum - (long) shnum_after_undo) + * gelf_fsize (dso->elf, ELF_T_SHDR, 1, EV_CURRENT)); + + if (shdr_after_undo[shstrndxnew].sh_size + < dso->shdr[dso->ehdr.e_shstrndx].sh_size) + { + Elf_Data *data = elf_getdata (dso->scn[dso->ehdr.e_shstrndx], NULL); + + assert (elf_getdata (dso->scn[dso->ehdr.e_shstrndx], data) == NULL); + assert (data->d_off == 0); + assert (shdr_after_undo[shstrndxnew].sh_size + == shdr[dso->ehdr.e_shstrndx].sh_size); + assert (data->d_size == dso->shdr[dso->ehdr.e_shstrndx].sh_size); + data->d_size = shdr_after_undo[shstrndxnew].sh_size; + } + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (move->new_to_old[i] == -1) + dso->shdr[i] = shdr[i]; + else + { + if (shdr[i].sh_type == SHT_PROGBITS + && dso->shdr[i].sh_type == SHT_NOBITS) + { + Elf_Data *data = elf_getdata (dso->scn[i], NULL); + + assert (data->d_buf == NULL); + data->d_size = shdr[i].sh_size; + if (data->d_size) + { + data->d_buf = calloc (shdr[i].sh_size, 1); + if (data->d_buf == NULL) + { + error (0, ENOMEM, "%s: Could not convert NOBITS section into PROGBITS", + dso->filename); + goto error_out; + } + } + data->d_type = ELF_T_BYTE; + } + dso->shdr[i].sh_type = shdr[i].sh_type; + dso->shdr[i].sh_addr = shdr[i].sh_addr; + dso->shdr[i].sh_size = shdr[i].sh_size; + dso->shdr[i].sh_offset = shdr[i].sh_offset; + } + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_LOAD) + { + GElf_Addr last_offset = dso->phdr[i].p_offset; + GElf_Addr adj = 0; + int sfirst = 0, slast = 0, last = 0; + + for (j = 1; j < dso->ehdr.e_shnum; ++j) + if ((dso->shdr[j].sh_size > 0 + || j == new_dynbss + || j == new_sdynbss) + && dso->shdr[j].sh_addr >= dso->phdr[i].p_vaddr + && dso->shdr[j].sh_addr + dso->shdr[j].sh_size + <= dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz) + { + if (dso->shdr[j].sh_type != SHT_NOBITS + || (dso->shdr[j].sh_flags & SHF_TLS)) + { + if (sfirst) + { + error (0, 0, "%s: NOBITS section followed by non-NOBITS section in the same segment", + dso->filename); + goto error_out; + } + continue; + } + + if (!sfirst) + sfirst = j; + if (strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[j].sh_name), ".plt") == 0) + slast = j + 1; + else if (j == new_dynbss || j == new_sdynbss) + slast = j; + } + + if (sfirst && slast) + { + for (j = sfirst; j < slast; ++j) + { + Elf_Data *data = elf_getdata (dso->scn[j], NULL); + + assert (data->d_size == dso->shdr[j].sh_size + || j == new_dynbss + 1 + || j == new_sdynbss + 1); + if (data->d_size) + { + data->d_buf = realloc (data->d_buf, data->d_size); + if (data->d_buf == NULL) + { + error (0, ENOMEM, "%s: Could not convert NOBITS section into PROGBITS", + dso->filename); + goto error_out; + } + } + memset (data->d_buf, 0, data->d_size); + data->d_type = ELF_T_BYTE; + dso->shdr[j].sh_type = SHT_PROGBITS; + } + + adj = dso->shdr[slast - 1].sh_addr + dso->shdr[slast - 1].sh_size + - dso->phdr[i].p_vaddr; + + if (adj > dso->phdr[i].p_filesz) + { + adj -= dso->phdr[i].p_filesz; + for (j = slast; + j < dso->ehdr.e_shnum + && (dso->shdr[j].sh_flags + & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR)); + ++j) + if (dso->shdr[j].sh_addr >= dso->phdr[i].p_vaddr + + dso->phdr[i].p_memsz) + adj = (adj + dso->shdr[j].sh_addralign - 1) + & ~(dso->shdr[j].sh_addralign - 1); + + dso->phdr[i].p_filesz += adj; + } + else + adj = 0; + } + + for (j = 1; j < dso->ehdr.e_shnum; ++j) + if ((dso->shdr[j].sh_size > 0 + || j == new_dynbss + || j == new_sdynbss) + && dso->shdr[j].sh_addr >= dso->phdr[i].p_vaddr + && dso->shdr[j].sh_addr + dso->shdr[j].sh_size + <= dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz) + { + last = j; + if (dso->shdr[j].sh_type == SHT_NOBITS) + { + last_offset += dso->shdr[j].sh_addralign - 1; + last_offset &= ~(dso->shdr[j].sh_addralign - 1); + if (last_offset > dso->phdr[i].p_offset + + dso->phdr[i].p_filesz) + last_offset = dso->phdr[i].p_offset + + dso->phdr[i].p_filesz; + dso->shdr[j].sh_offset = last_offset; + } + else if (dso->shdr[j].sh_addr + dso->shdr[j].sh_size + > dso->phdr[i].p_vaddr + dso->phdr[i].p_filesz) + { + error (0, 0, "%s: section spans beyond end of segment", + dso->filename); + goto error_out; + } + else + { + dso->shdr[j].sh_offset + = dso->phdr[i].p_offset + dso->shdr[j].sh_addr + - dso->phdr[i].p_vaddr; + last_offset = dso->shdr[j].sh_offset + dso->shdr[j].sh_size; + } + } + + if (adj) + { + for (j = i + 1; j < dso->ehdr.e_phnum; ++j) + if (dso->phdr[j].p_type == PT_LOAD + && dso->phdr[j].p_vaddr >= dso->shdr[slast - 1].sh_addr) + { + dso->phdr[j].p_vaddr += adj; + dso->phdr[j].p_paddr += adj; + dso->phdr[j].p_offset += adj; + } + + j = last + 1; + while (j < dso->ehdr.e_shnum + && (dso->shdr[j].sh_flags + & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR))) + { + dso->shdr[j].sh_offset += adj; + dso->shdr[j++].sh_addr += adj; + } + + if (adjust_dso_nonalloc (dso, last + 1, + dso->shdr[sfirst].sh_offset, + adj)) + goto error_out; + } + } + + /* Adjust .rel*.dyn (or .rel*.*) if necessary. */ + assert (new_reloc == -1 + || (rinfo.last - rinfo.first + == (move->old_to_new[rinfo.last] + - move->old_to_new[rinfo.first]))); + rinfo.first = move->old_to_new[rinfo.first]; + rinfo.last = move->old_to_new[rinfo.last]; + assert (new_reloc == -1 || rinfo.first == new[new_reloc]); + + if (rinfo.rel_to_rela) + { + assert (sizeof (Elf32_Rel) * 3 == sizeof (Elf32_Rela) * 2); + assert (sizeof (Elf64_Rel) * 3 == sizeof (Elf64_Rela) * 2); + assert (new_reloc != -1); + for (j = rinfo.first; j <= rinfo.last; ++j) + { + dso->shdr[j].sh_size + = dso->shdr[j].sh_size / 3 * 2; + if (convert_rel_to_rela (dso, j)) + goto error_out; + dso->shdr[j].sh_size = shdr[j].sh_size; + } + } + else if (rinfonew.rel_to_rela) + { + assert (new_reloc != -1); + for (j = rinfo.first; j <= rinfo.last; ++j) + { + dso->shdr[j].sh_entsize + = gelf_fsize (dso->elf, ELF_T_RELA, 1, EV_CURRENT); + dso->shdr[j].sh_type = SHT_RELA; + } + } + + /* Adjust .rel*.plt if necessary. */ + rinfo.plt = move->old_to_new[rinfo.plt]; + if (new_plt != -1) + { + assert (rinfo.plt == new[new_plt]); + if (rinfo.rel_to_rela_plt) + { + assert (sizeof (Elf32_Rel) * 3 == sizeof (Elf32_Rela) * 2); + assert (sizeof (Elf64_Rel) * 3 == sizeof (Elf64_Rela) * 2); + dso->shdr[rinfo.first].sh_size + = dso->shdr[rinfo.first].sh_size / 3 * 2; + if (convert_rel_to_rela (dso, rinfo.plt)) + goto error_out; + dso->shdr[rinfo.plt].sh_size = shdr[rinfo.plt].sh_size; + } + else if (rinfonew.rel_to_rela_plt) + { + dso->shdr[rinfo.plt].sh_entsize + = gelf_fsize (dso->elf, ELF_T_RELA, 1, EV_CURRENT); + dso->shdr[rinfo.plt].sh_type = SHT_RELA; + } + } + + /* Add new strings into .dynstr if necessary. */ + if (new_dynstr != -1) + { + Elf_Data *data; + char *ptr; + + i = new[new_dynstr]; + data = elf_getdata (dso->scn[i], NULL); + assert (data->d_off == 0); + data->d_buf = realloc (data->d_buf, dso->shdr[i].sh_size); + if (data->d_buf == NULL) + { + error (0, ENOMEM, "%s: Could not append names needed for .gnu.liblist to .dynstr", + dso->filename); + goto error_out; + } + ptr = data->d_buf + shdr_after_undo[dynstrndxnew].sh_size; + data->d_size = dso->shdr[i].sh_size; + for (j = 0; j < ndeps - 1; ++j) + if (liblist[j].l_name == 0) + { + liblist[j].l_name = ptr - (char *) data->d_buf; + ptr = stpcpy (ptr, info->sonames[j + 1]) + 1; + } + assert (ptr == (char *) data->d_buf + data->d_size); + } + + /* Create or update .sdynbss if necessary. */ + if (new_sdynbss != -1) + { + Elf_Data *data; + + if (old_sdynbss == -1) + { + dso->shdr[new_sdynbss] = dso->shdr[new_sdynbss + 1]; + + dso->shdr[new_sdynbss].sh_name = shstrtabadd (dso, ".sdynbss"); + if (dso->shdr[new_sdynbss].sh_name == 0) + goto error_out; + + dso->shdr[new_sdynbss].sh_size = + info->sdynbss_base + info->sdynbss_size + - dso->shdr[new_sdynbss].sh_addr; + + dso->shdr[new_sdynbss + 1].sh_size + -= dso->shdr[new_sdynbss].sh_size; + dso->shdr[new_sdynbss + 1].sh_addr + += dso->shdr[new_sdynbss].sh_size; + dso->shdr[new_sdynbss + 1].sh_offset + += dso->shdr[new_sdynbss].sh_size; + dso->shdr[new_sdynbss].sh_type = SHT_PROGBITS; + } + else + { + if (dso->shdr[new_sdynbss].sh_type != SHT_PROGBITS + || dso->shdr[new_sdynbss].sh_addr > info->sdynbss_base + || dso->shdr[new_sdynbss].sh_addr + + dso->shdr[new_sdynbss].sh_size + < info->sdynbss_base + info->sdynbss_size) + { + error (0, 0, "%s: Copy relocs don't point into .sdynbss section", + dso->filename); + goto error_out; + } + } + data = elf_getdata (dso->scn[new_sdynbss], NULL); + free (data->d_buf); + data->d_buf = info->sdynbss; + info->sdynbss = NULL; + data->d_off = info->sdynbss_base - dso->shdr[new_sdynbss].sh_addr; + data->d_size = info->sdynbss_size; + data->d_type = ELF_T_BYTE; + if (old_sdynbss == -1) + { + data = elf_getdata (dso->scn[new_sdynbss + 1], NULL); + assert (dso->shdr[new_sdynbss + 1].sh_type != SHT_NOBITS + || data->d_buf == NULL); + if (data->d_size != dso->shdr[new_sdynbss + 1].sh_size) + { + assert (data->d_size == dso->shdr[new_sdynbss].sh_size + + dso->shdr[new_sdynbss + 1].sh_size); + data->d_size -= dso->shdr[new_sdynbss].sh_size; + } + } + } + + /* Create or update .dynbss if necessary. */ + if (new_dynbss != -1) + { + Elf_Data *data; + + if (old_dynbss == -1) + { + GElf_Addr adj; + + dso->shdr[new_dynbss] = dso->shdr[new_dynbss + 1]; + + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[new_dynbss + 1].sh_name), + ".sbss") + && new_sdynbss == -1) + dso->shdr[new_dynbss].sh_name = shstrtabadd (dso, ".sdynbss"); + else + dso->shdr[new_dynbss].sh_name = shstrtabadd (dso, ".dynbss"); + if (dso->shdr[new_dynbss].sh_name == 0) + goto error_out; + + dso->shdr[new_dynbss].sh_size = + info->dynbss_base + info->dynbss_size + - dso->shdr[new_dynbss].sh_addr; + + dso->shdr[new_dynbss + 1].sh_size + -= dso->shdr[new_dynbss].sh_size; + dso->shdr[new_dynbss + 1].sh_addr + += dso->shdr[new_dynbss].sh_size; + dso->shdr[new_dynbss + 1].sh_offset + += dso->shdr[new_dynbss].sh_size; + dso->shdr[new_dynbss].sh_type = SHT_PROGBITS; + + if (dso->shdr[new_dynbss + 1].sh_type == SHT_NOBITS) + { + GElf_Addr last_offset; + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_LOAD + && dso->phdr[i].p_vaddr <= dso->shdr[new_dynbss].sh_addr + && dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz + >= info->dynbss_base + info->dynbss_size) + break; + assert (i < dso->ehdr.e_phnum); + + for (j = new_dynbss - 1; j; --j) + { + if (dso->shdr[j].sh_addr < dso->phdr[i].p_vaddr) + break; + if (dso->shdr[j].sh_type == SHT_NOBITS + && (dso->shdr[j].sh_flags & SHF_TLS) == 0) + { + error (0, 0, "%s: COPY relocs not present at start of first SHT_NOBITS section", + dso->filename); + goto error_out; + } + } + + if (dso->phdr[i].p_filesz + < info->dynbss_base + info->dynbss_size + - dso->phdr[i].p_vaddr) + { + dso->phdr[i].p_filesz = + info->dynbss_base + info->dynbss_size + - dso->phdr[i].p_vaddr; + assert (dso->phdr[i].p_filesz <= dso->phdr[i].p_memsz); + } + + adj = dso->phdr[i].p_offset + dso->shdr[new_dynbss].sh_addr + - dso->phdr[i].p_vaddr - dso->shdr[new_dynbss].sh_offset; + + dso->shdr[new_dynbss].sh_offset += adj; + dso->shdr[new_dynbss + 1].sh_offset += adj; + + adj += dso->shdr[new_dynbss].sh_size; + + for (j = new_dynbss + 2; + j < dso->ehdr.e_shnum + && (dso->shdr[j].sh_flags + & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR)); + ++j) + if (dso->shdr[j].sh_addr >= dso->phdr[i].p_vaddr + + dso->phdr[i].p_memsz) + adj = (adj + dso->shdr[j].sh_addralign - 1) + & ~(dso->shdr[j].sh_addralign - 1); + + for (j = i + 1; j < dso->ehdr.e_phnum; ++j) + if (dso->phdr[j].p_type == PT_LOAD + && dso->phdr[j].p_vaddr >= dso->shdr[new_dynbss].sh_addr) + { + dso->phdr[j].p_vaddr += adj; + dso->phdr[j].p_paddr += adj; + dso->phdr[j].p_offset += adj; + } + + last_offset = dso->shdr[new_dynbss + 1].sh_offset; + for (j = new_dynbss + 2; j < dso->ehdr.e_shnum; ++j) + if (dso->shdr[j].sh_type != SHT_NOBITS + || dso->shdr[j].sh_addr < dso->phdr[i].p_vaddr + || dso->shdr[j].sh_addr + dso->shdr[j].sh_size + > dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz) + break; + else + { + last_offset += dso->shdr[j].sh_addralign - 1; + last_offset &= ~(dso->shdr[j].sh_addralign - 1); + if (last_offset > dso->phdr[i].p_offset + + dso->phdr[i].p_filesz) + last_offset = dso->phdr[i].p_offset + + dso->phdr[i].p_filesz; + dso->shdr[j].sh_offset = last_offset; + } + + while (j < dso->ehdr.e_shnum + && (dso->shdr[j].sh_flags + & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR))) + { + dso->shdr[j].sh_offset += adj; + dso->shdr[j++].sh_addr += adj; + } + + if (adjust_dso_nonalloc (dso, new_dynbss + 2, + dso->shdr[new_dynbss].sh_offset, + adj)) + goto error_out; + } + } + else + { + if (dso->shdr[new_dynbss].sh_type != SHT_PROGBITS + || dso->shdr[new_dynbss].sh_addr > info->dynbss_base + || dso->shdr[new_dynbss].sh_addr + + dso->shdr[new_dynbss].sh_size + < info->dynbss_base + info->dynbss_size) + { + error (0, 0, "%s: Copy relocs don't point into .dynbss section", + dso->filename); + goto error_out; + } + } + data = elf_getdata (dso->scn[new_dynbss], NULL); + free (data->d_buf); + data->d_buf = info->dynbss; + info->dynbss = NULL; + data->d_off = info->dynbss_base - dso->shdr[new_dynbss].sh_addr; + data->d_size = info->dynbss_size; + data->d_type = ELF_T_BYTE; + if (old_dynbss == -1) + { + data = elf_getdata (dso->scn[new_dynbss + 1], NULL); + if (dso->shdr[new_dynbss + 1].sh_type == SHT_NOBITS + && data->d_buf != NULL) + { +#ifndef NDEBUG + char *buf_start = data->d_buf; + char *buf_end = buf_start + data->d_size; + + while (buf_start < buf_end) + if (*buf_start++) + break; + assert (buf_start == buf_end); +#endif + free (data->d_buf); + data->d_buf = NULL; + } + if (data->d_size != dso->shdr[new_dynbss + 1].sh_size) + { + assert (data->d_size == dso->shdr[new_dynbss].sh_size + + dso->shdr[new_dynbss + 1].sh_size); + data->d_size -= dso->shdr[new_dynbss].sh_size; + } + } + } + + /* Create the liblist. */ + i = new[new_liblist]; + dso->shdr[i].sh_flags = shdr[i].sh_flags; + dso->shdr[i].sh_addralign = shdr[i].sh_addralign; + dso->shdr[i].sh_entsize = shdr[i].sh_entsize; + dso->shdr[i].sh_name = shstrtabadd (dso, ".gnu.liblist"); + if (dso->shdr[i].sh_name == 0) + goto error_out; + else + { + Elf_Data *data; + + dso->shdr[i].sh_link + = new_dynstr != -1 ? new[new_dynstr] : move->old_to_new[dynstrndx]; + data = elf_getdata (dso->scn[i], NULL); + data->d_type = ELF_T_WORD; + data->d_size = (ndeps - 1) * sizeof (Elf32_Lib); + free (data->d_buf); + data->d_buf = liblist; + liblist = NULL; + data->d_off = 0; + data->d_align = sizeof (GElf_Word); + data->d_version = EV_CURRENT; + if (set_dynamic (dso, DT_GNU_LIBLIST, dso->shdr[i].sh_addr, 1)) + goto error_out; + if (set_dynamic (dso, DT_GNU_LIBLISTSZ, dso->shdr[i].sh_size, 1)) + goto error_out; + } + + /* Create the conflict list if necessary. */ + if (new_conflict != -1) + { + Elf_Data *data; + + i = new[new_conflict]; + data = elf_getdata (dso->scn[i], NULL); + data->d_type = ELF_T_RELA; + data->d_size = info->conflict_rela_size + * gelf_fsize (dso->elf, ELF_T_RELA, 1, EV_CURRENT); + data->d_off = 0; + data->d_align = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); + data->d_version = EV_CURRENT; + if (data->d_size) + { + data->d_buf = realloc (data->d_buf, data->d_size); + if (data->d_buf == NULL) + { + error (0, ENOMEM, "%s: Could not build .gnu.conflict section", + dso->filename); + goto error_out; + } + } + else + { + free (data->d_buf); + data->d_buf = NULL; + } + for (j = 0; j < info->conflict_rela_size; ++j) + gelfx_update_rela (dso->elf, data, j, info->conflict_rela + j); + free (info->conflict_rela); + info->conflict_rela = NULL; + + dso->shdr[i].sh_flags = shdr[i].sh_flags; + dso->shdr[i].sh_addralign = shdr[i].sh_addralign; + dso->shdr[i].sh_entsize = shdr[i].sh_entsize; + for (j = 1; j < dso->ehdr.e_shnum; ++j) + if (dso->shdr[j].sh_type == SHT_DYNSYM) + break; + assert (j < dso->ehdr.e_shnum); + dso->shdr[i].sh_link = j; + dso->shdr[i].sh_name = shstrtabadd (dso, ".gnu.conflict"); + if (dso->shdr[i].sh_name == 0) + goto error_out; + if (set_dynamic (dso, DT_GNU_CONFLICT, dso->shdr[i].sh_addr, 1)) + goto error_out; + if (set_dynamic (dso, DT_GNU_CONFLICTSZ, dso->shdr[i].sh_size, 1)) + goto error_out; + } + + if (undo != -1) + { + Elf_Scn *scn; + Elf_Data *data; + GElf_Addr newoffset; + + dso->shdr[undo].sh_name = shstrtabadd (dso, ".gnu.prelink_undo"); + if (dso->shdr[undo].sh_name == 0) + return 1; + dso->shdr[undo].sh_offset = dso->shdr[undo - 1].sh_offset; + if (dso->shdr[undo - 1].sh_type != SHT_NOBITS) + dso->shdr[undo].sh_offset += dso->shdr[undo - 1].sh_size; + dso->shdr[undo].sh_entsize = 1; + dso->shdr[undo].sh_size = dso->undo.d_size; + newoffset = dso->shdr[undo].sh_offset + dso->undo.d_align - 1; + newoffset &= ~(dso->shdr[undo].sh_addralign - 1); + if (adjust_dso_nonalloc (dso, undo + 1, dso->shdr[undo].sh_offset, + dso->undo.d_size + newoffset + - dso->shdr[undo].sh_offset)) + return 1; + dso->shdr[undo].sh_offset = newoffset; + scn = dso->scn[undo]; + data = elf_getdata (scn, NULL); + assert (data != NULL && elf_getdata (scn, data) == NULL); + free (data->d_buf); + *data = dso->undo; + dso->undo.d_buf = NULL; + } + + recompute_nonalloc_offsets (dso); + + if (update_dynamic_tags (dso, dso->shdr, old_shdr, move)) + goto error_out; + + if (update_dynamic_rel (dso, &rinfo)) + goto error_out; + + free (move); + return 0; +} diff --git a/src/execle_open.c b/src/execle_open.c new file mode 100644 index 0000000..2ee5cbc --- /dev/null +++ b/src/execle_open.c @@ -0,0 +1,81 @@ +/* Copyright (C) 2001 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <errno.h> +#include <error.h> +#include <stdio.h> +#include <sys/wait.h> +#include <unistd.h> + +static pid_t pid; + +int +execve_close (FILE *f) +{ + pid_t p; + int status; + + if (f != NULL) + fclose (f); + while ((p = waitpid (pid, &status, 0)) == -1 && errno == EINTR); + if (p == -1 || ! WIFEXITED (status)) + return -1; + return WEXITSTATUS (status); +} + +FILE * +execve_open (const char *path, char *const argv[], char *const envp[]) +{ + int p[2]; + FILE *f; + + if (pipe (p) < 0) + { + error (0, errno, "Could not run %s", path); + return NULL; + } + + switch (pid = vfork ()) + { + case -1: + error (0, errno, "Could not run %s", path); + return NULL; + case 0: + close (p[0]); + if (p[1] != 1) + { + dup2 (p[1], 1); + close (p[1]); + } + dup2 (1, 2); + execve (path, argv, envp); + error (0, errno, "Could not run %s", path); + _exit (127); + } + + close (p[1]); + + f = fdopen (p[0], "r"); + if (f == NULL) + { + close (p[0]); + execve_close (NULL); + } + + return f; +} diff --git a/src/execstack.c b/src/execstack.c new file mode 100644 index 0000000..dda6bc7 --- /dev/null +++ b/src/execstack.c @@ -0,0 +1,483 @@ +/* Copyright (C) 2003, 2005, 2010 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2003. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> + +#include "prelink.h" + +int set; +int execflag; + +const char *argp_program_version = EXECSTACK_PROG PKGVERSION " 1.0"; + +const char *argp_program_bug_address = REPORT_BUGS_TO; + +static char argp_doc[] = EXECSTACK_PROG " -- program to query or set executable stack flag"; + +static struct argp_option options[] = { + {"set-execstack", 's', 0, 0, "Set executable stack flag bit" }, + {"execstack", 's', 0, OPTION_HIDDEN, "" }, + {"clear-execstack", 'c', 0, 0, "Clear executable stack flag bit" }, + {"noexecstack", 'c', 0, OPTION_HIDDEN, "" }, + {"query", 'q', 0, 0, "Query executable stack flag bit" }, + { 0 } +}; + +/* The cached value of argv[0]. */ +const char *program_path; + +/* The full pathname of the prelink tool, or NULL if it hasn't been + computed yet. */ +const char *prelink_path; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 's': + set = 1; + execflag = 1; + break; + case 'c': + set = 1; + execflag = 0; + break; + case 'q': + set = 0; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { options, parse_opt, 0, argp_doc }; + +static int execstack_set (DSO *dso, int flag); + +static void +execstack_fill_phdr (DSO *dso, int i, int flag) +{ + memset (&dso->phdr[i], 0, sizeof (dso->phdr[i])); + dso->phdr[i].p_type = PT_GNU_STACK; + dso->phdr[i].p_flags = PF_W | PF_R | (flag ? PF_X : 0); + dso->phdr[i].p_align = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); +} + +static int +execstack_make_rdwr (DSO *dso, int flag) +{ + int i, fd = -1, status; + pid_t pid; + DSO *ndso = NULL; + char *p = NULL; + char filename[strlen (dso->filename) + sizeof ".#execstack#.XXXXXX"]; + extern char *make_relative_prefix (const char *, const char *, const char *); + char *dirname; + + for (i = 0; i < dso->ehdr.e_shnum; ++i) + { + const char *name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name); + if (strcmp (name, ".gnu.prelink_undo") == 0) + break; + } + + if (i == dso->ehdr.e_shnum) + return reopen_dso (dso, NULL, NULL) ? 1 : -1; + + /* We need to unprelink the file first, so that prelink --undo + or reprelinking it doesn't destroy the PT_GNU_STACK segment + header we've created. */ + sprintf (filename, "%s.#execstack#.XXXXXX", dso->filename); + + fd = wrap_mkstemp (filename); + if (fd == -1) + { + error (0, 0, "%s: Cannot create temporary file", + dso->filename); + goto error_out; + } + + p = strdup (dso->filename); + if (p == NULL) + { + error (0, ENOMEM, "%s: Cannot create temporary file", + dso->filename); + goto error_out; + } + + if (prelink_path == NULL) + { + dirname = make_relative_prefix (program_path, BINDIR, SBINDIR); + asprintf (&prelink_path, "%s/%s", dirname, PRELINK_PROG EXEEXT); + free (dirname); + } + + pid = vfork (); + if (pid == 0) + { + close (fd); + execl (prelink_path, prelink_path, "-u", "-o", filename, + dso->filename, NULL); + _exit (-1); + } + + if (pid < 0) + { + error (0, errno, "%s: Cannot run prelink --undo", + dso->filename); + goto error_out; + } + + if (waitpid (pid, &status, 0) < 0 + || !WIFEXITED (status) + || WEXITSTATUS (status)) + { + error (0, 0, "%s: prelink --undo failed", dso->filename); + goto error_out; + } + + ndso = open_dso (filename); + if (ndso == NULL) + { + error (0, 0, "%s: Couldn't open prelink --undo output", + dso->filename); + goto error_out; + } + + for (i = 0; i < ndso->ehdr.e_shnum; ++i) + { + const char *name = strptr (ndso, ndso->ehdr.e_shstrndx, + ndso->shdr[i].sh_name); + if (strcmp (name, ".gnu.prelink_undo") == 0) + break; + } + + if (i != ndso->ehdr.e_shnum) + { + error (0, 0, "%s: prelink --undo output contains .gnu.prelink_undo section", + dso->filename); + goto error_out; + } + + if (ndso->ehdr.e_type != dso->ehdr.e_type) + { + error (0, 0, "%s: Object type changed during prelink --undo operation", + dso->filename); + } + + if (ndso->filename != ndso->soname) + free ((char *) ndso->filename); + ndso->filename = p; + p = NULL; + + wrap_unlink (filename); + fsync (fd); + close (fd); + fd = -1; + close_dso (dso); + return execstack_set (ndso, flag); + +error_out: + free (p); + if (ndso != NULL) + close_dso (ndso); + if (fd != -1) + { + wrap_unlink (filename); + fsync (fd); + close (fd); + } + close_dso (dso); + return 1; +} + +static int +execstack_set (DSO *dso, int flag) +{ + int i, null = -1, last, ret; + GElf_Addr lowoff = ~(GElf_Addr) 0, start = 0, align = 0; + GElf_Addr adjust; + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_GNU_STACK) + { + /* Found PT_GNU_STACK. Check if we need any change or not. */ + if (flag ^ ((dso->phdr[i].p_flags & PF_X) != 0)) + { + ret = execstack_make_rdwr (dso, flag); + if (ret != -1) + return ret; + dso->phdr[i].p_flags ^= PF_X; + goto out_write; + } + else + goto out_close; + } + else if (dso->phdr[i].p_type == PT_NULL) + null = i; + + if (null != -1) + { + /* Overwrite PT_NULL segment with PT_GNU_STACK. */ + ret = execstack_make_rdwr (dso, flag); + if (ret != -1) + return ret; + execstack_fill_phdr (dso, i, flag); + goto out_write; + } + + if (dso->ehdr.e_shnum == 0) + { + error (0, 0, "%s: Section header table missing", dso->filename); + goto error_out; + } + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + if (lowoff > dso->shdr[i].sh_offset) + { + if (dso->shdr[i].sh_flags & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR)) + { + lowoff = dso->shdr[i].sh_offset; + start = dso->shdr[i].sh_addr; + } + else + { + error (0, 0, "%s: Non-alloced sections before alloced ones", + dso->filename); + goto error_out; + } + } + + if (dso->shdr[i].sh_addralign > align) + align = dso->shdr[i].sh_addralign; + } + + if (dso->ehdr.e_phoff >= lowoff) + { + error (0, 0, "%s: Program header table not before all sections", + dso->filename); + goto error_out; + } + + if (dso->ehdr.e_shoff <= lowoff) + { + error (0, 0, "%s: Section header table before first section", + dso->filename); + goto error_out; + } + + if (dso->ehdr.e_phoff + (dso->ehdr.e_phnum + 1) * dso->ehdr.e_phentsize + <= lowoff) + { + /* There is enough space for the headers even without reshuffling + anything. */ + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_PHDR) + { + if (dso->phdr[i].p_filesz + == dso->ehdr.e_phnum * dso->ehdr.e_phentsize) + dso->phdr[i].p_filesz += dso->ehdr.e_phentsize; + if (dso->phdr[i].p_memsz + == dso->ehdr.e_phnum * dso->ehdr.e_phentsize) + dso->phdr[i].p_memsz += dso->ehdr.e_phentsize; + } + i = dso->ehdr.e_phnum++; + ret = execstack_make_rdwr (dso, flag); + if (ret != -1) + return ret; + execstack_fill_phdr (dso, i, flag); + goto out_write; + } + + if (dso->ehdr.e_type != ET_DYN) + { + error (0, 0, "%s: Reshuffling of objects to make room for\n" + "program header entry only supported for shared libraries", + dso->filename); + goto error_out; + } + + adjust = dso->ehdr.e_phoff + (dso->ehdr.e_phnum + 1) * dso->ehdr.e_phentsize + - lowoff; + if (align) + adjust = (adjust + align - 1) & ~(align - 1); + + /* Need to make sure adjust doesn't cause different Phdr segments + to overlap on the same page. */ + last = -1; + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_LOAD + && dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz >= start) + { + if (last != -1 + && (((dso->phdr[last].p_vaddr + dso->phdr[last].p_memsz - 1) + ^ dso->phdr[i].p_vaddr) + & ~(dso->arch->max_page_size - 1)) + && !(((dso->phdr[last].p_vaddr + dso->phdr[last].p_memsz + + adjust - 1) + ^ (dso->phdr[i].p_vaddr + adjust)) + & ~(dso->arch->max_page_size - 1))) + { + if (align >= dso->arch->max_page_size) + { + error (0, 0, "%s: Cannot grow reloc sections", dso->filename); + goto error_out; + } + adjust = (adjust + dso->arch->max_page_size - 1) + & ~(dso->arch->max_page_size - 1); + } + last = i; + } + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_PHDR) + { + if (dso->phdr[i].p_filesz == dso->ehdr.e_phnum * dso->ehdr.e_phentsize) + dso->phdr[i].p_filesz += dso->ehdr.e_phentsize; + if (dso->phdr[i].p_memsz == dso->ehdr.e_phnum * dso->ehdr.e_phentsize) + dso->phdr[i].p_memsz += dso->ehdr.e_phentsize; + } + + i = dso->ehdr.e_phnum++; + ret = execstack_make_rdwr (dso, flag); + if (ret != -1) + return ret; + + if (adjust_dso (dso, start, adjust)) + goto error_out; + + execstack_fill_phdr (dso, i, flag); + +out_write: + if (dynamic_info_is_set (dso, DT_CHECKSUM_BIT) + && dso_is_rdwr (dso) + && prelink_set_checksum (dso)) + goto error_out; + + dso->permissive = 1; + + return update_dso (dso, NULL); + +out_close: + close_dso (dso); + return 0; + +error_out: + close_dso (dso); + return 1; +} + +static int +execstack_query (DSO *dso) +{ + int stack = '?', i; + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_GNU_STACK) + { + stack = (dso->phdr[i].p_flags & PF_X) ? 'X' : '-'; + break; + } + printf ("%c %s\n", stack, dso->filename); + close_dso (dso); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int remaining, failures = 0; + + program_path = argv[0]; + + setlocale (LC_ALL, ""); + + argp_parse (&argp, argc, argv, 0, &remaining, 0); + + elf_version (EV_CURRENT); + + if (remaining == argc) + error (EXIT_FAILURE, 0, "no files given"); + + while (remaining < argc) + { + DSO *dso = open_dso (argv[remaining++]); + int ret; + + if (dso == NULL) + { + ++failures; + continue; + } + + if (dso->ehdr.e_type != ET_DYN + && dso->ehdr.e_type != ET_EXEC) + { + ++failures; + error (0, 0, "%s is not a shared library nor executable", dso->filename); + continue; + } + + if (set) + ret = execstack_set (dso, execflag); + else + ret = execstack_query (dso); + + if (ret) + ++failures; + } + + return failures; +} + +/* FIXME: Dummy. When arch dependent files are split into adjust and prelink + parts, this can go away. */ +struct prelink_conflict * +prelink_conflict (struct prelink_info *info, GElf_Word r_sym, int reloc_type) +{ + abort (); +} + +GElf_Rela * +prelink_conflict_add_rela (struct prelink_info *info) +{ + abort (); +} + +ssize_t +send_file (int outfd, int infd, off_t *poff, size_t count) +{ + abort (); +} + +GElf_Addr mmap_reg_start; +GElf_Addr mmap_reg_end; +int exec_shield; diff --git a/src/fptr.c b/src/fptr.c new file mode 100644 index 0000000..cfe3aed --- /dev/null +++ b/src/fptr.c @@ -0,0 +1,465 @@ +/* Copyright (C) 2001, 2002, 2003, 2007 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include "fptr.h" + +struct opd_refent; + +struct opd_tabent +{ + struct opd_ent *ent; + struct opd_refent *ref; +}; + +struct opd_refent +{ + GElf_Addr val; + GElf_Addr gp; + struct opd_refent *first; + struct opd_tabent *tabent; + struct opd_refent *next, *nextref; + GElf_Word refcnt; +}; + +struct opd_fptr +{ + /* The first 2 fields have to match opd_refent. */ + GElf_Addr val; + GElf_Addr gp; + struct opd_ent *ent; +}; + +static void +opd_del (void *p) +{ + free (p); +} + +static hashval_t +opd_tabent_hash (const void *p) +{ + struct opd_tabent *e = (struct opd_tabent *)p; + + return e->ent->opd; +} + +static int +opd_tabent_eq (const void *p, const void *q) +{ + struct opd_tabent *e = (struct opd_tabent *)p; + struct opd_tabent *f = (struct opd_tabent *)q; + + return e->ent == f->ent; +} + +static hashval_t +opd_refent_hash (const void *p) +{ + struct opd_refent *e = (struct opd_refent *)p; + + return e->val ^ (e->val >> 31); +} + +static int +opd_refent_eq (const void *p, const void *q) +{ + struct opd_refent *e = (struct opd_refent *)p; + struct opd_refent *f = (struct opd_refent *)q; + + return e->val == f->val && e->gp == f->gp; +} + +static int +opd_gather_refent (void **p, void *info) +{ + struct opd_refent ***ptr = (struct opd_refent ***) info; + struct opd_refent *r = *(struct opd_refent **) p, *t; + + for (t = r; t; t = t->next) + { + *(*ptr)++ = t; + t->first = r; + } + return 1; +} + +static int +opd_refent_cmp (const void *A, const void *B) +{ + struct opd_refent *a = * (struct opd_refent **) A; + struct opd_refent *b = * (struct opd_refent **) B; + + if (a->refcnt > b->refcnt) + return -1; + if (a->refcnt < b->refcnt) + return 1; + return 0; +} + +int +opd_init (struct prelink_info *info) +{ + int i, j, nrefent = 0; + struct opd_lib *l; + struct opd_refent refent, *r, **refarr, **a; + struct opd_tabent tabent, *t; + void **tabslot; + htab_t tabent_htab = NULL, refent_htab = NULL; + + l = calloc (sizeof (struct opd_lib), 1); + if (l == NULL) + goto error_mem; + l->nrefs = (info->symtab_end - info->symtab_start) / info->symtab_entsize; + if (l->nrefs) + { + l->u.refp = calloc (l->nrefs, sizeof (struct opd_ref *)); + if (l->u.refp == NULL) + goto error_mem; + } + else + l->u.refp = NULL; + tabent_htab = htab_try_create (100, opd_tabent_hash, opd_tabent_eq, opd_del); + refent_htab = htab_try_create (100, opd_refent_hash, opd_refent_eq, opd_del); + l->htab = htab_try_create (100, opd_refent_hash, opd_refent_eq, opd_del); + if (tabent_htab == NULL || refent_htab == NULL || l->htab == NULL) + goto error_mem; + + for (i = 0; i < info->ent->ndepends; ++i) + { + struct prelink_entry *ent; + struct prelink_conflict *conflict; + struct opd_lib *ol; + size_t maxidx = 1; + + ent = info->ent->depends[i]; + ol = ent->opd; + if (info->conflicts[i + 1].hash != &info->conflicts[i + 1].first) + maxidx = 251; + for (j = 0; j < ol->nrefs; ++j) + { + GElf_Addr symoff = ol->u.refs[j].symoff; + refent.val = ol->u.refs[j].ent->val; + refent.gp = ol->u.refs[j].ent->gp; + for (conflict = info->conflicts[i + 1].hash[symoff % maxidx]; conflict; + conflict = conflict->next) + { + if (conflict->symoff == symoff + && conflict->reloc_class != RTYPE_CLASS_COPY + && conflict->reloc_class != RTYPE_CLASS_TLS) + break; + } + + if (conflict) + { + if (refent.val + != conflict->conflict.ent->base + conflict->conflictval + || refent.gp != conflict->conflict.ent->pltgot) + { + error (0, 0, "%s: OPD value changed during prelinking", + info->ent->filename); + goto error_out; + } + + refent.val = conflict->lookup.ent->base + conflict->lookupval; + refent.gp = conflict->lookup.ent->pltgot; + } + + if (ol->u.refs[j].ent->opd & OPD_ENT_PLT) + { + struct opd_ent_plt *entp + = (struct opd_ent_plt *) ol->u.refs[j].ent; + int k; + size_t idx = 0; + + for (k = 0; k < info->ent->ndepends; ++k) + if (info->ent->depends[k] == entp->lib) + break; + + assert (k < info->ent->ndepends); + + if (info->conflicts[k + 1].hash != &info->conflicts[k + 1].first) + idx = entp->symoff % 251; + for (conflict = info->conflicts[k + 1].hash[idx]; conflict; + conflict = conflict->next) + { + if (conflict->symoff == entp->symoff + && conflict->reloc_class == RTYPE_CLASS_PLT) + break; + } + + if (conflict) + { + if (ol->u.refs[j].ent->val + != conflict->conflict.ent->base + conflict->conflictval + || ol->u.refs[j].ent->gp + != conflict->conflict.ent->pltgot) + { + error (0, 0, "%s: OPD value changed during prelinking", + info->ent->filename); + goto error_out; + } + + /* FPTR originally pointed into .plt, but since they + now resolve to different values, this cannot be used. */ + if (refent.val + != conflict->lookup.ent->base + conflict->lookupval + || refent.gp != conflict->lookup.ent->pltgot) + continue; + } + else if (refent.val != ol->u.refs[j].ent->val + || refent.gp != ol->u.refs[j].ent->gp) + continue; + } + + tabslot = htab_find_slot (refent_htab, &refent, INSERT); + if (tabslot == NULL) + goto error_mem; + + if (*tabslot != NULL) + { + for (r = (struct opd_refent *) *tabslot; r; r = r->next) + if (r->tabent->ent == ol->u.refs[j].ent) + { + r->refcnt += ol->u.refs[j].refcnt; + break; + } + + if (r) + continue; + } + + r = (struct opd_refent *) calloc (sizeof (struct opd_refent), 1); + if (r == NULL) + goto error_mem; + + ++nrefent; + r->next = (struct opd_refent *) *tabslot; + *tabslot = r; + r->val = refent.val; + r->gp = refent.gp; + r->refcnt = ol->u.refs[j].refcnt; + + tabent.ent = ol->u.refs[j].ent; + + tabslot = htab_find_slot (tabent_htab, &tabent, INSERT); + if (tabslot == NULL) + goto error_mem; + + if (*tabslot != NULL) + { + t = (struct opd_tabent *) *tabslot; + t->ref->nextref = r; + r->nextref = t->ref; + } + else + { + t = (struct opd_tabent *) calloc (sizeof (struct opd_tabent), 1); + if (t == NULL) + goto error_mem; + t->ent = ol->u.refs[j].ent; + *tabslot = t; + r->nextref = r; + t->ref = r; + } + + r->tabent = t; + } + } + + refarr = alloca (nrefent * sizeof (struct opd_refent *)); + a = refarr; + htab_traverse (refent_htab, opd_gather_refent, &a); + assert (a == refarr + nrefent); + qsort (refarr, nrefent, sizeof (struct opd_refent *), opd_refent_cmp); + for (i = 0; i < nrefent; ++i) + { + struct opd_fptr *f; + + if (refarr[i]->tabent == NULL) + continue; + + f = (struct opd_fptr *) calloc (sizeof (struct opd_fptr), 1); + if (f == NULL) + goto error_mem; + + f->val = refarr[i]->val; + f->gp = refarr[i]->gp; + f->ent = refarr[i]->tabent->ent; + tabslot = htab_find_slot (l->htab, f, INSERT); + if (tabslot == NULL) + goto error_mem; + + *tabslot = f; + r = refarr[i]->tabent->ref; + do + { + if (r != refarr[i]) + r->tabent = NULL; + r = r->nextref; + } + while (r != refarr[i]->tabent->ref); + + for (r = refarr[i]->first; r; r = r->next) + r->tabent = NULL; + } + + htab_delete (tabent_htab); + htab_delete (refent_htab); + info->ent->opd = l; + return 0; + +error_mem: + error (0, ENOMEM, "%s: Could not create OPD table", + info->ent->filename); +error_out: + if (tabent_htab) + htab_delete (tabent_htab); + if (refent_htab) + htab_delete (refent_htab); + if (l && l->htab) + htab_delete (l->htab); + free (l); + return 1; +} + +int +opd_add (struct prelink_info *info, GElf_Word r_sym, int reloc_type) +{ + struct opd_fptr *f, fp; + void **tabslot; + struct opd_lib *l = info->ent->opd; + + if (l->u.refp[r_sym] != NULL) + { + ++l->u.refp[r_sym]->refcnt; + return 0; + } + + if (ELF64_ST_BIND (info->symtab [r_sym].st_info) + == STB_LOCAL) + { + fp.val = info->symtab [r_sym].st_value; + fp.gp = info->ent->pltgot; + } + else + { + fp.val = info->resolve (info, r_sym, reloc_type); + if (info->resolveent == NULL) + return 0; + fp.gp = info->resolveent->pltgot; + } + + l->u.refp[r_sym] = malloc (sizeof (struct opd_ref)); + if (l->u.refp[r_sym] == NULL) + goto error_mem; + l->u.refp[r_sym]->symoff = r_sym; + l->u.refp[r_sym]->refcnt = 1; + l->u.refp[r_sym]->ent = NULL; + + tabslot = htab_find_slot (l->htab, &fp, INSERT); + if (tabslot == NULL) + goto error_mem; + + if (*tabslot == NULL) + { + f = calloc (sizeof (struct opd_fptr), 1); + if (f == NULL) + goto error_mem; + f->val = fp.val; + f->gp = fp.gp; + *tabslot = f; + } + + l->u.refp[r_sym]->ent = *tabslot; + return 0; + +error_mem: + error (0, ENOMEM, "%s: Could not create OPD table", + info->ent->filename); + return 1; +} + +void +opd_note_plt (struct prelink_info *info, GElf_Word r_sym, int reloc_type, + GElf_Addr r_offset) +{ + struct opd_fptr *f, fp; + struct opd_lib *l = info->ent->opd; + struct opd_ent_plt *entp; + + if (ELF64_ST_BIND (info->symtab [r_sym].st_info) + == STB_LOCAL) + { + fp.val = info->symtab [r_sym].st_value; + fp.gp = info->ent->pltgot; + } + else + { + fp.val = info->resolve (info, r_sym, reloc_type); + if (info->resolveent == NULL) + return; + fp.gp = info->resolveent->pltgot; + } + + f = (struct opd_fptr *) htab_find (l->htab, &fp); + if (f == NULL || f->ent != NULL) + return; + + entp = calloc (sizeof (struct opd_ent_plt), 1); + if (entp == NULL) + return; + + entp->v.val = fp.val; + entp->v.gp = fp.gp; + entp->v.opd = (r_offset - l->plt_start) | (OPD_ENT_PLT | OPD_ENT_NEW); + entp->lib = info->ent; + entp->symoff = r_sym; + f->ent = &entp->v; +} + +GElf_Addr +opd_size (struct prelink_info *info, GElf_Word entsize) +{ + struct opd_lib *l = info->ent->opd; + int i; + GElf_Addr ret = 0; + struct opd_ent *e; + struct opd_fptr *f; + + for (i = 0; i < l->nrefs; ++i) + if ((f = (struct opd_fptr *) l->u.refp[i]->ent)->ent == NULL) + { + e = calloc (sizeof (struct opd_ent), 1); + if (e == NULL) + { + error (0, ENOMEM, "%s: Could not create OPD table", + info->ent->filename); + return -1; + } + + e->val = f->val; + e->gp = f->gp; + e->opd = ret | OPD_ENT_NEW; + ret += entsize; + } + + return ret; +} diff --git a/src/fptr.h b/src/fptr.h new file mode 100644 index 0000000..36ef7c6 --- /dev/null +++ b/src/fptr.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2001 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef FPTR_H +#define FPTR_H + +#include "prelink.h" +#include "hashtab.h" + +struct opd_ent +{ + GElf_Addr val; + GElf_Addr gp; + GElf_Addr opd; +#define OPD_ENT_PLT 1 +#define OPD_ENT_NEW 2 +}; + +struct opd_ent_plt +{ + struct opd_ent v; + struct prelink_entry *lib; + GElf_Word symoff; +}; + +struct opd_ref +{ + GElf_Word symoff; + GElf_Word refcnt; + struct opd_ent *ent; +}; + +struct opd_lib +{ + GElf_Addr symtab_start; + GElf_Addr opd_start; + GElf_Addr plt_start; + union + { + struct opd_ref *refs; + struct opd_ref **refp; + } u; + htab_t htab; + int nrefs; +}; + +int opd_init (struct prelink_info *info); +int opd_add (struct prelink_info *info, GElf_Word r_sym, int reloc_type); +void opd_note_plt (struct prelink_info *info, GElf_Word r_sym, int reloc_type, + GElf_Addr r_offset); +GElf_Addr opd_size (struct prelink_info *info, GElf_Word entsize); + +#endif /* FPTR_H */ diff --git a/src/gather.c b/src/gather.c new file mode 100644 index 0000000..c61626e --- /dev/null +++ b/src/gather.c @@ -0,0 +1,1496 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <ftw.h> +#include <glob.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "prelinktab.h" +#include "reloc.h" + +#ifndef HAVE_FTW_ACTIONRETVAL +# define FTW_ACTIONRETVAL 0 +# define FTW_CONTINUE 0 +# define FTW_STOP 1 +#endif + +static int gather_lib (struct prelink_entry *ent); +static int implicit; + +static struct prelink_dir *dirs; +static struct prelink_dir *blacklist; +#ifndef HAVE_FTW_ACTIONRETVAL +static char *blacklist_dir; +static size_t blacklist_dir_len; +#endif +static struct extension +{ + const char *ext; + size_t len; + int is_glob; +} *blacklist_ext; +static int blacklist_next; + +static int +gather_deps (DSO *dso, struct prelink_entry *ent) +{ + int i, j, seen = 0; + FILE *f = NULL; + const char *argv[8]; + const char *envp[5]; + char *line = NULL, *p, *q = NULL; + const char **depends = NULL, **depends_temp; + size_t ndepends = 0, ndepends_alloced = 0; + size_t len = 0; + ssize_t n; + Elf_Scn *scn; + Elf_Data *data; + Elf32_Lib *liblist = NULL; + int nliblist = 0; + const char *dl; + const char *ent_filename; + int etype = dso->ehdr.e_type; + + if (check_dso (dso)) + { + if (! undo) + ent->type = ET_UNPRELINKABLE; + goto error_out; + } + + ent->pltgot = dso->info[DT_PLTGOT]; + ent->soname = strdup (dso->soname); + ent->flags = (dso->arch->class == ELFCLASS64 ? PCF_ELF64 : 0) + | (dso->arch->machine & PCF_MACHINE); + if (ent->soname == NULL) + { + error (0, ENOMEM, "%s: Could not record SONAME", ent->filename); + goto error_out; + } + + dl = dynamic_linker ?: dso->arch->dynamic_linker; + if (strcmp (dso->filename, dl) == 0 + || is_ldso_soname (dso->soname)) + { + if (dynamic_info_is_set (dso, DT_GNU_PRELINKED_BIT) + && dynamic_info_is_set (dso, DT_CHECKSUM_BIT)) + { + if (! undo && dso->arch->read_opd) + dso->arch->read_opd (dso, ent); + ent->done = 2; + } + close_dso (dso); + return 0; + } + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + const char *name; + if (dso->shdr[i].sh_type == SHT_GNU_LIBLIST + && (name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name)) + && ! strcmp (name, ".gnu.liblist") + && (dso->shdr[i].sh_size % sizeof (Elf32_Lib)) == 0) + { + nliblist = dso->shdr[i].sh_size / sizeof (Elf32_Lib); + liblist = (Elf32_Lib *) alloca (dso->shdr[i].sh_size); + scn = dso->scn[i]; + data = elf_getdata (scn, NULL); + if (data == NULL || elf_getdata (scn, data) + || data->d_buf == NULL || data->d_off + || data->d_size != dso->shdr[i].sh_size) + liblist = NULL; + else + memcpy (liblist, data->d_buf, dso->shdr[i].sh_size); + if (! undo) + break; + } + else if (undo + && dso->shdr[i].sh_type == SHT_PROGBITS + && (name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name)) + && ! strcmp (name, ".gnu.prelink_undo")) + ent->done = 2; + } + + if (! undo && dso->arch->read_opd) + dso->arch->read_opd (dso, ent); + close_dso (dso); + dso = NULL; + + i = 0; + argv[i++] = dl; + if (strchr (ent->filename, '/') != NULL) + ent_filename = ent->filename; + else + { + size_t flen = strlen (ent->filename); + char *tp = alloca (2 + flen + 1); + memcpy (tp, "./", 2); + memcpy (tp + 2, ent->filename, flen + 1); + ent_filename = tp; + } + + if (prelink_rtld == NULL) + { + i = 0; + argv[i++] = dl; + if (ld_library_path) + { + argv[i++] = "--library-path"; + argv[i++] = ld_library_path; + } + argv[i++] = ent_filename; + argv[i] = NULL; + envp[0] = "LD_TRACE_LOADED_OBJECTS=1"; + envp[1] = "LD_TRACE_PRELINKING=1"; + envp[2] = "LD_WARN="; + envp[3] = NULL; + f = execve_open (dl, (char * const *)argv, (char * const *)envp); + } + else + { + char *path; + i = 0; + argv[i++] = prelink_rtld; + if (ld_library_path) + { + argv[i++] = "--library-path"; + argv[i++] = ld_library_path; + } + + if(etype == ET_EXEC && ld_preload) { + argv[i++] = "--ld-preload"; + argv[i++] = ld_preload; + } + argv[i++] = "--target-paths"; + argv[i++] = ent_filename; + argv[i] = NULL; + envp[0] = "RTLD_TRACE_PRELINKING=1"; + envp[1] = "RTLD_WARN="; + path = alloca (sizeof "PATH=" + strlen (getenv ("PATH"))); + sprintf (path, "PATH=%s", getenv ("PATH")); + envp[2] = path; + + if (sysroot) + { + envp[3] = alloca (sizeof "PRELINK_SYSROOT=" + strlen (sysroot)); + sprintf ((char *) envp[3], "PRELINK_SYSROOT=%s", sysroot); + envp[4] = NULL; + } + else + envp[3] = NULL; + + f = execve_open (prelink_rtld, (char * const *)argv, (char * const *)envp); + } + + if (f == NULL) + goto error_out; + + do + { + n = getline (&line, &len, f); + if (n < 0) + break; + + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + + p = strstr (line, " => "); + if (p) + { + q = strstr (p, " ("); + if (q == NULL && strcmp (p, " => not found") == 0) + { + error (0, 0, "%s: Could not find one of the dependencies: %s", + ent->filename, line); + goto error_out; + } + } + if (p == NULL || q == NULL) + { + if (strstr (line, "statically linked") != NULL) + error (0, 0, "%s: Library without dependencies", ent->filename); + else + { + p = strstr (line, "error while loading shared libraries: "); + if (p != NULL) + { + p += sizeof "error while loading shared libraries: " - 1; + q = strstr (line, "cannot open shared object file: " + "No such file or directory"); + if (q != NULL) + { + error (0, 0, + "%s: Could not find one of the dependencies: \n%s", + ent->filename, line); + goto error_out; + } + } + error (0, 0, "%s: Could not parse `%s'", ent->filename, line); + } + goto error_out; + } + + *p = '\0'; + p += sizeof " => " - 1; + *q = '\0'; + if (! strcmp (p, ent_filename)) + { + ++seen; + continue; + } + if (ndepends == ndepends_alloced) + { + ndepends_alloced += 10; + depends_temp = depends; + depends = + (const char **) realloc (depends, + ndepends_alloced * sizeof (char *)); + if (depends == NULL) + { + free(depends_temp); + error (0, ENOMEM, "%s: Could not record dependencies", + ent->filename); + goto error_out; + } + } + + depends[ndepends] = strdupa (p); + ++ndepends; + } while (!feof (f)); + + if (execve_close (f)) + { + f = NULL; + error (0, 0, "%s: Dependency tracing failed", ent->filename); + goto error_out; + } + + f = NULL; + if (seen != 1) + { + error (0, 0, "%s seen %d times in LD_TRACE_PRELINKING output, expected once", + ent->filename, seen); + goto error_out; + } + + free (line); + line = NULL; + + if (ndepends == 0) + ent->depends = NULL; + else + { + ent->depends = + (struct prelink_entry **) + malloc (ndepends * sizeof (struct prelink_entry *)); + if (ent->depends == NULL) + { + error (0, ENOMEM, "%s: Could not record dependencies", ent->filename); + goto error_out; + } + } + + ent->ndepends = ndepends; + char *cache_dyn_depends = NULL; + if (ndepends) + { + cache_dyn_depends = alloca (ndepends); + memset (cache_dyn_depends, '\0', ndepends); + } + for (i = 0; i < ndepends; ++i) + { + ent->depends[i] = prelink_find_entry (depends [i], NULL, 1); + if (ent->depends[i] == NULL) + goto error_out_free_depends; + + if (ent->depends[i]->type == ET_CACHE_DYN) + { + ent->depends[i]->type = ET_NONE; + free (ent->depends[i]->depends); + ent->depends[i]->depends = NULL; + ent->depends[i]->ndepends = 0; + cache_dyn_depends[i] = 1; + } + + if (ent->depends[i]->type != ET_NONE + && ent->depends[i]->type != ET_BAD + && ent->depends[i]->type != ET_DYN + && ent->depends[i]->type != ET_UNPRELINKABLE) + { + error (0, 0, "%s is not a shared library", depends [i]); +error_out_regather_libs: + for (i = 0; i < ndepends; ++i) + { + if (cache_dyn_depends[i] && ent->depends[i]->type == ET_NONE) + gather_lib (ent->depends[i]); + } + goto error_out_free_depends; + } + } + + free (depends); + depends = NULL; + + for (i = 0; i < ndepends; ++i) + if (ent->depends[i]->type == ET_NONE + && gather_lib (ent->depends[i])) + { + cache_dyn_depends[i] = 0; + goto error_out_regather_libs; + } + + for (i = 0; i < ndepends; ++i) + for (j = 0; j < ent->depends[i]->ndepends; ++j) + if (ent->depends[i]->depends[j] == ent) + { + error (0, 0, "%s has a dependency cycle", ent->canon_filename); + goto error_out_free_depends; + } + + for (i = 0; i < ndepends; ++i) + if (ent->depends[i]->type == ET_UNPRELINKABLE) + { + error (0, 0, "Could not prelink %s because its dependency %s could not be prelinked", + ent->filename, ent->depends[i]->filename); + ent->type = ET_UNPRELINKABLE; + goto error_out; + } + + if (! undo && (!nliblist || liblist) && nliblist == ndepends) + { + for (i = 0; i < ndepends; ++i) + if (liblist[i].l_time_stamp != ent->depends[i]->timestamp + || liblist[i].l_checksum != ent->depends[i]->checksum + || ! ent->depends[i]->done) + break; + + if (i == ndepends) + ent->done = 2; + } + + return 0; + +error_out_free_depends: + free (ent->depends); + ent->depends = NULL; + ent->ndepends = 0; +error_out: + if (f) + execve_close (f); + free (line); + free (depends); + if (dso) + close_dso (dso); + return 1; +} + +static int +gather_dso (DSO *dso, struct prelink_entry *ent) +{ + int prelinked; + + if (verbose > 5) + printf ("Checking shared library %s\n", ent->canon_filename); + + if (dso->ehdr.e_type != ET_DYN) + { + error (0, 0, "%s is not a shared library", ent->filename); + close_dso (dso); + return 1; + } + + prelinked = (dynamic_info_is_set (dso, DT_GNU_PRELINKED_BIT) + && dynamic_info_is_set (dso, DT_CHECKSUM_BIT)); + ent->timestamp = dso->info_DT_GNU_PRELINKED; + ent->checksum = dso->info_DT_CHECKSUM; + ent->base = dso->base; + ent->end = dso->end; + if (dso->arch->need_rel_to_rela != NULL && ! prelinked) + { + /* If the library has not been prelinked yet and we need + to convert REL to RELA, then make room for it. */ + struct reloc_info rinfo; + GElf_Addr adjust = 0; + int sec = dso->ehdr.e_shnum; + + if (find_reloc_sections (dso, &rinfo)) + { + close_dso (dso); + return 1; + } + + assert (sizeof (Elf32_Rel) * 3 == sizeof (Elf32_Rela) * 2); + assert (sizeof (Elf64_Rel) * 3 == sizeof (Elf64_Rela) * 2); + if (rinfo.rel_to_rela) + { + sec = rinfo.first; + adjust = (dso->shdr[rinfo.last].sh_addr + + dso->shdr[rinfo.last].sh_size + - dso->shdr[rinfo.first].sh_addr) / 2; + } + if (rinfo.rel_to_rela_plt) + { + if (rinfo.plt < sec) + sec = rinfo.plt; + adjust += dso->shdr[rinfo.plt].sh_size / 2; + } + if (adjust) + { + int align = 0, i, last; + GElf_Addr start; + + for (i = rinfo.plt ? rinfo.plt : rinfo.first; + i < dso->ehdr.e_shnum; i++) + { + if (dso->shdr[i].sh_addralign > align) + align = dso->shdr[i].sh_addralign; + } + + if (rinfo.plt) + start = dso->shdr[rinfo.plt].sh_addr + + dso->shdr[rinfo.plt].sh_size; + else + start = dso->shdr[rinfo.first].sh_addr + + dso->shdr[rinfo.first].sh_size; + + /* Need to make sure that all the remaining sections are properly + aligned. */ + if (align) + adjust = (adjust + align - 1) & ~(align - 1); + + /* Need to make sure adjust doesn't cause different Phdr segments + to overlap on the same page. */ + last = -1; + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_LOAD + && dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz >= start) + { + if (last != -1 + && (((dso->phdr[last].p_vaddr + dso->phdr[last].p_memsz + - 1) ^ dso->phdr[i].p_vaddr) + & ~(dso->arch->max_page_size - 1)) + && !(((dso->phdr[last].p_vaddr + dso->phdr[last].p_memsz + + adjust - 1) + ^ (dso->phdr[i].p_vaddr + adjust)) + & ~(dso->arch->max_page_size - 1))) + { + if (align >= dso->arch->max_page_size) + { + error (0, 0, "%s: Cannot grow reloc sections", + ent->filename); + close_dso (dso); + return 1; + } + adjust = (adjust + dso->arch->max_page_size - 1) + & ~(dso->arch->max_page_size - 1); + } + last = i; + } + + ent->end += adjust; + } + } + + if (gather_deps (dso, ent)) + return 1; + + if (ent->done && ! prelinked && ! undo) + ent->done = 0; + ent->type = ET_DYN; + return 0; +} + +static int +gather_lib (struct prelink_entry *ent) +{ + DSO *dso; + + ent->type = ET_BAD; + dso = open_dso (ent->filename); + if (dso == NULL) + return 1; + + return gather_dso (dso, ent); +} + +static int +gather_exec (DSO *dso, const struct stat64 *st) +{ + int i, j; + Elf_Data *data; + const char *dl; + struct prelink_entry *ent; + + if (verbose > 5) + printf ("Checking executable %s\n", dso->filename); + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_INTERP) + break; + + /* If there are no PT_INTERP segments, it is statically linked. */ + if (i == dso->ehdr.e_phnum) + { +make_unprelinkable: + if (undo) + goto error_out; + + ent = prelink_find_entry (dso->filename, st, 1); + if (ent == NULL) + goto error_out; + + assert (ent->type == ET_NONE); + ent->type = ET_UNPRELINKABLE; + close_dso (dso); + return 0; + } + + j = addr_to_sec (dso, dso->phdr[i].p_vaddr); + if (j == -1 || dso->shdr[j].sh_addr != dso->phdr[i].p_vaddr + || dso->shdr[j].sh_type != SHT_PROGBITS) + { + error (0, 0, "%s: PT_INTERP segment not corresponding to .interp section", + dso->filename); + goto make_unprelinkable; + } + + data = elf_getdata (dso->scn[j], NULL); + if (data == NULL) + { + error (0, 0, "%s: Could not read .interp section", dso->filename); + goto error_out; + } + + i = strnlen (data->d_buf, data->d_size); + if (i == data->d_size) + { + error (0, 0, "%s: .interp section not zero terminated", dso->filename); + goto error_out; + } + + dl = dynamic_linker ?: dso->arch->dynamic_linker; + if (strcmp (dl, data->d_buf) != 0 + && (dynamic_linker != NULL || dso->arch->dynamic_linker_alt == NULL + || strcmp (dso->arch->dynamic_linker_alt, data->d_buf) != 0)) + { + error (0, 0, "%s: Using %s, not %s as dynamic linker", dso->filename, + (char *) data->d_buf, dl); + goto error_out; + } + + if (dso_has_bad_textrel (dso)) + { + error (0, 0, "%s has text relocations", dso->filename); + goto make_unprelinkable; + } + + ent = prelink_find_entry (dso->filename, st, 1); + if (ent == NULL) + goto error_out; + + assert (ent->type == ET_NONE); + ent->u.explicit = 1; + + if (gather_deps (dso, ent)) + return 0; + + for (i = 0; i < ent->ndepends; ++i) + ++ent->depends[i]->refs; + + ent->type = ET_EXEC; + return 0; + +error_out: + if (dso) + close_dso (dso); + return 0; +} + +static int +add_dir_to_dirlist (const char *name, dev_t dev, int flags) +{ + const char *canon_name; + struct prelink_dir *dir; + size_t len; + + canon_name = prelink_canonicalize (name, NULL); + if (canon_name == NULL) + { + if (! all && implicit) + return 0; + error (0, errno, "Could not record directory %s", name); + return 1; + } + + len = strlen (canon_name); + + for (dir = blacklist; dir; dir = dir->next) + if (((dir->flags != FTW_CHDIR && len >= dir->len) + || (dir->flags == FTW_CHDIR && len == dir->len)) + && strncmp (dir->dir, canon_name, dir->len) == 0) + { + if (dir->flags == FTW_CHDIR) + break; + if ((dir->flags & FTW_MOUNT) && dir->dev != dev) + continue; + break; + } + + if (dir != NULL) + { + free ((char *) canon_name); + return 2; + } + + dir = malloc (sizeof (struct prelink_dir) + len + 1); + if (dir == NULL) + { + error (0, ENOMEM, "Could not record directory %s", name); + free ((char *) canon_name); + return 1; + } + + dir->next = dirs; + dir->flags = flags; + dir->dev = dev; + dir->len = len; + strcpy (dir->dir, canon_name); + free ((char *) canon_name); + dirs = dir; + return 0; +} + +/* Determine if a buffer holding an ELF header and program header + table may be that of a position-independent executable. */ +static int +maybe_pie (unsigned char *e_ident, int big_endian, int sixty_four) +{ + uint16_t num_phdrs; + uint16_t phdr; + size_t p_type_offset; + size_t phnum_offset; + unsigned char *phdr_table; + unsigned char *this_phdr; + + if (sixty_four) + { + uint64_t phdr_offset; + + p_type_offset = offsetof (Elf64_Phdr, p_type); + phnum_offset = offsetof (Elf64_Ehdr, e_phnum); + if (big_endian) + phdr_offset = buf_read_ube64 (&e_ident [offsetof (Elf64_Ehdr, + e_phoff)]); + else + phdr_offset = buf_read_ule64 (&e_ident [offsetof (Elf64_Ehdr, + e_phoff)]); + phdr_table = e_ident + phdr_offset; + } + else + { + uint32_t phdr_offset; + + p_type_offset = offsetof (Elf32_Phdr, p_type); + phnum_offset = offsetof (Elf32_Ehdr, e_phnum); + if (big_endian) + phdr_offset = buf_read_ube32 (&e_ident [offsetof (Elf32_Ehdr, + e_phoff)]); + else + phdr_offset = buf_read_ule32 (&e_ident [offsetof (Elf32_Ehdr, + e_phoff)]); + phdr_table = e_ident + phdr_offset; + } + + this_phdr = phdr_table; + + if (big_endian) + num_phdrs = buf_read_ube16 (&e_ident [phnum_offset]); + else + num_phdrs = buf_read_ule16 (&e_ident [phnum_offset]); + + for (phdr = 0; phdr < num_phdrs; phdr++) + { + unsigned char *p_type_start = this_phdr + p_type_offset; + uint32_t p_type; + + if (big_endian) + p_type = buf_read_ube32 (p_type_start); + else + p_type = buf_read_ule32 (p_type_start); + + if (p_type == PT_PHDR) + return 1; + + /* Any PT_PHDR entry must come before any PT_LOAD entry. */ + if (p_type == PT_LOAD) + return 0; + + this_phdr += sixty_four ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr); + } + + return 0; +} + +static int +gather_func (const char *name, const struct stat64 *st, int type, + struct FTW *ftwp) +{ + unsigned char e_ident [sizeof (Elf64_Ehdr) + sizeof (Elf64_Phdr)]; + +#ifndef HAVE_FTW_ACTIONRETVAL + if (blacklist_dir) + { + if (strncmp (name, blacklist_dir, blacklist_dir_len) == 0) + return FTW_CONTINUE; + free (blacklist_dir); + blacklist_dir = NULL; + } +#endif + if (type == FTW_F && S_ISREG (st->st_mode) && (st->st_mode & 0111)) + { + int fd, i; + DSO *dso; + struct prelink_entry *ent; + size_t len = strlen (name); + const char *base = NULL; + + for (i = 0; i < blacklist_next; ++i) + if (blacklist_ext[i].is_glob) + { + if (base == NULL) + { + base = strrchr (name, '/'); + if (base == NULL) + base = name; + else + ++base; + } + if (fnmatch (blacklist_ext[i].ext, base, FNM_PERIOD) == 0) + return FTW_CONTINUE; + } + else if (blacklist_ext[i].len <= len + && memcmp (name + len - blacklist_ext[i].len, + blacklist_ext[i].ext, blacklist_ext[i].len) == 0) + return FTW_CONTINUE; + + ent = prelink_find_entry (name, st, 0); + if (ent != NULL && ent->type != ET_NONE) + { + if (verbose > 5) + { + if (ent->type == ET_CACHE_EXEC || ent->type == ET_CACHE_DYN) + printf ("Assuming prelinked %s\n", name); + if (ent->type == ET_UNPRELINKABLE) + printf ("Assuming non-prelinkable %s\n", name); + } + ent->u.explicit = 1; + return FTW_CONTINUE; + } + + if (st->st_size < sizeof (e_ident)) + return FTW_CONTINUE; + + fd = wrap_open (name, O_RDONLY); + if (fd == -1) + return FTW_CONTINUE; + + if (read (fd, e_ident, sizeof (e_ident)) != sizeof (e_ident)) + { +close_it: + fsync (fd); + close (fd); + return FTW_CONTINUE; + } + + /* Quickly find ET_EXEC ELF binaries and most of PIE binaries. */ + + if (memcmp (e_ident, ELFMAG, SELFMAG) != 0) + { +make_unprelinkable: + if (! undo) + { + ent = prelink_find_entry (name, st, 1); + if (ent != NULL) + { + assert (ent->type == ET_NONE); + ent->type = ET_UNPRELINKABLE; + } + } + fsync (fd); + close (fd); + return FTW_CONTINUE; + } + + switch (e_ident [EI_DATA]) + { + case ELFDATA2LSB: + if (e_ident [EI_NIDENT + 1] != 0) + goto make_unprelinkable; + if (e_ident [EI_NIDENT] != ET_EXEC) + { + if (e_ident [EI_NIDENT] != ET_DYN) + goto make_unprelinkable; + else if (e_ident [EI_CLASS] == ELFCLASS32) + { + if (maybe_pie (e_ident, 0, 0)) + { +maybe_pie: + dso = fdopen_dso (fd, name); + if (dso == NULL) + goto close_it; + if (dynamic_info_is_set (dso, DT_DEBUG)) + { + close_dso (dso); + goto make_unprelinkable; + } + close_dso (dso); + } + goto close_it; + } + else if (e_ident [EI_CLASS] == ELFCLASS64) + { + if (maybe_pie (e_ident, 0, 1)) + goto maybe_pie; + goto close_it; + } + else + goto make_unprelinkable; + } + break; + case ELFDATA2MSB: + if (e_ident [EI_NIDENT] != 0) + goto make_unprelinkable; + if (e_ident [EI_NIDENT + 1] != ET_EXEC) + { + if (e_ident [EI_NIDENT + 1] != ET_DYN) + goto make_unprelinkable; + else if (e_ident [EI_CLASS] == ELFCLASS32) + { + if (maybe_pie (e_ident, 1, 0)) + goto maybe_pie; + goto close_it; + } + else if (e_ident [EI_CLASS] == ELFCLASS64) + { + if (maybe_pie (e_ident, 1, 1)) + goto maybe_pie; + goto close_it; + } + else + goto make_unprelinkable; + } + break; + default: + goto make_unprelinkable; + } + + dso = fdopen_dso (fd, name); + if (dso == NULL) + return FTW_CONTINUE; + + gather_exec (dso, st); + } + else if (type == FTW_D) + switch (add_dir_to_dirlist (name, st->st_dev, FTW_CHDIR)) + { + case 0: return FTW_CONTINUE; + default: return FTW_STOP; + case 2: +#ifdef HAVE_FTW_ACTIONRETVAL + return FTW_SKIP_SUBTREE; +#else + { + blacklist_dir_len = strlen (name) + 1; + if (blacklist_dir_len > 1 && name[blacklist_dir_len - 2] == '/') + blacklist_dir_len--; + blacklist_dir = malloc (blacklist_dir_len + 1); + if (blacklist_dir == NULL) + { + error (0, ENOMEM, "Cannot store blacklisted dir name"); + return FTW_STOP; + } + memcpy (blacklist_dir, name, blacklist_dir_len - 1); + blacklist_dir[blacklist_dir_len - 1] = '/'; + blacklist_dir[blacklist_dir_len] = '\0'; + return FTW_CONTINUE; + } +#endif + } + + return FTW_CONTINUE; +} + +static int +gather_binlib (const char *name, const struct stat64 *st) +{ + unsigned char e_ident [EI_NIDENT + 2]; + int fd, type; + DSO *dso; + struct prelink_entry *ent; + + if (! S_ISREG (st->st_mode)) + { + error (0, 0, "%s is not a regular file", name); + return 1; + } + + ent = prelink_find_entry (name, st, 0); + if (ent != NULL && ent->type == ET_UNPRELINKABLE) + { + free (ent->depends); + ent->depends = NULL; + ent->ndepends = 0; + ent->type = ET_NONE; + } + if (ent != NULL && ent->type != ET_NONE) + { + ent->u.explicit = 1; + return 0; + } + + fd = wrap_open (name, O_RDONLY); + if (fd == -1) + { + error (0, errno, "Could not open %s", name); + return 1; + } + + if (read (fd, e_ident, sizeof (e_ident)) != sizeof (e_ident)) + { + error (0, errno, "Could not read ELF header from %s", name); + fsync (fd); + close (fd); + return 1; + } + + /* Quickly find ET_EXEC/ET_DYN ELF binaries/libraries only. */ + + if (memcmp (e_ident, ELFMAG, SELFMAG) != 0) + { + error (0, 0, "%s is not an ELF object", name); + fsync (fd); + close (fd); + return 1; + } + + switch (e_ident [EI_DATA]) + { + case ELFDATA2LSB: + if (e_ident [EI_NIDENT + 1] != 0) + goto unsupported_type; + type = e_ident [EI_NIDENT]; + break; + case ELFDATA2MSB: + if (e_ident [EI_NIDENT] != 0) + goto unsupported_type; + type = e_ident [EI_NIDENT + 1]; + break; + default: + goto unsupported_type; + } + + if (type != ET_EXEC && type != ET_DYN) + { +unsupported_type: + error (0, 0, "%s is neither ELF executable nor ELF shared library", name); + fsync (fd); + close (fd); + return 1; + } + + dso = fdopen_dso (fd, name); + if (dso == NULL) + return 0; + + if (type == ET_EXEC) + { + int i; + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_INTERP) + break; + + /* If there are no PT_INTERP segments, it is statically linked. */ + if (i == dso->ehdr.e_phnum) + { + error (0, 0, "%s is statically linked", name); + close_dso (dso); + return 1; + } + + return gather_exec (dso, st); + } + + ent = prelink_find_entry (name, st, 1); + if (ent == NULL) + { + close_dso (dso); + return 1; + } + + assert (ent->type == ET_NONE); + ent->type = ET_BAD; + ent->u.explicit = 1; + return gather_dso (dso, ent); +} + +int +gather_object (const char *name, int deref, int onefs) +{ + struct stat64 st; + + if (wrap_stat64 (name, &st) < 0) + { + if (implicit) + return 0; + error (0, errno, "Could not stat %s", name); + return 1; + } + + if (S_ISDIR (st.st_mode)) + { + int flags = 0, ret; + if (! deref) flags |= FTW_PHYS; + if (onefs) flags |= FTW_MOUNT; + + if (implicit && ! deref) + { + ret = add_dir_to_dirlist (name, st.st_dev, flags); + if (ret) + return ret == 2 ? 0 : 1; + } + if (!all && implicit && ! deref) + return 0; + ++implicit; + ret = wrap_nftw64 (name, gather_func, 20, flags | FTW_ACTIONRETVAL); + --implicit; + if (ret < 0) + error (0, errno, "Failed searching %s", name); +#ifndef HAVE_FTW_ACTIONRETVAL + free (blacklist_dir); + blacklist_dir = NULL; +#endif + return ret; + } + else + return gather_binlib (name, &st); +} + +static struct config_line +{ + struct config_line *next; + char line[1]; +} *config_lines, **config_end = &config_lines; + +int +read_config (const char *config) +{ + FILE *file = fopen (config, "r"); + char *line = NULL; + size_t len, llen; + int ret = 0; + struct config_line *c; + + if (file == NULL) + { + error (0, errno, "Can't open configuration file %s", config); + return 1; + } + + do + { + ssize_t i = getline (&line, &len, file); + char *p; + + if (i < 0) + break; + + if (line[i - 1] == '\n') + line[i - 1] = '\0'; + + p = strchr (line, '#'); + if (p != NULL) + *p = '\0'; + + p = line + strspn (line, " \t"); + if (p[0] == '-' && p[1] == 'c' && (p[2] == ' ' || p[2] == '\t')) + { + glob_t g; + p += 2 + strspn (p + 2, " \t"); + + if (!wrap_glob (p, GLOB_BRACE, NULL, &g)) + { + size_t n; + + for (n = 0; n < g.gl_pathc; ++n) + if (read_config (g.gl_pathv[n])) + { + ret = 1; + break; + } + + globfree (&g); + if (ret) + break; + } + continue; + } + + llen = strlen (p); + c = malloc (sizeof (*c) + llen); + if (c == NULL) + { + error (0, ENOMEM, "Could not cache config file"); + ret = 1; + break; + } + + c->next = NULL; + memcpy (c->line, p, llen + 1); + *config_end = c; + config_end = &c->next; + } + while (!feof (file)); + + free (line); + fclose (file); + return ret; +} + +int +gather_config (void) +{ + struct config_line *c; + int ret = 0; + + implicit = 1; + for (c = config_lines; c; c = c->next) + { + int deref = 0; + int onefs = 0; + char *p = c->line; + + while (*p == '-') + { + switch (p[1]) + { + case 'h': deref = 1; break; + case 'l': onefs = 1; break; + case 'b': p = ""; continue; + default: + error (0, 0, "Unknown directory option `%s'\n", p); + break; + } + p = p + 2 + strspn (p + 2, " \t"); + } + + if (*p == '\0') + continue; + + if (strpbrk (p, "*?[{") == NULL) + { + ret = gather_object (p, deref, onefs); + if (ret) + { + ret = 1; + break; + } + } + else + { + glob_t g; + + if (!wrap_glob (p, GLOB_BRACE, NULL, &g)) + { + size_t n; + + for (n = 0; n < g.gl_pathc; ++n) + { + ret = gather_object (g.gl_pathv[n], deref, onefs); + if (ret) + { + ret = 1; + break; + } + } + + globfree (&g); + if (ret) + break; + } + } + } + + implicit = 0; + return ret; +} + +static int +gather_check_lib (void **p, void *info) +{ + struct prelink_entry *e = * (struct prelink_entry **) p; + + if (e->type != ET_DYN) + return 1; + + if (! e->u.explicit) + { + struct prelink_dir *dir; + const char *name; + size_t len; + + name = strrchr (e->canon_filename, '/'); + if (!name) + name = e->canon_filename; + len = name - e->canon_filename; + + for (dir = blacklist; dir; dir = dir->next) + if (((dir->flags != FTW_CHDIR && len >= dir->len) + || (dir->flags == FTW_CHDIR && len == dir->len)) + && strncmp (dir->dir, e->canon_filename, dir->len) == 0) + { + if (dir->flags == FTW_CHDIR) + break; + if ((dir->flags & FTW_MOUNT) && dir->dev != e->dev) + continue; + break; + } + + if (dir != NULL) + { + error (0, 0, "%s is present in a blacklisted directory %s", + e->canon_filename, dir->dir); + e->type = ET_BAD; + return 1; + } + + for (dir = dirs; dir; dir = dir->next) + if (((dir->flags != FTW_CHDIR && len >= dir->len) + || (dir->flags == FTW_CHDIR && len == dir->len)) + && strncmp (dir->dir, e->canon_filename, dir->len) == 0) + { + if (dir->flags == FTW_CHDIR) + break; + if ((dir->flags & FTW_MOUNT) && dir->dev != e->dev) + continue; + break; + } + + if (dir == NULL) + { + error (0, 0, "%s is not present in any config file directories, nor was specified on command line", + e->canon_filename); + e->type = ET_BAD; + return 1; + } + } + + return 1; +} + +int +gather_check_libs (void) +{ + struct prelink_dir *dir; + void *f; + + htab_traverse (prelink_filename_htab, gather_check_lib, NULL); + + dir = dirs; + while (dir != NULL) + { + f = dir; + dir = dir->next; + free (f); + } + + dir = blacklist; + while (dir != NULL) + { + f = dir; + dir = dir->next; + free (f); + } + + dirs = NULL; + blacklist = NULL; + return 0; +} + +int +add_to_blacklist (const char *name, int deref, int onefs) +{ + const char *canon_name; + struct prelink_dir *path; + size_t len; + struct stat64 st; + + if (wrap_stat64 (name, &st) < 0) + { + if (implicit) + return 0; + error (0, errno, "Could not stat %s", name); + return 1; + } + + if (!S_ISDIR (st.st_mode)) + { + struct prelink_entry *ent; + + ent = prelink_find_entry (name, &st, 1); + if (ent == NULL) + return 1; + + ent->type = ET_BAD; + ent->u.explicit = 1; + return 0; + } + + canon_name = prelink_canonicalize (name, NULL); + if (canon_name == NULL) + { + if (implicit) + return 0; + error (0, errno, "Could not canonicalize %s", name); + return 1; + } + + len = strlen (canon_name); + path = malloc (sizeof (struct prelink_dir) + len + 1); + if (path == NULL) + { + error (0, ENOMEM, "Could not record path %s", name); + free ((char *) canon_name); + return 1; + } + + path->next = blacklist; + path->flags = 0; + if (! deref) path->flags |= FTW_PHYS; + if (onefs) path->flags |= FTW_MOUNT; + path->dev = 0; + path->len = len; + strcpy (path->dir, canon_name); + free ((char *) canon_name); + blacklist = path; + return 0; +} + +void +add_blacklist_ext (const char *ext) +{ + blacklist_ext = realloc (blacklist_ext, + (blacklist_next + 1) * sizeof (*blacklist_ext)); + if (blacklist_ext == NULL) + error (EXIT_FAILURE, errno, "can't create blacklist extension list"); + if (*ext == '*' && strpbrk (ext + 1, "*?[{") == NULL) + { + blacklist_ext[blacklist_next].is_glob = 0; + ext++; + } + else + blacklist_ext[blacklist_next].is_glob = 1; + blacklist_ext[blacklist_next].ext = strdup (ext); + if (blacklist_ext[blacklist_next].ext == NULL) + error (EXIT_FAILURE, errno, "can't create blacklist extension list"); + blacklist_ext[blacklist_next].len = strlen (ext); + blacklist_next++; +} + +int +blacklist_from_config (void) +{ + struct config_line *c; + int ret = 0; + + implicit = 1; + for (c = config_lines; c; c = c->next) + { + int deref = 0; + int onefs = 0; + int blacklist = 0; + char *p = c->line; + + while (*p == '-') + { + switch (p[1]) + { + case 'h': deref = 1; break; + case 'l': onefs = 1; break; + case 'b': blacklist = 1; break; + } + p = p + 2 + strspn (p + 2, " \t"); + } + + if (*p == '\0' || !blacklist) + continue; + + if (strchr (p, '/') == NULL) + { + add_blacklist_ext (p); + continue; + } + + if (strpbrk (p, "*?[{") == NULL) + { + ret = add_to_blacklist (p, deref, onefs); + if (ret) + { + ret = 1; + break; + } + } + else + { + glob_t g; + + if (!wrap_glob (p, GLOB_BRACE | GLOB_PERIOD, NULL, &g)) + { + size_t n; + + for (n = 0; n < g.gl_pathc; ++n) + { + ret = add_to_blacklist (g.gl_pathv[n], deref, onefs); + if (ret) + { + ret = 1; + break; + } + } + + globfree (&g); + if (ret) + break; + } + } + } + + implicit = 0; + return ret; +} diff --git a/src/get.c b/src/get.c new file mode 100644 index 0000000..d10efcc --- /dev/null +++ b/src/get.c @@ -0,0 +1,760 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include "prelink.h" + +int +is_ldso_soname (const char *soname) +{ + if (! strcmp (soname, "ld-linux.so.2") + || ! strcmp (soname, "ld.so.1") + || ! strcmp (soname, "ld-linux-ia64.so.2") + || ! strcmp (soname, "ld-linux-x86-64.so.2") + || ! strcmp (soname, "ld64.so.1") + || ! strcmp (soname, "ld-linux.so.3") + || ! strcmp (soname, "ld-linux-armhf.so.3")) + return 1; + return 0; +} + +static void +conflict_hash_init (struct prelink_conflicts *conflicts) +{ + struct prelink_conflict **hash + = calloc (sizeof (struct prelink_conflict *), 251); + struct prelink_conflict *conflict, *next; + size_t idx; + + if (hash == NULL) + return; + + for (conflict = conflicts->first; conflict; conflict = next) + { + next = conflict->next; + idx = conflict->symoff % 251; + conflict->next = hash[idx]; + hash[idx] = conflict; + } + conflicts->hash = hash; +} + +static int +prelink_record_relocations (struct prelink_info *info, FILE *f, + const char *ent_filename) +{ + char buffer[8192]; + DSO *dso = info->dso; + struct prelink_entry *ent, *ent2; + struct prelink_tls *tls; + struct deps + { + struct prelink_entry *ent; + char *soname; + GElf_Addr start; + GElf_Addr l_addr; + GElf_Addr tls_modid; + GElf_Addr tls_offset; + } deps[info->ent->ndepends + 1]; + char *r; + int i, ndeps = 0, undef = 0, seen = 0, tdeps = 0; + int mask_32bit = (info->dso->ehdr.e_ident[EI_CLASS] == ELFCLASS32); + + /* Record the dependencies. */ + while ((r = fgets (buffer, 8192, f)) != NULL) + { + char *soname, *filename, *p, *q; + GElf_Addr start = 0, l_addr = 0, tls_modid = 0, tls_offset = 0; + unsigned long long l; + + if (buffer[0] != '\t' || (filename = strstr (buffer, " => ")) == NULL) + break; + soname = buffer + 1; + p = strstr (filename + sizeof (" => "), " (0x"); + if (p != NULL) + { + l = strtoull (p + sizeof (" (0x") - 1, &q, 16); + start = (GElf_Addr) l; + if (start != l || strncmp (q, ", 0x", sizeof (", 0x") - 1)) + p = NULL; + else + { + l = strtoull (q + sizeof (", 0x") - 1, &q, 16); + l_addr = (GElf_Addr) l; + if (l_addr != l || q[-1] == 'x') + p = NULL; + else if (strncmp (q, ") TLS(0x", sizeof (") TLS(0x") - 1) == 0) + { + l = strtoull (q + sizeof (") TLS(0x") - 1, &q, 16); + tls_modid = (GElf_Addr) l; + if (tls_modid != l || q[-1] == 'x' + || strncmp (q, ", 0x", sizeof (", 0x") - 1)) + p = NULL; + else + { + l = strtoull (q + sizeof (", 0x") - 1, &q, 16); + tls_offset = (GElf_Addr) l; + if (tls_offset != l || q[-1] == 'x') + p = NULL; + } + } + if (p && strcmp (q, ")\n")) + p = NULL; + } + } + if (p == NULL) + { + p = strchr (buffer, '\n'); + if (p != NULL) + *p = '\0'; + error (0, 0, "Could not parse line `%s'", buffer); + goto error_out; + } + *filename = '\0'; + filename += sizeof (" => ") - 1; + *p = '\0'; + + if (ndeps > info->ent->ndepends) + { + error (0, 0, "%s: Recorded %d dependencies, now seeing %d\n", + info->ent->filename, info->ent->ndepends, ndeps - 1); + goto error_out; + } + + tdeps = ndeps - seen + 1; + if (! seen + && (strcmp (info->ent->filename, filename) == 0 + || (info->ent->filename != ent_filename + && strcmp (ent_filename, filename) == 0) + || strcmp (info->ent->canon_filename, filename) == 0)) + { + seen = 1; + tdeps = 0; + } + else if (ent2 = info->ent->depends [tdeps - 1], + strcmp (ent2->filename, filename) != 0 + && strcmp (ent2->canon_filename, filename) != 0) + { + struct prelink_link *hardlink; + + for (hardlink = ent2->hardlink; hardlink; hardlink = hardlink->next) + if (strcmp (hardlink->canon_filename, filename) == 0) + break; + + if (hardlink == NULL) + { + struct stat64 st; + + if (wrap_stat64 (filename, &st) < 0) + { + error (0, errno, "%s: Could not stat %s", + info->ent->filename, filename); + goto error_out; + } + + if (st.st_dev != ent2->dev || st.st_ino != ent2->ino) + { + error (0, 0, "%s: %s => %s does not match recorded dependency", + info->ent->filename, soname, filename); + goto error_out; + } + } + } + + if (! tdeps) + deps[0].ent = info->ent; + else + deps[tdeps].ent = info->ent->depends[tdeps - 1]; + deps[tdeps].soname = strdup (soname); + if (deps[tdeps].soname == NULL) + { + error (0, ENOMEM, "Could not record `%s' SONAME", soname); + goto error_out; + } + deps[tdeps].start = start; + deps[tdeps].l_addr = l_addr; + deps[tdeps].tls_modid = tls_modid; + deps[tdeps].tls_offset = tls_offset; + ++ndeps; + } + + if (ndeps != info->ent->ndepends + 1) + { + error (0, 0, "%s: Recorded %d dependencies, now seeing %d\n", + info->ent->filename, info->ent->ndepends, ndeps - 1); + goto error_out; + } + + if (r == NULL && !ndeps) + { + error (0, 0, "%s: %s did not print any lookup lines", info->ent->filename, + dynamic_linker ?: dso->arch->dynamic_linker); + goto error_out; + } + + info->tls = malloc (ndeps * sizeof (struct prelink_tls)); + if (info->tls == NULL) + { + error (0, ENOMEM, "%s: Could not record dependency TLS information", + dso->filename); + goto error_out; + } + + for (i = 0; i < ndeps; i++) + { + info->tls[i].modid = deps[i].tls_modid; + info->tls[i].offset = deps[i].tls_offset; + } + + if (dso->ehdr.e_type == ET_EXEC || dso->arch->create_opd) + { + info->conflicts = (struct prelink_conflicts *) + calloc (sizeof (struct prelink_conflicts), ndeps); + if (info->conflicts == NULL) + { + error (0, ENOMEM, "%s: Can't build list of conflicts", info->ent->filename); + goto error_out; + } + for (i = 0; i < ndeps; i++) + info->conflicts[i].hash = &info->conflicts[i].first; + } + do + { + unsigned long long symstart, symoff, valstart[3], value[3]; + int reloc_class, len, type = 1, ifunc = 0; + char *symname; + + r = strchr (buffer, '\n'); + if (r) + *r = '\0'; + if (strncmp (buffer, "lookup ", sizeof ("lookup ") - 1) == 0) + { + struct prelink_symbol *s; + + if (sscanf (buffer, "lookup 0x%llx 0x%llx -> 0x%llx 0x%llx %n", + &symstart, &symoff, &valstart[0], &value[0], &len) != 4) + { + error (0, 0, "%s: Could not parse `%s'", info->ent->filename, buffer); + goto error_out; + } + + if (buffer[len] == '/') + { + ++len; + type = 0; + } + + reloc_class = strtoul (buffer + len, &symname, 16); + if (buffer + len == symname || (reloc_class == 0 && type) + || (*symname != ' ' && *symname != '\t')) + { + error (0, 0, "%s: Could not parse `%s'", info->ent->filename, buffer); + goto error_out; + } + + if (type) + reloc_class = dso->arch->reloc_class (reloc_class); + else + { + if (reloc_class & 8) + { + reloc_class = ((reloc_class & ~8) + | dso->arch->rtype_class_valid); + ifunc = 1; + } + else if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS) + reloc_class |= RTYPE_CLASS_VALID; + else + reloc_class |= dso->arch->rtype_class_valid; + } + + while (*symname == ' ' || *symname == '\t') ++symname; + + ent = NULL; + tls = NULL; + if (symstart == deps[0].start + || ((reloc_class == RTYPE_CLASS_TLS || ifunc) + && info->conflicts)) + { + for (i = 0; i < ndeps; i++) + if (deps[i].start == valstart[0]) + { + if (reloc_class == RTYPE_CLASS_TLS) + tls = info->tls + i; + else + { + ent = deps[i].ent; + /* If the library the symbol is bound to is already + prelinked, adjust the value so that it is relative + to library base. */ + if (mask_32bit) + value[0] -= (Elf32_Addr) (deps[i].start - deps[i].l_addr); + else + value[0] -= deps[i].start - deps[i].l_addr; + } + break; + } + + if (ent == NULL && tls == NULL && valstart[0]) + { + error (0, 0, "Could not find base 0x%08llx in the list of bases `%s'", + valstart[0], buffer); + goto error_out; + } + } + + if (symstart == deps[0].start && (!ifunc || info->conflicts == NULL)) + { + /* Only interested in relocations from the current object. */ + if (symoff < info->symtab_start || symoff >= info->symtab_end) + { + error (0, 0, "%s: Symbol `%s' offset 0x%08llx does not point into .dynsym section", + info->ent->filename, symname, symoff); + goto error_out; + } + + if (ent == info->ent + && reloc_class != RTYPE_CLASS_TLS) + value[0] = adjust_old_to_new (info->dso, value[0]); + + s = &info->symbols[(symoff - info->symtab_start) + / info->symtab_entsize]; + if (s->reloc_class) + { + while (s->reloc_class != reloc_class && s->next != NULL) + s = s->next; + if (s->reloc_class == reloc_class) + { + if ((reloc_class != RTYPE_CLASS_TLS && s->u.ent != ent) + || (reloc_class == RTYPE_CLASS_TLS + && s->u.tls != tls) + || s->value != value[0]) + { + error (0, 0, "%s: Symbol `%s' with the same reloc type resolves to different values each time", + info->ent->filename, symname); + goto error_out; + } + s = NULL; + } + else + { + s->next = (struct prelink_symbol *) + malloc (sizeof (struct prelink_symbol)); + if (s->next == NULL) + { + error (0, ENOMEM, "Cannot build symbol lookup map"); + goto error_out; + } + s = s->next; + } + } + if (s) + { + if (reloc_class == RTYPE_CLASS_TLS) + s->u.tls = tls; + else + s->u.ent = ent; + s->value = value[0]; + s->reloc_class = reloc_class; + s->next = NULL; + } + } + else if ((reloc_class == RTYPE_CLASS_TLS || ifunc) + && info->conflicts) + { + struct prelink_conflict *conflict; + int symowner; + size_t idx; + + for (symowner = 0; symowner < ndeps; symowner++) + if (deps[symowner].start == symstart) + break; + if (symowner == ndeps) + { + error (0, 0, "Could not find base 0x%08llx in the list of bases `%s'", + symstart, buffer); + goto error_out; + } + + idx = 0; + if (info->conflicts[symowner].hash != &info->conflicts[symowner].first) + idx = symoff % 251; + for (conflict = info->conflicts[symowner].hash[idx]; conflict; + conflict = conflict->next) + if (conflict->symoff == symoff + && conflict->reloc_class == reloc_class) + { + if ((reloc_class != RTYPE_CLASS_TLS + && (conflict->lookup.ent != ent + || conflict->conflict.ent != ent)) + || (reloc_class == RTYPE_CLASS_TLS + && (conflict->lookup.tls != tls + || conflict->conflict.tls != tls)) + || conflict->lookupval != value[0] + || conflict->conflictval != value[0]) + { + error (0, 0, "%s: Symbol `%s' with the same reloc type resolves to different values each time", + info->ent->filename, symname); + goto error_out; + } + break; + } + if (conflict == NULL) + { + conflict = malloc (sizeof (struct prelink_conflict)); + if (conflict == NULL) + { + error (0, ENOMEM, "Cannot build list of conflicts"); + goto error_out; + } + + conflict->next = info->conflicts[symowner].hash[idx]; + conflict->next2 = NULL; + info->conflicts[symowner].hash[idx] = conflict; + if (reloc_class != RTYPE_CLASS_TLS) + { + conflict->lookup.ent = ent; + conflict->conflict.ent = ent; + } + else + { + conflict->lookup.tls = tls; + conflict->conflict.tls = tls; + } + conflict->lookupval = value[0]; + conflict->conflictval = value[0]; + conflict->symoff = symoff; + conflict->reloc_class = reloc_class; + conflict->used = 0; + conflict->ifunc = ifunc; + conflict->symname = strdup(symname); + if (++info->conflicts[symowner].count == 16) + conflict_hash_init (&info->conflicts[symowner]); + } + } + } + else if (strncmp (buffer, "conflict ", sizeof ("conflict ") - 1) == 0) + { + if (sscanf (buffer, "conflict 0x%llx 0x%llx -> 0x%llx 0x%llx x 0x%llx 0x%llx %n", + &symstart, &symoff, &valstart[0], &value[0], + &valstart[1], &value[1], &len) != 6) + { + error (0, 0, "%s: Could not parse `%s'", info->ent->filename, buffer); + goto error_out; + } + + if (buffer[len] == '/') + { + ++len; + type = 0; + } + + reloc_class = strtoul (buffer + len, &symname, 16); + if (buffer + len == symname || (reloc_class == 0 && type) + || (*symname != ' ' && *symname != '\t')) + { + error (0, 0, "%s: Could not parse `%s'", info->ent->filename, buffer); + goto error_out; + } + + if (type) + reloc_class = dso->arch->reloc_class (reloc_class); + else + { + if (reloc_class & 8) + { + reloc_class = ((reloc_class & ~8) + | dso->arch->rtype_class_valid); + ifunc = 1; + } + else if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS) + reloc_class |= RTYPE_CLASS_VALID; + else + reloc_class |= dso->arch->rtype_class_valid; + } + + while (*symname == ' ' || *symname == '\t') ++symname; + + if (symstart == deps[0].start) + { + error (0, 0, "Conflict in _dl_loaded `%s'", buffer); + goto error_out; + } + + if (info->conflicts) + { + struct prelink_entry *ents[2]; + struct prelink_tls *tlss[2]; + struct prelink_conflict *conflict; + int symowner, j; + size_t idx; + + for (symowner = 1; symowner < ndeps; symowner++) + if (deps[symowner].start == symstart) + break; + if (symowner == ndeps) + { + error (0, 0, "Could not find base 0x%08llx in the list of bases `%s'", + symstart, buffer); + goto error_out; + } + + for (j = 0; j < 2; j++) + { + ents[j] = NULL; + tlss[j] = NULL; + for (i = 0; i < ndeps; i++) + if (deps[i].start == valstart[j]) + { + if (reloc_class == RTYPE_CLASS_TLS) + tlss[j] = info->tls + i; + else + { + ents[j] = deps[i].ent; + /* If the library the symbol is bound to is already + prelinked, adjust the value so that it is relative + to library base. */ + if (mask_32bit) + value[j] -= (Elf32_Addr) (deps[i].start - deps[i].l_addr); + else + value[j] -= deps[i].start - deps[i].l_addr; + } + break; + } + if (ents[j] == NULL && tlss[j] == NULL && valstart[j]) + { + error (0, 0, "Could not find base 0x%08llx in the list of bases `%s'", + valstart[j], buffer); + goto error_out; + } + } + + idx = 0; + if (info->conflicts[symowner].hash + != &info->conflicts[symowner].first) + idx = symoff % 251; + for (conflict = info->conflicts[symowner].hash[idx]; conflict; + conflict = conflict->next) + if (conflict->symoff == symoff + && conflict->reloc_class == reloc_class) + { + if ((reloc_class != RTYPE_CLASS_TLS + && (conflict->lookup.ent != ents[0] + || conflict->conflict.ent != ents[1])) + || (reloc_class == RTYPE_CLASS_TLS + && (conflict->lookup.tls != tlss[0] + || conflict->conflict.tls != tlss[1])) + || conflict->lookupval != value[0] + || conflict->conflictval != value[1]) + { + error (0, 0, "%s: Symbol `%s' with the same reloc type resolves to different values each time", + info->ent->filename, symname); + goto error_out; + } + break; + } + if (conflict == NULL) + { + conflict = malloc (sizeof (struct prelink_conflict)); + if (conflict == NULL) + { + error (0, ENOMEM, "Cannot build list of conflicts"); + goto error_out; + } + + conflict->next = info->conflicts[symowner].hash[idx]; + conflict->next2 = NULL; + info->conflicts[symowner].hash[idx] = conflict; + if (reloc_class != RTYPE_CLASS_TLS) + { + conflict->lookup.ent = ents[0]; + conflict->conflict.ent = ents[1]; + } + else + { + conflict->lookup.tls = tlss[0]; + conflict->conflict.tls = tlss[1]; + } + conflict->lookupval = value[0]; + conflict->conflictval = value[1]; + conflict->symoff = symoff; + conflict->reloc_class = reloc_class; + conflict->used = 0; + conflict->ifunc = ifunc; + conflict->symname = strdup(symname); + if (++info->conflicts[symowner].count == 16) + conflict_hash_init (&info->conflicts[symowner]); + } + } + } + else if (strncmp (buffer, "undefined symbol: ", + sizeof ("undefined symbol: ") - 1) == 0 && ! undef) + { + undef = 1; + if (verbose) + error (0, 0, "Warning: %s has undefined non-weak symbols", + info->ent->filename); + } + } while (fgets (buffer, 8192, f) != NULL); + + info->sonames = malloc (ndeps * sizeof (const char *)); + if (info->sonames == NULL) + { + error (0, ENOMEM, "%s: Could not record dependency SONAMEs", dso->filename); + goto error_out; + } + + for (i = 0; i < ndeps; i++) + info->sonames[i] = deps[i].soname; + + return 0; + +error_out: + for (i = 0; i < ndeps; i++) + free (deps[i].soname); + return 1; +} + +int +prelink_get_relocations (struct prelink_info *info) +{ + FILE *f; + DSO *dso = info->dso; + const char *argv[8]; + const char *envp[4]; + int i, ret, status; + char *p; + const char *dl = dynamic_linker ?: dso->arch->dynamic_linker; + const char *ent_filename; + int etype = info->dso->ehdr.e_type; + + if (info->ent->type == ET_DYN) + { + assert (info->ent->base == dso->base); + if (info->ent->end < dso->end) + { + error (0, 0, "%s: grew since it has been recorded", info->ent->filename); + return 0; + } + } + else + { + info->ent->base = dso->base; + info->ent->end = dso->end; + } + + if (is_ldso_soname (info->dso->soname)) + return 1; + + info->symbol_count = (info->symtab_end - info->symtab_start) + / info->symtab_entsize; + info->symbols = calloc (sizeof (struct prelink_symbol), info->symbol_count); + + if (strchr (info->ent->filename, '/') != NULL) + ent_filename = info->ent->filename; + else + { + size_t flen = strlen (info->ent->filename); + char *p = alloca (2 + flen + 1); + memcpy (p, "./", 2); + memcpy (p + 2, info->ent->filename, flen + 1); + ent_filename = p; + } + if (prelink_rtld == NULL) + { + i = 0; + argv[i++] = dl; + if (ld_library_path) + { + argv[i++] = "--library-path"; + argv[i++] = ld_library_path; + } + argv[i++] = ent_filename; + argv[i] = NULL; + envp[0] = "LD_TRACE_LOADED_OBJECTS=1"; + envp[1] = "LD_BIND_NOW=1"; + p = alloca (sizeof "LD_TRACE_PRELINKING=" + strlen (info->ent->filename)); + strcpy (stpcpy (p, "LD_TRACE_PRELINKING="), info->ent->filename); + envp[2] = p; + envp[3] = NULL; + ret = 2; + f = execve_open (dl, (char * const *)argv, (char * const *)envp); + } + else + { + i = 0; + argv[i++] = prelink_rtld; + if (ld_library_path) + { + argv[i++] = "--library-path"; + argv[i++] = ld_library_path; + } + + if(etype == ET_EXEC && ld_preload) { + argv[i++] = "--ld-preload"; + argv[i++] = ld_preload; + } + + argv[i++] = "--target-paths"; + argv[i++] = ent_filename; + argv[i] = NULL; + p = alloca (sizeof "RTLD_TRACE_PRELINKING=" + strlen (info->ent->filename)); + strcpy (stpcpy (p, "RTLD_TRACE_PRELINKING="), info->ent->filename); + envp[0] = p; + p = alloca (sizeof "PATH=" + strlen (getenv ("PATH"))); + sprintf (p, "PATH=%s", getenv ("PATH")); + envp[1] = p; + envp[2] = NULL; + if (sysroot) + { + p = alloca (sizeof "PRELINK_SYSROOT=" + strlen (sysroot)); + sprintf (p, "PRELINK_SYSROOT=%s", sysroot); + envp[2] = p; + envp[3] = NULL; + } + ret = 2; + f = execve_open (prelink_rtld, (char * const *)argv, (char * const *)envp); + } + + if (f == NULL) + { + error (0, errno, "%s: Could not trace symbol resolving", + info->ent->filename); + return 0; + } + + if (prelink_record_relocations (info, f, ent_filename)) + ret = 0; + + if ((status = execve_close (f))) + { + if (ret) + error (0, status == -1 ? errno : 0, + "%s Could not trace symbol resolving", info->ent->filename); + return 0; + } + + return ret; +} diff --git a/src/hashtab.c b/src/hashtab.c new file mode 100644 index 0000000..fa27446 --- /dev/null +++ b/src/hashtab.c @@ -0,0 +1,609 @@ +/* An expandable hash tables datatype. + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Vladimir Makarov (vmakarov@cygnus.com). + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +Libiberty is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with libiberty; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* This package implements basic hash table functionality. It is possible + to search for an entry, create an entry and destroy an entry. + + Elements in the table are generic pointers. + + The size of the table is not fixed; if the occupancy of the table + grows too high the hash table will be expanded. + + The abstract data implementation is based on generalized Algorithm D + from Knuth's book "The art of computer programming". Hash table is + expanded by creation of new hash table and transferring elements from + the old table to the new table. */ + +#include <config.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "hashtab.h" + +/* This macro defines reserved value for empty table entry. */ + +#define EMPTY_ENTRY ((void *) 0) + +/* This macro defines reserved value for table entry which contained + a deleted element. */ + +#define DELETED_ENTRY ((void *) 1) + +static unsigned long higher_prime_number (unsigned long); +static hashval_t hash_pointer (const void *); +static int eq_pointer (const void *, const void *); +static int htab_expand (htab_t); +static void **find_empty_slot_for_expand (htab_t, hashval_t); + +/* At some point, we could make these be NULL, and modify the + hash-table routines to handle NULL specially; that would avoid + function-call overhead for the common case of hashing pointers. */ +htab_hash htab_hash_pointer = hash_pointer; +htab_eq htab_eq_pointer = eq_pointer; + +/* The following function returns a nearest prime number which is + greater than N, and near a power of two. */ + +static unsigned long +higher_prime_number (n) + unsigned long n; +{ + /* These are primes that are near, but slightly smaller than, a + power of two. */ + static unsigned long primes[] = { + (unsigned long) 2, + (unsigned long) 7, + (unsigned long) 13, + (unsigned long) 31, + (unsigned long) 61, + (unsigned long) 127, + (unsigned long) 251, + (unsigned long) 509, + (unsigned long) 1021, + (unsigned long) 2039, + (unsigned long) 4093, + (unsigned long) 8191, + (unsigned long) 16381, + (unsigned long) 32749, + (unsigned long) 65521, + (unsigned long) 131071, + (unsigned long) 262139, + (unsigned long) 524287, + (unsigned long) 1048573, + (unsigned long) 2097143, + (unsigned long) 4194301, + (unsigned long) 8388593, + (unsigned long) 16777213, + (unsigned long) 33554393, + (unsigned long) 67108859, + (unsigned long) 134217689, + (unsigned long) 268435399, + (unsigned long) 536870909, + (unsigned long) 1073741789, + (unsigned long) 2147483647, + /* 4294967291L */ + ((unsigned long) 2147483647) + ((unsigned long) 2147483644), + }; + + unsigned long* low = &primes[0]; + unsigned long* high = &primes[sizeof(primes) / sizeof(primes[0])]; + + while (low != high) + { + unsigned long* mid = low + (high - low) / 2; + if (n > *mid) + low = mid + 1; + else + high = mid; + } + + /* If we've run out of primes, abort. */ + if (n > *low) + { + fprintf (stderr, "Cannot find prime bigger than %lu\n", n); + abort (); + } + + return *low; +} + +/* Returns a hash code for P. */ + +static hashval_t +hash_pointer (p) + const void * p; +{ + return (hashval_t) ((long)p >> 3); +} + +/* Returns non-zero if P1 and P2 are equal. */ + +static int +eq_pointer (p1, p2) + const void * p1; + const void * p2; +{ + return p1 == p2; +} + +/* This function creates table with length slightly longer than given + source length. The created hash table is initiated as empty (all the + hash table entries are EMPTY_ENTRY). The function returns the created + hash table. Memory allocation may fail; it may return NULL. */ + +htab_t +htab_try_create (size, hash_f, eq_f, del_f) + size_t size; + htab_hash hash_f; + htab_eq eq_f; + htab_del del_f; +{ + htab_t result; + + size = higher_prime_number (size); + result = (htab_t) calloc (1, sizeof (struct htab)); + if (result == NULL) + return NULL; + + result->entries = (void **) calloc (size, sizeof (void *)); + if (result->entries == NULL) + { + free (result); + return NULL; + } + + result->size = size; + result->hash_f = hash_f; + result->eq_f = eq_f; + result->del_f = del_f; + result->return_allocation_failure = 1; + return result; +} + +/* This function frees all memory allocated for given hash table. + Naturally the hash table must already exist. */ + +void +htab_delete (htab) + htab_t htab; +{ + int i; + + if (htab->del_f) + for (i = htab->size - 1; i >= 0; i--) + if (htab->entries[i] != EMPTY_ENTRY + && htab->entries[i] != DELETED_ENTRY) + (*htab->del_f) (htab->entries[i]); + + free (htab->entries); + free (htab); +} + +/* This function clears all entries in the given hash table. */ + +void +htab_empty (htab) + htab_t htab; +{ + int i; + + if (htab->del_f) + for (i = htab->size - 1; i >= 0; i--) + if (htab->entries[i] != EMPTY_ENTRY + && htab->entries[i] != DELETED_ENTRY) + (*htab->del_f) (htab->entries[i]); + + memset (htab->entries, 0, htab->size * sizeof (void *)); +} + +/* Similar to htab_find_slot, but without several unwanted side effects: + - Does not call htab->eq_f when it finds an existing entry. + - Does not change the count of elements/searches/collisions in the + hash table. + This function also assumes there are no deleted entries in the table. + HASH is the hash value for the element to be inserted. */ + +static void ** +find_empty_slot_for_expand (htab, hash) + htab_t htab; + hashval_t hash; +{ + size_t size = htab->size; + hashval_t hash2 = 1 + hash % (size - 2); + unsigned int index = hash % size; + + for (;;) + { + void **slot = htab->entries + index; + + if (*slot == EMPTY_ENTRY) + return slot; + else if (*slot == DELETED_ENTRY) + abort (); + + index += hash2; + if (index >= size) + index -= size; + } +} + +/* The following function changes size of memory allocated for the + entries and repeatedly inserts the table elements. The occupancy + of the table after the call will be about 50%. Naturally the hash + table must already exist. Remember also that the place of the + table entries is changed. If memory allocation failures are allowed, + this function will return zero, indicating that the table could not be + expanded. If all goes well, it will return a non-zero value. */ + +static int +htab_expand (htab) + htab_t htab; +{ + void **oentries; + void **olimit; + void **p; + + oentries = htab->entries; + olimit = oentries + htab->size; + + htab->size = higher_prime_number (htab->size * 2); + + if (htab->return_allocation_failure) + { + void **nentries = (void **) calloc (htab->size, sizeof (void **)); + if (nentries == NULL) + return 0; + htab->entries = nentries; + } + + htab->n_elements -= htab->n_deleted; + htab->n_deleted = 0; + + p = oentries; + do + { + void * x = *p; + + if (x != EMPTY_ENTRY && x != DELETED_ENTRY) + { + void **q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x)); + + *q = x; + } + + p++; + } + while (p < olimit); + + free (oentries); + return 1; +} + +/* This function searches for a hash table entry equal to the given + element. It cannot be used to insert or delete an element. */ + +void * +htab_find_with_hash (htab, element, hash) + htab_t htab; + const void * element; + hashval_t hash; +{ + unsigned int index; + hashval_t hash2; + size_t size; + void * entry; + + htab->searches++; + size = htab->size; + index = hash % size; + + entry = htab->entries[index]; + if (entry == EMPTY_ENTRY + || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element))) + return entry; + + hash2 = 1 + hash % (size - 2); + + for (;;) + { + htab->collisions++; + index += hash2; + if (index >= size) + index -= size; + + entry = htab->entries[index]; + if (entry == EMPTY_ENTRY + || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element))) + return entry; + } +} + +/* Like htab_find_slot_with_hash, but compute the hash value from the + element. */ + +void * +htab_find (htab, element) + htab_t htab; + const void * element; +{ + return htab_find_with_hash (htab, element, (*htab->hash_f) (element)); +} + +/* This function searches for a hash table slot containing an entry + equal to the given element. To delete an entry, call this with + INSERT = 0, then call htab_clear_slot on the slot returned (possibly + after doing some checks). To insert an entry, call this with + INSERT = 1, then write the value you want into the returned slot. + When inserting an entry, NULL may be returned if memory allocation + fails. */ + +void ** +htab_find_slot_with_hash (htab, element, hash, insert) + htab_t htab; + const void * element; + hashval_t hash; + enum insert_option insert; +{ + void **first_deleted_slot; + unsigned int index; + hashval_t hash2; + size_t size; + + if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4 + && htab_expand (htab) == 0) + return NULL; + + size = htab->size; + hash2 = 1 + hash % (size - 2); + index = hash % size; + + htab->searches++; + first_deleted_slot = NULL; + + for (;;) + { + void * entry = htab->entries[index]; + if (entry == EMPTY_ENTRY) + { + if (insert == NO_INSERT) + return NULL; + + htab->n_elements++; + + if (first_deleted_slot) + { + *first_deleted_slot = EMPTY_ENTRY; + return first_deleted_slot; + } + + return &htab->entries[index]; + } + + if (entry == DELETED_ENTRY) + { + if (!first_deleted_slot) + first_deleted_slot = &htab->entries[index]; + } + else if ((*htab->eq_f) (entry, element)) + return &htab->entries[index]; + + htab->collisions++; + index += hash2; + if (index >= size) + index -= size; + } +} + +/* Like htab_find_slot_with_hash, but compute the hash value from the + element. */ + +void ** +htab_find_slot (htab, element, insert) + htab_t htab; + const void * element; + enum insert_option insert; +{ + return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element), + insert); +} + +/* This function deletes an element with the given value from hash + table. If there is no matching element in the hash table, this + function does nothing. */ + +void +htab_remove_elt (htab, element) + htab_t htab; + void * element; +{ + void **slot; + + slot = htab_find_slot (htab, element, NO_INSERT); + if (*slot == EMPTY_ENTRY) + return; + + if (htab->del_f) + (*htab->del_f) (*slot); + + *slot = DELETED_ENTRY; + htab->n_deleted++; +} + +/* This function clears a specified slot in a hash table. It is + useful when you've already done the lookup and don't want to do it + again. */ + +void +htab_clear_slot (htab, slot) + htab_t htab; + void **slot; +{ + if (slot < htab->entries || slot >= htab->entries + htab->size + || *slot == EMPTY_ENTRY || *slot == DELETED_ENTRY) + abort (); + + if (htab->del_f) + (*htab->del_f) (*slot); + + *slot = DELETED_ENTRY; + htab->n_deleted++; +} + +/* This function scans over the entire hash table calling + CALLBACK for each live entry. If CALLBACK returns false, + the iteration stops. INFO is passed as CALLBACK's second + argument. */ + +void +htab_traverse (htab, callback, info) + htab_t htab; + htab_trav callback; + void * info; +{ + void **slot = htab->entries; + void **limit = slot + htab->size; + + do + { + void * x = *slot; + + if (x != EMPTY_ENTRY && x != DELETED_ENTRY) + if (!(*callback) (slot, info)) + break; + } + while (++slot < limit); +} + +/* Return the current size of given hash table. */ + +size_t +htab_size (htab) + htab_t htab; +{ + return htab->size; +} + +/* Return the current number of elements in given hash table. */ + +size_t +htab_elements (htab) + htab_t htab; +{ + return htab->n_elements - htab->n_deleted; +} + +/* Return the fraction of fixed collisions during all work with given + hash table. */ + +double +htab_collisions (htab) + htab_t htab; +{ + if (htab->searches == 0) + return 0.0; + + return (double) htab->collisions / (double) htab->searches; +} + +#ifndef NDEBUG +void +htab_dump (htab, name, dumpfn) + htab_t htab; + const char *name; + htab_dumpfn dumpfn; +{ + FILE *f = fopen (name, "w"); + size_t i, j; + + if (f == NULL) + abort (); + fprintf (f, "size %zd n_elements %zd n_deleted %zd\n", + htab->size, htab->n_elements, htab->n_deleted); + for (i = 0; i < htab->size; ++i) + { + if (htab->entries [i] == EMPTY_ENTRY + || htab->entries [i] == DELETED_ENTRY) + { + for (j = i + 1; j < htab->size; ++j) + if (htab->entries [j] != htab->entries [i]) + break; + fprintf (f, "%c%zd\n", + htab->entries [i] == EMPTY_ENTRY ? 'E' : 'D', + j - i); + i = j - 1; + } + else + { + fputc ('V', f); + (*dumpfn) (f, htab->entries [i]); + } + } + fclose (f); +} + +void +htab_restore (htab, name, restorefn) + htab_t htab; + const char *name; + htab_restorefn restorefn; +{ + FILE *f = fopen (name, "r"); + size_t size, n_elements, n_deleted, i, j, k; + int c; + + if (f == NULL) + abort (); + if (fscanf (f, "size %zd n_elements %zd n_deleted %zd\n", + &size, &n_elements, &n_deleted) != 3) + abort (); + htab_empty (htab); + free (htab->entries); + htab->entries = (void **) calloc (size, sizeof (void *)); + if (htab->entries == NULL) + abort (); + htab->size = size; + htab->n_elements = n_elements; + htab->n_deleted = n_deleted; + for (i = 0; i < htab->size; ++i) + { + switch ((c = fgetc (f))) + { + case 'E': + case 'D': + if (fscanf (f, "%zd\n", &j) != 1) + abort (); + if (i + j > htab->size) + abort (); + if (c == 'D') + for (k = i; k < i + j; ++k) + htab->entries [k] = DELETED_ENTRY; + i += j - 1; + break; + case 'V': + htab->entries [i] = (*restorefn) (f); + break; + default: + abort (); + } + } + fclose (f); +} +#endif diff --git a/src/hashtab.h b/src/hashtab.h new file mode 100644 index 0000000..31e63e0 --- /dev/null +++ b/src/hashtab.h @@ -0,0 +1,155 @@ +/* An expandable hash tables datatype. + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + Contributed by Vladimir Makarov (vmakarov@cygnus.com). + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This package implements basic hash table functionality. It is possible + to search for an entry, create an entry and destroy an entry. + + Elements in the table are generic pointers. + + The size of the table is not fixed; if the occupancy of the table + grows too high the hash table will be expanded. + + The abstract data implementation is based on generalized Algorithm D + from Knuth's book "The art of computer programming". Hash table is + expanded by creation of new hash table and transferring elements from + the old table to the new table. */ + +#ifndef __HASHTAB_H__ +#define __HASHTAB_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* The type for a hash code. */ +typedef unsigned int hashval_t; + +/* Callback function pointer types. */ + +/* Calculate hash of a table entry. */ +typedef hashval_t (*htab_hash) (const void *); + +/* Compare a table entry with a possible entry. The entry already in + the table always comes first, so the second element can be of a + different type (but in this case htab_find and htab_find_slot + cannot be used; instead the variants that accept a hash value + must be used). */ +typedef int (*htab_eq) (const void *, const void *); + +/* Cleanup function called whenever a live element is removed from + the hash table. */ +typedef void (*htab_del) (void *); + +/* Function called by htab_traverse for each live element. The first + arg is the slot of the element (which can be passed to htab_clear_slot + if desired), the second arg is the auxiliary pointer handed to + htab_traverse. Return 1 to continue scan, 0 to stop. */ +typedef int (*htab_trav) (void **, void *); + +/* Hash tables are of the following type. The structure + (implementation) of this type is not needed for using the hash + tables. All work with hash table should be executed only through + functions mentioned below. */ + +struct htab +{ + /* Pointer to hash function. */ + htab_hash hash_f; + + /* Pointer to comparison function. */ + htab_eq eq_f; + + /* Pointer to cleanup function. */ + htab_del del_f; + + /* Table itself. */ + void **entries; + + /* Current size (in entries) of the hash table */ + size_t size; + + /* Current number of elements including also deleted elements */ + size_t n_elements; + + /* Current number of deleted elements in the table */ + size_t n_deleted; + + /* The following member is used for debugging. Its value is number + of all calls of `htab_find_slot' for the hash table. */ + unsigned int searches; + + /* The following member is used for debugging. Its value is number + of collisions fixed for time of work with the hash table. */ + unsigned int collisions; + + /* This is non-zero if we are allowed to return NULL for function calls + that allocate memory. */ + int return_allocation_failure; +}; + +typedef struct htab *htab_t; + +/* An enum saying whether we insert into the hash table or not. */ +enum insert_option {NO_INSERT, INSERT}; + +/* The prototypes of the package functions. */ + +/* This function is like htab_create, but may return NULL if memory + allocation fails, and also signals that htab_find_slot_with_hash and + htab_find_slot are allowed to return NULL when inserting. */ +extern htab_t htab_try_create (size_t, htab_hash, htab_eq, htab_del); +extern void htab_delete (htab_t); +extern void htab_empty (htab_t); + +extern void *htab_find (htab_t, const void *); +extern void **htab_find_slot (htab_t, const void *, enum insert_option); +extern void *htab_find_with_hash (htab_t, const void *, hashval_t); +extern void **htab_find_slot_with_hash (htab_t, const void *, hashval_t, + enum insert_option); +extern void htab_clear_slot (htab_t, void **); +extern void htab_remove_elt (htab_t, void *); + +extern void htab_traverse (htab_t, htab_trav, void *); + +extern size_t htab_size (htab_t); +extern size_t htab_elements (htab_t); +extern double htab_collisions (htab_t); + +/* A hash function for pointers. */ +extern htab_hash htab_hash_pointer; + +/* An equality function for pointers. */ +extern htab_eq htab_eq_pointer; + +#ifndef NDEBUG + +#include <stdio.h> + +typedef void (*htab_dumpfn) (FILE *, const void *); +typedef void *(*htab_restorefn) (FILE *); + +extern void htab_dump (htab_t, const char *, htab_dumpfn); +extern void htab_restore (htab_t, const char *, htab_restorefn); + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __HASHTAB_H */ diff --git a/src/layout.c b/src/layout.c new file mode 100644 index 0000000..859ab66 --- /dev/null +++ b/src/layout.c @@ -0,0 +1,656 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2006, 2011 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <alloca.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <assert.h> +#include "prelinktab.h" +#include "layout.h" + +#ifndef NDEBUG +# define DEBUG_LAYOUT +#endif + +#ifdef DEBUG_LAYOUT +void +print_ent (struct prelink_entry *e) +{ + printf ("%s: %08x %08x/%08x\n", + e->filename, (int)e->base, (int)e->end, (int)e->layend); +} + +void +print_list (struct prelink_entry *e) +{ + for (; e; e = e->next) + print_ent (e); + printf ("\n"); +} +#endif + +static int +find_arches (void **p, void *info) +{ + struct layout_libs *l = (struct layout_libs *) info; + struct prelink_entry *e = * (struct prelink_entry **) p; + int i; + + if (e->type == ET_DYN || e->type == ET_EXEC + || e->type == ET_CACHE_DYN || e->type == ET_CACHE_EXEC) + { + for (i = 0; i < l->nbinlibs; ++i) + if ((l->binlibs[i]->flags & (PCF_ELF64 | PCF_MACHINE)) + == (e->flags & (PCF_ELF64 | PCF_MACHINE))) + return 1; + + l->binlibs[l->nbinlibs++] = e; + } + + return 1; +} + +static int +find_libs (void **p, void *info) +{ + struct layout_libs *l = (struct layout_libs *) info; + struct prelink_entry *e = * (struct prelink_entry **) p; + + if ((e->flags & (PCF_ELF64 | PCF_MACHINE)) != l->flags) + return 1; + + if (e->type == ET_DYN || e->type == ET_EXEC + || e->type == ET_CACHE_DYN || e->type == ET_CACHE_EXEC) + l->binlibs[l->nbinlibs++] = e; + if (e->type == ET_DYN || e->type == ET_CACHE_DYN) + l->libs[l->nlibs++] = e; + if (force) + e->done = 0; + if (e->type == ET_CACHE_DYN || e->type == ET_CACHE_EXEC) + e->done = 2; + if (e->base & (l->max_page_size - 1)) + { + e->done = 0; + e->end -= e->base; + e->base = 0; + } + + return 1; +} + +static int +refs_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + int i; + + /* Dynamic linkers first. */ + if (! a->ndepends && b->ndepends) + return -1; + if (a->ndepends && ! b->ndepends) + return 1; + /* Most widely used libraries first. */ + if (a->refs > b->refs) + return -1; + if (a->refs < b->refs) + return 1; + /* Largest libraries first. */ + if (a->layend - a->base > b->layend - b->base) + return -1; + if (a->layend - a->base < b->layend - b->base) + return 1; + if (a->refs) + { + i = strcmp (a->soname, b->soname); + if (i) + return i; + } + return strcmp (a->filename, b->filename); +} + +static int +refs_rnd_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + int i, refs; + + /* Dynamic linkers first. */ + if (! a->ndepends && b->ndepends) + return -1; + if (a->ndepends && ! b->ndepends) + return 1; + /* Most widely used libraries first with some randomization. */ + refs = a->refs < b->refs ? a->refs : b->refs; + if (refs < 8) + i = 1; + else if (refs < 32) + i = 2; + else if (refs < 128) + i = 4; + else + i = 8; + if (a->refs > b->refs && a->refs - b->refs >= i) + return -1; + if (a->refs < b->refs && b->refs - a->refs >= i) + return 1; + if (a->u.tmp < b->u.tmp) + return -1; + if (a->u.tmp > b->u.tmp) + return 1; + /* Largest libraries first. */ + if (a->layend - a->base > b->layend - b->base) + return -1; + if (a->layend - a->base < b->layend - b->base) + return 1; + if (a->refs && b->refs) + { + i = strcmp (a->soname, b->soname); + if (i) + return i; + } + return strcmp (a->filename, b->filename); +} + +static int +addr_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + + if (! a->done) + return b->done ? 1 : 0; + else if (! b->done) + return -1; + if (a->base < b->base) + return -1; + else if (a->base > b->base) + return 1; + if (a->layend < b->layend) + return -1; + else if (a->layend > b->layend) + return 1; + return 0; +} + +int deps_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + + if (a->base < b->base) + return -1; + if (a->base > b->base) + return 1; + return 0; +} + +int +layout_libs (void) +{ + struct layout_libs l; + int arch, *arches, narches; + struct prelink_entry **plibs, **pbinlibs; + + memset (&l, 0, sizeof (l)); + l.libs = plibs = + (struct prelink_entry **) alloca (prelink_entry_count + * sizeof (struct prelink_entry *)); + l.binlibs = pbinlibs = + (struct prelink_entry **) alloca (prelink_entry_count + * sizeof (struct prelink_entry *)); + htab_traverse (prelink_filename_htab, find_arches, &l); + narches = l.nbinlibs; + arches = (int *) alloca (narches * sizeof (int)); + for (arch = 0; arch < narches; ++arch) + arches[arch] = l.binlibs[arch]->flags & (PCF_ELF64 | PCF_MACHINE); + + for (arch = 0; arch < narches; ++arch) + { + struct PLArch *plarch; + extern struct PLArch __start_pl_arch[], __stop_pl_arch[]; + int i, j, k, m, done, class; + GElf_Addr mmap_start, mmap_base, mmap_end, mmap_fin, max_page_size; + GElf_Addr base, size; + struct prelink_entry *list, *e, *fake, **deps; + struct prelink_entry fakeent; + int fakecnt; + int (*layout_libs_pre) (struct layout_libs *l); + int (*layout_libs_post) (struct layout_libs *l); + + for (plarch = __start_pl_arch; plarch < __stop_pl_arch; plarch++) + if (plarch->class == (arches[arch] & PCF_ELF64 ? ELFCLASS64 : ELFCLASS32) + && plarch->machine == (arches[arch] & PCF_MACHINE)) + break; + + if (plarch == __stop_pl_arch) + error (EXIT_FAILURE, 0, "%d-bit ELF e_machine %04x not supported", + (arches[arch] & PCF_ELF64) ? 64 : 32, arches[arch] & PCF_MACHINE); + + list = NULL; + fake = NULL; + fakecnt = 0; + memset (&l, 0, sizeof (l)); + l.flags = arches[arch]; + l.libs = plibs; + l.binlibs = pbinlibs; + max_page_size = plarch->max_page_size; + if (layout_page_size > max_page_size) + max_page_size = layout_page_size; + l.max_page_size = max_page_size; + htab_traverse (prelink_filename_htab, find_libs, &l); + + /* Make sure there is some room between libraries. */ + for (i = 0; i < l.nlibs; ++i) + l.libs[i]->layend = (l.libs[i]->end + 8192 + max_page_size - 1) + & ~(max_page_size - 1); + + if (plarch->layout_libs_init) + { + plarch->layout_libs_init (&l); + mmap_base = l.mmap_base; + mmap_end = l.mmap_end; + } + else + { + mmap_base = plarch->mmap_base; + mmap_end = plarch->mmap_end; + } + if (mmap_reg_start != ~(GElf_Addr) 0) + mmap_base = mmap_reg_start; + if (mmap_reg_end != ~(GElf_Addr) 0) + mmap_end = mmap_reg_end; + if (mmap_base >= mmap_end) + error (EXIT_FAILURE, 0, + "--mmap-region-start cannot be bigger than --mmap-region-end"); + if ((mmap_base | mmap_end) & (max_page_size - 1)) + error (EXIT_FAILURE, 0, "--layout-page-size too large"); + class = plarch->class; + /* The code below relies on having a VA slot as big as <mmap_base,mmap_end) + above mmap_end for -R. */ + if (mmap_end + (mmap_end - mmap_base) <= mmap_end) + random_base = 0; + layout_libs_pre = plarch->layout_libs_pre; + layout_libs_post = plarch->layout_libs_post; + + deps = (struct prelink_entry **) + alloca (l.nlibs * sizeof (struct prelink_entry *)); + + /* Now see which already prelinked libraries have to be + re-prelinked to avoid overlaps. */ + for (i = 0; i < l.nbinlibs; ++i) + { + for (j = 0, k = 0; j < l.binlibs[i]->ndepends; ++j) + if (l.binlibs[i]->depends[j]->type == ET_DYN + && l.binlibs[i]->depends[j]->done) + { + assert (k < l.nlibs); + deps[k++] = l.binlibs[i]->depends[j]; + } + if (k) + { + qsort (deps, k, sizeof (struct prelink_entry *), deps_cmp); + for (j = 1; j < k; ++j) + if (deps[j]->base < deps[j - 1]->end + && (deps[j]->type == ET_DYN + || deps[j - 1]->type == ET_DYN)) + { + if (deps[j - 1]->refs < deps[j]->refs) + --j; + deps[j]->done = 0; + --k; + memmove (deps + j, deps + j + 1, (k - j) * sizeof (*deps)); + if (j > 0) + --j; + } + } + } + + /* If layout_libs_init or the for cycle above cleared + done flags for some libraries, make sure all libraries + that depend on them are re-prelinked as well. */ + for (i = 0; i < l.nlibs; ++i) + if (l.libs[i]->done) + for (j = 0; j < l.libs[i]->ndepends; ++j) + if (l.libs[i]->depends[j]->done == 0) + { + l.libs[i]->done = 0; + break; + } + + /* Put the already prelinked libs into double linked list. */ + qsort (l.libs, l.nlibs, sizeof (struct prelink_entry *), addr_cmp); + for (i = 0; i < l.nlibs; ++i) + if (! l.libs[i]->done || l.libs[i]->layend >= mmap_base) + break; + j = 0; + if (i < l.nlibs && l.libs[i]->done) + { + if (l.libs[i]->base < mmap_base) + random_base = 0; + for (j = i + 1; j < l.nlibs; ++j) + { + if (! l.libs[j]->done || l.libs[j]->base >= mmap_end) + break; + + if (l.libs[j]->base < mmap_base || l.libs[j]->layend > mmap_end) + random_base = 0; + l.libs[j]->prev = l.libs[j - 1]; + l.libs[j - 1]->next = l.libs[j]; + } + list = l.libs[i]; + list->prev = l.libs[j - 1]; + while (j < l.nlibs && l.libs[j]->done) ++j; + } + + mmap_start = mmap_base; + mmap_fin = mmap_end; + done = 1; + if (random_base & 2) + { + mmap_start = seed; + if (mmap_start < mmap_base || mmap_start >= mmap_end) + mmap_start = mmap_base; + + mmap_start = (mmap_start + max_page_size - 1) & ~(max_page_size - 1); + } + else if (random_base) + { + int fd = open ("/dev/urandom", O_RDONLY); + + mmap_start = 0; + if (fd != -1) + { + GElf_Addr x; + + if (read (fd, &x, sizeof (x)) == sizeof (x)) + { + mmap_start = x % (mmap_end - mmap_base); + mmap_start += mmap_base; + } + + fsync (fd); + close (fd); + } + + if (! mmap_start) + { + mmap_start = ((mmap_end - mmap_base) >> 16) + * (time (NULL) & 0xffff); + mmap_start += mmap_base; + } + + seed = mmap_start; + mmap_start = (mmap_start + max_page_size - 1) & ~(max_page_size - 1); + } + if (random_base) + { + srandom (mmap_start >> 12); + for (i = 0; i < l.nlibs; ++i) + l.libs[i]->u.tmp = random (); + qsort (l.libs, l.nlibs, sizeof (struct prelink_entry *), refs_rnd_cmp); + } + else + qsort (l.libs, l.nlibs, sizeof (struct prelink_entry *), refs_cmp); + + if (verbose && l.nlibs > j) + { + printf ("Laying out %d libraries in virtual address space %0*llx-%0*llx\n", + l.nlibs - j, class == ELFCLASS32 ? 8 : 16, (long long) mmap_base, + class == ELFCLASS32 ? 8 : 16, (long long) mmap_end); + if (mmap_start != mmap_base) + printf ("Random base 0x%0*llx\n", class == ELFCLASS32 ? 8 : 16, + (long long) mmap_start); + } + + if (layout_libs_pre) + { + l.list = list; + l.mmap_base = mmap_base; + l.mmap_start = mmap_start; + l.mmap_end = mmap_end; + layout_libs_pre (&l); + list = l.list; + mmap_base = l.mmap_base; + mmap_start = l.mmap_start; + mmap_fin = l.mmap_fin; + mmap_end = l.mmap_end; + fake = l.fake; + fakecnt = l.fakecnt; + } + + if (mmap_start != mmap_base && list) + { + for (e = list; e != NULL; e = e->next) + { + if (e->base >= mmap_start) + break; + if (e->layend > mmap_start) + mmap_start = (e->layend + max_page_size - 1) + & ~(max_page_size - 1); + e->base += mmap_end - mmap_base; + e->end += mmap_end - mmap_base; + e->layend += mmap_end - mmap_base; + e->done |= 0x80; + } + + if (mmap_start < mmap_end) + { + if (e && e != list) + { + memset (&fakeent, 0, sizeof (fakeent)); + fakeent.u.tmp = -1; + fakeent.base = mmap_end; + fakeent.end = mmap_end; + fakeent.layend = mmap_end; + fake = &fakeent; + fakecnt = 1; + fakeent.prev = list->prev; + fakeent.next = list; + list->prev = fake; + fakeent.prev->next = fake; + list = e; + e->prev->next = NULL; + } + } + else + { + mmap_start = mmap_base; + for (e = list; e != NULL; e = e->next) + if (e->done & 0x80) + { + e->done &= ~0x80; + e->base -= mmap_end - mmap_base; + e->end -= mmap_end - mmap_base; + e->layend -= mmap_end - mmap_base; + } + } + } + + if (mmap_start != mmap_base) + { + done |= 0x80; + mmap_fin = mmap_end + (mmap_start - mmap_base); + } + + for (i = 0; i < l.nlibs; ++i) + l.libs[i]->u.tmp = -1; + m = -1; + + for (i = 0; i < l.nlibs; ++i) + if (! l.libs[i]->done) + { + if (conserve_memory) + { + /* If conserving virtual address space, only consider libraries + which ever appear together with this one. Otherwise consider + all libraries. */ + m = i; + for (j = 0; j < l.nbinlibs; ++j) + { + for (k = 0; k < l.binlibs[j]->ndepends; ++k) + if (l.binlibs[j]->depends[k] == l.libs[i]) + { + for (k = 0; k < l.binlibs[j]->ndepends; ++k) + l.binlibs[j]->depends[k]->u.tmp = m; + break; + } + } + for (j = 0; j < fakecnt; ++j) + fake[j].u.tmp = m; + } + + size = l.libs[i]->layend - l.libs[i]->base; + base = mmap_start; + for (e = list; e; e = e->next) + if (e->u.tmp == m) + { + if (base + size <= e->base) + goto found; + + if (base < e->layend) + base = e->layend; + } + + if (base + size > mmap_fin) + goto not_found; +found: + l.libs[i]->end += base - l.libs[i]->base; + l.libs[i]->base = base; + l.libs[i]->layend = base + size; + if (base >= mmap_end) + l.libs[i]->done = done; + else + l.libs[i]->done = 1; + if (list == NULL) + { + list = l.libs[i]; + list->prev = list; + } + else + { + if (e == NULL) + e = list->prev; + else + e = e->prev; + while (e != list && e->base > base) + e = e->prev; + if (e->base > base) + { + l.libs[i]->next = list; + l.libs[i]->prev = list->prev; + list->prev = l.libs[i]; + list = l.libs[i]; + } + else + { + l.libs[i]->next = e->next; + l.libs[i]->prev = e; + if (e->next) + e->next->prev = l.libs[i]; + else + list->prev = l.libs[i]; + e->next = l.libs[i]; + } + } +#ifdef DEBUG_LAYOUT + { + struct prelink_entry *last = list; + base = 0; + for (e = list; e; last = e, e = e->next) + { + if (e->base < base) + abort (); + base = e->base; + if ((e == list && e->prev->next != NULL) + || (e != list && e->prev->next != e)) + abort (); + } + if (list->prev != last) + abort (); + } +#endif + continue; + +not_found: + error (EXIT_FAILURE, 0, "Could not find virtual address slot for %s", + l.libs[i]->filename); + } + + if (layout_libs_post) + { + l.list = list; + layout_libs_post (&l); + } + + if (done & 0x80) + for (e = list; e != NULL; e = e->next) + if (e->done & 0x80) + { + e->done &= ~0x80; + e->base -= mmap_end - mmap_base; + e->end -= mmap_end - mmap_base; + e->layend -= mmap_base - mmap_base; + } + + if (verbose) + { + if (narches == 1) + printf ("Assigned virtual address space slots for libraries:\n"); + else + printf ("Assigned virtual address space slots for %d-bit %s ELF libraries:\n", + class == ELFCLASS32 ? 32 : 64, plarch->name); + + for (i = 0; i < l.nlibs; ++i) + if (l.libs[i]->done >= 1) + printf ("%-60s %0*llx-%0*llx\n", l.libs[i]->filename, + class == ELFCLASS32 ? 8 : 16, (long long) l.libs[i]->base, + class == ELFCLASS32 ? 8 : 16, (long long) l.libs[i]->end); + } + +#ifdef DEBUG_LAYOUT + for (i = 0; i < l.nbinlibs; ++i) + { + for (j = 0; j < l.binlibs[i]->ndepends; ++j) + if ((l.binlibs[i]->depends[j]->type != ET_DYN + && l.binlibs[i]->depends[j]->type != ET_CACHE_DYN) + || l.binlibs[i]->depends[j]->done == 0) + break; + if (j < l.binlibs[i]->ndepends) + continue; + memcpy (deps, l.binlibs[i]->depends, + l.binlibs[i]->ndepends * sizeof (struct prelink_entry *)); + qsort (deps, l.binlibs[i]->ndepends, sizeof (struct prelink_entry *), + deps_cmp); + for (j = 1; j < l.binlibs[i]->ndepends; ++j) + if (deps[j]->base + < ((deps[j - 1]->end + max_page_size - 1) + & ~(max_page_size - 1)) + && (deps[j]->type == ET_DYN || deps[j - 1]->type == ET_DYN)) + abort (); + } +#endif + } + + return 0; +} diff --git a/src/layout.h b/src/layout.h new file mode 100644 index 0000000..f481d22 --- /dev/null +++ b/src/layout.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2001, 2004, 2006 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef LAYOUT_H +#define LAYOUT_H + +struct layout_libs + { + struct prelink_entry **libs; + struct prelink_entry **binlibs; + struct prelink_entry *list; + struct prelink_entry *fake; + GElf_Addr mmap_base, mmap_start, mmap_fin, mmap_end, max_page_size; + void *arch_data; + int flags; + int nlibs; + int nbinlibs; + int fakecnt; + }; + +#endif /* LAYOUT_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..c879d62 --- /dev/null +++ b/src/main.c @@ -0,0 +1,570 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2010, 2011 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> +#include <unistd.h> + +#include "prelink.h" + +#define PRELINK_CONF "/etc/prelink.conf" +#define PRELINK_CACHE "/etc/prelink.cache" + +int all; +int force; +int verbose; +int print_cache; +int reloc_only; +GElf_Addr reloc_base; +int no_update; +int random_base; +int conserve_memory; +int libs_only; +int dry_run; +int dereference; +int one_file_system; +int enable_cxx_optimizations = 1; +int exec_shield; +int undo, verify; +enum verify_method_t verify_method; +int quick; +int compute_checksum; +long long seed; +GElf_Addr mmap_reg_start = ~(GElf_Addr) 0; +GElf_Addr mmap_reg_end = ~(GElf_Addr) 0; +GElf_Addr layout_page_size = 0; +const char *dynamic_linker; +const char *ld_library_path; +const char *prelink_conf = PRELINK_CONF; +const char *prelink_cache = PRELINK_CACHE; +const char *undo_output; +char *ld_preload = NULL; +int noreexecinit; +time_t initctime; + +const char *argp_program_version = PRELINK_PROG PKGVERSION " 1.0"; + +const char *argp_program_bug_address = REPORT_BUGS_TO; + +static char argp_doc[] = PRELINK_PROG " -- program to relocate and prelink ELF shared libraries and programs"; + +#define OPT_DYNAMIC_LINKER 0x80 +#define OPT_LD_LIBRARY_PATH 0x81 +#define OPT_LIBS_ONLY 0x82 +#define OPT_CXX_DISABLE 0x83 +#define OPT_MMAP_REG_START 0x84 +#define OPT_MMAP_REG_END 0x85 +#define OPT_EXEC_SHIELD 0x86 +#define OPT_NO_EXEC_SHIELD 0x87 +#define OPT_SEED 0x88 +#define OPT_MD5 0x89 +#define OPT_SHA 0x8a +#define OPT_COMPUTE_CHECKSUM 0x8b +#define OPT_LAYOUT_PAGE_SIZE 0x8c +#define OPT_SYSROOT 0x8d +#define OPT_RTLD 0x8e +#define OPT_ALLOW_TEXTREL 0x8f +#define OPT_LD_PRELOAD 0x90 + +static struct argp_option options[] = { + {"all", 'a', 0, 0, "Prelink all binaries" }, + {"black-list", 'b', "PATH", 0, "Blacklist path" }, + {"cache-file", 'C', "CACHE", 0, "Use CACHE as cache file" }, + {"config-file", 'c', "CONF", 0, "Use CONF as configuration file" }, + {"force", 'f', 0, 0, "Force prelinking" }, + {"dereference", 'h', 0, 0, "Follow symlinks when processing directory trees from command line" }, + {"one-file-system", 'l', 0, 0, "Stay in local file system when processing directories from command line" }, + {"conserve-memory", 'm', 0, 0, "Allow libraries to overlap as long as they never appear in the same program" }, + {"no-update-cache", 'N', 0, 0, "Don't update prelink cache" }, + {"dry-run", 'n', 0, 0, "Don't actually prelink anything" }, + {"undo-output", 'o', "FILE", 0, "Undo output file" }, + {"print-cache", 'p', 0, 0, "Print prelink cache" }, + {"quick", 'q', 0, 0, "Quick scan" }, + {"random", 'R', 0, 0, "Choose random base for libraries" }, + {"reloc-only", 'r', "BASE_ADDRESS", 0, "Relocate library to given address, don't prelink" }, + {"root", OPT_SYSROOT, "ROOT_PATH", 0, "Prefix all paths with ROOT_PATH" }, + {"undo", 'u', 0, 0, "Undo prelink" }, + {"verbose", 'v', 0, 0, "Produce verbose output" }, + {"verify", 'y', 0, 0, "Verify file consistency by undoing and redoing prelink and printing original to standard output" }, + {"md5", OPT_MD5, 0, 0, "For verify print MD5 sum of original to standard output instead of content" }, + {"sha", OPT_SHA, 0, 0, "For verify print SHA sum of original to standard output instead of content" }, + {"dynamic-linker", OPT_DYNAMIC_LINKER, "DYNAMIC_LINKER", + 0, "Special dynamic linker path" }, + {"exec-shield", OPT_EXEC_SHIELD, 0, 0, "Lay out libraries for exec-shield on IA-32" }, + {"no-exec-shield", OPT_NO_EXEC_SHIELD, 0, 0, "Don't lay out libraries for exec-shield on IA-32" }, + {"ld-library-path", OPT_LD_LIBRARY_PATH, "PATHLIST", + 0, "What LD_LIBRARY_PATH should be used" }, + {"ld-preload", OPT_LD_PRELOAD, "PATHLIST", 0, "What LD_PRELOAD should be used" }, + {"libs-only", OPT_LIBS_ONLY, 0, 0, "Prelink only libraries, no binaries" }, + {"layout-page-size", OPT_LAYOUT_PAGE_SIZE, "SIZE", 0, "Layout start of libraries at given boundary" }, + {"disable-c++-optimizations", OPT_CXX_DISABLE, 0, OPTION_HIDDEN, "" }, + {"mmap-region-start", OPT_MMAP_REG_START, "BASE_ADDRESS", OPTION_HIDDEN, "" }, + {"mmap-region-end", OPT_MMAP_REG_END, "BASE_ADDRESS", OPTION_HIDDEN, "" }, + {"seed", OPT_SEED, "SEED", OPTION_HIDDEN, "" }, + {"compute-checksum", OPT_COMPUTE_CHECKSUM, 0, OPTION_HIDDEN, "" }, + {"rtld", OPT_RTLD, "RTLD", OPTION_HIDDEN, "" }, + {"init", 'i', 0, 0, "Do not re-execute init" }, + {"allow-textrel", OPT_ALLOW_TEXTREL, 0, 0, "Allow text relocations even on architectures where they may not work" }, + { 0 } +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + char *endarg; + + switch (key) + { + case 'a': + all = 1; + break; + case 'b': + if (add_to_blacklist (arg, dereference, one_file_system)) + exit (EXIT_FAILURE); + break; + case 'f': + force = 1; + break; + case 'p': + print_cache = 1; + break; + case 'q': + quick = 1; + break; + case 'v': + ++verbose; + break; + case 'R': + random_base |= 1; + break; + case OPT_SEED: + random_base |= 2; + seed = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0')) + error (EXIT_FAILURE, 0, "--seed option requires numberic argument"); + break; + case 'r': + reloc_only = 1; + reloc_base = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0')) + error (EXIT_FAILURE, 0, "-r option requires numberic argument"); + break; + case 'h': + dereference = 1; + break; + case 'l': + one_file_system = 1; + break; + case 'm': + conserve_memory = 1; + break; + case 'N': + no_update = 1; + break; + case 'n': + dry_run = 1; + break; + case 'C': + prelink_cache = arg; + break; + case 'c': + prelink_conf = arg; + break; + case 'u': + undo = 1; + break; + case 'y': + verify = 1; + break; + case 'o': + undo_output = arg; + break; + case OPT_DYNAMIC_LINKER: + dynamic_linker = arg; + break; + case OPT_LD_LIBRARY_PATH: + ld_library_path = arg; + break; + case OPT_LIBS_ONLY: + libs_only = 1; + break; + case OPT_MD5: + verify_method = VERIFY_MD5; + break; + case OPT_SHA: + verify_method = VERIFY_SHA; + break; + case OPT_CXX_DISABLE: + enable_cxx_optimizations = 0; + break; + case OPT_MMAP_REG_START: + mmap_reg_start = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0')) + error (EXIT_FAILURE, 0, "--mmap-region-start option requires numberic argument"); + break; + case OPT_MMAP_REG_END: + mmap_reg_end = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0')) + error (EXIT_FAILURE, 0, "--mmap-region-end option requires numberic argument"); + break; + case OPT_EXEC_SHIELD: + exec_shield = 1; + break; + case OPT_NO_EXEC_SHIELD: + exec_shield = 0; + break; + case OPT_COMPUTE_CHECKSUM: + compute_checksum = 1; + break; + case OPT_LAYOUT_PAGE_SIZE: + layout_page_size = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0') || (layout_page_size & (layout_page_size - 1))) + error (EXIT_FAILURE, 0, "--layout-page-size option requires numberic power-of-two argument"); + case OPT_SYSROOT: + sysroot = arg; + break; + case OPT_RTLD: + prelink_rtld = arg; + break; + case 'i': + noreexecinit=1; + break; + case OPT_ALLOW_TEXTREL: + allow_bad_textrel = 1; + break; + case OPT_LD_PRELOAD: + ld_preload = arg; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +time_t get_ctime(const char *file) { + struct stat st; + if(stat(file,&st) == 0) + return st.st_ctime; + return 0; +} + +void checkinit() { + if(initctime != get_ctime("/sbin/init")) { + printf("Executing /sbin/init U\n"); + system("/sbin/init U"); + } +} + +static struct argp argp = { options, parse_opt, "[FILES]", argp_doc }; + +const char *prelink_rtld = NULL; + +/* Disable detection, this is not appropriate when cross prelinking. */ +#if 0 && (defined (__i386__) || defined (__x86_64__)) && defined (__GNUC__) +static void +set_default_layout_page_size (void) +{ + /* From gcc.dg/20020523-1.c test in gcc 3.2 testsuite. */ + int fl1, fl2; + +#ifndef __x86_64__ + /* See if we can use cpuid. */ + __asm__ ("pushfl; pushfl; popl %0; movl %0,%1; xorl %2,%0;" + "pushl %0; popfl; pushfl; popl %0; popfl" + : "=&r" (fl1), "=&r" (fl2) + : "i" (0x00200000)); + if (((fl1 ^ fl2) & 0x00200000) == 0) + return; +#define cpuid(fl1, fl2, fn) \ + __asm__ ("movl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ + : "=a" (fl1), "=r" (fl2) : "0" (fn) : "ecx", "edx") +#else +#define cpuid(fl1, fl2, fn) \ + __asm__ ("cpuid" : "=a" (fl1), "=b" (fl2) : "0" (fn) : "ecx", "edx") +#endif + + /* See if CPUID gives capabilities. */ + cpuid (fl1, fl2, 0); + if (fl1 < 1 || fl2 != 0x68747541 /* Auth - AMD */) + return; + + /* CPUID 1. */ + cpuid (fl1, fl2, 1); + if (((fl1 >> 8) & 0x0f) + ((fl1 >> 20) & 0xff) == 0x15 /* Family */) + /* On AMD Bulldozer CPUs default to --layout-page-size=0x8000. */ + layout_page_size = 0x8000; +} +#else +# define set_default_layout_page_size() +#endif + +int +main (int argc, char *argv[]) +{ + int remaining, failures = 0; + + setlocale (LC_ALL, ""); + + exec_shield = 2; + + set_default_layout_page_size (); + + prelink_init_cache (); + + elf_version (EV_CURRENT); + + argp_parse (&argp, argc, argv, 0, &remaining, 0); + + if(!noreexecinit) { + initctime = get_ctime("/sbin/init"); + atexit(checkinit); + } + + if (ld_library_path == NULL) + ld_library_path = getenv ("LD_LIBRARY_PATH"); + + if (all && reloc_only) + error (EXIT_FAILURE, 0, "--all and --reloc-only options are incompatible"); + if ((undo || verify) && reloc_only) + error (EXIT_FAILURE, 0, "--undo and --reloc-only options are incompatible"); + if (verify && (undo || all)) + error (EXIT_FAILURE, 0, "--verify and either --undo or --all options are incompatible"); + if (dry_run && verify) + error (EXIT_FAILURE, 0, "--dry-run and --verify options are incompatible"); + if ((undo || verify) && quick) + error (EXIT_FAILURE, 0, "--undo and --quick options are incompatible"); + + /* Set the default for exec_shield. */ + if (exec_shield == 2) + { + if (sysroot == NULL && ! access ("/proc/sys/kernel/exec-shield", F_OK)) + exec_shield = 1; + else + exec_shield = 0; + } + +#ifdef DEFAULT_SYSROOT + if (sysroot == NULL) + { + extern char *make_relative_prefix (const char *, const char *, const char *); + sysroot = make_relative_prefix (argv[0], BINDIR, DEFAULT_SYSROOT); + } +#endif + + if (sysroot) + { + sysroot = canonicalize_file_name (sysroot); + if (sysroot == NULL) + error (EXIT_FAILURE, 0, "Could not canonicalize --root argument"); + asprintf ((char **) &prelink_conf, "%s%s", sysroot, prelink_conf); + } + if (prelink_rtld != NULL && prelink_rtld[0] == 0) + prelink_rtld = NULL; + else + if (prelink_rtld == NULL) + { + extern char *make_relative_prefix (const char *, const char *, const char *); + const char *path = make_relative_prefix (argv[0], BINDIR, BINDIR); + asprintf ((char **) &prelink_rtld, "%s/%s", path, + PRELINK_RTLD_PROG EXEEXT); + } + + if (print_cache) + { + prelink_load_cache (); + prelink_print_cache (); + return 0; + } + + if (remaining == argc && ! all) + error (EXIT_FAILURE, 0, "no files given and --all not used"); + + if (undo_output && (!undo || all)) + error (EXIT_FAILURE, 0, "-o can be only specified together with -u and without -a"); + + if (undo_output && remaining + 1 != argc) + error (EXIT_FAILURE, 0, "-o can only be used when undoing a single object"); + + if (compute_checksum) + { + while (remaining < argc) + { + DSO *dso = open_dso (argv[remaining++]); + + if (dso == NULL || reopen_dso (dso, NULL, NULL) + || prelink_set_checksum (dso)) + error (0, 0, "could not recompute checksum of %s", dso->filename); + close_dso (dso); + error (0, 0, "%08x %s\n", (unsigned int) dso->info_DT_CHECKSUM, dso->filename); + } + exit (0); + } + + if (verify) + { + if (remaining + 1 != argc) + error (EXIT_FAILURE, 0, "only one library or binary can be verified in a single command"); + return prelink_verify (argv[remaining]); + } + + if (reloc_only || (undo && ! all)) + { + while (remaining < argc) + { + DSO *dso = open_dso (argv[remaining++]); + int ret; + + if (dso == NULL) + { + ++failures; + continue; + } + + if (dso->ehdr.e_type != ET_DYN + && (reloc_only || dso->ehdr.e_type != ET_EXEC)) + { + ++failures; + error (0, 0, "%s is not a shared library", dso->filename); + continue; + } + + if (undo) + ret = prelink_undo (dso); + else + ret = relocate_dso (dso, reloc_base); + + if (ret) + { + ++failures; + close_dso (dso); + continue; + } + + if (dynamic_info_is_set (dso, DT_CHECKSUM_BIT) + && dso_is_rdwr (dso) + && prelink_set_checksum (dso)) + { + ++failures; + close_dso (dso); + continue; + } + + if (dry_run) + { + close_dso (dso); + continue; + } + + if (reloc_only) + dso->permissive = 1; + else if (undo_output) + { + const char *output, *orig_filename; + + if (!dso_is_rdwr (dso)) + { + struct stat64 st; + int err; + + if (fstat64 (dso->fd, &st) < 0) + { + error (0, errno, "Could not stat %s", dso->filename); + ++failures; + close_dso (dso); + continue; + } + err = copy_fd_to_file (dso->fd, undo_output, &st); + if (err) + { + error (0, err, "Could not undo %s to %s", dso->filename, + undo_output); + ++failures; + } + close_dso (dso); + continue; + } + + output = strdup (undo_output); + if (!output) + { + ++failures; + close_dso (dso); + continue; + } + if (dso->filename != dso->soname) + orig_filename = dso->filename; + else + orig_filename = strdup (dso->filename); + if (!orig_filename) + { + ++failures; + close_dso (dso); + continue; + } + dso->filename = output; + if (update_dso (dso, orig_filename)) + ++failures; + free ((char *) orig_filename); + continue; + } + + if (update_dso (dso, NULL)) + ++failures; + } + + return failures; + } + + if (read_config (prelink_conf)) + return EXIT_FAILURE; + + if (blacklist_from_config ()) + return EXIT_FAILURE; + + if (quick) + prelink_load_cache (); + + if (gather_config ()) + return EXIT_FAILURE; + + while (remaining < argc) + if (gather_object (argv[remaining++], dereference, one_file_system)) + return EXIT_FAILURE; + + if (gather_check_libs ()) + return EXIT_FAILURE; + + if (undo) + return undo_all (); + + if (! all && ! quick) + prelink_load_cache (); + + layout_libs (); + prelink_all (); + + if (! no_update && ! dry_run) + prelink_save_cache (all); + return 0; +} diff --git a/src/makecrc.c b/src/makecrc.c new file mode 100644 index 0000000..db52013 --- /dev/null +++ b/src/makecrc.c @@ -0,0 +1,63 @@ +/* Not copyrighted 1990 Mark Adler */ + +#ifndef lint +static char rcsid[] = "$Id: makecrc.c,v 0.6 1993/05/28 07:42:59 jloup Exp $"; +#endif + +#include <stdio.h> + +main() +/* + Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The table is simply the CRC of all possible eight bit values. This is all + the information needed to generate CRC's on data a byte at a time for all + combinations of CRC register values and incoming bytes. The table is + written to stdout as 256 long hexadecimal values in C language format. +*/ +{ + unsigned long c; /* crc shift register */ + unsigned long e; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* Make exclusive-or pattern from polynomial (0xedb88320) */ + e = 0; + for (i = 0; i < sizeof(p)/sizeof(int); i++) + e |= 1L << (31 - p[i]); + + /* Compute and print table of CRC's, five per line */ + printf(" 0x00000000"); + for (i = 1; i < 256; i++) + { + c = i; + /* The idea to initialize the register with the byte instead of + * zero was stolen from Haruhiko Okumura's ar002 + */ + for (k = 8; k; k--) + c = c & 1 ? (c >> 1) ^ e : c >> 1; + printf(i % 5 ? ", 0x%08lx" : ",\n 0x%08lx", c); + } + putchar('\n'); + return 0; +} diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..0d2fdef --- /dev/null +++ b/src/md5.c @@ -0,0 +1,362 @@ +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <endian.h> +#include <byteswap.h> +#include "md5.h" + +#if __BYTE_ORDER == __BIG_ENDIAN +# define SWAP(n) bswap_32 (n) +#else +# define SWAP(n) (n) +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (ctx) + struct md5_ctx *ctx; +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_read_ctx (ctx, resbuf) + const struct md5_ctx *ctx; + void *resbuf; +{ + ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_finish_ctx (ctx, resbuf) + struct md5_ctx *ctx; + void *resbuf; +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (buffer, len, resblock) + const char *buffer; + size_t len; + void *resblock; +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx (&ctx, resblock); +} + + +void +md5_process_bytes (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (md5_uint32) != 0) + if (UNALIGNED_P (buffer)) + while (len > 64) + { + md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + md5_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +void +md5_process_block (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + a = rol (a, s); \ + a += b; \ + } \ + while (0) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or + perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}' + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + a = rol (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..55f6195 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,105 @@ +/* md5.h - Declaration of functions and data types used for MD5 sum + computing library functions. + Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _MD5_H +#define _MD5_H 1 + +#include <limits.h> +#include <stdint.h> +typedef uint32_t md5_uint32; +typedef uintptr_t md5_uintptr; + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128]; +}; + +/* + * The following three functions are build up the low level used in + * the function `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +extern void md5_init_ctx (struct md5_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void md5_process_block __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void md5_process_bytes __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* Process the remaining bytes in the buffer and put result from CTX + in first 16 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF be correctly + aligned for a 32 bits value. */ +extern void *md5_finish_ctx (struct md5_ctx *ctx, void *resbuf); + + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *md5_read_ctx (const struct md5_ctx *ctx, void *resbuf); + + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *md5_buffer (const char *buffer, size_t len, void *resblock); + +/* The following is from gnupg-1.0.2's cipher/bithelp.h. */ +/* Rotate a 32 bit integer by n bytes */ +#if defined __GNUC__ && defined __i386__ +static inline md5_uint32 +rol(md5_uint32 x, int n) +{ + __asm__("roll %%cl,%0" + :"=r" (x) + :"0" (x),"c" (n)); + return x; +} +#else +# define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) +#endif + +#endif diff --git a/src/mdebug.c b/src/mdebug.c new file mode 100644 index 0000000..4c22d2f --- /dev/null +++ b/src/mdebug.c @@ -0,0 +1,692 @@ +/* Copyright (C) 2001 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <byteswap.h> +#include <endian.h> +#include <error.h> +#include <stddef.h> + +#include "prelink.h" + +#define F8(x) unsigned char x[1]; +#define F16(x) unsigned char x[2]; +#define F24(x) unsigned char x[3]; +#define F32(x) unsigned char x[4]; +#define F64(x) unsigned char x[8]; + +typedef struct +{ + F16(magic) + F16(vstamp) + F32(ilineMax) + F32(cbLine) + F32(cbLineOffset) + F32(idnMax) + F32(cbDnOffset) + F32(ipdMax) + F32(cbPdOffset) + F32(isymMax) + F32(cbSymOffset) + F32(ioptMax) + F32(cbOptOffset) + F32(iauxMax) + F32(cbAuxOffset) + F32(issMax) + F32(cbSsOffset) + F32(issExtMax) + F32(cbSsExtOffset) + F32(ifdMax) + F32(cbFdOffset) + F32(crfd) + F32(cbRfdOffset) + F32(iextMax) + F32(cbExtOffset) +} mdebug_hdr_32; + +typedef struct +{ + F16(magic) + F16(vstamp) + F32(ilineMax) + F32(idnMax) + F32(ipdMax) + F32(isymMax) + F32(ioptMax) + F32(iauxMax) + F32(issMax) + F32(issExtMax) + F32(ifdMax) + F32(crfd) + F32(iextMax) + F64(cbLine) + F64(cbLineOffset) + F64(cbDnOffset) + F64(cbPdOffset) + F64(cbSymOffset) + F64(cbOptOffset) + F64(cbAuxOffset) + F64(cbSsOffset) + F64(cbSsExtOffset) + F64(cbFdOffset) + F64(cbRfdOffset) + F64(cbExtOffset) +} mdebug_hdr_64; + +typedef struct +{ + F32(adr) + F32(rss) + F32(issBase) + F32(cbSs) + F32(isymBase) + F32(csym) + F32(ilineBase) + F32(cline) + F32(ioptBase) + F32(copt) + F16(ipdFirst) + F16(cpd) + F32(iauxBase) + F32(caux) + F32(rfdBase) + F32(crfd) + F8(bits1) + F24(bits2) + F32(cbLineOffset) + F32(cbLine) +} mdebug_fdr_32; + +typedef struct +{ + F64(adr) + F64(cbLineOffset) + F64(cbLine) + F64(cbSs) + F32(rss) + F32(issBase) + F32(isymBase) + F32(csym) + F32(ilineBase) + F32(cline) + F32(ioptBase) + F32(copt) + F32(ipdFirst) + F32(cpd) + F32(iauxBase) + F32(caux) + F32(rfdBase) + F32(crfd) + F8(bits1) + F24(bits2) + F32(padding) +} mdebug_fdr_64; + +typedef struct +{ + F32(iss) + F32(value) + F8(bits1) + F8(bits2) + F8(bits3) + F8(bits4) +} mdebug_sym_32; + +typedef struct +{ + F64(value) + F32(iss) + F8(bits1) + F8(bits2) + F8(bits3) + F8(bits4) +} mdebug_sym_64; + +typedef struct +{ + F8(bits1) + F8(bits2) + F16(fd) + mdebug_sym_32 asym; +} mdebug_ext_32; + +typedef struct +{ + mdebug_sym_64 asym; + F8(bits1) + F24(bits2) + F32(fd) +} mdebug_ext_64; + +typedef struct +{ + F32(adr) + F32(isym) + F32(iline) + F32(regmask) + F32(regoffset) + F32(iopt) + F32(fregmask) + F32(fregoffset) + F32(frameoffset) + F16(framereg) + F16(pcreg) + F32(lnLow) + F32(lnHigh) + F32(cbLineOffset) +} mdebug_pdr_32; + +typedef struct +{ + F64(adr) + F64(cbLineOffset) + F32(isym) + F32(iline) + F32(regmask) + F32(regoffset) + F32(iopt) + F32(fregmask) + F32(fregoffset) + F32(frameoffset) + F32(lnLow) + F32(lnHigh) + F8(gp_prologue) + F8(bits1) + F8(bits2) + F8(localoff) + F16(framereg) + F16(pcreg) +} mdebug_pdr_64; + +typedef struct +{ + F32(bits); +} mdebug_rndx; + +typedef struct +{ + F8(bits1) + F8(bits2) + F8(bits3) + F8(bits4) + mdebug_rndx rndx; + F32(offset) +} mdebug_opt; + +typedef struct +{ + F32(rfd) + F32(index) +} mdebug_dnr; + +typedef struct +{ + F32(rfd) +} mdebug_rfd; + +#define scNil 0 +#define scText 1 +#define scData 2 +#define scBss 3 +#define scRegister 4 +#define scAbs 5 +#define scUndefined 6 +#define scCdbLocal 7 +#define scBits 8 +#define scCdbSystem 9 +#define scDbx 9 +#define scRegImage 10 +#define scInfo 11 +#define scUserStruct 12 +#define scSData 13 +#define scSBss 14 +#define scRData 15 +#define scVar 16 +#define scCommon 17 +#define scSCommon 18 +#define scVarRegister 19 +#define scVariant 20 +#define scSUndefined 21 +#define scInit 22 +#define scBasedVar 23 +#define scXData 24 +#define scPData 25 +#define scFini 26 +#define scRConst 27 +#define scMax 32 + +#define stNil 0 +#define stGlobal 1 +#define stStatic 2 +#define stParam 3 +#define stLocal 4 +#define stLabel 5 +#define stProc 6 +#define stBlock 7 +#define stEnd 8 +#define stMember 9 +#define stTypedef 10 +#define stFile 11 +#define stRegReloc 12 +#define stForward 13 +#define stStaticProc 14 +#define stConstant 15 +#define stStaParam 16 +#define stStruct 26 +#define stUnion 27 +#define stEnum 28 +#define stIndirect 34 +#define stMax 64 + +struct mdebug +{ + uint32_t (*read_32) (char *); + GElf_Addr (*read_ptr) (char *); + void (*write_ptr) (char *, GElf_Addr); + void (*adjust_sym) (struct mdebug *, unsigned char *, GElf_Addr, GElf_Addr); + unsigned char *buf; + DSO *dso; +}; + +static uint32_t +read_native_32 (char *p) +{ + return *(uint32_t *)p; +} + +static uint32_t +read_swap_32 (char *p) +{ + return bswap_32 (*(uint32_t *)p); +} + +static GElf_Addr +read_native_ptr32 (char *p) +{ + return *(uint32_t *)p; +} + +static GElf_Addr +read_swap_ptr32 (char *p) +{ + return bswap_32 (*(uint32_t *)p); +} + +static void +write_native_ptr32 (char *p, GElf_Addr v) +{ + *(uint32_t *)p = v; +} + +static void +write_swap_ptr32 (char *p, GElf_Addr v) +{ + *(uint32_t *)p = bswap_32 (v); +} + +static GElf_Addr +read_native_ptr64 (char *p) +{ + return *(uint64_t *)p; +} + +static GElf_Addr +read_swap_ptr64 (char *p) +{ + return bswap_64 (*(uint64_t *)p); +} + +static void +write_native_ptr64 (char *p, GElf_Addr v) +{ + *(uint64_t *)p = v; +} + +static void +write_swap_ptr64 (char *p, GElf_Addr v) +{ + *(uint64_t *)p = bswap_64 (v); +} + +static inline int +mdebug_sym_relocate (unsigned int st, unsigned int sc) +{ + switch (sc) + { + case scData: + case scBss: + case scAbs: + case scSData: + case scSBss: + case scRData: + case scXData: + case scPData: + return 1; + case scText: + case scInit: + case scFini: + case scRConst: + if (st != stBlock && st != stEnd && st != stFile) + return 1; + default: + return 0; + } +} + +static void +adjust_mdebug_sym_le32 (struct mdebug *mdebug, mdebug_sym_32 *symptr, + GElf_Addr start, GElf_Addr adjust) +{ + unsigned int st, sc; + GElf_Addr addr; + + st = symptr->bits1[0] & 0x3f; + sc = (symptr->bits1[0] >> 6) | ((symptr->bits2[0] & 7) << 2); + if (mdebug_sym_relocate (st, sc)) + { + addr = mdebug->read_ptr (symptr->value); + if (addr >= start && (addr || sc != scAbs)) + mdebug->write_ptr (symptr->value, addr + adjust); + } +} + +static void +adjust_mdebug_sym_be32 (struct mdebug *mdebug, mdebug_sym_32 *symptr, + GElf_Addr start, GElf_Addr adjust) +{ + unsigned int st, sc; + GElf_Addr addr; + + st = symptr->bits1[0] >> 2; + sc = ((symptr->bits1[0] & 3) << 3) | (symptr->bits2[0] >> 5); + if (mdebug_sym_relocate (st, sc)) + { + addr = mdebug->read_ptr (symptr->value); + if (addr >= start && (addr || sc != scAbs)) + mdebug->write_ptr (symptr->value, addr + adjust); + } +} + +static void +adjust_mdebug_sym_le64 (struct mdebug *mdebug, mdebug_sym_64 *symptr, + GElf_Addr start, GElf_Addr adjust) +{ + unsigned int st, sc; + GElf_Addr addr; + + st = symptr->bits1[0] & 0x3f; + sc = (symptr->bits1[0] >> 6) | ((symptr->bits2[0] & 7) << 2); + if (mdebug_sym_relocate (st, sc)) + { + addr = mdebug->read_ptr (symptr->value); + if (addr >= start && (addr || sc != scAbs)) + mdebug->write_ptr (symptr->value, addr + adjust); + } +} + +static void +adjust_mdebug_sym_be64 (struct mdebug *mdebug, mdebug_sym_64 *symptr, + GElf_Addr start, GElf_Addr adjust) +{ + unsigned int st, sc; + GElf_Addr addr; + + st = symptr->bits1[0] >> 2; + sc = ((symptr->bits1[0] & 3) << 3) | (symptr->bits2[0] >> 5); + if (mdebug_sym_relocate (st, sc)) + { + addr = mdebug->read_ptr (symptr->value); + if (addr >= start && (addr || sc != scAbs)) + mdebug->write_ptr (symptr->value, addr + adjust); + } +} + +#define SIZEOf(x) \ + (dso->arch->class == ELFCLASS32 ? sizeof (x##_32) : sizeof (x##_64)) +#define SIZEOF(x) SIZEOf(x) +#define OFFSETOf(x,y) \ + (dso->arch->class == ELFCLASS32 ? offsetof (x##_32, y) : offsetof (x##_64, y)) +#define OFFSETOF(x,y) OFFSETOf(x,y) + +static int +start_mdebug (DSO *dso, int n, struct mdebug *mdebug) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + + data = elf_getdata (scn, NULL); + mdebug->buf = data->d_buf; + mdebug->dso = dso; + assert (data != NULL && data->d_buf != NULL); + assert (elf_getdata (scn, data) == NULL); + assert (data->d_off == 0 && data->d_size == dso->shdr[n].sh_size); + if (dso->mdebug_orig_offset == 0) + dso->mdebug_orig_offset = dso->shdr[n].sh_offset; +#if __BYTE_ORDER == __BIG_ENDIAN + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) +#elif __BYTE_ORDER == __LITTLE_ENDIAN + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) +#else +# error Not supported host endianess +#endif + { + mdebug->read_32 = read_native_32; + if (dso->arch->class == ELFCLASS32) + { + mdebug->read_ptr = read_native_ptr32; + mdebug->write_ptr = write_native_ptr32; + } + else + { + mdebug->read_ptr = read_native_ptr64; + mdebug->write_ptr = write_native_ptr64; + } + } +#if __BYTE_ORDER == __BIG_ENDIAN + else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) +#elif __BYTE_ORDER == __LITTLE_ENDIAN + else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) +#endif + { + mdebug->read_32 = read_swap_32; + if (dso->arch->class == ELFCLASS32) + { + mdebug->read_ptr = read_swap_ptr32; + mdebug->write_ptr = write_swap_ptr32; + } + else + { + mdebug->read_ptr = read_swap_ptr64; + mdebug->write_ptr = write_swap_ptr64; + } + } + else + { + error (0, 0, "%s: Wrong ELF data enconding", dso->filename); + return 1; + } + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) + { + if (dso->arch->class == ELFCLASS32) + mdebug->adjust_sym = (void *) adjust_mdebug_sym_le32; + else + mdebug->adjust_sym = (void *) adjust_mdebug_sym_le64; + } + else + { + if (dso->arch->class == ELFCLASS32) + mdebug->adjust_sym = (void *) adjust_mdebug_sym_be32; + else + mdebug->adjust_sym = (void *) adjust_mdebug_sym_be64; + } + + if (dso->shdr[n].sh_size < SIZEOF (mdebug_hdr)) + { + error (0, 0, "%s: .mdebug section too small", dso->filename); + return 1; + } + return 0; +} + +int +adjust_mdebug (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) +{ + struct mdebug mdebug; + struct { GElf_Off offset; GElf_Off size; size_t entsize; } regions [11]; + int i = 0; + unsigned char *symptr, *endptr; + + if (start_mdebug (dso, n, &mdebug)) + return 1; + +#define READ(x, y, longsize, sz) \ +do { \ + unsigned char *tmp; \ + tmp = mdebug.buf + OFFSETOF (mdebug_hdr, x); \ + regions[i].offset = mdebug.read_ptr (tmp); \ + tmp = mdebug.buf + OFFSETOF (mdebug_hdr, y); \ + if (longsize) \ + regions[i].size = mdebug.read_ptr (tmp); \ + else \ + regions[i].size = mdebug.read_32 (tmp); \ + regions[i].entsize = sz; \ + ++i; \ +} while (0) + + READ (cbLineOffset, cbLine, 1, sizeof (char)); + READ (cbDnOffset, idnMax, 0, sizeof (mdebug_dnr)); + READ (cbPdOffset, ipdMax, 0, SIZEOF (mdebug_pdr)); + READ (cbSymOffset, isymMax, 0, SIZEOF (mdebug_sym)); + READ (cbOptOffset, ioptMax, 0, sizeof (mdebug_opt)); + READ (cbAuxOffset, iauxMax, 0, 4 * sizeof (char)); + READ (cbSsOffset, issMax, 0, sizeof (char)); + READ (cbSsExtOffset, issExtMax, 0, sizeof (char)); + READ (cbFdOffset, ifdMax, 0, SIZEOF (mdebug_fdr)); + READ (cbRfdOffset, crfd, 0, sizeof (mdebug_rfd)); + READ (cbExtOffset, iextMax, 0, SIZEOF (mdebug_ext)); + +#undef READ + + for (i = 0; i < 11; ++i) + { + if (regions[i].offset) + regions[i].offset -= dso->mdebug_orig_offset; + regions[i].size *= regions[i].entsize; + if (regions[i].offset >= dso->shdr[n].sh_size + || regions[i].offset + regions[i].size > dso->shdr[n].sh_size) + { + error (0, 0, "%s: File offsets in .mdebug header point outside of .mdebug section", + dso->filename); + return 1; + } + } + + /* Adjust symbols. */ + if (regions[3].offset) + for (symptr = mdebug.buf + regions[3].offset, + endptr = symptr + regions[3].size; + symptr < endptr; + symptr += regions[3].entsize) + mdebug.adjust_sym (&mdebug, symptr, start, adjust); + + /* Adjust file descriptor's addresses. */ + if (regions[8].offset) + for (symptr = mdebug.buf + regions[8].offset, + endptr = symptr + regions[8].size; + symptr < endptr; + symptr += regions[8].entsize) + { + GElf_Addr addr; + + assert (offsetof (mdebug_fdr_32, adr) == 0); + assert (offsetof (mdebug_fdr_64, adr) == 0); + addr = mdebug.read_ptr (symptr); + if (addr >= start) + mdebug.write_ptr (symptr, addr + adjust); + } + + /* Adjust extended symbols. */ + if (regions[10].offset) + for (symptr = mdebug.buf + regions[10].offset + + OFFSETOF (mdebug_ext, asym), + endptr = symptr + regions[10].size; + symptr < endptr; + symptr += regions[10].entsize) + mdebug.adjust_sym (&mdebug, symptr, start, adjust); + + return 0; +} + +int +finalize_mdebug (DSO *dso) +{ + int i; + struct mdebug mdebug; + GElf_Addr adj; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + if ((dso->arch->machine == EM_ALPHA + && dso->shdr[i].sh_type == SHT_ALPHA_DEBUG) + || (dso->arch->machine == EM_MIPS + && dso->shdr[i].sh_type == SHT_MIPS_DEBUG)) + break; + + assert (i < dso->ehdr.e_shnum); + + /* If .mdebug's file position did not change, there is nothing to do. */ + adj = dso->shdr[i].sh_offset - dso->mdebug_orig_offset; + if (! adj) + return 0; + + if (start_mdebug (dso, i, &mdebug)) + return 1; + +#define ADJUST(x) \ +do { \ + unsigned char *tmp; \ + GElf_Addr val; \ + tmp = mdebug.buf + OFFSETOF (mdebug_hdr, x); \ + val = mdebug.read_ptr (tmp); \ + if (! val) \ + break; \ + val += adj; \ + if (val < dso->shdr[i].sh_offset \ + || val >= dso->shdr[i].sh_offset + dso->shdr[i].sh_size) \ + { \ + error (0, 0, "%s: File offsets in .mdebug header point outside of .mdebug section", \ + dso->filename); \ + return 1; \ + } \ + mdebug.write_ptr (tmp, val); \ +} while (0) + + ADJUST (cbLineOffset); + ADJUST (cbDnOffset); + ADJUST (cbPdOffset); + ADJUST (cbSymOffset); + ADJUST (cbOptOffset); + ADJUST (cbAuxOffset); + ADJUST (cbSsOffset); + ADJUST (cbSsExtOffset); + ADJUST (cbFdOffset); + ADJUST (cbRfdOffset); + ADJUST (cbExtOffset); + +#undef ADJUST + return 0; +} diff --git a/src/prelink.c b/src/prelink.c new file mode 100644 index 0000000..0798811 --- /dev/null +++ b/src/prelink.c @@ -0,0 +1,995 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <endian.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include "prelink.h" +#include "reloc.h" + +static GElf_Addr +resolve_ldso (struct prelink_info *info, GElf_Word r_sym, + int reloc_type __attribute__((unused))) +{ + /* Dynamic linker does not depend on any other library, + all symbols resolve to themselves with the exception + of SHN_UNDEF symbols which resolve to 0. */ + if (info->symtab[r_sym].st_shndx == SHN_UNDEF) + { + info->resolveent = NULL; + info->resolvetls = NULL; + return 0; + } + else + { + /* As the dynamic linker is relocated first, + l_addr will be 0. */ + info->resolveent = info->ent; + info->resolvetls = NULL; + return 0 + info->symtab[r_sym].st_value; + } +} + +static GElf_Addr +resolve_dso (struct prelink_info *info, GElf_Word r_sym, + int reloc_type) +{ + struct prelink_symbol *s; + int reloc_class = info->dso->arch->reloc_class (reloc_type); + + for (s = & info->symbols[r_sym]; s; s = s->next) + if (s->reloc_class == reloc_class) + break; + + info->resolveent = NULL; + info->resolvetls = NULL; + + if (s == NULL || s->u.ent == NULL) + return 0; + + if (reloc_class == RTYPE_CLASS_TLS) + { + info->resolvetls = s->u.tls; + return s->value; + } + + info->resolveent = s->u.ent; + return s->u.ent->base + s->value; +} + +static int +prelink_rel (DSO *dso, int n, struct prelink_info *info) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Rel rel; + int sec; + + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off; + + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; + ++ndx, addr += dso->shdr[n].sh_entsize) + { + gelfx_getrel (dso->elf, data, ndx, &rel); + sec = addr_to_sec (dso, rel.r_offset); + if (sec == -1) + continue; + + switch (dso->arch->prelink_rel (info, &rel, addr)) + { + case 2: + gelfx_update_rel (dso->elf, data, ndx, &rel); + break; + case 0: + break; + default: + return 1; + } + } + } + return 0; +} + +static int +prelink_rela (DSO *dso, int n, struct prelink_info *info) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Rela rela; + int sec; + + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off; + + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; + ++ndx, addr += dso->shdr[n].sh_entsize) + { + gelfx_getrela (dso->elf, data, ndx, &rela); + sec = addr_to_sec (dso, rela.r_offset); + if (sec == -1) + continue; + + switch (dso->arch->prelink_rela (info, &rela, addr)) + { + case 2: + gelfx_update_rela (dso->elf, data, ndx, &rela); + break; + case 0: + break; + default: + return 1; + } + } + } + return 0; +} + +int +prelink_prepare (DSO *dso) +{ + struct reloc_info rinfo; + int liblist = 0, libstr = 0, newlibstr = 0, undo = 0, newundo = 0; + int i; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + const char *name + = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name); + if (! strcmp (name, ".gnu.liblist")) + liblist = i; + else if (! strcmp (name, ".gnu.libstr")) + libstr = i; + else if (! strcmp (name, ".gnu.prelink_undo")) + undo = i; + } + + if (undo == 0) + { + Elf32_Shdr *shdr32; + Elf64_Shdr *shdr64; + Elf_Data src, dst; + + dso->undo.d_size = gelf_fsize (dso->elf, ELF_T_EHDR, 1, EV_CURRENT) + + gelf_fsize (dso->elf, ELF_T_PHDR, + dso->ehdr.e_phnum, EV_CURRENT) + + gelf_fsize (dso->elf, ELF_T_SHDR, + dso->ehdr.e_shnum - 1, EV_CURRENT); + dso->undo.d_buf = malloc (dso->undo.d_size); + if (dso->undo.d_buf == NULL) + { + error (0, ENOMEM, "%s: Could not create .gnu.prelink_undo section", + dso->filename); + return 1; + } + dso->undo.d_type = ELF_T_BYTE; + dso->undo.d_off = 0; + dso->undo.d_align = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT); + dso->undo.d_version = EV_CURRENT; + src = dso->undo; + src.d_type = ELF_T_EHDR; + src.d_size = gelf_fsize (dso->elf, ELF_T_EHDR, 1, EV_CURRENT); + dst = src; + switch (gelf_getclass (dso->elf)) + { + case ELFCLASS32: + src.d_buf = elf32_getehdr (dso->elf); + if (elf32_xlatetof (&dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + { + error (0, 0, "%s: Failed to create .gnu.prelink_undo section", + dso->filename); + return 1; + } + break; + case ELFCLASS64: + src.d_buf = elf64_getehdr (dso->elf); + if (elf64_xlatetof (&dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + { + error (0, 0, "%s: Failed to create .gnu.prelink_undo section", + dso->filename); + return 1; + } + break; + default: + return 1; + } + src.d_buf = dst.d_buf + src.d_size; + src.d_type = ELF_T_PHDR; + src.d_size = gelf_fsize (dso->elf, ELF_T_PHDR, dso->ehdr.e_phnum, + EV_CURRENT); + dst = src; + switch (gelf_getclass (dso->elf)) + { + case ELFCLASS32: + src.d_buf = elf32_getphdr (dso->elf); + if (elf32_xlatetof (&dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + { + error (0, 0, "%s: Failed to create .gnu.prelink_undo section", + dso->filename); + return 1; + } + break; + case ELFCLASS64: + src.d_buf = elf64_getphdr (dso->elf); + if (elf64_xlatetof (&dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + { + error (0, 0, "%s: Failed to create .gnu.prelink_undo section", + dso->filename); + return 1; + } + break; + } + src.d_buf = dst.d_buf + src.d_size; + src.d_type = ELF_T_SHDR; + src.d_size = gelf_fsize (dso->elf, ELF_T_SHDR, + dso->ehdr.e_shnum - 1, EV_CURRENT); + dst = src; + switch (gelf_getclass (dso->elf)) + { + case ELFCLASS32: + shdr32 = (Elf32_Shdr *) src.d_buf; + /* Note: cannot use dso->scn[i] below, since we want to save the + original section order before non-alloced sections were + sorted by sh_offset. */ + for (i = 1; i < dso->ehdr.e_shnum; ++i) + shdr32[i - 1] = *elf32_getshdr (elf_getscn (dso->elf, i)); + if (elf32_xlatetof (&dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + { + error (0, 0, "%s: Failed to create .gnu.prelink_undo section", + dso->filename); + return 1; + } + break; + case ELFCLASS64: + shdr64 = (Elf64_Shdr *) src.d_buf; + /* Note: cannot use dso->scn[i] below, since we want to save the + original section order before non-alloced sections were + sorted by sh_offset. */ + for (i = 1; i < dso->ehdr.e_shnum; ++i) + shdr64[i - 1] = *elf64_getshdr (elf_getscn (dso->elf, i)); + if (elf64_xlatetof (&dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + { + error (0, 0, "%s: Failed to create .gnu.prelink_undo section", + dso->filename); + return 1; + } + break; + } + } + + if (dso->ehdr.e_type != ET_DYN) + return 0; + + if (find_reloc_sections (dso, &rinfo)) + return 1; + + if (is_ldso_soname (dso->soname)) + { + liblist = -1; + libstr = -1; + } + + if (liblist && libstr && undo + && ! rinfo.rel_to_rela && ! rinfo.rel_to_rela_plt) + return 0; + + if (! liblist || ! libstr || ! undo) + { + struct section_move *move; + + move = init_section_move (dso); + if (move == NULL) + return 1; + + if (! liblist) + { + liblist = move->old_to_new [dso->ehdr.e_shstrndx]; + add_section (move, liblist); + } + else + liblist = 0; + + if (! libstr) + { + add_section (move, liblist + 1); + libstr = liblist + 1; + newlibstr = 1; + } + else if (libstr != -1) + libstr = move->old_to_new[libstr]; + + if (! undo) + { + if (libstr == -1) + { + undo = move->old_to_new [dso->ehdr.e_shstrndx]; + add_section (move, undo); + } + else + { + add_section (move, libstr + 1); + undo = libstr + 1; + } + newundo = 1; + } + else + undo = move->old_to_new[undo]; + + if (reopen_dso (dso, move, NULL)) + { + free (move); + return 1; + } + + free (move); + if (liblist) + { + memset (&dso->shdr[liblist], 0, sizeof (GElf_Shdr)); + dso->shdr[liblist].sh_name = shstrtabadd (dso, ".gnu.liblist"); + if (dso->shdr[liblist].sh_name == 0) + return 1; + dso->shdr[liblist].sh_type = SHT_GNU_LIBLIST; + dso->shdr[liblist].sh_offset = dso->shdr[liblist - 1].sh_offset; + if (dso->shdr[liblist - 1].sh_type != SHT_NOBITS) + dso->shdr[liblist].sh_offset += dso->shdr[liblist - 1].sh_size; + dso->shdr[liblist].sh_link = libstr; + dso->shdr[liblist].sh_addralign = sizeof (GElf_Word); + dso->shdr[liblist].sh_entsize = sizeof (Elf32_Lib); + } + + if (newlibstr) + { + memset (&dso->shdr[libstr], 0, sizeof (GElf_Shdr)); + dso->shdr[libstr].sh_name = shstrtabadd (dso, ".gnu.libstr"); + if (dso->shdr[libstr].sh_name == 0) + return 1; + dso->shdr[libstr].sh_type = SHT_STRTAB; + dso->shdr[libstr].sh_offset = dso->shdr[libstr - 1].sh_offset; + if (dso->shdr[libstr - 1].sh_type != SHT_NOBITS) + dso->shdr[libstr].sh_offset += dso->shdr[libstr - 1].sh_size; + dso->shdr[libstr].sh_addralign = 1; + } + + if (newundo) + { + Elf_Scn *scn; + Elf_Data *data; + GElf_Addr newoffset; + + memset (&dso->shdr[undo], 0, sizeof (GElf_Shdr)); + dso->shdr[undo].sh_name = shstrtabadd (dso, ".gnu.prelink_undo"); + if (dso->shdr[undo].sh_name == 0) + return 1; + dso->shdr[undo].sh_type = SHT_PROGBITS; + dso->shdr[undo].sh_offset = dso->shdr[undo - 1].sh_offset; + if (dso->shdr[undo - 1].sh_type != SHT_NOBITS) + dso->shdr[undo].sh_offset += dso->shdr[undo - 1].sh_size; + dso->shdr[undo].sh_addralign = dso->undo.d_align; + dso->shdr[undo].sh_entsize = 1; + dso->shdr[undo].sh_size = dso->undo.d_size; + newoffset = dso->shdr[undo].sh_offset + dso->undo.d_align - 1; + newoffset &= ~(dso->shdr[undo].sh_addralign - 1); + if (adjust_dso_nonalloc (dso, undo + 1, dso->shdr[undo].sh_offset, + dso->undo.d_size + newoffset + - dso->shdr[undo].sh_offset)) + return 1; + dso->shdr[undo].sh_offset = newoffset; + scn = dso->scn[undo]; + data = elf_getdata (scn, NULL); + assert (data != NULL && elf_getdata (scn, data) == NULL); + free (data->d_buf); + *data = dso->undo; + dso->undo.d_buf = NULL; + } + } + else if (reopen_dso (dso, NULL, NULL)) + return 1; + + if (rinfo.rel_to_rela || rinfo.rel_to_rela_plt) + { + /* On REL architectures, we might need to convert some REL + relocations to RELA relocs. */ + + int safe = 1, align = 0, last; + GElf_Addr start, adjust, adjust1, adjust2; + + for (i = 1; i < (rinfo.plt ? rinfo.plt : rinfo.first); i++) + switch (dso->shdr[i].sh_type) + { + case SHT_HASH: + case SHT_GNU_HASH: + case SHT_DYNSYM: + case SHT_REL: + case SHT_RELA: + case SHT_STRTAB: + case SHT_NOTE: + case SHT_GNU_verdef: + case SHT_GNU_verneed: + case SHT_GNU_versym: + /* These sections are safe, no relocations should point + to it, therefore enlarging a section after sections + from this set only (and SHT_REL) in ET_DYN just needs + adjusting the rest of the library. */ + break; + case SHT_DYNAMIC: + case SHT_MIPS_REGINFO: + case SHT_MIPS_OPTIONS: + /* The same applies to these sections on MIPS. The convention + is to put .dynamic and .reginfo near the beginning of the + read-only segment, before the program text. No relocations + may refer to them. */ + if (dso->ehdr.e_machine == EM_MIPS) + break; + /* FALLTHROUGH */ + default: + /* The rest of sections are not safe. */ + safe = 0; + break; + } + + if (! safe) + { + error (0, 0, "%s: Cannot safely convert %s' section from REL to RELA", + dso->filename, strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[rinfo.rel_to_rela + ? rinfo.first : rinfo.plt].sh_name)); + return 1; + } + + for (i = rinfo.plt ? rinfo.plt : rinfo.first; i < dso->ehdr.e_shnum; i++) + { + if (dso->shdr[i].sh_addralign > align) + align = dso->shdr[i].sh_addralign; + } + + if (rinfo.plt) + start = dso->shdr[rinfo.plt].sh_addr + dso->shdr[rinfo.plt].sh_size; + else + start = dso->shdr[rinfo.last].sh_addr + dso->shdr[rinfo.last].sh_size; + + adjust1 = 0; + adjust2 = 0; + assert (sizeof (Elf32_Rel) * 3 == sizeof (Elf32_Rela) * 2); + assert (sizeof (Elf64_Rel) * 3 == sizeof (Elf64_Rela) * 2); + if (rinfo.rel_to_rela) + { + for (i = rinfo.first; i <= rinfo.last; ++i) + { + GElf_Addr size = dso->shdr[i].sh_size / 2 * 3; + adjust1 += size - dso->shdr[i].sh_size; + if (convert_rel_to_rela (dso, i)) + return 1; + } + } + if (rinfo.rel_to_rela_plt) + { + GElf_Addr size = dso->shdr[rinfo.plt].sh_size / 2 * 3; + adjust2 = size - dso->shdr[rinfo.plt].sh_size; + if (convert_rel_to_rela (dso, rinfo.plt)) + return 1; + } + + adjust = adjust1 + adjust2; + + /* Need to make sure that all the remaining sections are properly + aligned. */ + if (align) + adjust = (adjust + align - 1) & ~(align - 1); + + /* Need to make sure adjust doesn't cause different Phdr segments + to overlap on the same page. */ + last = -1; + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_LOAD + && dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz >= start) + { + if (last != -1 + && (((dso->phdr[last].p_vaddr + dso->phdr[last].p_memsz - 1) + ^ dso->phdr[i].p_vaddr) + & ~(dso->arch->max_page_size - 1)) + && !(((dso->phdr[last].p_vaddr + dso->phdr[last].p_memsz + + adjust - 1) + ^ (dso->phdr[i].p_vaddr + adjust)) + & ~(dso->arch->max_page_size - 1))) + { + if (align >= dso->arch->max_page_size) + { + error (0, 0, "%s: Cannot grow reloc sections", dso->filename); + return 1; + } + adjust = (adjust + dso->arch->max_page_size - 1) + & ~(dso->arch->max_page_size - 1); + } + last = i; + } + + /* Adjust all addresses pointing into remaining sections. */ + if (adjust_dso (dso, start - 1, adjust)) + return 1; + + if (rinfo.rel_to_rela) + { + GElf_Addr adjust3 = 0; + for (i = rinfo.first; i <= rinfo.last; ++i) + { + GElf_Addr size = dso->shdr[i].sh_size / 2 * 3; + + dso->shdr[i].sh_addr += adjust3; + dso->shdr[i].sh_offset += adjust3; + adjust3 += size - dso->shdr[i].sh_size; + dso->shdr[i].sh_size = size; + } + assert (adjust1 == adjust3); + if (rinfo.plt) + { + dso->shdr[rinfo.plt].sh_addr += adjust1; + dso->shdr[rinfo.plt].sh_offset += adjust1; + } + } + if (rinfo.rel_to_rela_plt) + dso->shdr[rinfo.plt].sh_size += adjust2; + + if (update_dynamic_rel (dso, &rinfo)) + return 1; + } + + return 0; +} + +static int +prelink_dso (struct prelink_info *info) +{ + int liblist = 0, libstr = 0, nobits_plt = 0; + int i, ndeps = info->ent->ndepends + 1; + DSO *dso = info->dso; + Elf32_Lib *list = NULL; + Elf_Scn *scn; + Elf_Data *data; + GElf_Addr oldsize, oldoffset; + size_t strsize; + + if (dso->ehdr.e_type != ET_DYN) + return 0; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + const char *name + = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name); + if (! strcmp (name, ".gnu.liblist")) + liblist = i; + else if (! strcmp (name, ".gnu.libstr")) + libstr = i; + else if (! strcmp (name, ".plt") && dso->shdr[i].sh_type == SHT_NOBITS) + nobits_plt = i; +#if 0 + else if (dso->arch->create_opd && ! strcmp (name, ".opd")) + opd = i; +#endif + } + + if (nobits_plt) + { + int j, first; + GElf_Addr adj, last_offset; + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_LOAD + && dso->phdr[i].p_vaddr <= dso->shdr[nobits_plt].sh_addr + && dso->phdr[i].p_vaddr + dso->phdr[i].p_memsz + >= dso->shdr[nobits_plt].sh_addr + + dso->shdr[nobits_plt].sh_size) + break; + + if (i == dso->ehdr.e_phnum) + { + error (0, 0, "%s: .plt section not contained within a segment", + dso->filename); + return 1; + } + + for (j = i + 1; j < dso->ehdr.e_phnum; ++j) + if (dso->phdr[j].p_type == PT_LOAD) + { + error (0, 0, "%s: library's NOBITS .plt section not in loadable last segment", + dso->filename); + return 1; + } + + for (j = nobits_plt - 1; j > 0; --j) + if (dso->shdr[j].sh_addr < dso->phdr[i].p_vaddr + || dso->shdr[j].sh_type != SHT_NOBITS) + break; + first = j + 1; + + for (j = first; j <= nobits_plt; ++j) + { + Elf_Data *data = elf_getdata (dso->scn[j], NULL); + + assert (data->d_buf == NULL); + assert (data->d_size == dso->shdr[j].sh_size); + if (data->d_size) + { + data->d_buf = calloc (data->d_size, 1); + if (data->d_buf == NULL) + { + error (0, ENOMEM, "%s: Could not convert NOBITS section into PROGBITS", + dso->filename); + return 1; + } + } + data->d_type = ELF_T_BYTE; + dso->shdr[j].sh_type = SHT_PROGBITS; + dso->shdr[j].sh_offset = dso->phdr[i].p_offset + dso->shdr[j].sh_addr + - dso->phdr[i].p_vaddr; + } + + adj = dso->shdr[nobits_plt].sh_offset + dso->shdr[nobits_plt].sh_size + - dso->phdr[i].p_offset; + assert (adj <= dso->phdr[i].p_memsz); + if (adj > dso->phdr[i].p_filesz) + { + adj -= dso->phdr[i].p_filesz; + dso->phdr[i].p_filesz += adj; + if (adjust_dso_nonalloc (dso, nobits_plt + 1, + dso->shdr[first].sh_offset, adj)) + return 1; + } + + last_offset = dso->shdr[nobits_plt].sh_offset + + dso->shdr[nobits_plt].sh_size; + for (j = nobits_plt + 1; j < dso->ehdr.e_shnum; ++j) + if (!(dso->shdr[j].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR))) + break; + else + { + last_offset += dso->shdr[j].sh_addralign - 1; + last_offset &= ~(dso->shdr[j].sh_addralign - 1); + if (last_offset > dso->phdr[i].p_offset + dso->phdr[i].p_filesz) + last_offset = dso->phdr[i].p_offset + dso->phdr[i].p_filesz; + dso->shdr[j].sh_offset = last_offset; + } + } + + if (ndeps <= 1) + return 0; + + assert (liblist != 0); + assert (libstr != 0); + + list = calloc (ndeps - 1, sizeof (Elf32_Lib)); + if (list == NULL) + { + error (0, ENOMEM, "%s: Cannot build .gnu.liblist section", + dso->filename); + goto error_out; + } + + strsize = 1; + for (i = 0; i < ndeps - 1; ++i) + { + struct prelink_entry *ent = info->ent->depends[i]; + + strsize += strlen (info->sonames[i + 1]) + 1; + list[i].l_time_stamp = ent->timestamp; + list[i].l_checksum = ent->checksum; + } + + scn = dso->scn[libstr]; + data = elf_getdata (scn, NULL); + if (data == NULL) + data = elf_newdata (scn); + assert (elf_getdata (scn, data) == NULL); + + data->d_type = ELF_T_BYTE; + data->d_size = 1; + data->d_off = 0; + data->d_align = 1; + data->d_version = EV_CURRENT; + data->d_buf = realloc (data->d_buf, strsize); + if (data->d_buf == NULL) + { + error (0, ENOMEM, "%s: Could not build .gnu.libstr section", + dso->filename); + goto error_out; + } + + oldsize = dso->shdr[libstr].sh_size; + dso->shdr[libstr].sh_size = 1; + *(char *)data->d_buf = '\0'; + for (i = 0; i < ndeps - 1; ++i) + { + const char *name = info->sonames[i + 1]; + + list[i].l_name = strtabfind (dso, liblist, name); + if (list[i].l_name == 0) + { + size_t len = strlen (name) + 1; + + memcpy (data->d_buf + data->d_size, name, len); + list[i].l_name = data->d_size; + data->d_size += len; + dso->shdr[libstr].sh_size += len; + } + } + if (oldsize != dso->shdr[libstr].sh_size) + { + GElf_Addr adjust = dso->shdr[libstr].sh_size - oldsize; + + oldoffset = dso->shdr[libstr].sh_offset; + if (adjust_dso_nonalloc (dso, libstr + 1, oldoffset, adjust)) + goto error_out; + } + + scn = dso->scn[liblist]; + data = elf_getdata (scn, NULL); + if (data == NULL) + data = elf_newdata (scn); + assert (elf_getdata (scn, data) == NULL); + + data->d_type = ELF_T_WORD; + data->d_size = (ndeps - 1) * sizeof (Elf32_Lib); + data->d_off = 0; + data->d_align = sizeof (GElf_Word); + data->d_version = EV_CURRENT; + free (data->d_buf); + data->d_buf = list; + list = NULL; + + if (data->d_size != dso->shdr[liblist].sh_size) + { + GElf_Addr adjust = data->d_size - dso->shdr[liblist].sh_size; + GElf_Addr newoffset; + + oldoffset = dso->shdr[liblist].sh_offset; + newoffset = oldoffset; + if (newoffset & (data->d_align - 1)) + { + newoffset = (newoffset + data->d_align - 1) & ~(data->d_align - 1); + adjust += newoffset - dso->shdr[liblist].sh_offset; + } + if (adjust_dso_nonalloc (dso, liblist + 1, oldoffset, adjust)) + goto error_out; + dso->shdr[liblist].sh_offset = newoffset; + dso->shdr[liblist].sh_size = data->d_size; + } + + recompute_nonalloc_offsets (dso); + return 0; + +error_out: + free (list); + return 1; +} + +static int +prelink_set_timestamp (struct prelink_info *info) +{ + DSO *dso = info->dso; + + if (! verify) + info->ent->timestamp = getenv ("PRELINK_TIMESTAMP") ? + atoi (getenv ("PRELINK_TIMESTAMP")) + : (GElf_Word) time (NULL); + dso->info_DT_GNU_PRELINKED = info->ent->timestamp; + if (prelink_set_checksum (dso)) + return 1; + info->ent->checksum = dso->info_DT_CHECKSUM; + return 0; +} + +static void +free_info (struct prelink_info *info) +{ + int i; + + free (info->symtab); + free (info->dynbss); + free (info->sdynbss); + free (info->conflict_rela); + if (info->conflicts) + { + for (i = 0; i < info->ent->ndepends + 1; ++i) + { + if (info->conflicts[i].hash == &info->conflicts[i].first) + { + struct prelink_conflict *c = info->conflicts[i].first; + void *f; + + while (c != NULL) + { + f = c; + c = c->next; + free (f); + } + } + else + { + int j; + for (j = 0; j < 251; j++) + { + struct prelink_conflict *c = info->conflicts[i].hash[j]; + void *f; + + while (c != NULL) + { + f = c; + c = c->next; + free (f); + } + } + free (info->conflicts[i].hash); + } + if (info->conflicts[i].hash2 != NULL) + free (info->conflicts[i].hash2); + } + free (info->conflicts); + } + if (info->sonames) + { + for (i = 0; i < info->ent->ndepends + 1; ++i) + free ((char *) info->sonames[i]); + free (info->sonames); + } + free (info->tls); + if (info->symbols) + { + for (i = 0; i < info->symbol_count; ++i) + { + struct prelink_symbol *s = info->symbols[i].next; + void *f; + + while (s != NULL) + { + f = s; + s = s->next; + free (f); + } + } + free (info->symbols); + } +} + +int +prelink (DSO *dso, struct prelink_entry *ent) +{ + int i; + Elf_Scn *scn; + Elf_Data *data; + struct prelink_info info; + + ent->pltgot = dso->info[DT_PLTGOT]; + + if (! dso->info[DT_SYMTAB]) + return 0; + + if (! dso_is_rdwr (dso) && dso->ehdr.e_type == ET_DYN) + { + if (reopen_dso (dso, NULL, NULL)) + return 1; + } + + i = addr_to_sec (dso, dso->info[DT_SYMTAB]); + /* DT_SYMTAB should be found and should point to + start of .dynsym section. */ + if (i == -1 + || dso->info[DT_SYMTAB] != dso->shdr[i].sh_addr) + { + error (0, 0, "%s: Bad symtab", dso->filename); + return 1; + } + + memset (&info, 0, sizeof (info)); + info.ent = ent; + info.symtab_entsize = dso->shdr[i].sh_entsize; + info.symtab = calloc (dso->shdr[i].sh_size / dso->shdr[i].sh_entsize, + sizeof (GElf_Sym)); + if (info.symtab == NULL) + { + error (0, ENOMEM, "%s: Cannot convert .dynsym section", dso->filename); + return 1; + } + + scn = dso->scn[i]; + data = NULL; + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx, loc; + + loc = data->d_off / info.symtab_entsize; + maxndx = data->d_size / info.symtab_entsize; + for (ndx = 0; ndx < maxndx; ++ndx) + gelfx_getsym (dso->elf, data, ndx, info.symtab + loc + ndx); + } + info.symtab_start = + adjust_new_to_old (dso, dso->shdr[i].sh_addr - dso->base); + info.symtab_end = info.symtab_start + dso->shdr[i].sh_size; + info.dso = dso; + switch (prelink_get_relocations (&info)) + { + case 0: + goto error_out; + case 1: + info.resolve = resolve_ldso; + break; + case 2: + info.resolve = resolve_dso; + break; + } + + if (dso->arch->arch_pre_prelink && dso->arch->arch_pre_prelink (dso)) + goto error_out; + + if (dso->ehdr.e_type == ET_EXEC) + { + if (prelink_exec (&info)) + goto error_out; + } + else if (prelink_dso (&info)) + goto error_out; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + { + if (! (dso->shdr[i].sh_flags & SHF_ALLOC)) + continue; + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".gnu.conflict")) + continue; + switch (dso->shdr[i].sh_type) + { + case SHT_REL: + if (prelink_rel (dso, i, &info)) + goto error_out; + break; + case SHT_RELA: + if (prelink_rela (dso, i, &info)) + goto error_out; + break; + } + } + + if (dso->arch->arch_prelink && dso->arch->arch_prelink (&info)) + goto error_out; + + if (dso->arch->read_opd && dso->arch->read_opd (dso, ent)) + goto error_out; + + /* Must be last. */ + if (dso->ehdr.e_type == ET_DYN + && prelink_set_timestamp (&info)) + goto error_out; + + free_info (&info); + return 0; + +error_out: + free_info (&info); + return 1; +} diff --git a/src/prelink.h b/src/prelink.h new file mode 100644 index 0000000..299f1fa --- /dev/null +++ b/src/prelink.h @@ -0,0 +1,632 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011, + 2013 Red Hat, Inc. + Copyright (C) 2008 CodeSourcery. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + Updated by Maciej W. Rozycki <macro@codesourcery.com>, 2008. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PRELINK_H +#define PRELINK_H + +#include <elf.h> +#include <libelf.h> +#include <gelfx.h> +#include <ftw.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/stat.h> +#include <utime.h> +#include <glob.h> + +#ifndef HAVE_ELF64_BYTE +typedef uint8_t Elf64_Byte; +#endif + +#ifndef DT_GNU_LIBLIST +#define DT_GNU_LIBLIST 0x6ffffef9 +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 +#define DT_GNU_CONFLICT 0x6ffffef8 +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 +#define DT_GNU_PRELINKED 0x6ffffdf5 +#define SHT_GNU_LIBLIST 0x6ffffff7 +#endif + +#if DT_GNU_LIBLIST == 0x6ffffef7 +#undef DT_GNU_LIBLIST +#undef DT_GNU_CONFLICT +#undef SHT_GNU_LIBLIST +#define DT_GNU_LIBLIST 0x6ffffef9 +#define DT_GNU_CONFLICT 0x6ffffef8 +#define SHT_GNU_LIBLIST 0x6ffffff7 +#endif + +#ifndef DT_GNU_HASH +#define DT_GNU_HASH 0x6ffffef5 +#define SHT_GNU_HASH 0x6ffffff6 +#endif + +#ifndef DT_TLSDESC_PLT +#define DT_TLSDESC_PLT 0x6ffffef6 +#endif + +#ifndef DT_MIPS_RLD_VERSION +#define DT_MIPS_RLD_VERSION 0x70000001 +#define DT_MIPS_TIME_STAMP 0x70000002 +#define DT_MIPS_ICHECKSUM 0x70000003 +#define DT_MIPS_IVERSION 0x70000004 +#define DT_MIPS_FLAGS 0x70000005 +#define DT_MIPS_BASE_ADDRESS 0x70000006 +#define DT_MIPS_CONFLICT 0x70000008 +#define DT_MIPS_LIBLIST 0x70000009 +#define DT_MIPS_LOCAL_GOTNO 0x7000000a +#define DT_MIPS_CONFLICTNO 0x7000000b +#define DT_MIPS_LIBLISTNO 0x70000010 +#define DT_MIPS_SYMTABNO 0x70000011 +#define DT_MIPS_UNREFEXTNO 0x70000012 +#define DT_MIPS_GOTSYM 0x70000013 +#define DT_MIPS_HIPAGENO 0x70000014 +#define DT_MIPS_RLD_MAP 0x70000016 +#endif + +#ifndef R_MIPS_TLS_DTPMOD32 +#define R_MIPS_TLS_DTPMOD32 38 +#define R_MIPS_TLS_DTPREL32 39 +#define R_MIPS_TLS_TPREL32 47 +#endif + +#ifndef R_MIPS_TLS_DTPMOD64 +#define R_MIPS_TLS_DTPMOD64 40 +#define R_MIPS_TLS_DTPREL64 41 +#define R_MIPS_TLS_TPREL64 48 +#endif + +#ifndef R_MIPS_GLOB_DAT +#define R_MIPS_GLOB_DAT 51 +#endif + +#ifndef R_MIPS_COPY +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 +#define STO_MIPS_PLT 0x8 +#define DT_MIPS_PLTGOT 0x70000032 +#define DT_MIPS_RWPLT 0x70000034 +#endif + +#ifndef SHT_MIPS_DWARF +#define SHT_MIPS_DWARF 0x7000001e +#endif + +#ifndef RSS_UNDEF +#define RSS_UNDEF 0 +#endif + +#ifndef R_ARM_TLS_DESC +#define R_ARM_TLS_DESC 13 +#endif + +#ifndef R_ARM_TLS_DTPMOD32 +#define R_ARM_TLS_DTPMOD32 17 +#define R_ARM_TLS_DTPOFF32 18 +#define R_ARM_TLS_TPOFF32 19 +#endif + +#ifndef R_386_IRELATIVE +#define R_386_IRELATIVE 42 +#endif + +#ifndef R_X86_64_IRELATIVE +#define R_X86_64_IRELATIVE 37 +#endif + +#ifndef R_PPC_IRELATIVE +#define R_PPC_IRELATIVE 248 +#endif + +#ifndef R_PPC64_JMP_IREL +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#endif + +#ifndef R_390_IRELATIVE +#define R_390_IRELATIVE 61 +#endif + +#ifndef R_ARM_IRELATIVE +#define R_ARM_IRELATIVE 160 +#endif + +struct prelink_entry; +struct prelink_info; +struct PLArch; +struct opd_lib; + +struct PLAdjust +{ + GElf_Addr start; + GElf_Addr adjust; +}; + +struct section_move +{ + int old_shnum; + int new_shnum; + int *old_to_new; + int *new_to_old; +}; + +typedef struct +{ + Elf *elf, *elfro; + GElf_Ehdr ehdr; + GElf_Phdr *phdr; + Elf_Scn **scn; + GElf_Addr base, end, align; + GElf_Addr mask; + GElf_Addr info[DT_NUM]; + GElf_Addr info_DT_GNU_PRELINKED; + GElf_Addr info_DT_CHECKSUM; + GElf_Addr info_DT_VERNEED, info_DT_VERDEF, info_DT_VERSYM; + GElf_Addr info_DT_GNU_HASH; + GElf_Addr info_DT_TLSDESC_PLT; + GElf_Addr info_DT_MIPS_LOCAL_GOTNO; + GElf_Addr info_DT_MIPS_GOTSYM; + GElf_Addr info_DT_MIPS_SYMTABNO; + GElf_Addr info_DT_MIPS_PLTGOT; +#define DT_GNU_PRELINKED_BIT 50 +#define DT_CHECKSUM_BIT 51 +#define DT_VERNEED_BIT 52 +#define DT_VERDEF_BIT 53 +#define DT_VERSYM_BIT 54 +#define DT_FILTER_BIT 55 +#define DT_AUXILIARY_BIT 56 +#define DT_LOPROC_BIT 57 +#define DT_GNU_HASH_BIT 58 +#define DT_TLSDESC_PLT_BIT 59 + uint64_t info_set_mask; + int fd, fdro; + int lastscn, dynamic; + const char *soname; + const char *filename, *temp_filename; + struct PLArch *arch; + struct PLAdjust *adjust; + /* .mdebug has absolute file offsets in it. */ + GElf_Off mdebug_orig_offset; + Elf_Data undo; + int nadjust; + int permissive; + struct section_move *move; + GElf_Shdr shdr[0]; +} DSO; + +static inline int +dynamic_info_is_set (DSO *dso, int bit) +{ + return ((dso)->info_set_mask & (1ULL << (bit))) != 0; +} + +struct layout_libs; + +struct PLArch +{ + const char *name; + int class; + int machine; + int alternate_machine[3]; + int max_reloc_size; + const char *dynamic_linker; + const char *dynamic_linker_alt; + int R_COPY; + int R_JMP_SLOT; + int R_RELATIVE; + int rtype_class_valid; + int (*arch_adjust) (DSO *dso, GElf_Addr start, GElf_Addr adjust); + int (*adjust_section) (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust); + int (*adjust_dyn) (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, + GElf_Addr adjust); + int (*adjust_rel) (DSO *dso, GElf_Rel *rel, GElf_Addr start, + GElf_Addr adjust); + int (*adjust_rela) (DSO *dso, GElf_Rela *rela, GElf_Addr start, + GElf_Addr adjust); + int (*prelink_rel) (struct prelink_info *info, GElf_Rel *rel, + GElf_Addr reladdr); + int (*prelink_rela) (struct prelink_info *info, GElf_Rela *rela, + GElf_Addr relaaddr); + int (*prelink_conflict_rel) (DSO *dso, struct prelink_info *info, + GElf_Rel *rel, GElf_Addr reladdr); + int (*prelink_conflict_rela) (DSO *dso, struct prelink_info *info, + GElf_Rela *rela, GElf_Addr relaaddr); + int (*arch_prelink_conflict) (DSO *dso, struct prelink_info *info); + int (*apply_conflict_rela) (struct prelink_info *info, GElf_Rela *rela, + char *buf, GElf_Addr dest_addr); + int (*apply_rel) (struct prelink_info *info, GElf_Rel *rel, char *buf); + int (*apply_rela) (struct prelink_info *info, GElf_Rela *rela, char *buf); + int (*rel_to_rela) (DSO *dso, GElf_Rel *rel, GElf_Rela *rela); + int (*rela_to_rel) (DSO *dso, GElf_Rela *rela, GElf_Rel *rel); + int (*need_rel_to_rela) (DSO *dso, int first, int last); + GElf_Addr (*create_opd) (struct prelink_info *info, int first, int last, + int plt); + int (*read_opd) (DSO *dso, struct prelink_entry *ent); + int (*free_opd) (struct prelink_entry *ent); + /* Return reloc size in bytes for given non-COPY reloc type. */ + int (*reloc_size) (int); +#define RTYPE_CLASS_VALID 8 +#define RTYPE_CLASS_PLT (8|1) +#define RTYPE_CLASS_COPY (8|2) +#define RTYPE_CLASS_TLS (8|4) + int (*reloc_class) (int); + int (*arch_pre_prelink) (DSO *dso); + int (*arch_prelink) (struct prelink_info *info); + int (*arch_undo_prelink) (DSO *dso); + int (*undo_prelink_rel) (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr); + int (*undo_prelink_rela) (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr); + int (*layout_libs_init) (struct layout_libs *l); + int (*layout_libs_pre) (struct layout_libs *l); + int (*layout_libs_post) (struct layout_libs *l); + GElf_Addr mmap_base, mmap_end; + /* max_page_size is the ELF page size (ELF_MAXPAGESIZE in bfd), + page_size is PAGE_SIZE the architecture typically has, + or if there are more typical sizes, the smallest one. + It doesn't need to be the absolutely smallest supported one, + prelink only optimizes for such page_size. */ + GElf_Addr max_page_size, page_size; +} __attribute__((aligned(64))); + +DSO * open_dso (const char *name); +DSO * fdopen_dso (int fd, const char *name); +struct section_move *init_section_move (DSO *dso); +void add_section (struct section_move *move, int sec); +void remove_section (struct section_move *move, int sec); +int reopen_dso (DSO *dso, struct section_move *move, const char *); +int adjust_symbol_p (DSO *dso, GElf_Sym *sym); +int check_dso (DSO *dso); +int dso_is_rdwr (DSO *dso); +void read_dynamic (DSO *dso); +int set_dynamic (DSO *dso, GElf_Word tag, GElf_Addr value, int fatal); +int addr_to_sec (DSO *dso, GElf_Addr addr); +int adjust_dso (DSO *dso, GElf_Addr start, GElf_Addr adjust); +int adjust_nonalloc (DSO *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int first, + GElf_Addr start, GElf_Addr adjust); +int adjust_dso_nonalloc (DSO *dso, int first, GElf_Addr start, + GElf_Addr adjust); +int recompute_nonalloc_offsets (DSO *dso); +int adjust_stabs (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust); +int adjust_dwarf2 (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust); +int adjust_mdebug (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust); +int finalize_mdebug (DSO *dso); +int relocate_dso (DSO *dso, GElf_Addr base); +int copy_fd_to_file (int fdin, const char *name, struct stat64 *st); +int update_dso (DSO *dso, const char *); +int prepare_write_dso (DSO *dso); +int write_dso (DSO *dso); +int close_dso (DSO *dso); +GElf_Addr adjust_old_to_new (DSO *dso, GElf_Addr addr); +GElf_Addr adjust_new_to_old (DSO *dso, GElf_Addr addr); +int strtabfind (DSO *dso, int strndx, const char *name); +int shstrtabadd (DSO *dso, const char *name); +int dso_has_bad_textrel (DSO *dso); + +/* data.c */ + +/* Used for reading consecutive blocks of data from a DSO. */ +struct data_iterator { + /* The DSO that is being read. */ + DSO *dso; + + /* The data block that contained the last byte to be read. + NULL if no data has been read yet or if the end of the + DSO has been reached. */ + Elf_Data *data; + + /* The section that contains DATA, when DATA is nonnull. */ + int sec; + + /* The address of the next byte. */ + GElf_Addr addr; + + /* The offset of the next byte from the start of SEC, when DATA + is nonnull. */ + GElf_Addr sec_offset; +}; + +unsigned char * get_data (DSO *dso, GElf_Addr addr, int *scnp, Elf_Type *typep); +#define READWRITEPROTO(le,nn) \ +uint##nn##_t buf_read_u##le##nn (unsigned char *data); \ +uint##nn##_t read_u##le##nn (DSO *dso, GElf_Addr addr); \ +void buf_write_##le##nn (unsigned char *data, uint##nn##_t val);\ +int write_##le##nn (DSO *dso, GElf_Addr addr, uint##nn##_t val); +#define READWRITEPROTOSIZE(nn) \ +READWRITEPROTO(le,nn) \ +READWRITEPROTO(be,nn) \ +uint##nn##_t buf_read_une##nn (DSO *dso, unsigned char *data); \ +uint##nn##_t read_une##nn (DSO *dso, GElf_Addr addr); \ +void buf_write_ne##nn (DSO *dso, unsigned char *data, \ + uint##nn##_t val); \ +void write_ne##nn (DSO *dso, GElf_Addr addr, uint##nn##_t val); +READWRITEPROTO(,8) +READWRITEPROTOSIZE(16) +READWRITEPROTOSIZE(32) +READWRITEPROTOSIZE(64) +#undef READWRITEPROTO +#undef READWRITEPROTOSIZE +const char * strptr (DSO *dso, int sec, off_t offset); +void init_data_iterator (struct data_iterator *it, DSO *dso, GElf_Addr addr); +unsigned char *get_data_from_iterator (struct data_iterator *it, + GElf_Addr size); +int get_sym_from_iterator (struct data_iterator *it, GElf_Sym *sym); + +#define PL_ARCH(F) \ +static struct PLArch plarch_##F __attribute__((section("pl_arch"),used)) + +#define addr_adjust(addr, start, adjust) \ + do { \ + if (addr >= start) \ + addr += adjust; \ + } while (0) + +struct prelink_cache_entry +{ + uint32_t filename; + uint32_t depends; + uint32_t checksum; +#define PCF_UNPRELINKABLE 0x40000 +#define PCF_PRELINKED 0x20000 +#define PCF_ELF64 0x10000 +#define PCF_MACHINE 0x0ffff + uint32_t flags; + uint32_t ctime; + uint32_t mtime; + uint64_t base; + uint64_t end; +}; + +struct prelink_cache +{ +#define PRELINK_CACHE_NAME "prelink-ELF" +#define PRELINK_CACHE_VER "0.3.2" +#define PRELINK_CACHE_MAGIC PRELINK_CACHE_NAME PRELINK_CACHE_VER + const char magic [sizeof (PRELINK_CACHE_MAGIC) - 1]; + uint32_t nlibs; + uint32_t ndeps; + uint32_t len_strings; + uint32_t unused[9]; + struct prelink_cache_entry entry[0]; + /* uint32_t depends [ndeps]; */ + /* const char strings [len_strings]; */ +}; + +struct prelink_link +{ + struct prelink_link *next; + const char *canon_filename; +}; + +struct prelink_entry +{ + const char *filename; + const char *canon_filename; + const char *soname; + struct prelink_link *hardlink; + GElf_Word timestamp; + GElf_Word checksum; + GElf_Addr base, end, layend, pltgot; + dev_t dev; + ino64_t ino; +#define ET_BAD (ET_NUM) +#define ET_CACHE_EXEC (ET_NUM + 1) +#define ET_CACHE_DYN (ET_NUM + 2) +#define ET_UNPRELINKABLE (ET_NUM + 3) + int type, done, ndepends, refs, flags; + union + { + int explicit; + int tmp; + } u; + uint32_t ctime, mtime; + struct prelink_entry **depends; + struct prelink_entry *prev, *next; + struct opd_lib *opd; +}; + +struct prelink_dir +{ + dev_t dev; + struct prelink_dir *next; + size_t len; + int flags; + char dir[0]; +}; + +struct prelink_tls +{ + GElf_Addr modid; + GElf_Addr offset; +}; + +struct prelink_symbol +{ + union + { + struct prelink_entry *ent; + struct prelink_tls *tls; + } u; + struct prelink_symbol *next; + GElf_Addr value; + int reloc_class; +}; + +struct prelink_conflict +{ + struct prelink_conflict *next; + struct prelink_conflict *next2; + /* Object which it was relocated to. */ + union + { + struct prelink_entry *ent; + struct prelink_tls *tls; + } lookup, + /* Object which the relocation was prelinked to. */ + conflict; + /* Offset from start of owner to owner's symbol. */ + GElf_Addr symoff; + /* Value it has in lookup.ent. */ + GElf_Addr lookupval; + /* Value it has in conflict.ent. */ + GElf_Addr conflictval; + int reloc_class; + unsigned char used; + unsigned char ifunc; + char * symname; +}; + +struct prelink_conflicts +{ + struct prelink_conflict *first; + struct prelink_conflict **hash; + struct prelink_conflict **hash2; + size_t count; +}; + +#define conflict_lookup_value(cfl) \ + (((cfl)->reloc_class != RTYPE_CLASS_TLS ? (cfl)->lookup.ent->base : 0) \ + + (cfl)->lookupval) + +struct prelink_info +{ + DSO *dso; + DSO **dsos; + struct prelink_entry *ent; + struct prelink_symbol *symbols; + struct prelink_conflicts *conflicts; + struct prelink_conflicts *curconflicts; + struct prelink_tls *tls, *curtls; + const char **sonames; + char *dynbss, *sdynbss; + GElf_Addr dynbss_base, sdynbss_base; + size_t dynbss_size, sdynbss_size, symtab_entsize; + int symbol_count; + GElf_Sym *symtab; + GElf_Rela *conflict_rela; + size_t conflict_rela_alloced, conflict_rela_size; + GElf_Addr symtab_start, symtab_end; + GElf_Addr (*resolve) (struct prelink_info *info, GElf_Word r_sym, + int reloc_type); + struct prelink_entry *resolveent; + struct prelink_tls *resolvetls; +}; + +int prelink_prepare (DSO *dso); +int prelink (DSO *dso, struct prelink_entry *ent); +int prelink_init_cache (void); +int prelink_load_cache (void); +int prelink_print_cache (void); +int prelink_save_cache (int do_warn); +struct prelink_entry * + prelink_find_entry (const char *filename, const struct stat64 *stp, + int insert); +struct prelink_conflict * + prelink_conflict (struct prelink_info *info, GElf_Word r_sym, + int reloc_type); +GElf_Rela *prelink_conflict_add_rela (struct prelink_info *info); +int prelink_get_relocations (struct prelink_info *info); +int prelink_build_conflicts (struct prelink_info *info); +int update_dynamic_tags (DSO *dso, GElf_Shdr *shdr, GElf_Shdr *old_shdr, + struct section_move *move); +int prelink_exec (struct prelink_info *info); +int prelink_set_checksum (DSO *dso); +int is_ldso_soname (const char *soname); + +int prelink_undo (DSO *dso); + +int prelink_verify (const char *filename); +ssize_t send_file (int outfd, int infd, off_t *poff, size_t count); + +int gather_object (const char *dir, int deref, int onefs); +int read_config (const char *config); +int gather_config (void); +int gather_check_libs (void); +int add_to_blacklist (const char *name, int deref, int onefs); +int blacklist_from_config (void); + +FILE *execve_open (const char *path, char *const argv[], char *const envp[]); +int execve_close (FILE *f); + +int remove_redundant_cxx_conflicts (struct prelink_info *info); +int get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr, + char *buf, GElf_Word size, GElf_Addr dest_addr); + +int layout_libs (void); + +void prelink_all (void); + +int undo_all (void); + +char *prelink_canonicalize (const char *name, struct stat64 *stp); + +extern const char *dynamic_linker; +extern const char *ld_library_path; +extern const char *prelink_cache; +extern const char *prelink_conf; +extern const char *undo_output; +extern int all; +extern int force; +extern int random_base; +extern int conserve_memory; +extern int verbose; +extern int dry_run; +extern int libs_only; +extern int enable_cxx_optimizations; +extern int exec_shield; +extern int undo; +extern int verify; +extern int print_cache; +enum verify_method_t { VERIFY_CONTENT, VERIFY_MD5, VERIFY_SHA }; +extern enum verify_method_t verify_method; +extern int quick; +extern long long seed; +extern GElf_Addr mmap_reg_start, mmap_reg_end, layout_page_size; +extern char *ld_preload; + +extern const char *sysroot; + +extern int allow_bad_textrel; + +int wrap_readlink (const char *path, char *buf, int len); +int wrap_lstat64 (const char *file, struct stat64 *buf); +int wrap_stat64 (const char *file, struct stat64 *buf); +int wrap_open (const char *file, int mode, ...); +int wrap_access (const char *file, int mode); +int wrap_rename (const char *old, const char *new); +int wrap_link (const char *old, const char *new); +int wrap_nftw64 (const char *dir, __nftw64_func_t func, + int descriptors, int flag); +int wrap_utime (const char *file, struct utimbuf *file_times); +int wrap_mkstemp (char *filename); +int wrap_unlink (const char *filename); +ssize_t wrap_listxattr (const char *path, char *list, size_t size); +ssize_t wrap_getxattr (const char *path, const char *name, void *value, + size_t size); +int wrap_setxattr (const char *path, const char *name, const void *value, + size_t size, int flags); +int wrap_glob (const char *pattern, int flags, + int (*errfunc) (const char *epath, int eerrno), + glob_t *pglob); + +char *sysroot_file_name (const char *name, int allow_last_link); + +extern const char *prelink_rtld; + +#endif /* PRELINK_H */ diff --git a/src/prelinktab.h b/src/prelinktab.h new file mode 100644 index 0000000..f41c79c --- /dev/null +++ b/src/prelinktab.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2001 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PRELINKTAB_H +#define PRELINKTAB_H + +#include "hashtab.h" +#include "prelink.h" + +extern htab_t prelink_devino_htab, prelink_filename_htab; +extern int prelink_entry_count; + +#endif /* PRELINKTAB_H */ diff --git a/src/reloc-info.c b/src/reloc-info.c new file mode 100644 index 0000000..4ce333c --- /dev/null +++ b/src/reloc-info.c @@ -0,0 +1,215 @@ +/* Copyright (C) 2008 CodeSourcery + Written by Maciej W. Rozycki <macro@codesourcery.com>, 2008. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> + +#include "prelink.h" +#include "reloc-info.h" + +/* A structure to lay out generic relocation information + * in a way specific to 64-bit MIPS. */ +union mips64_r_info +{ + /* Generic r_info interpretation. */ + Elf64_Xword r_info; + + /* 64-bit MIPS r_info interpretation. */ + struct + { + /* Symbol index for the first relocation. */ + Elf64_Word r_sym; + + /* Special symbol for the second relocation. */ + Elf64_Byte r_ssym; + + /* Third relocation. */ + Elf64_Byte r_type3; + + /* Second relocation. */ + Elf64_Byte r_type2; + + /* First relocation. */ + Elf64_Byte r_type; + } + s_info; +}; + +/* Extract the symbol index from 64-bit MIPS reloc info. */ + +static GElf_Xword +mips64_r_sym (DSO *dso, GElf_Xword r_info) +{ + union mips64_r_info mips64_r_info; + + buf_write_ne64 (dso, (unsigned char *) &mips64_r_info.r_info, r_info); + return buf_read_une32 (dso, (unsigned char *) &mips64_r_info.s_info.r_sym); +} + +/* Extract the special symbol index from 64-bit MIPS reloc info. */ + +static GElf_Xword +mips64_r_ssym (DSO *dso, GElf_Xword r_info) +{ + union mips64_r_info mips64_r_info; + + buf_write_ne64 (dso, (unsigned char *) &mips64_r_info.r_info, r_info); + return mips64_r_info.s_info.r_ssym; +} + +/* Extract the first reloc type from 64-bit MIPS reloc info. */ + +static GElf_Xword +mips64_r_type (DSO *dso, GElf_Xword r_info) +{ + union mips64_r_info mips64_r_info; + + buf_write_ne64 (dso, (unsigned char *) &mips64_r_info.r_info, r_info); + return mips64_r_info.s_info.r_type; +} + +/* Extract the second reloc type from 64-bit MIPS reloc info. */ + +static GElf_Xword +mips64_r_type2 (DSO *dso, GElf_Xword r_info) +{ + union mips64_r_info mips64_r_info; + + buf_write_ne64 (dso, (unsigned char *) &mips64_r_info.r_info, r_info); + return mips64_r_info.s_info.r_type2; +} + +/* Extract the third reloc type from 64-bit MIPS reloc info. */ + +static GElf_Xword +mips64_r_type3 (DSO *dso, GElf_Xword r_info) +{ + union mips64_r_info mips64_r_info; + + buf_write_ne64 (dso, (unsigned char *) &mips64_r_info.r_info, r_info); + return mips64_r_info.s_info.r_type3; +} + +/* Construct 64-bit MIPS reloc info from symbol indices and reloc types. */ + +static GElf_Xword +mips64_r_info_ext (DSO *dso, GElf_Word r_sym, Elf64_Byte r_ssym, + Elf64_Byte r_type, Elf64_Byte r_type2, Elf64_Byte r_type3) +{ + union mips64_r_info mips64_r_info; + + buf_write_ne32 (dso, (unsigned char *) &mips64_r_info.s_info.r_sym, r_sym); + mips64_r_info.s_info.r_ssym = r_ssym; + mips64_r_info.s_info.r_type = r_type; + mips64_r_info.s_info.r_type2 = r_type2; + mips64_r_info.s_info.r_type3 = r_type3; + return buf_read_une64 (dso, (unsigned char *) &mips64_r_info.r_info); +} + + +/* Extract the symbol index from reloc info. */ + +GElf_Xword +reloc_r_sym (DSO *dso, GElf_Xword r_info) +{ + if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64 + && dso->ehdr.e_machine == EM_MIPS) + return mips64_r_sym (dso, r_info); + else + return GELF_R_SYM (r_info); +} + +/* Extract the special symbol index from reloc info. */ + +GElf_Xword +reloc_r_ssym (DSO *dso, GElf_Xword r_info) +{ + if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64 + && dso->ehdr.e_machine == EM_MIPS) + return mips64_r_ssym (dso, r_info); + else + return RSS_UNDEF; +} + +/* Extract the first reloc type from reloc info. */ + +GElf_Xword +reloc_r_type (DSO *dso, GElf_Xword r_info) +{ + if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64 + && dso->ehdr.e_machine == EM_MIPS) + return mips64_r_type (dso, r_info); + else + return GELF_R_TYPE (r_info); +} + +/* Extract the second reloc type from reloc info. */ + +GElf_Xword +reloc_r_type2 (DSO *dso, GElf_Xword r_info) +{ + if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64 + && dso->ehdr.e_machine == EM_MIPS) + return mips64_r_type2 (dso, r_info); + else + return 0; +} + +/* Extract the third reloc type from reloc info. */ + +GElf_Xword +reloc_r_type3 (DSO *dso, GElf_Xword r_info) +{ + if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64 + && dso->ehdr.e_machine == EM_MIPS) + return mips64_r_type3 (dso, r_info); + else + return 0; +} + +/* Construct reloc info from symbol index and reloc type. */ + +GElf_Xword +reloc_r_info (DSO *dso, GElf_Word r_sym, GElf_Word r_type) +{ + if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64 + && dso->ehdr.e_machine == EM_MIPS) + switch (r_type) + { + case R_MIPS_REL32: + case R_MIPS_GLOB_DAT: + return mips64_r_info_ext (dso, r_sym, RSS_UNDEF, + r_type, R_MIPS_64, R_MIPS_NONE); + default: + return mips64_r_info_ext (dso, r_sym, RSS_UNDEF, + r_type, R_MIPS_NONE, R_MIPS_NONE); + } + else + return GELF_R_INFO (r_sym, r_type); +} + +/* Construct reloc info from symbol index and reloc type. */ + +GElf_Xword +reloc_r_info_ext (DSO *dso, GElf_Word r_sym, Elf64_Byte r_ssym, + Elf64_Byte r_type, Elf64_Byte r_type2, Elf64_Byte r_type3) +{ + if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64 + && dso->ehdr.e_machine == EM_MIPS) + return mips64_r_info_ext (dso, r_sym, r_ssym, r_type, r_type2, r_type3); + else + return GELF_R_INFO (r_sym, r_type); +} diff --git a/src/reloc-info.h b/src/reloc-info.h new file mode 100644 index 0000000..a8f8b7c --- /dev/null +++ b/src/reloc-info.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2008 CodeSourcery + Written by Maciej W. Rozycki <macro@codesourcery.com>, 2008. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef RELOC_INFO_H +#define RELOC_INFO_H + +#include "prelink.h" + +/* Reloc info primitives. */ +GElf_Xword reloc_r_sym (DSO *dso, GElf_Xword r_info); +GElf_Xword reloc_r_ssym (DSO *dso, GElf_Xword r_info); +GElf_Xword reloc_r_type (DSO *dso, GElf_Xword r_info); +GElf_Xword reloc_r_type2 (DSO *dso, GElf_Xword r_info); +GElf_Xword reloc_r_type3 (DSO *dso, GElf_Xword r_info); + +GElf_Xword reloc_r_info (DSO *dso, GElf_Word r_sym, GElf_Word r_type); +GElf_Xword reloc_r_info_ext (DSO *dso, GElf_Word r_sym, Elf64_Byte r_ssym, + Elf64_Byte r_type, Elf64_Byte r_type2, + Elf64_Byte r_type3); + +#endif /* RELOC_INFO_H */ diff --git a/src/reloc.c b/src/reloc.c new file mode 100644 index 0000000..deb0abe --- /dev/null +++ b/src/reloc.c @@ -0,0 +1,427 @@ +/* Copyright (C) 2001, 2002, 2003, 2005 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "prelink.h" +#include "reloc.h" + +int +find_reloc_sections (DSO *dso, struct reloc_info *rinfo) +{ + int first, last, rela, i, pltfirst; + GElf_Addr start, end, pltstart, pltend; + + memset (rinfo, 0, sizeof (*rinfo)); + + if (dynamic_info_is_set (dso, DT_REL) + && dynamic_info_is_set (dso, DT_RELA)) + { + error (0, 0, "%s: Cannot prelink object with both DT_REL and DT_RELA tags", + dso->filename); + return 1; + } + + rela = dynamic_info_is_set (dso, DT_RELA); + + if (rela) + { + start = dso->info[DT_RELA]; + end = dso->info[DT_RELA] + dso->info[DT_RELASZ]; + } + else + { + start = dso->info[DT_REL]; + end = dso->info[DT_REL] + dso->info[DT_RELSZ]; + } + rinfo->reldyn_rela = rela; + + if (dso->info[DT_JMPREL]) + { + pltstart = dso->info[DT_JMPREL]; + pltend = dso->info[DT_JMPREL] + dso->info[DT_PLTRELSZ]; + pltfirst = first = addr_to_sec (dso, pltstart); + last = addr_to_sec (dso, pltend - 1); + if (first == -1 + || last == -1 + || first != last + || dso->shdr[first].sh_addr != pltstart + || dso->shdr[first].sh_addr + dso->shdr[first].sh_size != pltend + || (dso->info[DT_PLTREL] != DT_REL + && dso->info[DT_PLTREL] != DT_RELA) + || dso->shdr[first].sh_type + != (dso->info[DT_PLTREL] == DT_RELA ? SHT_RELA : SHT_REL) + || strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[first].sh_name), + dso->info[DT_PLTREL] == DT_RELA + ? ".rela.plt" : ".rel.plt")) + { + error (0, 0, "%s: DT_JMPREL tags don't surround .rel%s.plt section", + dso->filename, dso->info[DT_PLTREL] == DT_RELA ? "a" : ""); + return 1; + } + rinfo->plt = first; + rinfo->plt_rela = (dso->shdr[first].sh_type == SHT_RELA); + if (dso->shdr[first].sh_type == SHT_REL + && dso->arch->need_rel_to_rela != NULL + && dso->arch->need_rel_to_rela (dso, first, first)) + rinfo->rel_to_rela_plt = 1; + } + else + { + pltstart = end; + pltend = end; + pltfirst = 0; + } + + if (start == 0 && end == 0) + { + /* No non-PLT relocations. */ + return 0; + } + + if (start == end) + { + first = 0; + last = 0; + } + else + { + first = addr_to_sec (dso, start); + last = addr_to_sec (dso, end - 1); + + if (first == -1 + || last == -1 + || dso->shdr[first].sh_addr != start + || dso->shdr[last].sh_addr + dso->shdr[last].sh_size != end) + { + error (0, 0, "%s: DT_REL%s tags don't surround whole relocation sections", + dso->filename, rela ? "A" : ""); + return 1; + } + + for (i = first; i <= last; i++) + if (dso->shdr[i].sh_type != (rela ? SHT_RELA : SHT_REL)) + { + error (0, 0, "%s: DT_REL%s tags don't surround relocation sections of expected type", + dso->filename, rela ? "A" : ""); + return 1; + } + } + + if (pltstart != end && pltend != end + /* There is a gap between .rel(a).dyn and .rel(a).plt sections. + The gap may be due to a linker optimization, in which case + the sections are still adjacent, with a zero-filled gap in-between. */ + && last + 1 != pltfirst) + { + error (0, 0, "%s: DT_JMPREL tag not adjacent to DT_REL%s relocations", + dso->filename, rela ? "A" : ""); + return 1; + } + + if (pltstart == start && pltend == end) + { + /* No non-PLT relocations. */ + rinfo->overlap = 1; + return 0; + } + + if (pltstart != end && pltend == end) + { + rinfo->overlap = 1; + --last; + } + + rinfo->first = first; + rinfo->last = last; + if (! rela + && first + && dso->arch->need_rel_to_rela != NULL + && dso->arch->need_rel_to_rela (dso, first, last)) + rinfo->rel_to_rela = 1; + return 0; +} + +int +convert_rel_to_rela (DSO *dso, int i) +{ + Elf_Data d1, d2, *d; + Elf_Scn *scn; + GElf_Rel rel; + GElf_Rela rela; + int ndx, maxndx; + + scn = dso->scn[i]; + d = elf_getdata (scn, NULL); + assert (elf_getdata (scn, d) == NULL); + assert (d->d_off == 0); + assert (d->d_size == dso->shdr[i].sh_size); + d1 = *d; + d2 = *d; + assert (sizeof (Elf32_Rel) * 3 == sizeof (Elf32_Rela) * 2); + assert (sizeof (Elf64_Rel) * 3 == sizeof (Elf64_Rela) * 2); + d1.d_size = d->d_size / 2 * 3; + d1.d_buf = malloc (d1.d_size); + d1.d_type = ELF_T_RELA; + if (d1.d_buf == NULL) + { + error (0, ENOMEM, "Cannot convert REL section to RELA"); + return 1; + } + + maxndx = d->d_size / dso->shdr[i].sh_entsize; + for (ndx = 0; ndx < maxndx; ndx++) + { + if (gelfx_getrel (dso->elf, d, ndx, &rel) == 0 + || dso->arch->rel_to_rela (dso, &rel, &rela)) + { + free (d1.d_buf); + return 1; + } + /* gelf_update_rel etc. should have Elf * argument, so that + we don't have to do this crap. */ + *d = d1; + if (gelfx_update_rela (dso->elf, d, ndx, &rela) == 0) + { + *d = d2; + free (d1.d_buf); + return 1; + } + *d = d2; + } + + free (d2.d_buf); + *d = d1; + dso->shdr[i].sh_entsize + = gelf_fsize (dso->elf, ELF_T_RELA, 1, EV_CURRENT); + dso->shdr[i].sh_type = SHT_RELA; + return 0; +} + +int +convert_rela_to_rel (DSO *dso, int i) +{ + Elf_Data d1, d2, *d; + Elf_Scn *scn; + GElf_Rel rel; + GElf_Rela rela; + int ndx, maxndx; + + scn = dso->scn[i]; + d = elf_getdata (scn, NULL); + assert (elf_getdata (scn, d) == NULL); + assert (d->d_off == 0); + assert (d->d_size == dso->shdr[i].sh_size); + d1 = *d; + d2 = *d; + assert (sizeof (Elf32_Rel) * 3 == sizeof (Elf32_Rela) * 2); + assert (sizeof (Elf64_Rel) * 3 == sizeof (Elf64_Rela) * 2); + d1.d_size = d->d_size / 3 * 2; + d1.d_buf = malloc (d1.d_size); + d1.d_type = ELF_T_REL; + if (d1.d_buf == NULL) + { + error (0, ENOMEM, "Cannot convert RELA section to REL"); + return 1; + } + + maxndx = d->d_size / dso->shdr[i].sh_entsize; + for (ndx = 0; ndx < maxndx; ndx++) + { + if (gelfx_getrela (dso->elf, d, ndx, &rela) == 0 + || dso->arch->rela_to_rel (dso, &rela, &rel)) + { + free (d1.d_buf); + return 1; + } + /* gelf_update_rela etc. should have Elf * argument, so that + we don't have to do this crap. */ + *d = d1; + if (gelfx_update_rel (dso->elf, d, ndx, &rel) == 0) + { + *d = d2; + free (d1.d_buf); + return 1; + } + *d = d2; + } + + free (d2.d_buf); + *d = d1; + dso->shdr[i].sh_entsize + = gelf_fsize (dso->elf, ELF_T_REL, 1, EV_CURRENT); + dso->shdr[i].sh_type = SHT_REL; + return 0; +} + +int +update_dynamic_rel (DSO *dso, struct reloc_info *rinfo) +{ + GElf_Dyn *info[DT_NUM], *info_DT_RELCOUNT, *info_DT_RELACOUNT; + GElf_Dyn *dynamic = NULL; + int rel = rinfo->first, plt = rinfo->plt, overlap = rinfo->overlap; + int dynsec, count = 0, loc; + Elf_Data *data; + Elf_Scn *scn = NULL; + + memset (&info, 0, sizeof (info)); + info_DT_RELCOUNT = NULL; + info_DT_RELACOUNT = NULL; + for (dynsec = 0; dynsec < dso->ehdr.e_shnum; dynsec++) + if (dso->shdr[dynsec].sh_type == SHT_DYNAMIC) + { + scn = dso->scn[dynsec]; + dynamic = alloca (dso->shdr[dynsec].sh_size + / dso->shdr[dynsec].sh_entsize * sizeof (GElf_Dyn)); + loc = 0; + data = NULL; + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + + maxndx = data->d_size / dso->shdr[dynsec].sh_entsize; + for (ndx = 0; ndx < maxndx; ++ndx, ++loc) + { + gelfx_getdyn (dso->elf, data, ndx, dynamic + loc); + if (dynamic[loc].d_tag == DT_NULL) + break; + else if ((GElf_Xword) dynamic[loc].d_tag < DT_NUM) + info[dynamic[loc].d_tag] = dynamic + loc; + else if (dynamic[loc].d_tag == DT_RELCOUNT) + info_DT_RELCOUNT = dynamic + loc; + else if (dynamic[loc].d_tag == DT_RELACOUNT) + info_DT_RELACOUNT = dynamic + loc; + } + if (ndx < maxndx) + break; + } + count = loc; + break; + } + + if (rel && plt && overlap) + { + if (dso->shdr[rel].sh_type != dso->shdr[plt].sh_type) + overlap = 0; + } + + if (rel || (plt && overlap)) + { + int dt_RELENT, dt_REL, dt_RELSZ; + + if (rinfo->reldyn_rela) + { + dt_RELENT = DT_RELAENT; + dt_REL = DT_RELA; + dt_RELSZ = DT_RELASZ; + } + else + { + dt_RELENT = DT_RELENT; + dt_REL = DT_REL; + dt_RELSZ = DT_RELSZ; + } + + assert (dso->info[dt_RELENT] + == gelf_fsize (dso->elf, rinfo->reldyn_rela + ? ELF_T_RELA : ELF_T_REL, 1, EV_CURRENT)); + assert (dso->info[dt_REL] != 0); + assert (dso->info[dt_RELSZ] != 0); + + info[dt_REL]->d_un.d_ptr = dso->shdr[rel ?: plt].sh_addr; + if (plt && overlap) + info[dt_RELSZ]->d_un.d_val = + dso->shdr[plt].sh_addr + dso->shdr[plt].sh_size; + else + info[dt_RELSZ]->d_un.d_val = + dso->shdr[rinfo->last].sh_addr + dso->shdr[rinfo->last].sh_size; + info[dt_RELSZ]->d_un.d_val -= info[dt_REL]->d_un.d_ptr; + + if (!rinfo->reldyn_rela && dso->shdr[rel ?: plt].sh_type == SHT_RELA) + { + info[DT_RELENT]->d_un.d_val = + gelf_fsize (dso->elf, ELF_T_RELA, 1, EV_CURRENT); + info[DT_REL]->d_tag = DT_RELA; + info[DT_RELSZ]->d_tag = DT_RELASZ; + info[DT_RELENT]->d_tag = DT_RELAENT; + if (info_DT_RELCOUNT) + info_DT_RELCOUNT->d_tag = DT_RELACOUNT; + } + else if (rinfo->reldyn_rela && dso->shdr[rel ?: plt].sh_type == SHT_REL) + { + info[DT_RELAENT]->d_un.d_val = + gelf_fsize (dso->elf, ELF_T_REL, 1, EV_CURRENT); + info[DT_RELA]->d_tag = DT_REL; + info[DT_RELASZ]->d_tag = DT_RELSZ; + info[DT_RELAENT]->d_tag = DT_RELENT; + if (info_DT_RELACOUNT) + info_DT_RELACOUNT->d_tag = DT_RELCOUNT; + } + } + + if (plt) + { + assert (dso->info[DT_JMPREL] != 0); + assert (dso->info[DT_PLTREL] == rinfo->plt_rela ? DT_RELA : DT_REL); + + info[DT_JMPREL]->d_un.d_ptr = dso->shdr[plt].sh_addr; + if (!rinfo->plt_rela && dso->shdr[plt].sh_type == SHT_RELA) + { + info[DT_PLTREL]->d_un.d_val = DT_RELA; + info[DT_PLTRELSZ]->d_un.d_val = dso->shdr[plt].sh_size; + } + else if (rinfo->plt_rela && dso->shdr[plt].sh_type == SHT_REL) + { + info[DT_PLTREL]->d_un.d_val = DT_REL; + info[DT_PLTRELSZ]->d_un.d_val = dso->shdr[plt].sh_size; + } + + if (!rel && !overlap) + { + int dt_REL = rinfo->reldyn_rela ? DT_RELA : DT_REL; + + if (info[dt_REL] && info[dt_REL]->d_un.d_ptr) + info[dt_REL]->d_un.d_ptr = info[DT_JMPREL]->d_un.d_ptr; + } + } + + loc = 0; + data = NULL; + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + + maxndx = data->d_size / dso->shdr[dynsec].sh_entsize; + for (ndx = 0; ndx < maxndx && loc < count; ++ndx, ++loc) + if ((GElf_Xword) dynamic[loc].d_tag < DT_NUM + || dynamic[loc].d_tag == DT_RELCOUNT + || dynamic[loc].d_tag == DT_RELACOUNT) + gelfx_update_dyn (dso->elf, data, ndx, dynamic + loc); + if (ndx < maxndx) + break; + } + + read_dynamic (dso); + return 0; +} diff --git a/src/reloc.h b/src/reloc.h new file mode 100644 index 0000000..a2ceff9 --- /dev/null +++ b/src/reloc.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2001, 2002 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef RELOC_H +#define RELOC_H + +#include "prelink.h" + +struct reloc_info +{ + int first; /* First dynamic SHT_REL* section. */ + int last; /* Last dynamic SHT_REL* section not counting .rel*.plt. */ + int plt; /* .rel*.plt section. */ + int overlap; /* 1 if DT_REL{,A}SZ range includes DT_PLTRELSZ range. */ + int reldyn_rela; /* first..last sections were originally RELA. */ + int plt_rela; /* plt section was originally RELA. */ + int rel_to_rela; /* first..last sections have to be converted REL->RELA. */ + int rel_to_rela_plt; /* plt section has to be converted REL->RELA. */ + int relcount; /* DT_RELCOUNT resp. DT_RELACOUNT. */ +}; + +int find_reloc_sections (DSO *dso, struct reloc_info *rinfo); +int convert_rel_to_rela (DSO *dso, int i); +int convert_rela_to_rel (DSO *dso, int i); +int update_dynamic_rel (DSO *dso, struct reloc_info *rinfo); +int undo_sections (DSO *dso, int undo, struct section_move *move, + struct reloc_info *rinfo, GElf_Ehdr *ehdr, + GElf_Phdr *phdr, GElf_Shdr *shdr); + +#endif /* RELOC_H */ diff --git a/src/rtld/COPYING b/src/rtld/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/src/rtld/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/src/rtld/COPYING.LIB b/src/rtld/COPYING.LIB new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/src/rtld/COPYING.LIB @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/src/rtld/ChangeLog b/src/rtld/ChangeLog new file mode 100644 index 0000000..ae208d7 --- /dev/null +++ b/src/rtld/ChangeLog @@ -0,0 +1,267 @@ +2015-10-21 Mark Hatle <mark.hatle@windriver.com> + * rtld/: Resync to glibc-2.22 + * rtld/*: Update copyright dates to match glibc-2.22 + * rtld/rtld.c: Update the elf_machine_type class entries + Add support for ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA via + new extern_protected_data function. + rename reloc_typeclass to elf_machine_type_class + add machine_no_rela, machine_no_rel funcs + Update debug msg + Fix missing dso_list->map = NULL + * rtld/dl-tls.c: (rtld_determine_tlsoffsets) add NIOS2 definition + * rtld/dl-lookup.c: Add EXTERN_PROTECTED_DATA support + * rtld/dl-lookupX.h: Add EXTERN_PROTECTED_DATA support + update debug msgs + * rtld/dl-load.c: (create_map_object_from_dso_ent) Add ld.so like debug + When an executable sets a load address use it + Update the load address calculation, prevents visual overlaps + * rtld/dl-version.c: update debug msgs + * rtld/rtld.h: define _dl_debug_printf to act like ld.so debug + define RTLD_DEBUG_PID to set the debug prefix + + * glibc changes directly affecting the implementation: + + 2013-12-04 Ulrich Weigand <Ulrich.Weigand@de.ibm.com> + Alan Modra <amodra@gmail.com> + * libc/sysdeps/powerpc/powerpc64/dl-machine.h + (elf_machine_type_class): Use SHN_UNDEF PLT handling for ELFv2 ABI. + + 2015-01-18 Chung-Lin Tang <cltang@codesourcery.com> + Sandra Loosemore <sandra@codesourcery.com> + Andrew Jenner <andrew@codesourcery.com> + Joseph Myers <joseph@codesourcery.com> + Nathan Sidwell <nathan@codesourcery.com> + * sysdeps/nios2/dl-machine.h: New file. + + 2015-03-31 H.J. Lu <hongjiu.lu@intel.com> + * elf/dl-lookup.c (do_lookup_x): When UNDEF_MAP is NULL, which + indicates it is called from do_lookup_x on relocation against + protected data, skip the data definion in the executable from + copy reloc. + (_dl_lookup_symbol_x): Pass ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA, + instead of ELF_RTYPE_CLASS_PLT, to do_lookup_x for + EXTERN_PROTECTED_DATA relocation against STT_OBJECT symbol. + * sysdeps/i386/dl-machine.h (elf_machine_type_class): Set class + to ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA for R_386_GLOB_DAT. + * sysdeps/x86_64/dl-machine.h (elf_machine_type_class): Set class + to ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA for R_X86_64_GLOB_DAT. + + 2015-07-24 Szabolcs Nagy <szabolcs.nagy@arm.com> + * sysdeps/aarch64/dl-machine.h (elf_machine_type_class): Handle + ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA. + * sysdeps/arm/dl-machine.h (elf_machine_type_class): Handle + ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA. + +2015-09-11 Vaneet Narang <v.narang@samsung.com> + * rtld/rtld.c: Add ability to specify preloaded libraries + +2015-09-09 Maninder Singh <maninder1.s@samsung.com> + Mark Hatle <mark.hatle@windriver.com> + * rtld/rtld.c: dso null pointer check fix + +2015-04-06 Mark Hatle <mark.hatle@windriver.com> + Maninder Singh <maninder1.s@samsung.com> + * rtld/dl-version.c: Add debug for mising ld-linux or libc.so + +2014-12-10 Mark Hatle <mark.hatle@windriver.com> + * rtld/rtld.c: Sync aarch64 elf_machine_type_class (dl-machine.h) + (do_relocs): fix comparison pltrel_end >= rel_end + * rtld/dl-tls.c: Add basic aarch64 support + +2014-12-10 Mark Hatle <mark.hatle@windriver.com> + * rtld/: Resync to glibc-2.20 + Replace referenced to GLRO_dl_debug_mask to + GLRO(dl_debug_mask) + + * rtld/rtld.h: Sync DL_DEBUG_* defines from ldsodefs.h + add DSO_FILENAME and RTLD_PROGNAME definitions + Move to __glibc_unlikely/likely instead of __builtin_expect + rename link_map and update unique_sym_table to match glibc + + * rtld/dl-hash.h: + Apply glibc changes: + 2011-12-03 Ulrich Drepper <drepper@gmail.com> + Fix more warnings + + 2011-12-04 Ulrich Drepper <drepper@gmail.com> + Fix attreibute for _dl_elf_hash + + 2011-12-04 Ulrich Drepper <drepper@gmail.com> + Small optimization of generic ELF hash function + + 2011-12-10 Ulrich Drepper <drepper@gmail.com> + Optimize generic ELF hash function a bit more + + 2012-02-09 Paul Eggert <eggert@cs.ucla.edu> + Replace FSF snail mail address with URLs. + + 2013-01-02 Joseph Myers <joseph@codesourcery.com> + Update copyright notices with scripts/update-copyrights. + + 2014-01-01 Allan McRae <allan@archlinux.org> + Update copyright notices with scripts/update-copyrights + + * rtld/dl-load.c: split (_dl_new_object) move to dl-object + Remove VERSYMIDX, already defined in rtld.h + + Apply glibc changes: + 2012-02-09 Paul Eggert <eggert@cs.ucla.edu> + Replace FSF snail mail address with URLs. + + 2012-04-04 Siddhesh Poyarekar <siddhesh@redhat.com> + (Updated copyright date) + + 2013-01-02 Joseph Myers <joseph@codesourcery.com> + Update copyright notices with scripts/update-copyrights. + + 2014-01-01 Allan McRae <allan@archlinux.org> + Update copyright notices with scripts/update-copyrights + + 2014-02-10 Ond<C5><99>ej B<C3><AD>lka <neleai@seznam.cz> + Use glibc_likely instead __builtin_expect. + + * rtld/dl-object.c: + Apply glibc changes: + 2013-11-11 Jan Kratochvil <jan.kratochvil@redhat.com> + [BZ #387] + * elf/dl-object.c (_dl_new_object): Initialize L_NAME from NEWNAME if + it is empty. + + * rtld/dl-lookup.c, rtld/dl-lookupX.h: + Apply glibc changes: + 2012-02-09 Paul Eggert <eggert@cs.ucla.edu> + Replace FSF snail mail address with URLs. + + 2012-04-05 David S. Miller <davem@davemloft.net> + * elf/dl-lookup (_dl_lookup_symbol_x): If DL_DEBUG_UNUSED, ignore + undefined symbol errors. + + 2012-08-14 Roland McGrath <roland@hack.frob.com> + (Updated copyright date) + + 2013-01-02 Joseph Myers <joseph@codesourcery.com> + Update copyright notices with scripts/update-copyrights. + + 2013-05-29 Siddhesh Poyarekar <siddhesh@redhat.com> + Avoid crashing in LD_DEBUG when program name is unavailable + + 2013-11-13 Marcus Shawcroft <marcus.shawcroft@linaro.org> + Avoid passing NULL to DSO_FILENAME. + + 2014-01-01 Allan McRae <allan@archlinux.org> + Update copyright notices with scripts/update-copyrights + + 2014-02-10 Ond<C5><99>ej B<C3><AD>lka <neleai@seznam.cz> + Use glibc_likely instead __builtin_expect. + + 2014-02-11 Joseph Myers <joseph@codesourcery.com> + Merge MIPS dl-lookup.c into generic file. + * elf/dl-lookup.c (ELF_MACHINE_SYM_NO_MATCH): Define if not + already defined. + (do_lookup_x): Use ELF_MACHINE_SYM_NO_MATCH. + * sysdeps/mips/dl-lookup.c: Remove. + * sysdeps/mips/dl-machine.h (ELF_MACHINE_SYM_NO_MATCH): New macro. + + 2014-02-28 Carlos O'Donell <carlos@redhat.com> + Promote do_lookup_x:check_match to a full function. + + 2014-04-02 Will Newton <will.newton@linaro.org> + elf/dl-lookup.c: Remove obsolete comment about nested function + * elf/dl-lookup.c (do_lookup_x): Remove comment + referring to nested function and move variable + declarations down to before first use. + + 2014-04-04 Will Newton <will.newton@linaro.org> + elf/dl-lookup.c: Remove unnecessary static variable + * elf/dl-lookup.c (undefined_msg): Remove variable. + (_dl_lookup_symbol_x): Replace undefined_msg with string + literal. + + 2014-04-11 Will Newton <will.newton@linaro.org> + elf/dl-lookup.c: Use __glibc_likely and __glibc_unlikely + + * rtld/dl-misc.c: + Apply glibc changes: + 2012-02-09 Paul Eggert <eggert@cs.ucla.edu> + Replace FSF snail mail address with URLs. + + 2013-01-02 Joseph Myers <joseph@codesourcery.com> + Update copyright notices with scripts/update-copyrights. + + 2014-01-01 Allan McRae <allan@archlinux.org> + Update copyright notices with scripts/update-copyrights + + * rtld/dl-tls.c: + Sync spacing with glibc for easier diffs (content remained the same) + + Apply glibc changes: + 2012-02-09 Paul Eggert <eggert@cs.ucla.edu> + Replace FSF snail mail address with URLs. + + 2014-01-01 Allan McRae <allan@archlinux.org> + Update copyright notices with scripts/update-copyrights + + * rtld/dl-version: + Apply glibc changes: + 2012-02-09 Paul Eggert <eggert@cs.ucla.edu> + Replace FSF snail mail address with URLs. + + 2013-01-02 Joseph Myers <joseph@codesourcery.com> + Update copyright notices with scripts/update-copyrights. + + 2013-05-29 Siddhesh Poyarekar <siddhesh@redhat.com> + Avoid crashing in LD_DEBUG when program name is unavailable + + 2014-01-01 Allan McRae <allan@archlinux.org> + Update copyright notices with scripts/update-copyrights + + 2014-02-10 Ond<C5><99>ej B<C3><AD>lka <neleai@seznam.cz> + Use glibc_likely instead __builtin_expect. + +2014-12-10 Mark Hatle <mark.hatle@windriver.com> + * rtld/COPYING, rtld/COPYING.LIB, + rtld/ChangeLog, rtld/README-rtld: Add local history + information to setup for a resync to glibc-2.20 + +2012-09-12 Joseph Myers <joseph@codesourcery.com> + * rtld/dl-lookup.c: Fix variable copy reloc when host/target + byte size is different + +2012-04-10 Maxim Kuvyrkov <maxim@codesourcery.com> + * rtld/rtld.c (find_lib_by_soname): Follow ld.so's behavior of + pulling its name from PT_INTERP. + +2012-01-26 Mark Hatle <mark.hatle@windriver.com> + * elf.h, rtld/dl-lookupX.h, rtld/rtld.c: Sync to eglibc 2.15 + +2011-12-08 Mark Hatle <mark.hatle@windriver.com> + * rtld/rtld.c: Add support for $ORIGIN, $PLATFORM and $LIB. + Note: $PLATFORM = "" + +2011-12-08 Mark Hatle <mark.hatle@windriver.com> + * rtld/rtld.c: Fix an issue where missing objects would trigger + an assert in dl-version.c + * rtld/rtld.h: Add _dl_new_object prototype + +2011-09-13 Mark Hatle <mark.hatle@windriver.com> + * Fix printf problem causing prelink-rtld issues on x86 (32-bit) + and arm + +2011-08-26 Mark Hatle <mark.hatle@windriver.com> + * Add a special check for invalid GNU_HASH entries + +2011-08-26 Mark Hatle <mark.hatle@windriver.com> + * Sync to eglibc 2.13 ld.so code + * sync elf_machine_type_class macros for supports archs + +2011-08-26 Mark Hatle <mark.hatle@windriver.com> + * Sync to eglibc 2.13 ld.so code + * mips specific items from ports + +2011-08-26 Mark Hatle <mark.hatle@windriver.com> + * Rename ld-libs.c to rtld.c + * Sync to eglibc 2.13 ld.so code + +2011-08-18 Mark Hatle <mark.hatle@windriver.com> + * Move prelink-rtld specific components to rtld + diff --git a/src/rtld/Makefile.am b/src/rtld/Makefile.am new file mode 100644 index 0000000..b02c745 --- /dev/null +++ b/src/rtld/Makefile.am @@ -0,0 +1,32 @@ +## Process this file with automake to create Makefile.in + +AUTOMAKE_OPTIONS = 1.4 gnu + +PKGVERSION = "\"@PKGVERSION@\"" +REPORT_BUGS_TO = "\"@REPORT_BUGS_TO@\"" + +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -Wall -Wno-pointer-sign +AM_CFLAGS = -Wall -Wno-pointer-sign +AM_CPPFLAGS = -DSBINDIR='"@sbindir@"' -DBINDIR='"@bindir@"' \ + -DEXECSTACK_PROG="\"`echo execstack | sed '$(transform)'`\"" \ + -DPRELINK_PROG="\"`echo prelink | sed '$(transform)'`\"" \ + -DPRELINK_RTLD_PROG="\"`echo prelink-rtld | \ + sed '$(transform)'`\"" \ + -DEXEEXT='"$(EXEEXT)"' \ + -DPKGVERSION=$(PKGVERSION) \ + -DREPORT_BUGS_TO=$(REPORT_BUGS_TO) +INCLUDES = -I$(top_srcdir)/src @GELFINCLUDE@ + +sbin_PROGRAMS = prelink-rtld + +prelink_rtld_SOURCES = $(top_srcdir)/src/data.c $(top_srcdir)/src/dso.c \ + $(top_srcdir)/src/canonicalize.c $(top_srcdir)/src/wrap-file.c \ + $(top_srcdir)/src/reloc-info.c $(top_srcdir)/src/reloc-info.h \ + rtld.c ld-libs.h \ + dl-hash.h dl-object.c dl-load.c \ + dl-tls.c dl-version.c dl-misc.c \ + dl-lookup.c dl-lookupX.h + +prelink_rtld_LDADD = @LIBGELF@ -liberty +prelink_rtld_CFLAGS = -DDSO_READONLY +prelink_rtld_LDFLAGS = diff --git a/src/rtld/README-rtld b/src/rtld/README-rtld new file mode 100644 index 0000000..fa737dc --- /dev/null +++ b/src/rtld/README-rtld @@ -0,0 +1,18 @@ +The rtld emulation is based on the system libc ld.so code. + +The original version of this code was written by Daniel Jacobowitz in +2003. It needed little modification/updating until recently (2011) when +new constructs, such as STB_GNU_UNIQUE, were introduced into the dynamic +linking. + +The 2011 work was done by Mark Hatle and based on eglibc-2.13. I +attempted to document all of the code that had origins in eglibc and where +the code originated from. + +As eglibc continues to advance, similar resyncs will be needed over time. +Hopefully not such a dramatic resync will be required in the future. + +Mark Hatle <mark.hatle@windriver.com>, +August 2011 + +See the ChangeLog for additional changes. diff --git a/src/rtld/dl-hash.h b/src/rtld/dl-hash.h new file mode 100644 index 0000000..2b1f0b7 --- /dev/null +++ b/src/rtld/dl-hash.h @@ -0,0 +1,77 @@ +/* glibc-2.22: sysdeps/generic/dl-hash.h */ + +/* Compute hash value for given string according to ELF standard. + Copyright (C) 1995-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _DL_HASH_H +#define _DL_HASH_H 1 + +#define _dl_elf_hash rtld_elf_hash + +/* This is the hashing function specified by the ELF ABI. In the + first five operations no overflow is possible so we optimized it a + bit. */ +static unsigned int +_dl_elf_hash (const char *name_arg) +{ + const unsigned char *name = (const unsigned char *) name_arg; + unsigned long int hash = *name; + if (hash != 0 && name[1] != '\0') + { + hash = (hash << 4) + name[1]; + if (name[2] != '\0') + { + hash = (hash << 4) + name[2]; + if (name[3] != '\0') + { + hash = (hash << 4) + name[3]; + if (name[4] != '\0') + { + hash = (hash << 4) + name[4]; + name += 5; + while (*name != '\0') + { + unsigned long int hi; + hash = (hash << 4) + *name++; + hi = hash & 0xf0000000; + + /* The algorithm specified in the ELF ABI is as + follows: + + if (hi != 0) + hash ^= hi >> 24; + + hash &= ~hi; + + But the following is equivalent and a lot + faster, especially on modern processors. */ + + hash ^= hi >> 24; + } + + /* Second part of the modified formula. This + operation can be lifted outside the loop. */ + hash &= 0x0fffffff; + } + } + } + } + return hash; +} + +#endif /* dl-hash.h */ diff --git a/src/rtld/dl-load.c b/src/rtld/dl-load.c new file mode 100644 index 0000000..5dc913e --- /dev/null +++ b/src/rtld/dl-load.c @@ -0,0 +1,279 @@ +/* Restructure code containing the original statement: + Copyright (C) 2003 MontaVista Software, Inc. + Written by Daniel Jacobowitz <drow@mvista.com>, 2003 + + Restructed and synced to latest eglibc 2.13 by + Mark Hatle <mark.hatle@windriver.com>, 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* glibc-2.20: elf/dl-load.c */ + +/* Map in a shared object's segments from the file. + Copyright (C) 1995-2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <error.h> +#include <errno.h> +#include <string.h> +#include "rtld.h" + +/* Add `name' to the list of names for a particular shared object. + `name' is expected to have been allocated with malloc and will + be freed if the shared object already has this name. + Returns false if the object already had this name. */ +static void +add_name_to_object (struct link_map *l, const char *name) +{ + struct libname_list *lnp, *lastp; + struct libname_list *newname; + size_t name_len; + + lastp = NULL; + for (lnp = l->l_libname; lnp != NULL; lastp = lnp, lnp = lnp->next) + if (strcmp (name, lnp->name) == 0) + return; + + name_len = strlen (name) + 1; + newname = (struct libname_list *) malloc (sizeof *newname + name_len); + if (newname == NULL) + { + /* No more memory. */ + _dl_signal_error (ENOMEM, name, NULL, ("cannot allocate name record")); + return; + } + /* The object should have a libname set from _dl_new_object. */ + assert (lastp != NULL); + + newname->name = memcpy (newname + 1, name, name_len); + newname->next = NULL; + lastp->next = newname; +} + +const char *rtld_progname; + +static Elf64_Addr load_addr = 0xdead0000; +static Elf64_Addr dynamic_addr = 0xfeed0000; + +/* mimic behavior of _dl_map_object_from_fd(...) + Note: this is not a copy of the function! */ +void +create_map_object_from_dso_ent (struct dso_list *cur_dso_ent) +{ + struct link_map *l = NULL; + DSO *dso = cur_dso_ent->dso; + + int i; + Elf_Data *data; + + + const char * realname, * name, *soname; + int l_type; + + soname = dso->soname; + realname = dso->filename; + name = dso->filename; + + l_type = (dso->ehdr.e_type == ET_EXEC ? lt_executable : lt_library); + + + /* Print debug message. */ + if ((l_type == lt_library && !is_ldso_soname(soname)) && + __glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf("file=%s [0]; generating link map\n", soname); + + + l = _dl_new_object (realname, name, l_type); + if (l == NULL) + { + _dl_signal_error(errno, name, NULL, "cannot create shared object descriptor"); + } + + if (soname) + add_name_to_object(l, soname); + + if (name) + add_name_to_object(l, name); + + l->filename = dso->filename; + + /* Set the elfclass */ + l->elfclass = gelf_getclass (dso->elf); + + /*** Setup the l_info as if this had been loaded into memory ***/ + + /* FIXME: gelfify, endianness issues */ + /* and leaks? */ + i = addr_to_sec (dso, dso->info[DT_SYMTAB]); + if (i != -1) + { + data = elf_getdata (dso->scn[i], NULL); + l->l_info[DT_SYMTAB] = data->d_buf; + } + + i = addr_to_sec (dso, dso->info[DT_STRTAB]); + if (i != -1) + { + data = elf_getdata (dso->scn[i], NULL); + l->l_info[DT_STRTAB] = data->d_buf; + } + + if (dynamic_info_is_set (dso, DT_GNU_HASH_BIT)) + { + i = addr_to_sec (dso, dso->info_DT_GNU_HASH); + if (i != -1) + { + data = elf_getdata (dso->scn[i], NULL); + +#if 0 + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + printf("l_info DT_GNU_HASH: offset %d -- addr %p (0x%lx) - type %d\n", + (DT_ADDRTAGIDX(DT_GNU_HASH) + DT_NUM + + DT_THISPROCNUM + DT_VERSIONTAGNUM + + DT_EXTRANUM + DT_VALNUM), + data->d_buf, (unsigned long) data->d_size); +#endif + + l->l_info[DT_ADDRTAGIDX(DT_GNU_HASH) + DT_NUM + + DT_THISPROCNUM + DT_VERSIONTAGNUM + + DT_EXTRANUM + DT_VALNUM] = data->d_buf; + + /* PPC64 workaround */ + l->l_buckets_start = data->d_buf; + l->l_buckets_end = (char *)data->d_buf + data->d_size; + /* end workaround */ + } + } + + i = addr_to_sec (dso, dso->info[DT_HASH]); + if (i != -1) + { + data = elf_getdata (dso->scn[i], NULL); + l->l_info[DT_HASH] = data->d_buf; + } + + if (dynamic_info_is_set (dso, DT_VERNEED_BIT)) + { + i = addr_to_sec (dso, dso->info_DT_VERNEED); + if (i != -1) + { + data = elf_getdata (dso->scn[i], NULL); + l->l_info[VERSYMIDX (DT_VERNEED)] = data->d_buf; + } + } + + if (dynamic_info_is_set (dso, DT_VERDEF_BIT)) + { + i = addr_to_sec (dso, dso->info_DT_VERDEF); + if (i != -1) + { + data = elf_getdata (dso->scn[i], NULL); + l->l_info[VERSYMIDX (DT_VERDEF)] = data->d_buf; + } + } + + if (dynamic_info_is_set (dso, DT_VERSYM_BIT)) + { + i = addr_to_sec (dso, dso->info_DT_VERSYM); + if (i != -1) + { + data = elf_getdata (dso->scn[i], NULL); + l->l_info[VERSYMIDX (DT_VERSYM)] = data->d_buf; + } + } + + if (dso->base) { + l->l_map_start = dso->base; + + /* We need to ensure that we don't have two DSOs loading at the same place! */ + struct dso_list * dso_list_ptr; + for (dso_list_ptr = cur_dso_ent->prev; dso_list_ptr; dso_list_ptr = dso_list_ptr->prev) + { + /* This looks for fairly obvious overlaps... */ + if ((dso_list_ptr->dso->base <= dso->base && dso->base <= dso_list_ptr->dso->end) || \ + (dso->base <= dso_list_ptr->dso->base && dso_list_ptr->dso->base <= dso->end)) + { + l->l_map_start = (Elf64_Addr)NULL; + break; + } + } + } + + if (l->l_map_start == (Elf64_Addr)NULL) + { + l->l_map_start = load_addr; + load_addr += ( ((dso->end - dso->base) + (0x1000 - 1)) & (~(0x1000-1)) ); + } + + l->sym_base = dso->info[DT_SYMTAB] - dso->base; + + if ((l_type == lt_library && !is_ldso_soname(soname)) + && (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))) { + _dl_debug_printf ("\ + dynamic: 0x%0*lx base: 0x%0*lx size: 0x%0*Zx\n", + (int) sizeof (void *) * (gelf_getclass (dso->elf) == ELFCLASS64 ? 2 : 1), + (unsigned long int) dynamic_addr, + (int) sizeof (void *) * (gelf_getclass (dso->elf) == ELFCLASS64 ? 2 : 1), + (unsigned long int) l->l_map_start, + (int) sizeof (void *) * (gelf_getclass (dso->elf) == ELFCLASS64 ? 2 : 1), + (dso->end - dso->base)); + _dl_debug_printf ("\ + entry: 0x%0*lx phdr: 0x%0*lx phnum: %*u\n", + (int) sizeof (void *) * (gelf_getclass (dso->elf) == ELFCLASS64 ? 2 : 1), + (unsigned long int) l->l_map_start + dso->ehdr.e_entry, + (int) sizeof (void *) * (gelf_getclass (dso->elf) == ELFCLASS64 ? 2 : 1), + (unsigned long int) l->l_map_start + dso->ehdr.e_ehsize, + (int) sizeof (void *) * (gelf_getclass (dso->elf) == ELFCLASS64 ? 2 : 1), + dso->ehdr.e_phnum); + _dl_debug_printf ("\n"); + + /* Only used for debugging output */ + dynamic_addr += ( ((dso->end - dso->base) + (0x1000 - 1)) & (~(0x1000-1)) ); + } + + /* Set up the symbol hash table. */ + _dl_setup_hash (l); + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_TLS) + { + l->l_tls_blocksize = dso->phdr[i].p_memsz; + l->l_tls_align = dso->phdr[i].p_align; + if (l->l_tls_align == 0) + l->l_tls_firstbyte_offset = 0; + else + l->l_tls_firstbyte_offset = dso->phdr[i].p_vaddr & (l->l_tls_align - 1); + break; + } + + l->machine = dso->ehdr.e_machine; + + cur_dso_ent->map = l; +} diff --git a/src/rtld/dl-lookup.c b/src/rtld/dl-lookup.c new file mode 100644 index 0000000..6688966 --- /dev/null +++ b/src/rtld/dl-lookup.c @@ -0,0 +1,131 @@ +/* glibc-2.22: elf/dl-lookup.c */ + +/* Look up a symbol in the loaded objects. + Copyright (C) 1995-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is based on the original eglibc-2.13 libc/elf/dl-lookup.c + code. + + It has been split into two pieces dl-lookup.c and dl-lookupX.c, + the purpose of the split is to enable both 32-bit and 64-bit ELF + processing in the same application. This file contains the ELF + size neutral routines. + */ + +#include <config.h> +#include <alloca.h> +#include <libintl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <dl-hash.h> + +#include <assert.h> + +#include <errno.h> + +#include <elf.h> + +#include <inttypes.h> + +#include "prelinktab.h" +#include "reloc.h" + +#include "rtld.h" + +/* Return nonzero if check_match should consider SYM to fail to match a + symbol reference for some machine-specific reason. */ +#ifndef ELF_MACHINE_SYM_NO_MATCH +/* glibc-2.20: sysdeps/mips/dl-machine.h */ +/* The semantics of zero/non-zero values of undefined symbols differs + depending on whether the non-PIC ABI is in use. Under the non-PIC + ABI, a non-zero value indicates that there is an address reference + to the symbol and thus it must always be resolved (except when + resolving a jump slot relocation) to the PLT entry whose address is + provided as the symbol's value; a zero value indicates that this + canonical-address behaviour is not required. Yet under the classic + MIPS psABI, a zero value indicates that there is an address + reference to the function and the dynamic linker must resolve the + symbol immediately upon loading. To avoid conflict, symbols for + which the dynamic linker must assume the non-PIC ABI semantics are + marked with the STO_MIPS_PLT flag. */ +#define ELF_MACHINE_SYM_NO_MATCH(sym) \ + (map->machine == EM_MIPS && \ + ((sym)->st_shndx == SHN_UNDEF && !((sym)->st_other & STO_MIPS_PLT)) \ + ) +#endif + +struct unique_sym_table * _ns_unique_sym_table = NULL; + +/* This file is from eglibc 2.13, libc/elf/dl-lookup.c + It has been split into two pieces dl-lookup.c and dl-lookupX.c, + the purpose of the split is to enable both 32-bit and 64-bit ELF + processing in the same application. This file contains the common + routines ... and is the entry to the overall set of files. + */ + +#define make_string(string, rest...) \ + ({ \ + const char *all[] = { string, ## rest }; \ + size_t len, cnt; \ + char *result, *cp; \ + \ + len = 1; \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + len += strlen (all[cnt]); \ + \ + cp = result = alloca (len); \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + cp = __stpcpy (cp, all[cnt]); \ + \ + result; \ + }) + +static uint_fast32_t +dl_new_hash (const char *s) +{ + uint_fast32_t h = 5381; + unsigned char c = *s; + for (c = *s; c != '\0'; c = *++s) + h = h * 33 + c; + return h & 0xffffffff; +} + +#undef RTLD_ELF_SIZE +#define RTLD_ELF_SIZE 32 +#include "dl-lookupX.h" + +#undef RTLD_ELF_SIZE +#define RTLD_ELF_SIZE 64 +#include "dl-lookupX.h" + +#undef RTLD_ELF_SIZE + +void +_dl_setup_hash (struct link_map *map) +{ + if (map) + { + if (map->elfclass == ELFCLASS32) + rtld_setup_hash32(map); + else if (map->elfclass == ELFCLASS64) + rtld_setup_hash64(map); + else + _dl_signal_error(EINVAL, map->l_name, NULL, "elfclass is not defined 32-bit or 64-bit!"); + } +} diff --git a/src/rtld/dl-lookupX.h b/src/rtld/dl-lookupX.h new file mode 100644 index 0000000..dc6d4bf --- /dev/null +++ b/src/rtld/dl-lookupX.h @@ -0,0 +1,896 @@ +/* glibc-2.22: elf/dl-lookup.c */ + +/* Look up a symbol in the loaded objects. + Copyright (C) 1995-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is from eglibc 2.15, libc/elf/dl-lookup.c + It has been split into two pieces dl-lookup.c and dl-lookupX.c, + the purpose of the split is to enable both 32-bit and 64-bit ELF + processing in the same application. This file contains the ELF + size specific routines. It must be included from dl-lookup.c and + not used directly! + */ + +#if RTLD_ELF_SIZE == 32 + #define enter_unique_sym enter_unique_sym_val32 + #define sym_val sym_val32 + #define check_match check_match32 + #define do_lookup_unique do_lookup_unique32 + #define do_lookup_x do_lookup_x32 + #undef _dl_setup_hash + #define _dl_setup_hash rtld_setup_hash32 + #define _dl_debug_bindings rtld_debug_bindings32 + #define _dl_lookup_symbol_x rtld_lookup_symbol_x32 + #define rtld_size_t uint32_t + #define rtld_size_fmtx PRIx32 + +#elif RTLD_ELF_SIZE == 64 + #define enter_unique_sym enter_unique_sym_val64 + #define sym_val sym_val64 + #define check_match check_match64 + #define do_lookup_unique do_lookup_unique64 + #define do_lookup_x do_lookup_x64 + #undef _dl_setup_hash + #define _dl_setup_hash rtld_setup_hash64 + #define _dl_debug_bindings rtld_debug_bindings64 + #define _dl_lookup_symbol_x rtld_lookup_symbol_x64 + #define rtld_size_t uint64_t + #define rtld_size_fmtx PRIx64 + +#else + #error "You must declare RTLD_ELF_SIZE to be either 32 or 64" +#endif + +#define __ELF_NATIVE_CLASS RTLD_ELF_SIZE + +/* From eglibc 2.15 - elf/link.h */ + +/* We use this macro to refer to ELF types independent of the native wordsize. + `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */ +#define ELFW(type) _ElfW (ELF, __ELF_NATIVE_CLASS, type) +#define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type) +#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t) +#define _ElfW_1(e,w,t) e##w##t + +struct sym_val + { + const ElfW(Sym) *s; + struct link_map *m; + }; + +/* Utility function for do_lookup_x. The caller is called with undef_name, + ref, version, flags and type_class, and those are passed as the first + five arguments. The caller then computes sym, symidx, strtab, and map + and passes them as the next four arguments. Lastly the caller passes in + versioned_sym and num_versions which are modified by check_match during + the checking process. */ +static const ElfW(Sym) * +check_match (const char *const undef_name, + const ElfW(Sym) *const ref, + const struct r_found_version *const version, + const int flags, + const int type_class, + const ElfW(Sym) *const sym, + const Elf_Symndx symidx, + const char *const strtab, + const struct link_map *const map, + const ElfW(Sym) **const versioned_sym, + int *const num_versions) +{ + unsigned int stt = ELFW(ST_TYPE) (sym->st_info); + assert (ELF_RTYPE_CLASS_PLT == 1); + if (__glibc_unlikely ((sym->st_value == 0 /* No value. */ + && stt != STT_TLS) + || ELF_MACHINE_SYM_NO_MATCH (sym) + || (type_class & (sym->st_shndx == SHN_UNDEF)))) + return NULL; + + /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC, + STT_COMMON, STT_TLS, and STT_GNU_IFUNC since these are no + code/data definitions. */ +#define ALLOWED_STT \ + ((1 << STT_NOTYPE) | (1 << STT_OBJECT) | (1 << STT_FUNC) \ + | (1 << STT_COMMON) | (1 << STT_TLS) | (1 << STT_GNU_IFUNC)) + if (__glibc_unlikely (((1 << stt) & ALLOWED_STT) == 0)) + return NULL; + + if (sym != ref && strcmp (strtab + sym->st_name, undef_name)) + /* Not the symbol we are looking for. */ + return NULL; + + const ElfW(Half) *verstab = map->l_versyms; + if (version != NULL) + { + if (__glibc_unlikely (verstab == NULL)) + { + /* We need a versioned symbol but haven't found any. If + this is the object which is referenced in the verneed + entry it is a bug in the library since a symbol must + not simply disappear. + + It would also be a bug in the object since it means that + the list of required versions is incomplete and so the + tests in dl-version.c haven't found a problem.*/ + assert (version->filename == NULL + || ! _dl_name_match_p (version->filename, map)); + + /* Otherwise we accept the symbol. */ + } + else + { + /* We can match the version information or use the + default one if it is not hidden. */ + ElfW(Half) ndx = verstab[symidx] & 0x7fff; + if ((map->l_versions[ndx].hash != version->hash + || strcmp (map->l_versions[ndx].name, version->name)) + && (version->hidden || map->l_versions[ndx].hash + || (verstab[symidx] & 0x8000))) + /* It's not the version we want. */ + return NULL; + } + } + else + { + /* No specific version is selected. There are two ways we + can got here: + + - a binary which does not include versioning information + is loaded + + - dlsym() instead of dlvsym() is used to get a symbol which + might exist in more than one form + + If the library does not provide symbol version information + there is no problem at all: we simply use the symbol if it + is defined. + + These two lookups need to be handled differently if the + library defines versions. In the case of the old + unversioned application the oldest (default) version + should be used. In case of a dlsym() call the latest and + public interface should be returned. */ + if (verstab != NULL) + { + if ((verstab[symidx] & 0x7fff) + >= ((flags & DL_LOOKUP_RETURN_NEWEST) ? 2 : 3)) + { + /* Don't accept hidden symbols. */ + if ((verstab[symidx] & 0x8000) == 0 + && (*num_versions)++ == 0) + /* No version so far. */ + *versioned_sym = sym; + + return NULL; + } + } + } + + /* There cannot be another entry for this symbol so stop here. */ + return sym; +} + +/* Utility function for do_lookup_unique. Add a symbol to TABLE. */ +static void +enter_unique_sym (struct unique_sym *table, size_t size, + unsigned int hash, const char *name, + const ElfW(Sym) *sym, const struct link_map *map) +{ + size_t idx = hash % size; + size_t hash2 = 1 + hash % (size - 2); + while (table[idx].name != NULL) + { + idx += hash2; + if (idx >= size) + idx -= size; + } + + table[idx].hashval = hash; + table[idx].name = name; + table[idx].sym = sym; + table[idx].map = map; +} + +/* Utility function for do_lookup_x. Lookup an STB_GNU_UNIQUE symbol + in the unique symbol table, creating a new entry if necessary. + Return the matching symbol in RESULT. */ +static void +do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, + const struct link_map *map, struct sym_val *result, + int type_class, const ElfW(Sym) *sym, const char *strtab, + const ElfW(Sym) *ref, const struct link_map *undef_map) +{ + /* We have to determine whether we already found a symbol with this + name before. If not then we have to add it to the search table. + If we already found a definition we have to use it. */ + + struct unique_sym_table *tab + = _ns_unique_sym_table; + + __rtld_lock_lock_recursive (tab->lock); + + struct unique_sym *entries = tab->entries; + size_t size = tab->size; + if (entries != NULL) + { + size_t idx = new_hash % size; + size_t hash2 = 1 + new_hash % (size - 2); + while (1) + { + if (entries[idx].hashval == new_hash + && strcmp (entries[idx].name, undef_name) == 0) + { + if ((type_class & ELF_RTYPE_CLASS_COPY) != 0) + { + /* We possibly have to initialize the central + copy from the copy addressed through the + relocation. */ + result->s = sym; + result->m = (struct link_map *) map; + } + else + { + result->s = entries[idx].sym; + result->m = (struct link_map *) entries[idx].map; + } + __rtld_lock_unlock_recursive (tab->lock); + return; + } + + if (entries[idx].name == NULL) + break; + + idx += hash2; + if (idx >= size) + idx -= size; + } + + if (size * 3 <= tab->n_elements * 4) + { + /* Expand the table. */ +#ifdef RTLD_CHECK_FOREIGN_CALL + /* This must not happen during runtime relocations. */ + assert (!RTLD_CHECK_FOREIGN_CALL); +#endif + size_t newsize = _dl_higher_prime_number (size + 1); + struct unique_sym *newentries + = calloc (sizeof (struct unique_sym), newsize); + if (newentries == NULL) + { + nomem: + __rtld_lock_unlock_recursive (tab->lock); + _dl_fatal_printf ("out of memory\n"); + } + + for (idx = 0; idx < size; ++idx) + if (entries[idx].name != NULL) + enter_unique_sym (newentries, newsize, entries[idx].hashval, + entries[idx].name, entries[idx].sym, + entries[idx].map); + + tab->free (entries); + tab->size = newsize; + size = newsize; + entries = tab->entries = newentries; + tab->free = free; + } + } + else + { +#ifdef RTLD_CHECK_FOREIGN_CALL + /* This must not happen during runtime relocations. */ + assert (!RTLD_CHECK_FOREIGN_CALL); +#endif + +#if 1 + /* If tab->entries is NULL, but tab->size is not, it means + this is the second, conflict finding, lookup for + LD_TRACE_PRELINKING in _dl_debug_bindings. Don't + allocate anything and don't enter anything into the + hash table. */ + if (__glibc_unlikely (tab->size)) + { + assert (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK); + goto success; + } +#endif + +#define INITIAL_NUNIQUE_SYM_TABLE 31 + size = INITIAL_NUNIQUE_SYM_TABLE; + entries = calloc (sizeof (struct unique_sym), size); + if (entries == NULL) + goto nomem; + + tab->entries = entries; + tab->size = size; + tab->free = free; + } + + if ((type_class & ELF_RTYPE_CLASS_COPY) != 0) + enter_unique_sym (entries, size, new_hash, strtab + sym->st_name, ref, + undef_map); + else + { + enter_unique_sym (entries, size, + new_hash, strtab + sym->st_name, sym, map); + +#if 0 + if (map->l_type == lt_loaded) + /* Make sure we don't unload this object by + setting the appropriate flag. */ + ((struct link_map *) map)->l_flags_1 |= DF_1_NODELETE; +#endif + } + ++tab->n_elements; + +#if 1 + success: +#endif + + result->s = sym; + result->m = (struct link_map *) map; +} + +/* Inner part of the lookup functions. We return a value > 0 if we + found the symbol, the value 0 if nothing is found and < 0 if + something bad happened. */ +static int +do_lookup_x (const char *undef_name, uint_fast32_t new_hash, + unsigned long int *old_hash, const ElfW(Sym) *ref, + struct sym_val *result, struct r_scope_elem *scope, size_t i, + const struct r_found_version *const version, int flags, + struct link_map *skip, int type_class, struct link_map *undef_map) +{ + size_t n = scope->r_nlist; + /* Make sure we read the value before proceeding. Otherwise we + might use r_list pointing to the initial scope and r_nlist being + the value after a resize. That is the only path in dl-open.c not + protected by GSCOPE. A read barrier here might be to expensive. */ + __asm volatile ("" : "+r" (n), "+m" (scope->r_list)); + struct link_map **list = scope->r_list; + + do + { + const struct link_map *map = list[i]; + + /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */ + if (map == skip) + continue; + + /* Don't search the executable when resolving a copy reloc. */ + if ((type_class & ELF_RTYPE_CLASS_COPY) && map->l_type == lt_executable) + continue; + + /* Print some debugging info if wanted. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS)) + _dl_debug_printf ("symbol=%s; lookup in file=%s [%lu]\n", + undef_name, DSO_FILENAME (map->l_name), + 0UL); + + /* If the hash table is empty there is nothing to do here. */ + if (map->l_nbuckets == 0) + continue; + + Elf_Symndx symidx; + int num_versions = 0; + const ElfW(Sym) *versioned_sym = NULL; + + /* The tables for this map. */ + const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]); + const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + + const ElfW(Sym) *sym; + const ElfW(Addr) *bitmask = map->l_gnu_bitmask; + if (__glibc_likely (bitmask != NULL)) + { + ElfW(Addr) bitmask_word + = bitmask[(new_hash / __ELF_NATIVE_CLASS) + & map->l_gnu_bitmask_idxbits]; + + unsigned int hashbit1 = new_hash & (__ELF_NATIVE_CLASS - 1); + unsigned int hashbit2 = ((new_hash >> map->l_gnu_shift) + & (__ELF_NATIVE_CLASS - 1)); + + if (__glibc_unlikely ((bitmask_word >> hashbit1) + & (bitmask_word >> hashbit2) & 1)) + { + Elf32_Word bucket = map->l_gnu_buckets[new_hash + % map->l_nbuckets]; + +/* PPC64 workaround */ + /* There is a bad hash entry and it's pointing beyond + the end of the bucket list. */ + assert ((void *)&map->l_gnu_chain_zero[bucket] < map->l_buckets_end); +/* END PPC64 workaround */ + if (bucket != 0) + { + const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket]; + + do + if (((*hasharr ^ new_hash) >> 1) == 0) + { + symidx = hasharr - map->l_gnu_chain_zero; + sym = check_match (undef_name, ref, version, flags, + type_class, &symtab[symidx], symidx, + strtab, map, &versioned_sym, + &num_versions); + if (sym != NULL) + goto found_it; + } + while ((*hasharr++ & 1u) == 0); + } + } + /* No symbol found. */ + symidx = SHN_UNDEF; + } + else + { + if (*old_hash == 0xffffffff) + *old_hash = _dl_elf_hash (undef_name); + + /* Use the old SysV-style hash table. Search the appropriate + hash bucket in this object's symbol table for a definition + for the same symbol name. */ + for (symidx = map->l_buckets[*old_hash % map->l_nbuckets]; + symidx != STN_UNDEF; + symidx = map->l_chain[symidx]) + { + sym = check_match (undef_name, ref, version, flags, + type_class, &symtab[symidx], symidx, + strtab, map, &versioned_sym, + &num_versions); + if (sym != NULL) + goto found_it; + } + } + + /* If we have seen exactly one versioned symbol while we are + looking for an unversioned symbol and the version is not the + default version we still accept this symbol since there are + no possible ambiguities. */ + sym = num_versions == 1 ? versioned_sym : NULL; + + if (sym != NULL) + { + found_it: + /* When UNDEF_MAP is NULL, which indicates we are called from + do_lookup_x on relocation against protected data, we skip + the data definion in the executable from copy reloc. */ + if (ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(map->machine) + && undef_map == NULL + && map->l_type == lt_executable + && type_class == ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(map->machine)) + { + const ElfW(Sym) *s; + unsigned int i; + +if (!ELF_MACHINE_NO_RELA(map->machine)) { /* #if ! ELF_MACHINE_NO_RELA */ + if (map->l_info[DT_RELA] != NULL + && map->l_info[DT_RELASZ] != NULL + && ((ElfW(Dyn) *)(map->l_info[DT_RELASZ]))->d_un.d_val != 0) + { + const ElfW(Rela) *rela + = (const ElfW(Rela) *) D_PTR (map, l_info[DT_RELA]); + unsigned int rela_count + = ((ElfW(Dyn) *)(map->l_info[DT_RELASZ]))->d_un.d_val / sizeof (*rela); + + for (i = 0; i < rela_count; i++, rela++) + if (elf_machine_type_class (ELFW(R_TYPE) (rela->r_info), map->machine) + == ELF_RTYPE_CLASS_COPY) + { + s = &symtab[ELFW(R_SYM) (rela->r_info)]; + if (!strcmp (strtab + s->st_name, undef_name)) + goto skip; + } + } +} /* #endif */ +if (!ELF_MACHINE_NO_REL(map->machine)) { /* #if ! ELF_MACHINE_NO_REL */ + if (map->l_info[DT_REL] != NULL + && map->l_info[DT_RELSZ] != NULL + && ((ElfW(Dyn) *)(map->l_info[DT_RELSZ]))->d_un.d_val != 0) + { + const ElfW(Rel) *rel + = (const ElfW(Rel) *) D_PTR (map, l_info[DT_REL]); + unsigned int rel_count + = ((ElfW(Dyn) *)(map->l_info[DT_RELSZ]))->d_un.d_val / sizeof (*rel); + + for (i = 0; i < rel_count; i++, rel++) + if (elf_machine_type_class (ELFW(R_TYPE) (rel->r_info), map->machine) + == ELF_RTYPE_CLASS_COPY) + { + s = &symtab[ELFW(R_SYM) (rel->r_info)]; + if (!strcmp (strtab + s->st_name, undef_name)) + goto skip; + } + } +} /* #endif */ + } + + switch (ELFW(ST_BIND) (sym->st_info)) + { + case STB_WEAK: + /* Weak definition. Use this value if we don't find another. */ + if (__glibc_unlikely (GLRO(dl_dynamic_weak))) + { + if (! result->s) + { + result->s = sym; + result->m = (struct link_map *) map; + } + break; + } + /* FALLTHROUGH */ + case STB_GLOBAL: + /* Global definition. Just what we need. */ + result->s = sym; + result->m = (struct link_map *) map; + return 1; + + case STB_GNU_UNIQUE:; + do_lookup_unique (undef_name, new_hash, map, result, type_class, + sym, strtab, ref, undef_map); + return 1; + + default: + /* Local symbols are ignored. */ + break; + } + } + +skip: + /* If this current map is the one mentioned in the verneed entry + and we have not found a weak entry, it is a bug. */ + if (symidx == STN_UNDEF && version != NULL && version->filename != NULL + && __glibc_unlikely (_dl_name_match_p (version->filename, map))) + return -1; + } + while (++i < n); + + /* We have not found anything until now. */ + return 0; +} + + +static void +_dl_debug_bindings (const char *undef_name, struct link_map *undef_map, + const ElfW(Sym) **ref, struct sym_val *value, + const struct r_found_version *version, int type_class, + int protected); + + +/* Search loaded objects' symbol tables for a definition of the symbol + UNDEF_NAME, perhaps with a requested version for the symbol. + + We must never have calls to the audit functions inside this function + or in any function which gets called. If this would happen the audit + code might create a thread which can throw off all the scope locking. */ +lookup_t +_dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, + const ElfW(Sym) **ref, + struct r_scope_elem *symbol_scope[], + const struct r_found_version *version, + int type_class, int flags, struct link_map *skip_map) +{ + const uint_fast32_t new_hash = dl_new_hash (undef_name); + unsigned long int old_hash = 0xffffffff; + struct sym_val current_value = { NULL, NULL }; + struct r_scope_elem **scope = symbol_scope; + + /* No other flag than DL_LOOKUP_ADD_DEPENDENCY or DL_LOOKUP_GSCOPE_LOCK + is allowed if we look up a versioned symbol. */ + assert (version == NULL + || (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK)) + == 0); + + size_t i = 0; + if (__glibc_unlikely (skip_map != NULL)) + /* Search the relevant loaded objects for a definition. */ + while ((*scope)->r_list[i] != skip_map) + ++i; + + /* Search the relevant loaded objects for a definition. */ + size_t start; /* requires C99 to put it into the loop */ + for (start = i; *scope != NULL; start = 0, ++scope) + { + int res = do_lookup_x (undef_name, new_hash, &old_hash, *ref, + ¤t_value, *scope, start, version, flags, + skip_map, type_class, undef_map); + if (res > 0) + break; + + if (__glibc_unlikely (res < 0) && skip_map == NULL) + { + /* Oh, oh. The file named in the relocation entry does not + contain the needed symbol. This code is never reached + for unversioned lookups. */ + assert (version != NULL); + const char *reference_name = undef_map ? undef_map->l_name : ""; + + /* XXX We cannot translate the message. */ + _dl_signal_cerror (0, DSO_FILENAME (reference_name), + "relocation error", + make_string ("symbol ", undef_name, ", version ", + version->name, + " not defined in file ", + version->filename, + " with link time reference", + res == -2 + ? " (no version symbols)" : "")); + *ref = NULL; + return 0; + } + } + + if (__glibc_unlikely (current_value.s == NULL)) + { + if ((*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK) + && skip_map == NULL + && !(GLRO(dl_debug_mask) & DL_DEBUG_UNUSED)) + { + /* We could find no value for a strong reference. */ + const char *reference_name = undef_map ? undef_map->l_name : ""; + const char *versionstr = version ? ", version " : ""; + const char *versionname = (version && version->name + ? version->name : ""); + + /* XXX We cannot translate the message. */ + _dl_signal_cerror (0, DSO_FILENAME (reference_name), + ("symbol lookup error"), + make_string ("undefined symbol: ", undef_name, + versionstr, versionname)); + } + *ref = NULL; + return 0; + } + + int protected = (*ref + && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED); + if (__glibc_unlikely (protected != 0)) + { + /* It is very tricky. We need to figure out what value to + return for the protected symbol. */ + if (type_class == ELF_RTYPE_CLASS_PLT) + { + if (current_value.s != NULL && current_value.m != undef_map) + { + current_value.s = *ref; + current_value.m = undef_map; + } + } + else + { + struct sym_val protected_value = { NULL, NULL }; + + for (scope = symbol_scope; *scope != NULL; i = 0, ++scope) + if (do_lookup_x (undef_name, new_hash, &old_hash, *ref, + &protected_value, *scope, i, version, flags, + skip_map, + (ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(skip_map->machine) + && ELFW(ST_TYPE) ((*ref)->st_info) == STT_OBJECT + && type_class == ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(skip_map->machine)) + ? ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(skip_map->machine) + : ELF_RTYPE_CLASS_PLT, NULL) != 0) + break; + + if (protected_value.s != NULL && protected_value.m != undef_map) + { + current_value.s = *ref; + current_value.m = undef_map; + } + } + } + +#if 0 + /* We have to check whether this would bind UNDEF_MAP to an object + in the global scope which was dynamically loaded. In this case + we have to prevent the latter from being unloaded unless the + UNDEF_MAP object is also unloaded. */ + if (__glibc_unlikely (current_value.m->l_type == lt_loaded) + /* Don't do this for explicit lookups as opposed to implicit + runtime lookups. */ + && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0 + /* Add UNDEF_MAP to the dependencies. */ + && add_dependency (undef_map, current_value.m, flags) < 0) + /* Something went wrong. Perhaps the object we tried to reference + was just removed. Try finding another definition. */ + return _dl_lookup_symbol_x (undef_name, undef_map, ref, + symbol_scope, + version, type_class, flags, skip_map); +#endif + + if (__glibc_unlikely (GLRO(dl_debug_mask) + & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK))) + _dl_debug_bindings (undef_name, undef_map, ref, + ¤t_value, version, type_class, protected); + + *ref = current_value.s; + return LOOKUP_VALUE (current_value.m); +} + + +/* Cache the location of MAP's hash table. */ + +void +_dl_setup_hash (struct link_map *map) +{ + Elf_Symndx *hash; + + if (__glibc_likely (map->l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + + DT_THISPROCNUM + DT_VERSIONTAGNUM + + DT_EXTRANUM + DT_VALNUM] != NULL)) + { + Elf32_Word *hash32 + = (void *) D_PTR (map, l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + + DT_THISPROCNUM + DT_VERSIONTAGNUM + + DT_EXTRANUM + DT_VALNUM]); + map->l_nbuckets = *hash32++; + Elf32_Word symbias = *hash32++; + Elf32_Word bitmask_nwords = *hash32++; + /* Must be a power of two. */ + assert ((bitmask_nwords & (bitmask_nwords - 1)) == 0); + map->l_gnu_bitmask_idxbits = bitmask_nwords - 1; + map->l_gnu_shift = *hash32++; + + map->l_gnu_bitmask = (ElfW(Addr) *) hash32; + hash32 += __ELF_NATIVE_CLASS / 32 * bitmask_nwords; + + map->l_gnu_buckets = hash32; + hash32 += map->l_nbuckets; + map->l_gnu_chain_zero = hash32 - symbias; + return; + } + + if (!map->l_info[DT_HASH]) + return; + hash = (void *) D_PTR (map, l_info[DT_HASH]); + + map->l_nbuckets = *hash++; + /* Skip nchain. */ + hash++; + map->l_buckets = hash; + hash += map->l_nbuckets; + map->l_chain = hash; +} + + +static void +_dl_debug_bindings (const char *undef_name, struct link_map *undef_map, + const ElfW(Sym) **ref, struct sym_val *value, + const struct r_found_version *version, int type_class, + int protected) +{ + const char *reference_name = undef_map->l_name; + + if (GLRO(dl_debug_mask) & DL_DEBUG_BINDINGS) + { + _dl_debug_printf ("binding file %s [%lu] to %s [%lu]: %s symbol `%s'", + DSO_FILENAME (reference_name), + 0UL, + DSO_FILENAME (value->m->l_name), + 0UL, + protected ? "protected" : "normal", undef_name); + if (version) + printf (" [%s]\n", version->name); + else + printf ("\n"); + } +#if 1 + if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) + { + int conflict = 0; + struct sym_val val = { NULL, NULL }; + + /* We need to always process these or we can miss conflict symbols when + RTLD_TRACE_PRELINKING=<library> */ + if (1) + { + const uint_fast32_t new_hash = dl_new_hash (undef_name); + unsigned long int old_hash = 0xffffffff; + struct unique_sym *saved_entries + = _ns_unique_sym_table->entries; + + _ns_unique_sym_table->entries = NULL; + do_lookup_x (undef_name, new_hash, &old_hash, *ref, &val, + undef_map->l_local_scope[0], 0, version, 0, NULL, + type_class, undef_map); + if (val.s != value->s || val.m != value->m) + conflict = 1; + else if (1 + && val.s + && __glibc_unlikely (ELFW(ST_BIND) (val.s->st_info) + == STB_GNU_UNIQUE)) + { + /* If it is STB_GNU_UNIQUE and undef_map's l_local_scope + contains any DT_SYMBOLIC libraries, unfortunately there + can be conflicts even if the above is equal. As symbol + resolution goes from the last library to the first and + if a STB_GNU_UNIQUE symbol is found in some late DT_SYMBOLIC + library, it would be the one that is looked up. */ + struct sym_val val2 = { NULL, NULL }; + size_t n; + struct r_scope_elem *scope = undef_map->l_local_scope[0]; + + for (n = 0; n < scope->r_nlist; n++) + if (scope->r_list[n] == val.m) + break; + + for (n++; n < scope->r_nlist; n++) + if (scope->r_list[n]->l_info[DT_SYMBOLIC] != NULL + && do_lookup_x (undef_name, new_hash, &old_hash, *ref, + &val2, + scope, + 0, version, 0, NULL, type_class, + undef_map) > 0) + { + conflict = 1; + val = val2; + break; + } + } + _ns_unique_sym_table->entries = saved_entries; + } + + if (value->s) + { + if (__glibc_unlikely (ELFW(ST_TYPE) (value->s->st_info) + == STT_TLS)) + type_class = 4; + else if (__glibc_unlikely (ELFW(ST_TYPE) (value->s->st_info) + == STT_GNU_IFUNC)) + type_class |= 8; + } + + if (conflict + || GLRO(dl_trace_prelink_map) == undef_map + || GLRO(dl_trace_prelink_map) == NULL + || type_class >= 4) + { + printf ("%s 0x%0*"rtld_size_fmtx" 0x%0*"rtld_size_fmtx" -> 0x%0*"rtld_size_fmtx" 0x%0*"rtld_size_fmtx" ", + conflict ? "conflict" : "lookup", + (int) sizeof (ElfW(Addr)) * 2, + (rtld_size_t) undef_map->l_map_start, + (int) sizeof (ElfW(Addr)) * 2, + (rtld_size_t) (((char *)*ref) - ((char *)undef_map->l_info[DT_SYMTAB]) + (char *)undef_map->sym_base), + (int) sizeof (ElfW(Addr)) * 2, + (rtld_size_t) (value->s ? value->m->l_map_start : 0), + (int) sizeof (ElfW(Addr)) * 2, + (rtld_size_t) (value->s ? value->s->st_value : 0)); + + if (conflict) + printf ("x 0x%0*"rtld_size_fmtx" 0x%0*"rtld_size_fmtx" ", + (int) sizeof (ElfW(Addr)) * 2, + (rtld_size_t) (val.s ? val.m->l_map_start : 0), + (int) sizeof (ElfW(Addr)) * 2, + (rtld_size_t) (val.s ? val.s->st_value : 0)); + + printf ("/%x %s\n", type_class, undef_name); + } + } +#endif +} + +#undef enter_unique_sym +#undef sym_val +#undef check_match +#undef do_lookup_unique +#undef do_lookup_x +#undef _dl_setup_hash +#define _dl_setup_hash rtld_setup_hash +#undef _dl_debug_bindings +#undef _dl_lookup_symbol_x +#undef rtld_size_t +#undef rtld_size_fmtx diff --git a/src/rtld/dl-misc.c b/src/rtld/dl-misc.c new file mode 100644 index 0000000..c70956d --- /dev/null +++ b/src/rtld/dl-misc.c @@ -0,0 +1,106 @@ +/* glibc 2.22, elf/dl-misc.c */ + +/* Miscellaneous support functions for dynamic linker + Copyright (C) 1997-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <error.h> +#include <errno.h> +#include <string.h> +#include "rtld.h" + +/* Test whether given NAME matches any of the names of the given object. */ +int +_dl_name_match_p (const char *name, const struct link_map *map) +{ + if (strcmp (name, map->l_name) == 0) + return 1; + + struct libname_list *runp = map->l_libname; + + while (runp != NULL) + if (strcmp (name, runp->name) == 0) + return 1; + else + runp = runp->next; + + return 0; +} + +unsigned long int +_dl_higher_prime_number (unsigned long int n) +{ + /* These are primes that are near, but slightly smaller than, a + power of two. */ + static const uint32_t primes[] = { + UINT32_C (7), + UINT32_C (13), + UINT32_C (31), + UINT32_C (61), + UINT32_C (127), + UINT32_C (251), + UINT32_C (509), + UINT32_C (1021), + UINT32_C (2039), + UINT32_C (4093), + UINT32_C (8191), + UINT32_C (16381), + UINT32_C (32749), + UINT32_C (65521), + UINT32_C (131071), + UINT32_C (262139), + UINT32_C (524287), + UINT32_C (1048573), + UINT32_C (2097143), + UINT32_C (4194301), + UINT32_C (8388593), + UINT32_C (16777213), + UINT32_C (33554393), + UINT32_C (67108859), + UINT32_C (134217689), + UINT32_C (268435399), + UINT32_C (536870909), + UINT32_C (1073741789), + UINT32_C (2147483647), + /* 4294967291L */ + UINT32_C (2147483647) + UINT32_C (2147483644) + }; + + const uint32_t *low = &primes[0]; + const uint32_t *high = &primes[sizeof (primes) / sizeof (primes[0])]; + + while (low != high) + { + const uint32_t *mid = low + (high - low) / 2; + if (n > *mid) + low = mid + 1; + else + high = mid; + } + +#if 0 + /* If we've run out of primes, abort. */ + if (n > *low) + { + fprintf (stderr, "Cannot find prime bigger than %lu\n", n); + abort (); + } +#endif + + return *low; +} diff --git a/src/rtld/dl-object.c b/src/rtld/dl-object.c new file mode 100644 index 0000000..19f0265 --- /dev/null +++ b/src/rtld/dl-object.c @@ -0,0 +1,57 @@ +/* glibc-2.22: elf/dl-object.c */ + +/* Storage management for the chain of loaded shared objects. + Copyright (C) 1995-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <error.h> +#include <errno.h> +#include <string.h> +#include "rtld.h" + +/* Allocate a `struct link_map' for a new object being loaded, + and enter it into the _dl_loaded list. */ +struct link_map * +_dl_new_object (const char *realname, const char *libname, int type) +{ + size_t libname_len = strlen (libname) + 1; + struct link_map *new; + struct libname_list *newname; + + new = (struct link_map *) calloc (sizeof (*new) + + + sizeof (*newname) + libname_len, 1); + + if (new == NULL) + return NULL; + + new->l_libname = newname + = (struct libname_list *) ((char *) (new + 1)); + newname->name = (char *) memcpy (newname + 1, libname, libname_len); + /* newname->next = NULL; We use calloc therefore not necessary. */ + + /* When we create the executable link map, or a VDSO link map, we start + with "" for the l_name. In these cases "" points to ld.so rodata + and won't get dumped during core file generation. Therefore to assist + gdb and to create more self-contained core files we adjust l_name to + point at the newly allocated copy (which will get dumped) instead of + the ld.so rodata copy. */ + new->l_name = *realname ? realname : (char *) newname->name + libname_len - 1; + new->l_type = type; + + return new; +} diff --git a/src/rtld/dl-tls.c b/src/rtld/dl-tls.c new file mode 100644 index 0000000..8b972ff --- /dev/null +++ b/src/rtld/dl-tls.c @@ -0,0 +1,287 @@ +/* Copyright (C) 2011 Wind River Systems, Inc. + + Code reorganized from original code bearing the following copyright: + Copyright (C) 2003 MontaVista Software, Inc. + Written by Daniel Jacobowitz <drow@mvista.com>, 2003 + + Code updated by Mark Hatle <mark.hatle@windriver.com>, 2011 + to sync to eglibc 2.13 tls behavior... + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* glibc 2.22: elf/dl-tls.c */ + +/* Thread-local storage handling in the ELF dynamic linker. Generic version. + Copyright (C) 2002-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "rtld.h" + +/* Assign TLS offsets for every loaded library. This code is taken + almost directly from glibc! */ + +#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) + +/* The following function needs to mimic the _dl_determine_tlsoffset in eglibc */ +void +rtld_determine_tlsoffsets (int e_machine, struct r_scope_elem *search_list) +{ + uint64_t modid = 1; + + uint64_t i; + + /* skip max_align */ + uint64_t freetop = 0; + uint64_t freebottom = 0; + + /* This comes from each architecture's ABI. If TLS_TCB_AT_TP, then + set offset to -1; if TLS_DTV_AT_TP, then set offset to + TLS_TCB_SIZE. */ + + int tls_tcb_at_tp = 0; + int tls_dtv_at_tp = 0; + uint64_t tls_tcb_size; + + switch (e_machine) + { + case EM_X86_64: + tls_tcb_at_tp = 1; + tls_tcb_size = -1; + break; + + case EM_386: + tls_tcb_at_tp = 1; + tls_tcb_size = -1; + break; + + case EM_SH: + tls_dtv_at_tp = 1; + tls_tcb_size = 8; + break; + + case EM_PPC: + tls_dtv_at_tp = 1; + tls_tcb_size = 0; + break; + + case EM_PPC64: + tls_dtv_at_tp = 1; + tls_tcb_size = 0; + break; + + case EM_ARM: + tls_dtv_at_tp = 1; + tls_tcb_size = 8; + break; + + case EM_AARCH64: + tls_dtv_at_tp = 1; + tls_tcb_size = 16; + break; + + case EM_MIPS: + tls_dtv_at_tp = 1; + tls_tcb_size = 0; + break; + + case EM_SPARC: + case EM_SPARC32PLUS: + tls_tcb_at_tp = 1; + tls_tcb_size = -1; + break; + + case EM_SPARCV9: + tls_tcb_at_tp = 1; + tls_tcb_size = -1; + break; + + case EM_ALTERA_NIOS2: + tls_dtv_at_tp = 1; + tls_tcb_size = 0; + break; + + default: + /* Hope there's no TLS! */ + for (i = 0; i < search_list->r_nlist; i++) + { + struct link_map *map = search_list->r_list[i]; + + if (map->l_tls_blocksize > 0) + _dl_signal_error(0, map->l_name, NULL, "cannot handle TLS data"); + } + + return; + } + + /* eglibc 2.20: elf/dl-tls.c: _dl_determine_tlsoffset (void) */ + /* Determining the offset of the various parts of the static TLS + block has several dependencies. In addition we have to work + around bugs in some toolchains. + + Each TLS block from the objects available at link time has a size + and an alignment requirement. The GNU ld computes the alignment + requirements for the data at the positions *in the file*, though. + I.e, it is not simply possible to allocate a block with the size + of the TLS program header entry. The data is layed out assuming + that the first byte of the TLS block fulfills + + p_vaddr mod p_align == &TLS_BLOCK mod p_align + + This means we have to add artificial padding at the beginning of + the TLS block. These bytes are never used for the TLS data in + this module but the first byte allocated must be aligned + according to mod p_align == 0 so that the first byte of the TLS + block is aligned according to p_vaddr mod p_align. This is ugly + and the linker can help by computing the offsets in the TLS block + assuming the first byte of the TLS block is aligned according to + p_align. + + The extra space which might be allocated before the first byte of + the TLS block need not go unused. The code below tries to use + that memory for the next TLS block. This can work if the total + memory requirement for the next TLS block is smaller than the + gap. */ + + /* Loop over the loaded DSOs. We use the symbol search order; this + should be the same as glibc's ordering, which traverses l_next. + It's somewhat important that we use both the same ordering to + assign module IDs and the same algorithm to assign offsets, + because the prelinker will resolve all relocations using these + offsets... and then glibc will recalculate them. Future dynamic + relocations in any loaded modules will use glibc's values. Also + if we take too much space here, glibc won't allocate enough + static TLS area to hold it. */ + +/* #if TLS_TCB_AT_TP */ if (tls_tcb_at_tp == 1) { + /* We simply start with zero. */ + uint64_t offset = 0; + + for (i = 0; i < search_list->r_nlist; i++) + { + struct link_map *map = search_list->r_list[i]; + + uint64_t firstbyte = (-map->l_tls_firstbyte_offset + & (map->l_tls_align - 1)); + uint64_t off; + + /* elf/rtld.c would have caused us to skip this block.. so emulate this */ + if (map->l_tls_blocksize == 0) + continue; + + /* allocate_tls_init would nomrally Increment the module id */ + map->l_tls_modid = modid++; + + if (freebottom - freetop >= map->l_tls_blocksize) + { + off = roundup (freetop + map->l_tls_blocksize + - firstbyte, map->l_tls_align) + + firstbyte; + if (off <= freebottom) + { + freetop = off; + + /* XXX For some architectures we perhaps should store the + negative offset. */ + map->l_tls_offset = off; + continue; + } + } + + off = roundup (offset + map->l_tls_blocksize - firstbyte, + map->l_tls_align) + firstbyte; + if (off > offset + map->l_tls_blocksize + + (freebottom - freetop)) + { + freetop = offset; + freebottom = off - map->l_tls_blocksize; + } + offset = off; + + /* XXX For some architectures we perhaps should store the + negative offset. */ + map->l_tls_offset = off; + } +/* #elif TLS_DTV_AT_TP */ } else if (tls_dtv_at_tp == 1) { + /* The TLS blocks start right after the TCB. */ + uint64_t offset = tls_tcb_size; + + for (i = 0; i < search_list->r_nlist; i++) + { + struct link_map *map = search_list->r_list[i]; + + uint64_t firstbyte = (-map->l_tls_firstbyte_offset + & (map->l_tls_align - 1)); + uint64_t off; + + /* elf/rtld.c would have caused us to skip this block.. so emulate this */ + if (map->l_tls_blocksize == 0) + continue; + + /* allocate_tls_init would nomrally Increment the module id */ + map->l_tls_modid = modid++; + + if (map->l_tls_blocksize <= freetop - freebottom) + { + off = roundup (freebottom, map->l_tls_align); + if (off - freebottom < firstbyte) + off += map->l_tls_align; + if (off + map->l_tls_blocksize - firstbyte <= freetop) + { + map->l_tls_offset = off - firstbyte; + freebottom = (off + map->l_tls_blocksize + - firstbyte); + continue; + } + } + + off = roundup (offset, map->l_tls_align); + if (off - offset < firstbyte) + off += map->l_tls_align; + + map->l_tls_offset = off - firstbyte; + if (off - firstbyte - offset > freetop - freebottom) + { + freebottom = offset; + freetop = off - firstbyte; + } + + offset = off + map->l_tls_blocksize - firstbyte; + } +/* #else */ } else { + /* Should never happen... */ + _dl_signal_error(0, NULL, NULL, "Neither TLS_TCB_AT_TP nor TLS_DTV_AT_TP is defined for this architecture"); +/* #endif */ } +} diff --git a/src/rtld/dl-version.c b/src/rtld/dl-version.c new file mode 100644 index 0000000..042a70f --- /dev/null +++ b/src/rtld/dl-version.c @@ -0,0 +1,369 @@ +/* Based on code originally in eglibc 2.13, libc/elf/dl-version.c */ + +/* Handle symbol and library versioning. + Copyright (C) 1997-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <error.h> +#include <errno.h> +#include <string.h> +#include "rtld.h" + +#define make_string(string, rest...) \ + ({ \ + const char *all[] = { string, ## rest }; \ + size_t len, cnt; \ + char *result, *cp; \ + \ + len = 1; \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + len += strlen (all[cnt]); \ + \ + cp = result = alloca (len); \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + cp = __stpcpy (cp, all[cnt]); \ + \ + result; \ + }) + + +static inline struct link_map * +find_needed (const char *name, struct link_map *map) +{ + struct link_map *tmap = map; + unsigned int n = 0; + + for (n = 0; n < map->l_local_scope[0]->r_nlist; n++) + { + tmap = map->l_local_scope[0]->r_list[n]; + if (_dl_name_match_p (name, tmap)) + return tmap; + } + + if (_dl_name_match_p (name, map)) + return map; + + /* Should never happen. */ + return NULL; +} + + +static int +match_symbol (const char *name, Elf64_Word hash, const char *string, + struct link_map *map, int verbose, int weak) +{ + const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + Elf64_Verdef *def; + /* Initialize to make the compiler happy. */ + const char *errstring = NULL; + int result = 0; + + /* Display information about what we are doing while debugging. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_VERSIONS)) + _dl_debug_printf ("\ +checking for version `%s' in file %s [0] required by file %s [0]\n", + string, DSO_FILENAME (map->l_name), + name); + + if (__glibc_unlikely (map->l_info[VERSYMIDX (DT_VERDEF)] == NULL)) + { + /* The file has no symbol versioning. I.e., the dependent + object was linked against another version of this file. We + only print a message if verbose output is requested. */ + if (verbose) + { + /* XXX We cannot translate the messages. */ + errstring = make_string ("\ +no version information available (required by ", name, ")"); + goto call_cerror; + } + return 0; + } + + def = (Elf64_Verdef *) ((char *) map->l_info[VERSYMIDX (DT_VERDEF)]); + while (1) + { + /* Currently the version number of the definition entry is 1. + Make sure all we see is this version. */ + if (__builtin_expect (def->vd_version, 1) != 1) + { + char buf[20]; + buf[sizeof (buf) - 1] = '\0'; + /* XXX We cannot translate the message. */ + /* _itoa (def->vd_version, + &buf[sizeof (buf) - 1], 10, 0), */ + errstring = make_string ("unsupported version ", + " of Verdef record"); + result = 1; + goto call_cerror; + } + + /* Compare the hash values. */ + if (hash == def->vd_hash) + { + Elf64_Verdaux *aux = (Elf64_Verdaux *) ((char *) def + def->vd_aux); + + /* To be safe, compare the string as well. */ + if (__builtin_expect (strcmp (string, strtab + aux->vda_name), 0) + == 0) + /* Bingo! */ + return 0; + } + + /* If no more definitions we failed to find what we want. */ + if (def->vd_next == 0) + break; + + /* Next definition. */ + def = (Elf64_Verdef *) ((char *) def + def->vd_next); + } + + /* Symbol not found. If it was a weak reference it is not fatal. */ + if (__glibc_likely (weak)) + { + if (verbose) + { + /* XXX We cannot translate the message. */ + errstring = make_string ("weak version `", string, + "' not found (required by ", name, ")"); + goto call_cerror; + } + return 0; + } + + /* XXX We cannot translate the message. */ + errstring = make_string ("version `", string, "' not found (required by ", + name, ")"); + result = 1; + call_cerror: + _dl_signal_cerror (0, DSO_FILENAME (map->l_name), + ("version lookup error"), errstring); + return result; +} + + +int +_dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) +{ + int result = 0; + const char *strtab; + /* Pointer to section with needed versions. */ + Elf64_Verneed *dyn; + /* This file may require special versions from its dependencies. */ + Elf64_Verneed *ent; + /* Pointer to dynamic section with definitions. */ + Elf64_Verdef *def; + /* We need to find out which is the highest version index used + in a dependecy. */ + unsigned int ndx_high = 0; + /* Initialize to make the compiler happy. */ + const char *errstring = NULL; + int errval = 0; + + /* If we don't have a string table, we must be ok. */ + if (map->l_info[DT_STRTAB] == NULL) + return 0; + strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + + dyn = map->l_info[VERSYMIDX (DT_VERNEED)]; + def = map->l_info[VERSYMIDX (DT_VERDEF)]; + ent = (Elf64_Verneed *) (dyn); + + if (dyn != NULL) + { + + /* Currently the version number of the needed entry is 1. + Make sure all we see is this version. */ + if (__builtin_expect (ent->vn_version, 1) != 1) + { + char buf[20]; + buf[sizeof (buf) - 1] = '\0'; + /* XXX We cannot translate the message. */ + /* _itoa (def->vn_version, + &buf[sizeof (buf) - 1], 10, 0), */ + errstring = make_string ("unsupported version ", + " of Verneed record\n"); + call_error: + _dl_signal_error (errval, DSO_FILENAME (map->l_name), + NULL, errstring); + } + + while (1) + { + Elf64_Vernaux *aux; + struct link_map *needed = find_needed (strtab + ent->vn_file, map); + + /* If NEEDED is NULL this means a dependency was not found + and no stub entry was created. This should never happen. */ + if (needed == NULL) + { + _dl_signal_error (errval, NULL, NULL, strtab + ent->vn_file); + printf("error while loading shared libraries: %s", strtab + ent->vn_file); + exit (1); + } + assert (needed != NULL); + + /* Make sure this is no stub we created because of a missing + dependency. */ + if (__builtin_expect (! trace_mode, 1)) + { + /* NEEDED is the map for the file we need. Now look for the + dependency symbols. */ + aux = (Elf64_Vernaux *) ((char *) ent + ent->vn_aux); + while (1) + { + /* Match the symbol. */ + result |= match_symbol (DSO_FILENAME (map->l_name), + aux->vna_hash, + strtab + aux->vna_name, + needed, verbose, + aux->vna_flags & VER_FLG_WEAK); + + /* Compare the version index. */ + if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high) + ndx_high = aux->vna_other & 0x7fff; + + if (aux->vna_next == 0) + /* No more symbols. */ + break; + + /* Next symbol. */ + aux = (Elf64_Vernaux *) ((char *) aux + aux->vna_next); + } + } + + if (ent->vn_next == 0) + /* No more dependencies. */ + break; + + /* Next dependency. */ + ent = (Elf64_Verneed *) ((char *) ent + ent->vn_next); + } + } + + /* We also must store the names of the defined versions. Determine + the maximum index here as well. + + XXX We could avoid the loop by just taking the number of definitions + as an upper bound of new indeces. */ + if (def != NULL) + { + Elf64_Verdef *ent; + ent = (Elf64_Verdef *) (def); + while (1) + { + if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high) + ndx_high = ent->vd_ndx & 0x7fff; + + if (ent->vd_next == 0) + /* No more definitions. */ + break; + + ent = (Elf64_Verdef *) ((char *) ent + ent->vd_next); + } + } + + if (ndx_high > 0) + { + /* Now we are ready to build the array with the version names + which can be indexed by the version index in the VERSYM + section. */ + map->l_versions = (struct r_found_version *) + calloc (ndx_high + 1, sizeof (*map->l_versions)); + if (__glibc_unlikely (map->l_versions == NULL)) + { + errstring = ("cannot allocate version reference table"); + errval = ENOMEM; + goto call_error; + } + + /* Store the number of available symbols. */ + map->l_nversions = ndx_high + 1; + + /* Compute the pointer to the version symbols. */ + map->l_versyms = (void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]); + + if (dyn != NULL) + { + Elf64_Verneed *ent; + ent = (Elf64_Verneed *) (dyn); + while (1) + { + Elf64_Vernaux *aux; + aux = (Elf64_Vernaux *) ((char *) ent + ent->vn_aux); + while (1) + { + Elf64_Half ndx = aux->vna_other & 0x7fff; + /* In trace mode, dependencies may be missing. */ + if (__glibc_likely (ndx < map->l_nversions)) + { + map->l_versions[ndx].hash = aux->vna_hash; + map->l_versions[ndx].hidden = aux->vna_other & 0x8000; + map->l_versions[ndx].name = &strtab[aux->vna_name]; + map->l_versions[ndx].filename = &strtab[ent->vn_file]; + } + + if (aux->vna_next == 0) + /* No more symbols. */ + break; + + /* Advance to next symbol. */ + aux = (Elf64_Vernaux *) ((char *) aux + aux->vna_next); + } + + if (ent->vn_next == 0) + /* No more dependencies. */ + break; + + /* Advance to next dependency. */ + ent = (Elf64_Verneed *) ((char *) ent + ent->vn_next); + } + } + + /* And insert the defined versions. */ + if (def != NULL) + { + Elf64_Verdef *ent; + ent = (Elf64_Verdef *) (def); + while (1) + { + Elf64_Verdaux *aux; + aux = (Elf64_Verdaux *) ((char *) ent + ent->vd_aux); + + if ((ent->vd_flags & VER_FLG_BASE) == 0) + { + /* The name of the base version should not be + available for matching a versioned symbol. */ + Elf64_Half ndx = ent->vd_ndx & 0x7fff; + map->l_versions[ndx].hash = ent->vd_hash; + map->l_versions[ndx].name = &strtab[aux->vda_name]; + map->l_versions[ndx].filename = NULL; + } + + if (ent->vd_next == 0) + /* No more definitions. */ + break; + + ent = (Elf64_Verdef *) ((char *) ent + ent->vd_next); + } + } + } + + return result; +} diff --git a/src/rtld/rtld.c b/src/rtld/rtld.c new file mode 100644 index 0000000..82ba4b2 --- /dev/null +++ b/src/rtld/rtld.c @@ -0,0 +1,1425 @@ +/* Copyright (C) 2003 MontaVista Software, Inc. + Written by Daniel Jacobowitz <drow@mvista.com>, 2003 + + Copyright (C) 2011 Wind River Systems, Inc. + Significantly updated by Mark Hatle <mark.hatle@windriver.com>, 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <ctype.h> +#include <error.h> +#include <errno.h> +#include <argp.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <libgen.h> + +#include <inttypes.h> + +#include "prelinktab.h" +#include "reloc.h" +#include "reloc-info.h" + +#include "rtld.h" + +unsigned int static_binary = 0; + +unsigned int _dl_debug_mask = 0; + +/* LD_DYNAMIC_WEAK option. Default is off, changing to 1 + is equivalent to setting LD_DYNAMIC_WEAK. */ +unsigned int _dl_dynamic_weak = 0; +#define MAX_PRELOADED_LIBS 20 + +struct search_path +{ + int maxlen, count, allocated; + char **dirs; +}; + +struct search_path ld_dirs, ld_library_search_path; +int host_paths; + +char * dst_ORIGIN; +char * dst_PLATFORM = ""; /* undefined */ +char * dst_LIB = "lib"; +char * ld_preload = NULL; + + +void string_to_path (struct search_path *path, const char *string); + +const char *argp_program_version = PRELINK_RTLD_PROG PKGVERSION " 1.0"; + +const char *argp_program_bug_address = REPORT_BUGS_TO; + +static char argp_doc[] = PRELINK_RTLD_PROG " -- program to simulate the runtime linker"; + +#define OPT_SYSROOT 0x8c +#define OPT_LIBRARY_PATH 0x8e +#define OPT_TARGET_PATHS 0x8f +#define OPT_LD_PRELOAD 0x90 + +static struct argp_option options[] = { + {"library-path", OPT_LIBRARY_PATH, "LIBRARY_PATH", 0, "Set library search path to LIBRARY_PATH" }, + {"root", OPT_SYSROOT, "ROOT_PATH", 0, "Prefix all paths with ROOT_PATH" }, + {"target-paths", OPT_TARGET_PATHS, 0, 0, "Specified paths are based on ROOT_PATH" }, + {"ld-preload", OPT_LD_PRELOAD, "PATHLIST", 0, "List of LD_PRELOAD libraries"}, + { 0 } +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case OPT_SYSROOT: + sysroot = arg; + break; + case OPT_LIBRARY_PATH: + string_to_path(&ld_library_search_path, arg); + break; + case OPT_TARGET_PATHS: + host_paths = 0; + break; + case OPT_LD_PRELOAD: + ld_preload = arg; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +/* This function returns the same constants expected by glibc's + symbol lookup routines. This is slightly different from the + equivalent routines in prelink. It should return PLT for any + relocation where an undefined symbol in the application should + be ignored: typically, this means any jump slot or TLS relocations, + but not copy relocations. Don't return the prelinker's + RTYPE_CLASS_TLS. */ + +/* The following needs to be kept in sync with the + sysdeps/.../dl-machine.h: elf_machine_type_class macro */ + +/* From glibc-2.22: sysdeps/i386/dl-machine.h */ +# define i386_elf_machine_type_class(type) \ + ((((type) == R_386_JMP_SLOT || (type) == R_386_TLS_DTPMOD32 \ + || (type) == R_386_TLS_DTPOFF32 || (type) == R_386_TLS_TPOFF32 \ + || (type) == R_386_TLS_TPOFF || (type) == R_386_TLS_DESC) \ + * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_386_COPY) * ELF_RTYPE_CLASS_COPY) \ + | (((type) == R_386_GLOB_DAT) * ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(EM_386))) + +/* From glibc-2.22: sysdeps/x86_64/dl-machine.h */ +# define x86_64_elf_machine_type_class(type) \ + ((((type) == R_X86_64_JUMP_SLOT \ + || (type) == R_X86_64_DTPMOD64 \ + || (type) == R_X86_64_DTPOFF64 \ + || (type) == R_X86_64_TPOFF64 \ + || (type) == R_X86_64_TLSDESC) \ + * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_X86_64_COPY) * ELF_RTYPE_CLASS_COPY) \ + | (((type) == R_X86_64_GLOB_DAT) * ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(EM_X86_64))) + +/* From glibc-2.22: ports/sysdeps/arm/dl-machine.h */ +# define arm_elf_machine_type_class(type) \ + ((((type) == R_ARM_JUMP_SLOT || (type) == R_ARM_TLS_DTPMOD32 \ + || (type) == R_ARM_TLS_DTPOFF32 || (type) == R_ARM_TLS_TPOFF32 \ + || (type) == R_ARM_TLS_DESC) \ + * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY) \ + | (((type) == R_ARM_GLOB_DAT) * ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(EM_ARM))) + +/* From glibc-2.22: ports/sysdeps/aarch64/dl-machine.h */ +# define aarch64_elf_machine_type_class(type) \ + ((((type) == R_AARCH64_JUMP_SLOT || \ + (type) == R_AARCH64_TLS_DTPMOD || \ + (type) == R_AARCH64_TLS_DTPREL || \ + (type) == R_AARCH64_TLS_TPREL || \ + (type) == R_AARCH64_TLSDESC) * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_AARCH64_COPY) * ELF_RTYPE_CLASS_COPY) \ + | (((type) == R_AARCH64_GLOB_DAT) * ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(EM_AARCH64))) + +/* From glibc-2.22: sysdeps/sh/dl-machine.h */ +# define sh_elf_machine_type_class(type) \ + ((((type) == R_SH_JMP_SLOT || (type) == R_SH_TLS_DTPMOD32 \ + || (type) == R_SH_TLS_DTPOFF32 || (type) == R_SH_TLS_TPOFF32) \ + * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_SH_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* From glibc-2.22: sysdeps/powerpc/powerpc32/dl-machine.h */ +#define powerpc32_elf_machine_type_class(type) \ + ((((type) == R_PPC_JMP_SLOT \ + || (type) == R_PPC_REL24 \ + || ((type) >= R_PPC_DTPMOD32 /* contiguous TLS */ \ + && (type) <= R_PPC_DTPREL32) \ + || (type) == R_PPC_ADDR24) * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_PPC_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* From glibc-2.22: sysdeps/powerpc/powerpc64/dl-machine.h */ +/* we only support ELFv2 at this point */ +#define IS_PPC64_TLS_RELOC(R) \ + (((R) >= R_PPC64_TLS && (R) <= R_PPC64_DTPREL16_HIGHESTA) \ + || ((R) >= R_PPC64_TPREL16_HIGH && (R) <= R_PPC64_DTPREL16_HIGHA)) + +#define powerpc64_elf_machine_type_class(type) \ + ((((type) == R_PPC64_JMP_SLOT \ + || (type) == R_PPC64_ADDR24 \ + || IS_PPC64_TLS_RELOC (type)) * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_PPC64_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* From glibc-2.22: sysdeps/mips/dl-machine.h */ +#define ELF_MACHINE_JMP_SLOT R_MIPS_JUMP_SLOT +#define mips_elf_machine_type_class(type) \ + ((((type) == ELF_MACHINE_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_MIPS_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* From glibc-2.22: sysdeps/sparc/sparc32/dl-machine.h */ +#define sparc_elf_machine_type_class(type) \ + ((((type) == R_SPARC_JMP_SLOT \ + || ((type) >= R_SPARC_TLS_GD_HI22 && (type) <= R_SPARC_TLS_TPOFF64)) \ + * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_SPARC_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* From glibc-2.22: sysdeps/sparc/sparc64/dl-machine.h */ +# define sparc64_elf_machine_type_class(type) \ + ((((type) == R_SPARC_JMP_SLOT \ + || ((type) >= R_SPARC_TLS_GD_HI22 && (type) <= R_SPARC_TLS_TPOFF64)) \ + * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_SPARC_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* From glibc-2.22: sysdeps/nios2/dl-machine.h */ +# define nios2_elf_machine_type_class(type) \ + ((((type) == R_NIOS2_JUMP_SLOT \ + || (type) == R_NIOS2_TLS_DTPMOD \ + || (type) == R_NIOS2_TLS_DTPREL \ + || (type) == R_NIOS2_TLS_TPREL) * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_NIOS2_COPY) * ELF_RTYPE_CLASS_COPY) \ + | (((type) == R_NIOS2_GLOB_DAT) * ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(EM_ALTERA_NIOS2))) + +int +elf_machine_type_class (int type, int machine) +{ + switch (machine) + { + case EM_386: + return i386_elf_machine_type_class(type); + case EM_X86_64: + return x86_64_elf_machine_type_class(type); + case EM_ARM: + return arm_elf_machine_type_class(type); + case EM_AARCH64: + return aarch64_elf_machine_type_class(type); + case EM_SH: + return sh_elf_machine_type_class(type); + case EM_PPC: + return powerpc32_elf_machine_type_class(type); + case EM_PPC64: + return powerpc64_elf_machine_type_class(type); + case EM_MIPS: + return mips_elf_machine_type_class(type); + case EM_SPARC: + case EM_SPARC32PLUS: + return sparc_elf_machine_type_class(type); + case EM_SPARCV9: + return sparc64_elf_machine_type_class(type); + case EM_ALTERA_NIOS2: + return nios2_elf_machine_type_class(type); + + default: + printf ("Unknown architecture!\n"); + exit (1); + return 0; + } +} + +int +extern_protected_data(int machine) +{ + switch (machine) + { + case EM_386: + case EM_X86_64: + case EM_ARM: + case EM_AARCH64: + case EM_ALTERA_NIOS2: + return 4; + default: + return 0; + } +} + +int +machine_no_rela (int machine) +{ + switch (machine) + { + case EM_386: + case EM_X86_64: + case EM_ARM: + case EM_AARCH64: + case EM_SH: + case EM_PPC: + case EM_PPC64: + case EM_MIPS: + case EM_SPARC: + case EM_SPARC32PLUS: + case EM_SPARCV9: + case EM_ALTERA_NIOS2: + return 0; + default: + return 1; + } +} + +int +machine_no_rel (int machine) +{ + switch (machine) + { + case EM_386: + case EM_MIPS: + case EM_ARM: + return 0; + default: + return 1; + } +} + +int +is_ldso_soname (const char *soname) +{ + if ( ! strcmp (soname, "ld-linux.so.2") + || ! strcmp (soname, "ld-linux.so.3") + || ! strcmp (soname, "ld.so.1") + || ! strcmp (soname, "ld-linux-ia64.so.2") + || ! strcmp (soname, "ld-linux-x86-64.so.2") + || ! strcmp (soname, "ld64.so.2") + || ! strcmp (soname, "ld-linux-armhf.so.3") + || ! strcmp (soname, "ld-linux-aarch64.so.1") + || ! strcmp (soname, "ld-linux-aarch64_be.so.1") + || ! strcmp (soname, "ld-linux-nios2.so.1") + ) + return 1; + return 0; +} + +static int dso_open_error = 0; + +static void +free_needed (struct needed_list *p) +{ + struct needed_list *old_p = p; + while (old_p) + { + old_p = p->next; + free (p); + p = old_p; + } +} + +static struct dso_list * +in_dso_list (struct dso_list *dso_list, const char *soname, const char *filename) +{ + while (dso_list != NULL) + { + if (dso_list->dso != NULL) + { + if (strcmp (dso_list->dso->soname, soname) == 0) + return dso_list; + } + + if (strcmp (dso_list->name, soname) == 0) + return dso_list; + + if (filename && dso_list->canon_filename + && strcmp (dso_list->canon_filename, filename) == 0) + return dso_list; + + dso_list = dso_list->next; + } + return NULL; +} + +static int +in_needed_list (struct needed_list *needed_list, const char *soname) +{ + while (needed_list != NULL) + { + if (needed_list->ent->dso != NULL + && strcmp (needed_list->ent->dso->soname, soname) == 0) + return 1; + needed_list = needed_list->next; + } + return 0; +} + + +/****/ + +void +add_dir (struct search_path *path, const char *dir, int dirlen) +{ + if (path->allocated == 0) + { + path->allocated = 5; + path->dirs = malloc (sizeof (char *) * 5); + } + else if (path->count == path->allocated) + { + path->allocated *= 2; + path->dirs = realloc (path->dirs, sizeof (char *) * path->allocated); + } + path->dirs[path->count] = malloc (dirlen + 1); + memcpy (path->dirs[path->count], dir, dirlen); + path->dirs[path->count++][dirlen] = 0; + + if (path->maxlen < dirlen) + path->maxlen = dirlen; +} + +void +free_path (struct search_path *path) +{ + if (path->allocated) + { + int i; + for (i = 0; i < path->count; i++) + free (path->dirs[i]); + free (path->dirs); + } +} + +void +load_ld_so_conf (int use_64bit, int use_mipsn32) +{ + int fd; + FILE *conf; + char buf[1024]; + + memset (&ld_dirs, 0, sizeof (ld_dirs)); + + /* Only use the correct machine, to prevent mismatches if we + have both /lib/ld.so and /lib64/ld.so on x86-64. */ + if (use_64bit) + { + dst_LIB = "lib64"; + add_dir (&ld_dirs, "/lib64/tls", strlen ("/lib64/tls")); + add_dir (&ld_dirs, "/lib64", strlen ("/lib64")); + add_dir (&ld_dirs, "/usr/lib64/tls", strlen ("/usr/lib64/tls")); + add_dir (&ld_dirs, "/usr/lib64", strlen ("/usr/lib64")); + } + else if (use_mipsn32) + { + dst_LIB = "lib32"; + add_dir (&ld_dirs, "/lib32/tls", strlen ("/lib32/tls")); + add_dir (&ld_dirs, "/lib32", strlen ("/lib32")); + add_dir (&ld_dirs, "/usr/lib32/tls", strlen ("/usr/lib32/tls")); + add_dir (&ld_dirs, "/usr/lib32", strlen ("/usr/lib32")); + } + else + { + dst_LIB = "lib"; + add_dir (&ld_dirs, "/lib/tls", strlen ("/lib/tls")); + add_dir (&ld_dirs, "/lib", strlen ("/lib")); + add_dir (&ld_dirs, "/usr/lib/tls", strlen ("/usr/lib/tls")); + add_dir (&ld_dirs, "/usr/lib", strlen ("/usr/lib")); + } + + fd = wrap_open ("/etc/ld.so.conf", O_RDONLY); + if (fd == -1) + return; + conf = fdopen (fd, "r"); + while (fgets (buf, 1024, conf) != NULL) + { + int len; + char *p; + + p = strchr (buf, '#'); + if (p) + *p = 0; + len = strlen (buf); + while (len > 0 && isspace (buf[len - 1])) + buf[--len] = 0; + + if (len > 0) + add_dir (&ld_dirs, buf, len); + } + fclose (conf); +} + +void +string_to_path (struct search_path *path, const char *string) +{ + const char *start, *end, *end_tmp; + + start = string; + while (1) { + end = start; + while (*end && *end != ':' && *end != ';') + end ++; + + /* Eliminate any trailing '/' characters, but be sure to leave a + leading slash if someeone wants / in their RPATH. */ + end_tmp = end; + while (end_tmp > start + 1 && end_tmp[-1] == '/') + end_tmp --; + + add_dir (path, start, end_tmp - start); + + if (*end == 0) + break; + + /* Skip the separator. */ + start = end + 1; + } +} + +char * +find_lib_in_path (struct search_path *path, const char *soname, + int elfclass, int machine) +{ + char *ret = NULL; + int i; + int alt_machine; + + switch (machine) + { + case EM_SPARC: + alt_machine = EM_SPARC32PLUS; + break; + case EM_SPARC32PLUS: + alt_machine = EM_SPARC; + break; + default: + alt_machine = machine; + break; + } + + for (i = 0; i < path->count; i++) + { + char * fixup_path, * path_string; + + path_string = fixup_path = strdup(path->dirs[i]); + + while ((path_string = strchr (path_string, '$'))) + { + char * replace = NULL; + size_t len = 0; + + if (strncmp("$ORIGIN", path_string, 7) == 0) { + replace = dst_ORIGIN; + len = 7; + } else if (strncmp("${ORIGIN}", path_string, 9) == 0) { + replace = dst_ORIGIN; + len = 9; + } else if (strncmp("$PLATFORM", path_string, 9) == 0) { + replace = dst_PLATFORM; + len = 9; + } else if (strncmp("${PLATFORM}", path_string, 11) == 0) { + replace = dst_PLATFORM; + len = 11; + } else if (strncmp("$LIB", path_string, 4) == 0) { + replace = dst_LIB; + len = 4; + } else if (strncmp("${LIB}", path_string, 6) == 0) { + replace = dst_LIB; + len = 6; + } else { + /* Not a defined item, so we skip to the next character */ + path_string += 1; + } + + if (replace) { + size_t new_path_len = strlen(fixup_path) - len + strlen(replace) + 1; + char * new_path = malloc(new_path_len); + + /* Calculate the new path_string position based on the old position */ + size_t new_path_pos = (path_string - fixup_path) + strlen(replace); + + snprintf(new_path, new_path_len, "%.*s%s%s", (int)(path_string-fixup_path), + fixup_path, replace, path_string + len); + + free(fixup_path); + + fixup_path = new_path; + path_string = fixup_path + new_path_pos; + } + } + + ret = malloc (strlen (soname) + 2 + strlen(fixup_path)); + sprintf (ret, "%s/%s", fixup_path, soname); + + if (wrap_access (ret, F_OK) == 0) + { + DSO *dso = open_dso (ret); + int dso_class, dso_machine; + + if (dso == NULL) + continue; + + dso_class = gelf_getclass (dso->elf); + dso_machine = (dso_class == ELFCLASS32) ? + elf32_getehdr (dso->elf)->e_machine : + elf64_getehdr (dso->elf)->e_machine; + + /* Skip 32-bit libraries when looking for 64-bit. Also + skip libraries for alternative machines. */ + if (gelf_getclass (dso->elf) != elfclass + || (dso_machine != machine && dso_machine != alt_machine)) + { + close_dso (dso); + continue; + } + + close_dso (dso); + return ret; + } + } + + free (ret); + return NULL; +} + +char * +find_lib_by_soname (const char *soname, struct dso_list *loader, + int elfclass, int machine) +{ + char *ret; + + if (strchr (soname, '/')) + return strdup (soname); + + if (is_ldso_soname (soname)) + /* For dynamic linker, pull the path out of PT_INTERP header. + When loading an executable the dynamic linker creates an entry for + itself under the name stored in PT_INTERP, and the name that we + record in .gnu.liblist should match that exactly. */ + { + struct dso_list *loader_p = loader; + + while (loader_p) + { + if (loader_p->dso->ehdr.e_type == ET_EXEC) + { + int i; + + for (i = 0; i < loader_p->dso->ehdr.e_phnum; ++i) + if (loader_p->dso->phdr[i].p_type == PT_INTERP) + { + const char *interp; + interp = get_data (loader_p->dso, + loader_p->dso->phdr[i].p_vaddr, + NULL, NULL); + return strdup (interp); + } + } + loader_p = loader_p->loader; + } + } + + if (loader->dso->info[DT_RUNPATH] == 0) + { + /* Search DT_RPATH all the way up. */ + struct dso_list *loader_p = loader; + while (loader_p) + { + if (loader_p->dso->info[DT_RPATH]) + { + struct search_path r_path; + const char *rpath = get_data (loader_p->dso, + loader_p->dso->info[DT_STRTAB] + + loader_p->dso->info[DT_RPATH], + NULL, NULL); + memset (&r_path, 0, sizeof (r_path)); + string_to_path (&r_path, rpath); + ret = find_lib_in_path (&r_path, soname, elfclass, machine); + free_path (&r_path); + if (ret) + return ret; + } + loader_p = loader_p->loader; + } + } + + ret = find_lib_in_path (&ld_library_search_path, soname, elfclass, machine); + if (ret) + return ret; + + if (loader->dso->info[DT_RUNPATH]) + { + struct search_path r_path; + const char *rpath = get_data (loader->dso, + loader->dso->info[DT_STRTAB] + + loader->dso->info[DT_RUNPATH], + NULL, NULL); + memset (&r_path, 0, sizeof (r_path)); + string_to_path (&r_path, rpath); + ret = find_lib_in_path (&r_path, soname, elfclass, machine); + free_path (&r_path); + if (ret) + return ret; + } + + ret = find_lib_in_path (&ld_dirs, soname, elfclass, machine); + if (ret) + return ret; + + return NULL; +} + +static struct dso_list * +load_dsos (DSO *dso, int host_paths) +{ + struct dso_list *dso_list, *dso_list_tail, *cur_dso_ent, *new_dso_ent; + struct stat64 st; + int total_preload = 0; + char * libname[MAX_PRELOADED_LIBS] = {NULL}; + + /* Assume it's static unless we find DT_NEEDED entries */ + static_binary = 1; + + dso_list = malloc (sizeof (struct dso_list)); + dso_list->dso = dso; + dso_list->map = NULL; + dso_list->next = NULL; + dso_list->prev = NULL; + dso_list->needed = NULL; + dso_list->name = dso->filename; + dso_list->loader = NULL; + + if (host_paths) + dso_list->canon_filename = canonicalize_file_name (dso->filename); + else + dso_list->canon_filename = prelink_canonicalize (dso->filename, &st); + + if (dso_list->canon_filename == NULL) + dso_list->canon_filename = strdup (dso->filename); + + cur_dso_ent = dso_list_tail = dso_list; + + if(dso->ehdr.e_type == ET_EXEC && ld_preload) { + char *next_lib = ld_preload; + libname[total_preload] = ld_preload; + total_preload++; + next_lib=strchr(ld_preload,':'); + while(next_lib!=NULL){ + *next_lib = '\0'; + next_lib++; + libname[total_preload] = next_lib; + total_preload++; + next_lib=strchr(next_lib,':'); + } + } + else { + total_preload = 0; + } + while (cur_dso_ent != NULL) + { + DSO *cur_dso, *new_dso; + Elf_Scn *scn; + Elf_Data *data; + GElf_Dyn dyn; + + cur_dso = cur_dso_ent->dso; + if (cur_dso == NULL) + { + cur_dso_ent = cur_dso_ent->next; + continue; + } + + scn = cur_dso->scn[cur_dso->dynamic]; + data = NULL; + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + maxndx = data->d_size / cur_dso->shdr[cur_dso->dynamic].sh_entsize; + for (ndx = 0; ndx < maxndx + total_preload; ++ndx) + { + + if(ndx - total_preload >= 0) { + gelfx_getdyn (cur_dso->elf, data, ndx - total_preload, &dyn); + } + else { + dyn.d_tag = DT_NEEDED; + } + + if (dyn.d_tag == DT_NULL) + break; + if (dyn.d_tag == DT_NEEDED) + { + /* Not static... */ + static_binary = 0; + + char *new_name=NULL, *new_canon_name=NULL; + const char * soname = NULL; + if(ndx - total_preload >= 0) { + soname = get_data (cur_dso, + cur_dso->info[DT_STRTAB] + + dyn.d_un.d_val, + NULL, NULL); + } + else { + soname = libname[ndx]; + } + + new_dso_ent = in_dso_list (dso_list, soname, NULL); + if (new_dso_ent == NULL) + { + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) + _dl_debug_printf ("file=%s [0]; needed by %s [0]\n", + soname, cur_dso->filename); + + int machine; + int class = gelf_getclass (dso->elf); + machine = (class == ELFCLASS32) ? + elf32_getehdr (dso->elf)->e_machine : + elf64_getehdr (dso->elf)->e_machine; + new_name = find_lib_by_soname (soname, cur_dso_ent, + class, machine); + if (new_name == 0 || wrap_access (new_name, R_OK) < 0) + { + dso_open_error ++; + + new_dso_ent = malloc (sizeof (struct dso_list)); + dso_list_tail->next = new_dso_ent; + dso_list_tail->next->prev = dso_list_tail; + dso_list_tail = dso_list_tail->next; + dso_list_tail->next = NULL; + dso_list_tail->dso = NULL; + dso_list_tail->map = NULL; + dso_list_tail->needed = NULL; + dso_list_tail->name = soname; + dso_list_tail->loader = NULL; + dso_list_tail->canon_filename = strdup(soname); + dso_list_tail->err_no = errno; + + continue; + } + + /* See if the filename we found has already been + opened (possibly under a different SONAME via + some symlink). */ + new_canon_name = prelink_canonicalize (new_name, NULL); + if (new_canon_name == NULL) + new_canon_name = strdup (new_name); + new_dso_ent = in_dso_list (dso_list, soname, new_canon_name); + } + else if (new_dso_ent->dso == NULL) + continue; + + if (new_dso_ent == NULL) + { + new_dso = open_dso (new_name); + free (new_name); + new_dso_ent = malloc (sizeof (struct dso_list)); + dso_list_tail->next = new_dso_ent; + dso_list_tail->next->prev = dso_list_tail; + dso_list_tail = dso_list_tail->next; + dso_list_tail->next = NULL; + dso_list_tail->dso = new_dso; + dso_list_tail->map = NULL; + dso_list_tail->needed = NULL; + dso_list_tail->loader = cur_dso_ent; + dso_list_tail->canon_filename = new_canon_name; + dso_list_tail->err_no = 0; + + if (is_ldso_soname (new_dso->soname)) + dso_list_tail->name = new_dso->filename; + else if (strcmp (new_dso->soname, new_dso->filename) == 0) + /* new_dso->soname might be a full path if the library + had no SONAME. Use the original SONAME instead. */ + dso_list_tail->name = soname; + else + /* Use the new SONAME if possible, in case some library + links to this one using an incorrect SONAME. */ + dso_list_tail->name = new_dso->soname; + } + + if (!cur_dso_ent->needed) + { + cur_dso_ent->needed = malloc (sizeof (struct needed_list)); + cur_dso_ent->needed_tail = cur_dso_ent->needed; + cur_dso_ent->needed_tail->ent = new_dso_ent; + cur_dso_ent->needed_tail->next = NULL; + } + else if (!in_needed_list (cur_dso_ent->needed, soname)) + { + cur_dso_ent->needed_tail->next = malloc (sizeof (struct needed_list)); + cur_dso_ent->needed_tail = cur_dso_ent->needed_tail->next; + cur_dso_ent->needed_tail->ent = new_dso_ent; + cur_dso_ent->needed_tail->next = NULL; + } + + continue; + } + if (dyn.d_tag == DT_FILTER || dyn.d_tag == DT_AUXILIARY) + { + // big fat warning; + } + } + } + cur_dso_ent = cur_dso_ent->next; + } + return dso_list; +} + +struct +{ + void *symptr; + int rtypeclass; +} cache; + +void +do_reloc (DSO *dso, struct link_map *map, struct r_scope_elem *scope[], + GElf_Word sym, GElf_Word type) +{ + struct r_found_version *ver; + int rtypeclass; + void *symptr; + const char *name; + Elf64_Word st_name; + + if (map->l_versyms) + { + int vernum = map->l_versyms[sym] & 0x7fff; + ver = &map->l_versions[vernum]; + + /* Memory was allocated for the hash table, but it's empty! */ + if (ver && (ver->name == NULL || ver->hash == 0)) + ver = NULL; + } + else + ver = NULL; + + rtypeclass = elf_machine_type_class (type, dso->ehdr.e_machine); + + if (gelf_getclass (dso->elf) == ELFCLASS32) + { + Elf32_Sym *sym32 = &((Elf32_Sym *)map->l_info[DT_SYMTAB])[sym]; + + if (ELF32_ST_BIND (sym32->st_info) == STB_LOCAL) + return; + symptr = sym32; + st_name = sym32->st_name; + } + else + { + Elf64_Sym *sym64 = &((Elf64_Sym *)map->l_info[DT_SYMTAB])[sym]; + + if (ELF64_ST_BIND (sym64->st_info) == STB_LOCAL) + return; + symptr = sym64; + st_name = sym64->st_name; + } + + if (cache.symptr == symptr && cache.rtypeclass == rtypeclass) + return; + cache.symptr = symptr; + cache.rtypeclass = rtypeclass; + + name = ((const char *)map->l_info[DT_STRTAB]) + st_name; + + if (gelf_getclass (dso->elf) == ELFCLASS32) + { + const Elf32_Sym *sym32 = symptr; + rtld_lookup_symbol_x32 (name, map, &sym32, scope, ver, rtypeclass, + 0, NULL); + symptr = (void *) sym32; + } + else + { + const Elf64_Sym *sym64 = symptr; + rtld_lookup_symbol_x64 (name, map, &sym64, scope, ver, rtypeclass, + 0, NULL); + symptr = (void *) sym64; + } +} + +void +do_rel_section (DSO *dso, struct link_map *map, + struct r_scope_elem *scope[], + int tag, int section) +{ + Elf_Data *data; + int ndx, maxndx, sym, type; + + data = elf_getdata (dso->scn[section], NULL); + maxndx = data->d_size / dso->shdr[section].sh_entsize; + for (ndx = 0; ndx < maxndx; ndx++) + { + if (tag == DT_REL) + { + GElf_Rel rel; + gelfx_getrel (dso->elf, data, ndx, &rel); + sym = reloc_r_sym (dso, rel.r_info); + type = reloc_r_type (dso, rel.r_info); + } + else + { + GElf_Rela rela; + gelfx_getrela (dso->elf, data, ndx, &rela); + sym = reloc_r_sym (dso, rela.r_info); + type = reloc_r_type (dso, rela.r_info); + } + if (sym != 0) + do_reloc (dso, map, scope, sym, type); + } +} + +void +do_relocs (DSO *dso, struct link_map *map, struct r_scope_elem *scope[], int tag) +{ + GElf_Addr rel_start, rel_end; + GElf_Addr pltrel_start, pltrel_end; + int first, last; + + /* Load the DT_REL or DT_RELA section. */ + if (dso->info[tag] != 0) + { + rel_start = dso->info[tag]; + rel_end = rel_start + dso->info[tag == DT_REL ? DT_RELSZ : DT_RELASZ]; + first = addr_to_sec (dso, rel_start); + last = addr_to_sec (dso, rel_end - 1); + while (first <= last) + do_rel_section (dso, map, scope, tag, first++); + + /* If the DT_JMPREL relocs are of the same type and not included, + load them too. Assume they overlap completely or not at all, + and are in at most a single section. They also need to be adjacent. */ + if (dso->info[DT_PLTREL] == tag) + { + pltrel_start = dso->info[DT_JMPREL]; + pltrel_end = pltrel_start + dso->info[DT_PLTRELSZ]; + if (pltrel_start < rel_start || pltrel_end >= rel_end) + do_rel_section (dso, map, scope, tag, addr_to_sec (dso, pltrel_start)); + } + } + else if (dso->info[DT_PLTREL] == tag) + do_rel_section (dso, map, scope, tag, addr_to_sec (dso, dso->info[DT_JMPREL])); +} + +/* MIPS GOTs are not handled by normal relocations. Instead, entry X + in the global GOT is associated with symbol DT_MIPS_GOTSYM + X. + For the purposes of symbol lookup and conflict resolution, we need + to act as though entry X had a dynamic relocation against symbol + DT_MIPS_GOTSYM + X. */ + +void +do_mips_global_got_relocs (DSO *dso, struct link_map *map, + struct r_scope_elem *scope[]) +{ + GElf_Word i; + + for (i = dso->info_DT_MIPS_GOTSYM; i < dso->info_DT_MIPS_SYMTABNO; i++) + do_reloc (dso, map, scope, i, R_MIPS_REL32); +} + +void +handle_relocs_in_entry (struct dso_list *entry, struct dso_list *dso_list) +{ + do_relocs (entry->dso, entry->map, dso_list->map->l_local_scope, DT_REL); + do_relocs (entry->dso, entry->map, dso_list->map->l_local_scope, DT_RELA); + if (entry->dso->ehdr.e_machine == EM_MIPS) + do_mips_global_got_relocs (entry->dso, entry->map, + dso_list->map->l_local_scope); +} + +void +handle_relocs (DSO *dso, struct dso_list *dso_list) +{ + struct dso_list *ldso, *tail; + + /* do them all last to first. + skip the dynamic linker; then do it last + in glibc this is conditional on the opencount; but every binary + should be linked to libc and thereby have an opencount for ld.so... + besides, that's the only way it would get on our dso list. */ + + tail = dso_list; + while (tail->next) + tail = tail->next; + + ldso = NULL; + while (tail) + { + if (is_ldso_soname (tail->dso->soname)) + ldso = tail; + else + handle_relocs_in_entry (tail, dso_list); + tail = tail->prev; + } + + if (ldso) + handle_relocs_in_entry (ldso, dso_list); +} + +void +add_to_scope (struct r_scope_elem *scope[], struct dso_list *ent) +{ + struct needed_list *n; + int i; + + for (i = 0; i < scope[0]->r_nlist; i++) + if (scope[0]->r_list[i] == ent->map) + return; + + scope[0]->r_list[scope[0]->r_nlist++] = ent->map; + n = ent->needed; + while (n) + { + add_to_scope (scope, n->ent); + n = n->next; + } +} + +void +build_local_scope (struct dso_list *ent, int max) +{ + ent->map->l_local_scope[0] = malloc (sizeof (struct r_scope_elem)); + ent->map->l_local_scope[0]->r_list = malloc (sizeof (struct link_map *) * max); + ent->map->l_local_scope[0]->r_nlist = 0; + add_to_scope (ent->map->l_local_scope, ent); +} + +static struct argp argp = { options, parse_opt, "[FILES]", argp_doc }; + +struct link_map *requested_map = NULL; + +static void process_one_dso (DSO *dso, int host_paths); + +int +main(int argc, char **argv) +{ + int remaining; + int multiple = 0; + host_paths = 1; + + char * debug = NULL; + + sysroot = getenv ("PRELINK_SYSROOT"); +#ifdef DEFAULT_SYSROOT + if (sysroot == NULL) + { + extern char *make_relative_prefix (const char *, const char *, const char *); + sysroot = make_relative_prefix (argv[0], BINDIR, DEFAULT_SYSROOT); + } +#endif + + elf_version (EV_CURRENT); + + argp_parse (&argp, argc, argv, 0, &remaining, 0); + + if (sysroot) + sysroot = canonicalize_file_name (sysroot); + + if (remaining == argc) + error (1, 0, "missing file arguments\nTry `%s: --help' for more information.", argv[0]); + + if ((argc-remaining) >= 2) + multiple = 1; + + /* More simplistic then glibc, one option only... */ + debug = getenv ("RTLD_DEBUG"); + if (debug && (!strcmp(debug, "files") || !strcmp(debug, "all"))) + _dl_debug_mask |= DL_DEBUG_FILES; + + if (debug && (!strcmp(debug, "symbols") || !strcmp(debug, "all"))) + _dl_debug_mask |= DL_DEBUG_SYMBOLS; + + if (debug && (!strcmp(debug, "versions") || !strcmp(debug, "all"))) + _dl_debug_mask |= DL_DEBUG_VERSIONS; + + if (debug && (!strcmp(debug, "bindings") || !strcmp(debug, "all"))) + _dl_debug_mask |= DL_DEBUG_BINDINGS; + + while (remaining < argc) + { + DSO *dso = NULL; + int i, fd; + + struct stat64 st; + + if (host_paths) + fd = open (argv[remaining], O_RDONLY); + else + fd = wrap_open (argv[remaining], O_RDONLY); + + if (fd >= 0 && fstat64(fd, &st) == 0) + if (!S_ISREG(st.st_mode)) + { + error (0, 0, "%s: %s", + argv[remaining], + "not regular file"); + goto exit; + } + + if (fd >= 0) { + dso = fdopen_dso (fd, argv[remaining]); + dst_ORIGIN = dirname(strdup(dso->filename)); + } + + if (dso == NULL) + { + error (0, 0, "%s: %s", + argv[remaining], + strerror(errno)); + goto exit; + } + + load_ld_so_conf (gelf_getclass (dso->elf) == ELFCLASS64, + ( dso->ehdr.e_machine == EM_MIPS) && ( dso->ehdr.e_flags & EF_MIPS_ABI2 ) ); + + if (multiple) + printf ("%s:\n", argv[remaining]); + + for (i = 0; i < dso->ehdr.e_phnum; ++i) + if (dso->phdr[i].p_type == PT_INTERP) + break; + + /* If there are no PT_INTERP segments, it is statically linked. */ + if (dso->ehdr.e_type == ET_EXEC && i == dso->ehdr.e_phnum) + printf ("\tnot a dynamic executable\n"); + else + { + int j; + Elf_Data *data; + j = addr_to_sec (dso, dso->phdr[i].p_vaddr); + if (j != -1) + { + data = elf_getdata (dso->scn[j], NULL); + if (data != NULL) + { + i = strnlen (data->d_buf, data->d_size); + if (i == data->d_size) + { + rtld_signal_error (0, dso->filename, NULL, ".interp section not zero terminated", 0); + } + else + { + rtld_progname = strdup (data->d_buf); + } + } + } + process_one_dso (dso, host_paths); + } + +exit: + remaining++; + } + + return 0; +} + +/* If you run ldd /lib/ld.so you get: + \tstatically linked + + The prelink-rtld does not do this, and returns blank... + */ +static void +process_one_dso (DSO *dso, int host_paths) +{ + struct dso_list *dso_list, *cur_dso_ent, *old_dso_ent; + const char *req; + int i; + int ld_warn = 1; + + if ((req = getenv ("RTLD_TRACE_PRELINKING")) != NULL) + _dl_debug_mask |= DL_DEBUG_PRELINK; + + /* Close enough. Really it's if LD_WARN is "" and RTLD_TRACE_PRELINKING. */ + if (getenv ("RTLD_WARN") == NULL) + ld_warn = 0; + + /* Initialize unique symtable list */ + _ns_unique_sym_table = calloc(sizeof (struct unique_sym_table), 1); + + dso_list = load_dsos (dso, host_paths); + + cur_dso_ent = dso_list; + i = 0; + while (cur_dso_ent) + { + if (cur_dso_ent->dso) + { + create_map_object_from_dso_ent (cur_dso_ent); + if ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) && strcmp (req, cur_dso_ent->dso->filename) == 0) + requested_map = cur_dso_ent->map; + } + else + { + /* This is a dummy entry, we couldn't find the object */ + cur_dso_ent->map = _dl_new_object(cur_dso_ent->name, cur_dso_ent->canon_filename, lt_library); + } + i++; + cur_dso_ent = cur_dso_ent->next; + } + dso_list->map->l_local_scope[0] = malloc (sizeof (struct r_scope_elem)); + dso_list->map->l_local_scope[0]->r_list = malloc (sizeof (struct link_map *) * i); + dso_list->map->l_local_scope[0]->r_nlist = i; + cur_dso_ent = dso_list; + i = 0; + while (cur_dso_ent) + { + if (cur_dso_ent->map) + { + dso_list->map->l_local_scope[0]->r_list[i] = cur_dso_ent->map; + if (cur_dso_ent != dso_list) + build_local_scope (cur_dso_ent, dso_list->map->l_local_scope[0]->r_nlist); + + i++; + } + cur_dso_ent = cur_dso_ent->next; + } + + cur_dso_ent = dso_list; + + for (i = 0; i < cur_dso_ent->map->l_local_scope[0]->r_nlist; ++i) + if (cur_dso_ent->map->l_local_scope[0]->r_list[i]->l_versions == NULL) + _dl_check_map_versions (cur_dso_ent->map->l_local_scope[0]->r_list[i], 0, 0); + + rtld_determine_tlsoffsets (dso->ehdr.e_machine, dso_list->map->l_local_scope[0]); + + cur_dso_ent = dso_list; + + /* In ldd mode, do not show the application. Note that we do show it + in list-loaded-objects RTLD_TRACE_PRELINK mode. */ + if (!(GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) && cur_dso_ent) + { + /* Based on the presence of DT_NEEDED, see load_dsos */ + if (static_binary) + { + printf ("\tstatically linked\n"); + } + cur_dso_ent = cur_dso_ent->next; + } + while (cur_dso_ent) + { + char *filename; + + if (host_paths && sysroot && cur_dso_ent->dso) + { + const char *rooted_filename; + + if (cur_dso_ent->dso->filename[0] == '/') + rooted_filename = cur_dso_ent->dso->filename; + else + rooted_filename = cur_dso_ent->canon_filename; + + filename = malloc (strlen (rooted_filename) + strlen (sysroot) + 1); + strcpy (filename, sysroot); + strcat (filename, rooted_filename); + } + else if (cur_dso_ent->dso) + filename = strdup (cur_dso_ent->dso->filename); + else + filename = NULL; + + int size_pointer = 16; + if (cur_dso_ent && cur_dso_ent->dso && gelf_getclass (cur_dso_ent->dso->elf) == ELFCLASS32) + size_pointer = 8; + + /* The difference between the two numbers must be dso->base, + and the first number must be unique. */ + if (dso_open_error && ld_warn && (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)) + { + if (cur_dso_ent->dso == NULL) + rtld_signal_error(cur_dso_ent->err_no, cur_dso_ent->name, NULL, "cannot open shared object file", 0); + } + else if (cur_dso_ent->dso == NULL) + printf ("\t%s => not found\n", cur_dso_ent->name); + else if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) + { + struct link_map * l = cur_dso_ent->map; + if (size_pointer == 16) + printf ("\t%s => %s (0x%016"PRIx64", 0x%016"PRIx64")", + cur_dso_ent->name ? cur_dso_ent->name + : rtld_progname ?: "<main program>", + filename ? filename + : rtld_progname ?: "<main program>", + (uint64_t) l->l_map_start, + (uint64_t) (l->l_map_start == cur_dso_ent->dso->base ? 0 : l->l_map_start)); + else + printf ("\t%s => %s (0x%08"PRIx32", 0x%08"PRIx32")", + cur_dso_ent->name ? cur_dso_ent->name + : rtld_progname ?: "<main program>", + filename ? filename + : rtld_progname ?: "<main program>", + (uint32_t) l->l_map_start, + (uint32_t) (l->l_map_start == cur_dso_ent->dso->base ? 0 : l->l_map_start)); + + if (l->l_tls_modid) + if (size_pointer == 16) + printf (" TLS(0x%"PRIx64", 0x%016"PRIx64")\n", + (uint64_t) l->l_tls_modid, + (uint64_t) l->l_tls_offset); + else + printf (" TLS(0x%"PRIx32", 0x%08"PRIx32")\n", + (uint32_t) l->l_tls_modid, + (uint32_t) l->l_tls_offset); + else + printf ("\n"); + } + else + { + struct link_map * l = cur_dso_ent->map; + if (!(GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) && strcmp (cur_dso_ent->name, filename) == 0) + if (size_pointer == 16) + printf ("\t%s (0x%016"PRIx64")\n", cur_dso_ent->name, + (uint64_t) l->l_map_start); + else + printf ("\t%s (0x%08"PRIx32")\n", cur_dso_ent->name, + (uint32_t) l->l_map_start); + else + if (size_pointer == 16) + printf ("\t%s => %s (0x%016"PRIx64")\n", cur_dso_ent->name, + filename, + (uint64_t) l->l_map_start); + else + printf ("\t%s => %s (0x%08"PRIx32")\n", cur_dso_ent->name, + filename, + (uint32_t) l->l_map_start); + } + + if (filename) + free (filename); + + cur_dso_ent = cur_dso_ent->next; + } + + if (dso_open_error) + exit (127); + + if (!ld_warn && (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)) + handle_relocs (dso_list->dso, dso_list); + + cur_dso_ent = dso_list; + while (cur_dso_ent) + { + if (cur_dso_ent->dso) + close_dso (cur_dso_ent->dso); + old_dso_ent = cur_dso_ent; + cur_dso_ent = cur_dso_ent->next; + if (old_dso_ent->needed) + free_needed (old_dso_ent->needed); + free (old_dso_ent); + } +} diff --git a/src/rtld/rtld.h b/src/rtld/rtld.h new file mode 100644 index 0000000..c7b5d91 --- /dev/null +++ b/src/rtld/rtld.h @@ -0,0 +1,338 @@ +#ifndef _LD_LIBS_H +#define _LD_LIBS_H + +#include "prelinktab.h" + +#include <elf.h> + +#if !defined (__linux__) +#define DT_VERSIONTAGNUM 16 +#endif + +#define link_map ldlibs_link_map + +struct needed_list +{ + struct dso_list *ent; + struct needed_list *next; +}; + +struct dso_list +{ + DSO *dso; + struct link_map *map; + struct dso_list *next, *prev; + struct needed_list *needed, *needed_tail; + const char *name; + struct dso_list *loader; + const char *canon_filename; + int err_no; +}; + +/* A data structure for a simple single linked list of strings. */ +struct libname_list + { + const char *name; /* Name requested (before search). */ + struct libname_list *next; /* Link to next name for this object. */ + }; + +struct link_map; + +struct r_scope_elem +{ + struct link_map **r_list; + unsigned int r_nlist; +}; + +struct r_found_version + { + const char *name; + Elf64_Word hash; + + int hidden; + const char *filename; + }; + +struct unique_sym_table + { + struct unique_sym + { + uint32_t hashval; + const char *name; + const void *sym; + const struct link_map *map; + } *entries; + size_t size; + size_t n_elements; + void (*free) (void *); + }; + +extern struct unique_sym_table * _ns_unique_sym_table; + +/* The size of entries in .hash. Only Alpha and 64-bit S/390 use 64-bit + entries; those are not currently supported. */ +typedef uint32_t Elf_Symndx; + +/* Mimic libc/include/link.h struct link_map */ + +struct link_map + { + int elfclass; + + const char *l_name; + + struct libname_list *l_libname; + +#undef DT_THISPROCNUM +#define DT_THISPROCNUM 0 + + void *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM]; + + /* Array with vesion names. */ + struct r_found_version *l_versions; + unsigned int l_nversions; + + /* Symbol hash table. */ + Elf_Symndx l_nbuckets; + /* Begin PPC64 workaround */ + void *l_buckets_start; + void *l_buckets_end; + /* end workaround */ + Elf32_Word l_gnu_bitmask_idxbits; + Elf32_Word l_gnu_shift; + void *l_gnu_bitmask; + union + { + const Elf32_Word *l_gnu_buckets; + const Elf_Symndx *l_chain; + }; + union + { + const Elf32_Word *l_gnu_chain_zero; + const Elf_Symndx *l_buckets; + }; + + enum /* Where this object came from. */ + { + lt_executable, /* The main executable program. */ + lt_library, /* Library needed by main executable. */ + lt_loaded /* Extra run-time loaded shared object. */ + } l_type:2; + + /* Pointer to the version information if available. */ + Elf64_Versym *l_versyms; + + /* Start and finish of memory map for this object. l_map_start + need not be the same as l_addr. */ + Elf64_Addr l_map_start; + + /* A similar array, this time only with the local scope. This is + used occasionally. */ + struct r_scope_elem *l_local_scope[2]; + + /* Thread-local storage related info. */ + + /* Size of the TLS block. */ + uint64_t l_tls_blocksize; + /* Alignment requirement of the TLS block. */ + uint64_t l_tls_align; + /* Offset of first byte module alignment. */ + uint64_t l_tls_firstbyte_offset; + + /* For objects present at startup time: offset in the static TLS block. */ + uint64_t l_tls_offset; + /* Index of the module in the dtv array. */ + uint64_t l_tls_modid; + + Elf64_Addr sym_base; + const char *filename; + + Elf64_Half machine; + }; + +#define ELF_RTYPE_CLASS_COPY 2 +#define ELF_RTYPE_CLASS_PLT 1 + +int elf_machine_type_class(int type, int machine); + +int extern_protected_data(int machine); +#define ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA(machine) extern_protected_data(machine) + +int machine_no_rela(int machine); +#define ELF_MACHINE_NO_RELA(machine) machine_no_rela(machine) + +int machine_no_rel(int machine); +#define ELF_MACHINE_NO_REL(machine) machine_no_rel(machine) + +#define GL(x) _##x +#define GLRO(x) _##x +#define INTUSE(x) x + +#define D_PTR(MAP,MEM) MAP->MEM +#define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym)) + +extern unsigned int _dl_debug_mask; +extern unsigned int _dl_dynamic_weak; + +extern const char *rtld_progname; + +/* This is an optional value before the ':' when debuging is enabled. */ +/* Typically glibc sets this to a number of spaces and the pid of the process*/ +#ifndef RTLD_DEBUG_PID +# define RTLD_DEBUG_PID " " +#endif +#define _dl_debug_printf(...) printf( RTLD_DEBUG_PID ":\t" __VA_ARGS__) + +#define __rtld_lock_lock_recursive(NAME) +#define __rtld_lock_unlock_recursive(NAME) + +/* glibc-2.20: sysdeps/generic/ldsodefs.h */ +/* The filename itself, or the main program name, if available. */ +#define DSO_FILENAME(name) ((name)[0] ? (name) \ + : (rtld_progname ?: "<main program>")) + +#define RTLD_PROGNAME (rtld_progname ?: "<program name unknown>") + +/* glibc-2.20: sysdeps/generic/ldsodefs.h */ +#define DL_DEBUG_LIBS (1 << 0) +#define DL_DEBUG_IMPCALLS (1 << 1) +#define DL_DEBUG_BINDINGS (1 << 2) +#define DL_DEBUG_SYMBOLS (1 << 3) +#define DL_DEBUG_VERSIONS (1 << 4) +#define DL_DEBUG_RELOC (1 << 5) +#define DL_DEBUG_FILES (1 << 6) +#define DL_DEBUG_STATISTICS (1 << 7) +#define DL_DEBUG_UNUSED (1 << 8) +#define DL_DEBUG_SCOPES (1 << 9) +/* These two are used only internally. */ +#define DL_DEBUG_HELP (1 << 10) +#define DL_DEBUG_PRELINK (1 << 11) + +#define _dl_trace_prelink_map requested_map + +extern struct link_map *requested_map; + +#ifndef __glibc_unlikely +#define __glibc_unlikely(a) (a) +#endif + +#ifndef __glibc_likely +#define __glibc_likely(a) (a) +#endif + +/* dl-load.c */ + +#define _dl_new_object rtld_new_object + +struct link_map * _dl_new_object (const char *realname, const char *libname, int type); + +/* dl-lookup.c */ + +#define lookup_t struct link_map * +#define LOOKUP_VALUE(map) map + +/* Search loaded objects' symbol tables for a definition of the symbol + referred to by UNDEF. *SYM is the symbol table entry containing the + reference; it is replaced with the defining symbol, and the base load + address of the defining object is returned. SYMBOL_SCOPE is a + null-terminated list of object scopes to search; each object's + l_searchlist (i.e. the segment of the dependency tree starting at that + object) is searched in turn. REFERENCE_NAME should name the object + containing the reference; it is used in error messages. + TYPE_CLASS describes the type of symbol we are looking for. */ +enum + { + /* If necessary add dependency between user and provider object. */ + DL_LOOKUP_ADD_DEPENDENCY = 1, + /* Return most recent version instead of default version for + unversioned lookup. */ + DL_LOOKUP_RETURN_NEWEST = 2, + /* Set if dl_lookup* called with GSCOPE lock held. */ + DL_LOOKUP_GSCOPE_LOCK = 4, + }; + +#define _dl_setup_hash rtld_setup_hash +void _dl_setup_hash (struct link_map *map); + +#define _dl_lookup_symbol_x32 rtld_lookup_symbol_x32 +#define _dl_lookup_symbol_x64 rtld_lookup_symbol_x64 + +/* Lookup versioned symbol. */ +inline lookup_t _dl_lookup_symbol_x (const char *undef, + struct link_map *undef_map, + const Elf64_Sym **sym, + struct r_scope_elem *symbol_scope[], + const struct r_found_version *version, + int type_class, int flags, + struct link_map *skip_map); + +/* Lookup versioned symbol. */ +lookup_t _dl_lookup_symbol_x32 (const char *undef, + struct link_map *undef_map, + const Elf32_Sym **sym, + struct r_scope_elem *symbol_scope[], + const struct r_found_version *version, + int type_class, int flags, + struct link_map *skip_map); + +/* Lookup versioned symbol. */ +lookup_t _dl_lookup_symbol_x64 (const char *undef, + struct link_map *undef_map, + const Elf64_Sym **sym, + struct r_scope_elem *symbol_scope[], + const struct r_found_version *version, + int type_class, int flags, + struct link_map *skip_map); + +/* dl-version.c */ + +#define _dl_check_map_versions rtld_check_map_versions +int _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode); + +#define _dl_name_match_p rtld_name_match_p +int _dl_name_match_p (const char *name, const struct link_map *map); + +/* Error handling */ + +#include <error.h> +#include <errno.h> + +/* Mimic the behavior and output of _dl_signal_error */ +#define rtld_signal_error(errcode, objname, occation, errstring, status) \ + error(status, errcode, "%s: %s%s%s", \ + occation ?: "error while loading shared libraries", \ + objname ?: "", (objname && *(char *)objname) ? ": " : "", \ + errstring ?: "DYNAMIC LINKER BUG!!!") + +#define _dl_signal_error(errcode, objname, occation, errstring) rtld_signal_error(errcode, objname, occation, errstring, 1) +#define _dl_signal_cerror(errcode, objname, occation, errstring) rtld_signal_error(errcode, objname, occation, errstring, 0) +#define _dl_fatal_printf(errstring) rtld_signal_error(EINVAL, NULL, NULL, errstring, 1) + +/* dl-load.c */ + +extern void create_map_object_from_dso_ent (struct dso_list *); + +/* dl-tls.c */ + +void rtld_determine_tlsoffsets (int e_machine, struct r_scope_elem *search_list); + +#define _dl_determine_tlsoffsets rtld_determine_tlsoffsets + +/* dl-misc.c */ + +#define _dl_name_match_p rtld_name_match_p +#define _dl_higher_prime_number rtld_higher_prime_number + +extern int _dl_name_match_p (const char *name, const struct link_map *map); +extern unsigned long int _dl_higher_prime_number (unsigned long int n); + + +#if defined(__MINGW32__) +# define HOST_LONG_LONG_FORMAT "I64" +#else +# define HOST_LONG_LONG_FORMAT "ll" +#endif + +#endif + diff --git a/src/sha.c b/src/sha.c new file mode 100644 index 0000000..cfbc956 --- /dev/null +++ b/src/sha.c @@ -0,0 +1,331 @@ +/* sha.c - Functions to compute the SHA1 hash (message-digest) of files + or blocks of memory. Complies to the NIST specification FIPS-180-1. + + Copyright (C) 2000, 2001, 2003 Scott G. Miller + + Credits: + Robert Klep <robert@ilse.nl> -- Expansion function fix + NOTE: The canonical source of this file is maintained in GNU coreutils. +*/ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <byteswap.h> +#include "md5.h" +#include "sha.h" + +/* + Not-swap is a macro that does an endian swap on architectures that are + big-endian, as SHA needs some data in a little-endian format +*/ + +#if __BYTE_ORDER == __BIG_ENDIAN +# define SWAP(n) bswap_32 (n) +# define NOTSWAP(n) (n) +#else +# define SWAP(n) (n) +# define NOTSWAP(n) bswap_32 (n) +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* + Takes a pointer to a 160 bit block of data (five 32 bit ints) and + intializes it to the start constants of the SHA1 algorithm. This + must be called before using hash in the call to sha_hash +*/ +void +sha_init_ctx (struct sha_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + ctx->E = 0xc3d2e1f0; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 20 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +sha_read_ctx (const struct sha_ctx *ctx, void *resbuf) +{ + ((md5_uint32 *) resbuf)[0] = NOTSWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = NOTSWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = NOTSWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = NOTSWAP (ctx->D); + ((md5_uint32 *) resbuf)[4] = NOTSWAP (ctx->E); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +sha_finish_ctx (struct sha_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = NOTSWAP (ctx->total[0] << 3); + *(md5_uint32 *) &ctx->buffer[bytes + pad] = NOTSWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + sha_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return sha_read_ctx (ctx, resbuf); +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +sha_buffer (const char *buffer, size_t len, void *resblock) +{ + struct sha_ctx ctx; + + /* Initialize the computation context. */ + sha_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + sha_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return sha_finish_ctx (&ctx, resblock); +} + +void +sha_process_bytes (const void *buffer, size_t len, struct sha_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + sha_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (md5_uint32) != 0) + if (UNALIGNED_P (buffer)) + while (len > 64) + { + sha_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else + { + sha_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + sha_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + +/* --- Code below is the primary difference between md5.c and sha.c --- */ + +/* SHA1 round constants */ +#define K1 0x5a827999L +#define K2 0x6ed9eba1L +#define K3 0x8f1bbcdcL +#define K4 0xca62c1d6L + +/* Round functions. Note that F2 is the same as F4. */ +#define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) ) +#define F2(B,C,D) (B ^ C ^ D) +#define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) ) +#define F4(B,C,D) (B ^ C ^ D) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. + Most of this code comes from GnuPG's cipher/sha1.c. */ + +void +sha_process_block (const void *buffer, size_t len, struct sha_ctx *ctx) +{ + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 x[16]; + md5_uint32 a = ctx->A; + md5_uint32 b = ctx->B; + md5_uint32 c = ctx->C; + md5_uint32 d = ctx->D; + md5_uint32 e = ctx->E; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + +#define M(I) ( tm = x[I&0x0f] ^ x[(I-14)&0x0f] \ + ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \ + , (x[I&0x0f] = rol(tm, 1)) ) + +#define R(A,B,C,D,E,F,K,M) do { E += rol( A, 5 ) \ + + F( B, C, D ) \ + + K \ + + M; \ + B = rol( B, 30 ); \ + } while(0) + + while (words < endp) + { + md5_uint32 tm; + int t; + /* FIXME: see sha1.c for a better implementation. */ + for (t = 0; t < 16; t++) + { + x[t] = NOTSWAP (*words); + words++; + } + + R( a, b, c, d, e, F1, K1, x[ 0] ); + R( e, a, b, c, d, F1, K1, x[ 1] ); + R( d, e, a, b, c, F1, K1, x[ 2] ); + R( c, d, e, a, b, F1, K1, x[ 3] ); + R( b, c, d, e, a, F1, K1, x[ 4] ); + R( a, b, c, d, e, F1, K1, x[ 5] ); + R( e, a, b, c, d, F1, K1, x[ 6] ); + R( d, e, a, b, c, F1, K1, x[ 7] ); + R( c, d, e, a, b, F1, K1, x[ 8] ); + R( b, c, d, e, a, F1, K1, x[ 9] ); + R( a, b, c, d, e, F1, K1, x[10] ); + R( e, a, b, c, d, F1, K1, x[11] ); + R( d, e, a, b, c, F1, K1, x[12] ); + R( c, d, e, a, b, F1, K1, x[13] ); + R( b, c, d, e, a, F1, K1, x[14] ); + R( a, b, c, d, e, F1, K1, x[15] ); + R( e, a, b, c, d, F1, K1, M(16) ); + R( d, e, a, b, c, F1, K1, M(17) ); + R( c, d, e, a, b, F1, K1, M(18) ); + R( b, c, d, e, a, F1, K1, M(19) ); + R( a, b, c, d, e, F2, K2, M(20) ); + R( e, a, b, c, d, F2, K2, M(21) ); + R( d, e, a, b, c, F2, K2, M(22) ); + R( c, d, e, a, b, F2, K2, M(23) ); + R( b, c, d, e, a, F2, K2, M(24) ); + R( a, b, c, d, e, F2, K2, M(25) ); + R( e, a, b, c, d, F2, K2, M(26) ); + R( d, e, a, b, c, F2, K2, M(27) ); + R( c, d, e, a, b, F2, K2, M(28) ); + R( b, c, d, e, a, F2, K2, M(29) ); + R( a, b, c, d, e, F2, K2, M(30) ); + R( e, a, b, c, d, F2, K2, M(31) ); + R( d, e, a, b, c, F2, K2, M(32) ); + R( c, d, e, a, b, F2, K2, M(33) ); + R( b, c, d, e, a, F2, K2, M(34) ); + R( a, b, c, d, e, F2, K2, M(35) ); + R( e, a, b, c, d, F2, K2, M(36) ); + R( d, e, a, b, c, F2, K2, M(37) ); + R( c, d, e, a, b, F2, K2, M(38) ); + R( b, c, d, e, a, F2, K2, M(39) ); + R( a, b, c, d, e, F3, K3, M(40) ); + R( e, a, b, c, d, F3, K3, M(41) ); + R( d, e, a, b, c, F3, K3, M(42) ); + R( c, d, e, a, b, F3, K3, M(43) ); + R( b, c, d, e, a, F3, K3, M(44) ); + R( a, b, c, d, e, F3, K3, M(45) ); + R( e, a, b, c, d, F3, K3, M(46) ); + R( d, e, a, b, c, F3, K3, M(47) ); + R( c, d, e, a, b, F3, K3, M(48) ); + R( b, c, d, e, a, F3, K3, M(49) ); + R( a, b, c, d, e, F3, K3, M(50) ); + R( e, a, b, c, d, F3, K3, M(51) ); + R( d, e, a, b, c, F3, K3, M(52) ); + R( c, d, e, a, b, F3, K3, M(53) ); + R( b, c, d, e, a, F3, K3, M(54) ); + R( a, b, c, d, e, F3, K3, M(55) ); + R( e, a, b, c, d, F3, K3, M(56) ); + R( d, e, a, b, c, F3, K3, M(57) ); + R( c, d, e, a, b, F3, K3, M(58) ); + R( b, c, d, e, a, F3, K3, M(59) ); + R( a, b, c, d, e, F4, K4, M(60) ); + R( e, a, b, c, d, F4, K4, M(61) ); + R( d, e, a, b, c, F4, K4, M(62) ); + R( c, d, e, a, b, F4, K4, M(63) ); + R( b, c, d, e, a, F4, K4, M(64) ); + R( a, b, c, d, e, F4, K4, M(65) ); + R( e, a, b, c, d, F4, K4, M(66) ); + R( d, e, a, b, c, F4, K4, M(67) ); + R( c, d, e, a, b, F4, K4, M(68) ); + R( b, c, d, e, a, F4, K4, M(69) ); + R( a, b, c, d, e, F4, K4, M(70) ); + R( e, a, b, c, d, F4, K4, M(71) ); + R( d, e, a, b, c, F4, K4, M(72) ); + R( c, d, e, a, b, F4, K4, M(73) ); + R( b, c, d, e, a, F4, K4, M(74) ); + R( a, b, c, d, e, F4, K4, M(75) ); + R( e, a, b, c, d, F4, K4, M(76) ); + R( d, e, a, b, c, F4, K4, M(77) ); + R( c, d, e, a, b, F4, K4, M(78) ); + R( b, c, d, e, a, F4, K4, M(79) ); + + a = ctx->A += a; + b = ctx->B += b; + c = ctx->C += c; + d = ctx->D += d; + e = ctx->E += e; + } +} diff --git a/src/sha.h b/src/sha.h new file mode 100644 index 0000000..13583e3 --- /dev/null +++ b/src/sha.h @@ -0,0 +1,69 @@ +/* sha.h - Declaration of functions and datatypes for SHA1 sum computing + library functions. + + Copyright (C) 1999, Scott G. Miller +*/ + +#ifndef _SHA_H +# define _SHA_H 1 + +# include "md5.h" + +/* Structure to save state of computation between the single steps. */ +struct sha_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + md5_uint32 E; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128]; +}; + + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void sha_process_block (const void *buffer, size_t len, + struct sha_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void sha_process_bytes (const void *buffer, size_t len, + struct sha_ctx *ctx); + +/* Initialize structure containing state of computation. */ +extern void sha_init_ctx (struct sha_ctx *ctx); + +/* Process the remaining bytes in the buffer and put result from CTX + in first 16 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *sha_finish_ctx (struct sha_ctx *ctx, void *resbuf); + + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *sha_read_ctx (const struct sha_ctx *ctx, void *resbuf); + + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *sha_buffer (const char *buffer, size_t len, void *resblock); + +#endif diff --git a/src/space.c b/src/space.c new file mode 100644 index 0000000..4bd4760 --- /dev/null +++ b/src/space.c @@ -0,0 +1,767 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "prelink.h" +#include "reloc.h" +#include "space.h" + +#define DEBUG_SECTIONS + +#ifdef DEBUG_SECTIONS +void +print_sections (DSO *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr) +{ + int elf64 = ehdr->e_ident[EI_CLASS] == ELFCLASS64; + int i, j, shf, flag; + char buf[32], *q; + const char *p; + static struct { int sh_type; const char *type_name; } types[] = + { + { SHT_NULL, "NULL" }, + { SHT_PROGBITS, "PROGBITS" }, + { SHT_SYMTAB, "SYMTAB" }, + { SHT_STRTAB, "STRTAB" }, + { SHT_RELA, "RELA" }, + { SHT_HASH, "HASH" }, + { SHT_DYNAMIC, "DYNAMIC" }, + { SHT_NOTE, "NOTE" }, + { SHT_NOBITS, "NOBITS" }, + { SHT_REL, "REL" }, + { SHT_SHLIB, "SHLIB" }, + { SHT_DYNSYM, "DYNSYM" }, + { SHT_INIT_ARRAY, "INIT_ARRAY" }, + { SHT_FINI_ARRAY, "FINI_ARRAY" }, + { SHT_PREINIT_ARRAY, "PREINIT_ARRAY" }, + { SHT_GROUP, "GROUP" }, + { SHT_SYMTAB_SHNDX, "SYMTAB SECTION INDICIES" }, + { SHT_GNU_verdef, "VERDEF" }, + { SHT_GNU_verneed, "VERNEED" }, + { SHT_GNU_versym, "VERSYM" }, + { SHT_GNU_LIBLIST, "LIBLIST" }, + { SHT_GNU_HASH, "GNU_HASH" }, + { 0, NULL } + }; + + if (elf64) + printf (" [Nr] Name Type Address Off Size ES Flg Lk Inf Al\n"); + else + printf (" [Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n"); + for (i = 0; i < ehdr->e_shnum; ++i) + { + p = NULL; + for (j = 0; types[j].type_name; ++j) + if (types[j].sh_type == shdr[i].sh_type) + { + p = types[j].type_name; + break; + } + + if (p == NULL) + { + if (shdr[i].sh_type >= SHT_LOPROC && shdr[i].sh_type <= SHT_HIPROC) + sprintf (buf, "LOPROC+%x", shdr[i].sh_type - SHT_LOPROC); + else if (shdr[i].sh_type >= SHT_LOOS && shdr[i].sh_type <= SHT_HIOS) + sprintf (buf, "LOOS+%x", shdr[i].sh_type - SHT_LOOS); + else if (shdr[i].sh_type >= SHT_LOUSER && shdr[i].sh_type <= SHT_HIUSER) + sprintf (buf, "LOUSER+%x", shdr[i].sh_type - SHT_LOUSER); + else + sprintf (buf, "Unknown: %x", shdr[i].sh_type); + p = buf; + } + + printf (" [%2d] %-17.17s %-15.15s ", i, + strptr (dso, ehdr->e_shstrndx, shdr[i].sh_name), p); + + q = buf; + shf = shdr[i].sh_flags; + while (shf) + { + flag = shf & -shf; + shf &= ~flag; + switch (flag) + { + case SHF_WRITE: *q++ = 'W'; break; + case SHF_ALLOC: *q++ = 'A'; break; + case SHF_EXECINSTR: *q++ = 'X'; break; + case SHF_MERGE: *q++ = 'M'; break; + case SHF_STRINGS: *q++ = 'S'; break; + case SHF_INFO_LINK: *q++ = 'I'; break; + case SHF_LINK_ORDER: *q++ = 'L'; break; + case SHF_OS_NONCONFORMING: *q++ = 'O'; break; + case SHF_TLS: *q++ = 'T'; break; + default: + if (flag & SHF_MASKOS) + *q++ = 'o', shf &= ~SHF_MASKOS; + else if (flag & SHF_MASKPROC) + *q++ = 'p', shf &= ~SHF_MASKPROC; + else + *q++ = 'x'; + break; + } + } + *q = '\0'; + if (elf64) + printf (" %16.16llx %6.6llx %6.6llx %2.2lx %3s %2ld %3lx %2ld\n", + (long long) shdr[i].sh_addr, (long long) shdr[i].sh_offset, + (long long) shdr[i].sh_size, (long) shdr[i].sh_entsize, + buf, (long) shdr[i].sh_link, (long) shdr[i].sh_info, + (long) shdr[i].sh_addralign); + else + printf (" %8.8lx %6.6lx %6.6lx %2.2lx %3s %2ld %3lx %2ld\n", + (long) shdr[i].sh_addr, (long) shdr[i].sh_offset, + (long) shdr[i].sh_size, (long) shdr[i].sh_entsize, + buf, (long) shdr[i].sh_link, (long) shdr[i].sh_info, + (long) shdr[i].sh_addralign); + } +} +#endif + +void +insert_readonly_section (GElf_Ehdr *ehdr, GElf_Shdr *shdr, int n, + struct readonly_adjust *adjust) +{ + int i; + + memmove (&shdr[n + 1], &shdr[n], + (ehdr->e_shnum - n) * sizeof (GElf_Shdr)); + ++ehdr->e_shnum; + for (i = 0; i < adjust->newcount; ++i) + if (adjust->new[i] >= n) + ++adjust->new[i]; +} + +int +remove_readonly_section (GElf_Ehdr *ehdr, GElf_Shdr *shdr, int n, + struct readonly_adjust *adjust) +{ + int i, ret = -1; + + memmove (&shdr[n], &shdr[n + 1], + (ehdr->e_shnum - n) * sizeof (GElf_Shdr)); + --ehdr->e_shnum; + for (i = 0; i < adjust->newcount; ++i) + if (adjust->new[i] > n) + --adjust->new[i]; + else if (adjust->new[i] == n) + { + adjust->new[i] = -1; + ret = i; + } + + return ret; +} + +static inline int +readonly_is_movable (DSO *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int k) +{ + if (! (shdr[k].sh_flags & (SHF_ALLOC | SHF_WRITE))) + return 0; + + switch (shdr[k].sh_type) + { + case SHT_HASH: + case SHT_GNU_HASH: + case SHT_DYNSYM: + case SHT_REL: + case SHT_RELA: + case SHT_STRTAB: + case SHT_NOTE: + case SHT_GNU_verdef: + case SHT_GNU_verneed: + case SHT_GNU_versym: + case SHT_GNU_LIBLIST: + return 1; + default: + if (strcmp (strptr (dso, ehdr->e_shstrndx, + shdr[k].sh_name), ".interp") == 0) + return 1; + return 0; + } +} + +int +find_readonly_space (DSO *dso, GElf_Shdr *add, GElf_Ehdr *ehdr, + GElf_Phdr *phdr, GElf_Shdr *shdr, + struct readonly_adjust *adjust) +{ + int i, j; + GElf_Addr addr; + GElf_Off p_filesz; + + if (add->sh_addr) + { + /* Prefer the current address if possible. */ + for (i = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_LOAD + && (phdr[i].p_flags & (PF_R | PF_W)) == PF_R + && phdr[i].p_vaddr <= add->sh_addr + && phdr[i].p_vaddr + phdr[i].p_filesz + >= add->sh_addr + add->sh_size) + break; + + if (i < ehdr->e_phnum) + for (j = 1; j < ehdr->e_shnum; ++j) + if ((shdr[j].sh_flags & SHF_ALLOC) + && shdr[j].sh_addr >= add->sh_addr) + { + if (shdr[j].sh_addr >= add->sh_addr + add->sh_size + && shdr[j - 1].sh_addr + shdr[j - 1].sh_size <= add->sh_addr) + { + insert_readonly_section (ehdr, shdr, j, adjust); + shdr[j] = *add; + shdr[j].sh_offset = (shdr[j].sh_addr - phdr[i].p_vaddr) + + phdr[i].p_offset; + return j; + } + break; + } + } + + for (i = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_LOAD + && (phdr[i].p_flags & (PF_R | PF_W)) == PF_R) + { + GElf_Addr start = phdr[i].p_vaddr; + int after = -1, min; + + if (phdr[i].p_offset < ehdr->e_phoff) + { + for (j = 0; j < ehdr->e_phnum; j++) + if (phdr[j].p_type == PT_PHDR + && phdr[j].p_offset == ehdr->e_phoff + && phdr[j].p_filesz >= ehdr->e_phnum * ehdr->e_phentsize) + break; + start += ehdr->e_phoff; + if (j < ehdr->e_phnum) + start += phdr[j].p_filesz; + else + start += ehdr->e_phnum * ehdr->e_phentsize; + start -= phdr[i].p_offset; + } + start = (start + add->sh_addralign - 1) & ~(add->sh_addralign - 1); + for (j = 1; j < ehdr->e_shnum; ++j) + if ((shdr[j].sh_flags & SHF_ALLOC) + && shdr[j].sh_addr >= phdr[i].p_vaddr + && shdr[j].sh_addr + shdr[j].sh_size + <= phdr[i].p_vaddr + phdr[i].p_filesz) + { + if (after == -1) + after = j - 1; + if (start + add->sh_size > shdr[j].sh_addr) + { + start = shdr[j].sh_addr + shdr[j].sh_size; + start = (start + add->sh_addralign - 1) + & ~(add->sh_addralign - 1); + after = j; + } + } + + min = -1; + for (j = i + 1; j < ehdr->e_phnum; ++j) + if (phdr[j].p_offset >= phdr[i].p_offset + phdr[i].p_filesz + && (min == -1 || phdr[min].p_offset > phdr[j].p_offset)) + min = j; + + if (after != -1 + && (start + add->sh_size <= phdr[i].p_vaddr + phdr[i].p_filesz + || (phdr[i].p_filesz == phdr[i].p_memsz + && (min == -1 + || start + add->sh_size - phdr[i].p_vaddr + <= phdr[min].p_offset)))) + { + insert_readonly_section (ehdr, shdr, after + 1, adjust); + shdr[after + 1] = *add; + shdr[after + 1].sh_addr = start; + shdr[after + 1].sh_offset = (start - phdr[i].p_vaddr) + + phdr[i].p_offset; + if (start + add->sh_size > phdr[i].p_vaddr + phdr[i].p_filesz) + { + adjust_nonalloc (dso, ehdr, shdr, 0, 0, + start + add->sh_size - phdr[i].p_vaddr + - phdr[i].p_filesz); + phdr[i].p_filesz = start + add->sh_size - phdr[i].p_vaddr; + phdr[i].p_memsz = phdr[i].p_filesz; + } + return after + 1; + } + } + + /* If SHT_NOBITS sections are small, just extend the last PT_LOAD + segment. Small enough here means that the whole .bss fits into + the same CPU page as the alloced part of it. */ + for (i = -1, j = 0; j < ehdr->e_phnum; ++j) + if (phdr[j].p_type == PT_LOAD) + i = j; + p_filesz = phdr[i].p_filesz; + + /* If we'll be converting NOBITS .plt to PROGBITS, account for that in the + calculation. */ + for (j = 1; j < ehdr->e_shnum; ++j) + { + if (shdr[j].sh_type == SHT_NOBITS + && shdr[j].sh_addr >= phdr[i].p_vaddr + && shdr[j].sh_addr + shdr[j].sh_size + <= phdr[i].p_vaddr + phdr[i].p_memsz + && !strcmp (strptr (dso, ehdr->e_shstrndx, shdr[j].sh_name), ".plt")) + { + if (shdr[j].sh_addr + shdr[j].sh_size - phdr[i].p_vaddr > p_filesz) + p_filesz = shdr[j].sh_addr + shdr[j].sh_size - phdr[i].p_vaddr; + break; + } + } + + if (phdr[i].p_filesz + && p_filesz <= phdr[i].p_memsz + && !(((phdr[i].p_vaddr + phdr[i].p_memsz - 1) + ^ (phdr[i].p_vaddr + p_filesz - 1)) & ~(dso->arch->page_size - 1))) + { + for (j = 1; j < ehdr->e_shnum; ++j) + { + if (!(shdr[j].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_ALLOC))) + break; + if (shdr[j].sh_type == SHT_NOBITS + && (shdr[j].sh_flags & SHF_TLS) == 0 + && shdr[j].sh_addr >= phdr[i].p_vaddr) + shdr[j].sh_type = SHT_PROGBITS; + } + + insert_readonly_section (ehdr, shdr, j, adjust); + shdr[j] = *add; + shdr[j].sh_addr = (shdr[j - 1].sh_addr + shdr[j - 1].sh_size + + add->sh_addralign - 1) & ~(add->sh_addralign - 1); + shdr[j].sh_offset = (shdr[j].sh_addr - phdr[i].p_vaddr) + + phdr[i].p_offset; + phdr[i].p_filesz = shdr[j].sh_addr + add->sh_size - phdr[i].p_vaddr; + phdr[i].p_memsz = phdr[i].p_filesz; + adjust_nonalloc (dso, ehdr, shdr, 0, 0, phdr[i].p_offset + + phdr[i].p_filesz - shdr[j + 1].sh_offset); + return j; + } + + /* See if we can decrease binary's base VMA and thus gain space. + This trick is mainly useful for IA-32. */ + for (i = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_LOAD) + break; + + addr = (add->sh_size + add->sh_addralign - 1 + phdr[i].p_align - 1) + & ~(phdr[i].p_align - 1); + if (phdr[i].p_align <= dso->arch->page_size + && phdr[i].p_flags == (PF_R | PF_X) + && phdr[i].p_filesz == phdr[i].p_memsz + && phdr[i].p_vaddr - addr + && ! (((phdr[i].p_vaddr - addr) ^ phdr[i].p_vaddr) + & ~(phdr[i].p_align * 256 - 1))) + { + int moveend; + if (! adjust->basemove_end) + { + for (moveend = 1; moveend < ehdr->e_shnum; ++moveend) + if (strcmp (strptr (dso, ehdr->e_shstrndx, + shdr[moveend].sh_name), ".interp") + && shdr[moveend].sh_type != SHT_NOTE) + break; + if (moveend < ehdr->e_shnum && moveend > 1) + { + adjust->basemove_end = shdr[moveend].sh_addr; + adjust->moveend = moveend; + } + } + else + moveend = adjust->moveend; + if (moveend < ehdr->e_shnum && moveend > 1 + && (shdr[moveend].sh_flags & (SHF_ALLOC | SHF_WRITE))) + { + int k = moveend; + GElf_Addr adj = addr; + + if (add->sh_addr && ! adjust->move2 + && phdr[i].p_vaddr <= add->sh_addr + && phdr[i].p_vaddr + phdr[i].p_filesz > add->sh_addr) + { + for (k = moveend; k < ehdr->e_shnum; ++k) + { + if (! (shdr[k].sh_flags & (SHF_ALLOC | SHF_WRITE))) + { + k = ehdr->e_shnum; + break; + } + + if (shdr[k].sh_addr > add->sh_addr) + { + /* Don't allow inserting in between reloc sections + if they are adjacent. Similarly for adjacent + note sections. */ + int sh_type1 = (shdr[k - 1].sh_type == SHT_RELA) + ? SHT_REL : shdr[k - 1].sh_type; + int sh_type2 = (shdr[k].sh_type == SHT_RELA) + ? SHT_REL : shdr[k].sh_type; + + if (sh_type1 != sh_type2) + break; + + if (sh_type1 != SHT_REL && sh_type1 != SHT_NOTE) + break; + if ((shdr[k - 1].sh_addr + + ((shdr[k - 1].sh_size + 3) & -4)) + != shdr[k].sh_addr) + break; + } + + if (! readonly_is_movable (dso, ehdr, shdr, k)) + { + k = ehdr->e_shnum; + break; + } + } + + if (k < ehdr->e_shnum) + { + GElf_Addr a; + + a = shdr[k].sh_addr; + a -= shdr[k - 1].sh_addr + shdr[k - 1].sh_size; + assert (add->sh_addralign <= phdr[i].p_align); + assert (add->sh_size > a); + a = (add->sh_size - a + phdr[i].p_align - 1) + & ~(phdr[i].p_align - 1); + if (a < adj) + { + adjust->move2 = 1; + adj = a; + } + else + k = moveend; + } + else + k = moveend; + } + + for (j = 1; j < k; ++j) + shdr[j].sh_addr -= adj; + phdr[i].p_vaddr -= adj; + phdr[i].p_paddr -= adj; + phdr[i].p_filesz += adj; + phdr[i].p_memsz += adj; + for (j = 0; j < ehdr->e_phnum; ++j) + { + if (j == i) + continue; + /* Leave STACK segment alone, it has p_vaddr == p_paddr == 0 + and p_offset == p_filesz == p_memsz == 0. */ + if (phdr[j].p_type == PT_GNU_STACK) + continue; + if (phdr[j].p_vaddr + < adjust->basemove_end - adjust->basemove_adjust) + { + phdr[j].p_vaddr -= adj; + phdr[j].p_paddr -= adj; + } + else + phdr[j].p_offset += adj; + } + adjust->basemove_adjust += adj; + insert_readonly_section (ehdr, shdr, k, adjust); + shdr[k] = *add; + if (k == moveend) + { + addr = shdr[k - 1].sh_addr + shdr[k - 1].sh_size; + addr = (addr + add->sh_addralign - 1) & ~(add->sh_addralign - 1); + } + else + { + addr = (shdr[k + 1].sh_addr - add->sh_size) + & ~(add->sh_addralign - 1); + } + + shdr[k].sh_addr = addr; + shdr[k].sh_offset = (addr - phdr[i].p_vaddr) + phdr[i].p_offset; + adjust_nonalloc (dso, ehdr, shdr, 0, 0, adj); + return k; + } + } + + /* We have to create new PT_LOAD if at all possible. */ + for (j = 0; j < ehdr->e_phnum; ++j) + if (phdr[j].p_type == PT_NULL) + break; + + if (j < ehdr->e_phnum) + { + memmove (phdr, &phdr[j + 1], + (ehdr->e_phnum - j - 1) * sizeof (GElf_Phdr)); + ehdr->e_phnum--; + } + else + { + addr = ehdr->e_phoff + (ehdr->e_phnum + 1) * ehdr->e_phentsize; + for (j = 1; j < ehdr->e_shnum; ++j) + { + if (addr > shdr[j].sh_offset) + { + GElf_Addr start, addstart, endaddr, *old_addr; + GElf_Addr minsize = ~(GElf_Addr) 0; + int movesec = -1, last, k, e; + + if (ehdr->e_phoff < phdr[i].p_offset + || ehdr->e_phoff + (ehdr->e_phnum + 1) * ehdr->e_phentsize + > phdr[i].p_offset + phdr[i].p_filesz + || ! readonly_is_movable (dso, ehdr, shdr, j) + || shdr[j].sh_addr >= phdr[i].p_vaddr + phdr[i].p_filesz) + { + error (0, 0, "%s: No space in ELF segment table to add new ELF segment", + dso->filename); + return 0; + } + + start = phdr[i].p_vaddr - phdr[i].p_offset + ehdr->e_phoff + + (ehdr->e_phnum + 1) * ehdr->e_phentsize; + for (last = 1; last < ehdr->e_shnum; ++last) + if (! readonly_is_movable (dso, ehdr, shdr, last) + || shdr[last].sh_addr >= phdr[i].p_vaddr + phdr[i].p_filesz) + break; + for (j = 1; j < last; ++j) + { + addstart = (start + add->sh_addralign - 1) + & ~(add->sh_addralign - 1); + start = (start + shdr[j].sh_addralign - 1) + & ~(shdr[j].sh_addralign - 1); + endaddr = -1; + if (j + 1 < ehdr->e_shnum) + endaddr = shdr[j + 1].sh_addr; + if (phdr[i].p_vaddr + phdr[i].p_filesz < endaddr) + endaddr = phdr[i].p_vaddr + phdr[i].p_filesz; + + switch (shdr[j].sh_type) + { + case SHT_HASH: + case SHT_GNU_HASH: + case SHT_DYNSYM: + case SHT_STRTAB: + case SHT_GNU_verdef: + case SHT_GNU_verneed: + case SHT_GNU_versym: + case SHT_GNU_LIBLIST: + if (endaddr >= start + && endaddr - start < minsize) + { + minsize = endaddr - start; + movesec = j; + } + if (endaddr > addstart + && endaddr - addstart > add->sh_size + && endaddr - addstart - add->sh_size + < minsize) + { + minsize = endaddr - addstart - add->sh_size; + movesec = j; + } + break; + case SHT_REL: + case SHT_RELA: + case SHT_NOTE: + /* Don't allow inserting in between reloc sections + if they are adjacent. Similarly for adjacent + note sections. */ + if (j + 1 < ehdr->e_shnum) + { + if (shdr[j].sh_type == SHT_NOTE) + { + if (shdr[j + 1].sh_type != SHT_NOTE) + break; + } + else if (shdr[j + 1].sh_type != SHT_REL + && shdr[j + 1].sh_type != SHT_RELA) + { + break; + } + + if ((shdr[j].sh_addr + + ((shdr[j].sh_size + 3) & -4)) + != shdr[j + 1].sh_addr) + break; + + start += shdr[j].sh_size; + continue; + } + break; + } + + if (start + shdr[j].sh_size <= endaddr) + { + movesec = j + 1; + break; + } + start += shdr[j].sh_size; + } + + if (movesec == -1) + { + error (0, 0, "%s: No space in ELF segment table to add new ELF segment", + dso->filename); + return 0; + } + + start = phdr[i].p_vaddr - phdr[i].p_offset + ehdr->e_phoff + + (ehdr->e_phnum + 1) * ehdr->e_phentsize; + old_addr = (GElf_Addr *) alloca (movesec * sizeof (GElf_Addr)); + for (k = 1; k < movesec; ++k) + { + start = (start + shdr[k].sh_addralign - 1) + & ~(shdr[k].sh_addralign - 1); + old_addr[k] = shdr[k].sh_addr; + shdr[k].sh_addr = start; + shdr[k].sh_offset = start + phdr[i].p_offset + - phdr[i].p_vaddr; + start += shdr[k].sh_size; + } + + for (e = 0; e < ehdr->e_phnum; ++e) + if (phdr[e].p_type != PT_LOAD + && phdr[e].p_type != PT_GNU_STACK) + for (k = 1; k < movesec; ++k) + if (old_addr[k] == phdr[e].p_vaddr) + { + if (phdr[e].p_filesz != shdr[k].sh_size + || phdr[e].p_memsz != shdr[k].sh_size) + { + int k1 = -1; + if (phdr[e].p_type == PT_NOTE + && shdr[k].sh_type == SHT_NOTE + && phdr[e].p_filesz == phdr[e].p_memsz) + { + k1 = k; + while (k1 < movesec) + { + if (shdr[k1].sh_type != SHT_NOTE + || shdr[k1].sh_addr - old_addr[k1] + != shdr[k].sh_addr - old_addr[k] + || old_addr[k1] + shdr[k1].sh_size + > phdr[e].p_vaddr + phdr[e].p_filesz) + { + k1 = -1; + break; + } + if (old_addr[k1] + shdr[k1].sh_size + == phdr[e].p_vaddr + phdr[e].p_filesz) + break; + ++k1; + } + if (k1 == movesec) + k1 = -1; + } + if (k1 == -1) + { + error (0, 0, "%s: Non-PT_LOAD segment spanning more than one section", + dso->filename); + return 0; + } + } + phdr[e].p_vaddr += shdr[k].sh_addr - old_addr[k]; + phdr[e].p_paddr += shdr[k].sh_addr - old_addr[k]; + phdr[e].p_offset += shdr[k].sh_addr - old_addr[k]; + break; + } + + if (j < last) + /* Now continue as if there was place for a new PT_LOAD + in ElfW(Phdr) table initially. */ + break; + else + { + GElf_Shdr moveshdr; + int newidx, ret, movedidx, oldidx; + + moveshdr = shdr[movesec]; + newidx = remove_readonly_section (ehdr, shdr, movesec, adjust); + oldidx = adjust->move->new_to_old[movesec]; + remove_section (adjust->move, movesec); + ret = find_readonly_space (dso, add, ehdr, phdr, shdr, adjust); + if (ret == 0) + return 0; + movedidx = find_readonly_space (dso, &moveshdr, ehdr, phdr, + shdr, adjust); + if (movedidx == 0) + return 0; + if (newidx != -1) + adjust->new[newidx] = movedidx; + add_section (adjust->move, movedidx); + if (oldidx != -1) + { + adjust->move->old_to_new[oldidx] = movedidx; + adjust->move->new_to_old[movedidx] = oldidx; + } + if (movedidx <= ret) + ++ret; + return ret; + } + } + } + } + + for (i = 0, j = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_LOAD) + j = i; + else if (phdr[i].p_type == PT_PHDR) + { + if (phdr[i].p_filesz == ehdr->e_phnum * ehdr->e_phentsize) + phdr[i].p_filesz += ehdr->e_phentsize; + if (phdr[i].p_memsz == ehdr->e_phnum * ehdr->e_phentsize) + phdr[i].p_memsz += ehdr->e_phentsize; + } + + memmove (&phdr[j + 2], &phdr[j + 1], + (ehdr->e_phnum - j - 1) * sizeof (GElf_Phdr)); + ++ehdr->e_phnum; + phdr[++j].p_type = PT_LOAD; + phdr[j].p_offset = phdr[j - 1].p_offset + phdr[j - 1].p_filesz; + phdr[j].p_offset = (phdr[j].p_offset + add->sh_addralign - 1) + & ~(add->sh_addralign - 1); + phdr[j].p_align = phdr[j - 1].p_align; + phdr[j].p_vaddr = phdr[j - 1].p_vaddr + phdr[j - 1].p_memsz; + phdr[j].p_vaddr += (phdr[j].p_align - 1); + phdr[j].p_vaddr &= ~(phdr[j].p_align - 1); + phdr[j].p_vaddr += (phdr[j].p_offset & (phdr[j].p_align - 1)); + phdr[j].p_paddr = phdr[j].p_vaddr; + /* Although the content of the segment is read-only, unless it ends on + a page boundary, we must make it writeable. This is because the rest of + the last page in the segment will be used as sbrk area which is assumed + to be writeable. */ + phdr[j].p_flags = (PF_R | PF_W); + phdr[j].p_filesz = add->sh_size; + phdr[j].p_memsz = add->sh_size; + for (i = 1; i < ehdr->e_shnum; ++i) + if (! (shdr[i].sh_flags & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR))) + break; + assert (i < ehdr->e_shnum); + insert_readonly_section (ehdr, shdr, i, adjust); + shdr[i] = *add; + shdr[i].sh_addr = phdr[j].p_vaddr; + shdr[i].sh_offset = phdr[j].p_offset; + adjust_nonalloc (dso, ehdr, shdr, 0, 0, + phdr[j].p_offset + phdr[j].p_filesz - phdr[j - 1].p_offset + - phdr[j - 1].p_filesz); + return i; +} diff --git a/src/space.h b/src/space.h new file mode 100644 index 0000000..5c9f725 --- /dev/null +++ b/src/space.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2001, 2004 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef SPACE_H +#define SPACE_H + +struct readonly_adjust +{ + off_t basemove_adjust; + GElf_Addr basemove_end; + int moveend; + int move2; + int newcount, *new; + struct section_move *move; +}; + +void insert_readonly_section (GElf_Ehdr *ehdr, GElf_Shdr *shdr, int n, + struct readonly_adjust *adjust); +int remove_readonly_section (GElf_Ehdr *ehdr, GElf_Shdr *shdr, int n, + struct readonly_adjust *adjust); +int find_readonly_space (DSO *dso, GElf_Shdr *add, GElf_Ehdr *ehdr, + GElf_Phdr *phdr, GElf_Shdr *shdr, + struct readonly_adjust *adjust); + +#endif /* SPACE_H */ diff --git a/src/stabs.c b/src/stabs.c new file mode 100644 index 0000000..c0a5a6a --- /dev/null +++ b/src/stabs.c @@ -0,0 +1,188 @@ +/* Copyright (C) 2001, 2005, 2006 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <byteswap.h> +#include <endian.h> +#include <error.h> + +#include "prelink.h" + +#define N_ZERO 0x00 +#define N_GSYM 0x20 +#define N_FNAME 0x22 +#define N_FUN 0x24 +#define N_STSYM 0x26 +#define N_LCSYM 0x28 +#define N_MAIN 0x2a +#define N_BNSYM 0x2e +#define N_PC 0x30 +#define N_NSYMS 0x32 +#define N_NOMAP 0x34 +#define N_OBJ 0x38 +#define N_OPT 0x3c +#define N_RSYM 0x40 +#define N_M2C 0x42 +#define N_SLINE 0x44 +#define N_DSLINE 0x46 +#define N_BSLINE 0x48 +#define N_BROWS 0x48 +#define N_DEFD 0x4a +#define N_ENSYM 0x4e +#define N_EHDECL 0x50 +#define N_MOD2 0x50 +#define N_CATCH 0x54 +#define N_SSYM 0x60 +#define N_SO 0x64 +#define N_LSYM 0x80 +#define N_BINCL 0x82 +#define N_SOL 0x84 +#define N_PSYM 0xa0 +#define N_EINCL 0xa2 +#define N_ENTRY 0xa4 +#define N_LBRAC 0xc0 +#define N_EXCL 0xc2 +#define N_SCOPE 0xc4 +#define N_RBRAC 0xe0 +#define N_BCOMM 0xe2 +#define N_ECOMM 0xe4 +#define N_ECOML 0xe8 +#define N_LENG 0xfe + +static uint32_t +read_native (char *p) +{ + return *(uint32_t *)p; +} + +static uint32_t +read_swap (char *p) +{ + return bswap_32 (*(uint32_t *)p); +} + +static void +write_native (char *p, uint32_t v) +{ + *(uint32_t *)p = v; +} + +static void +write_swap (char *p, uint32_t v) +{ + *(uint32_t *)p = bswap_32 (v); +} + +int +adjust_stabs (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + off_t off; + uint32_t (*read_32) (char *p); + void (*write_32) (char *p, uint32_t v); + uint32_t value; + int sec, type; + + assert (dso->shdr[n].sh_entsize == 12); + data = elf_getdata (scn, NULL); + assert (data != NULL && data->d_buf != NULL); + assert (elf_getdata (scn, data) == NULL); + assert (data->d_off == 0 && data->d_size == dso->shdr[n].sh_size); +#if __BYTE_ORDER == __BIG_ENDIAN + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) +#elif __BYTE_ORDER == __LITTLE_ENDIAN + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) +#else +# error Not supported host endianess +#endif + { + read_32 = read_native; + write_32 = write_native; + } +#if __BYTE_ORDER == __BIG_ENDIAN + else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) +#elif __BYTE_ORDER == __LITTLE_ENDIAN + else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) +#endif + { + read_32 = read_swap; + write_32 = write_swap; + } + else + { + error (0, 0, "%s: Wrong ELF data enconding", dso->filename); + return 1; + } + + for (off = 0; off < data->d_size; off += 12) + { + switch ((type = *(uint8_t *)(data->d_buf + off + 4))) + { + case N_FUN: + /* If string is "", N_FUN is function length, otherwise + it is function start address. */ + if (read_32 (data->d_buf + off) == 0) + break; + /* FALLTHROUGH */ + case N_STSYM: + case N_LCSYM: + case N_CATCH: + case N_SO: + case N_SOL: + case N_BNSYM: + case N_ENSYM: + value = read_32 (data->d_buf + off + 8); + sec = addr_to_sec (dso, value); + if (sec != -1) + { + addr_adjust (value, start, adjust); + write_32 (data->d_buf + off + 8, value); + } + break; + /* These should be always 0. */ + case N_GSYM: + case N_BINCL: + case N_EINCL: + case N_EXCL: + case N_BCOMM: + case N_ECOMM: + /* These contain other values. */ + case N_ZERO: + case N_NSYMS: + case N_NOMAP: + case N_RSYM: + case N_LSYM: + case N_PSYM: + case N_OPT: + /* These are relative. */ + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + case N_BSLINE: + case N_DSLINE: + break; + default: + error (0, 0, "%s: Unknown stabs code 0x%02x\n", dso->filename, type); + return 1; + } + } + + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + return 0; +} diff --git a/src/undo.c b/src/undo.c new file mode 100644 index 0000000..8a55bf2 --- /dev/null +++ b/src/undo.c @@ -0,0 +1,713 @@ +/* Copyright (C) 2001, 2002, 2003, 2005, 2010 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <endian.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include "prelink.h" +#include "reloc.h" + +static int +undo_prelink_rel (DSO *dso, int n) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Rel rel; + int sec; + + if (dso->arch->undo_prelink_rel == NULL) + return 0; + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off; + + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; + ++ndx, addr += dso->shdr[n].sh_entsize) + { + gelfx_getrel (dso->elf, data, ndx, &rel); + sec = addr_to_sec (dso, rel.r_offset); + if (sec == -1) + continue; + + switch (dso->arch->undo_prelink_rel (dso, &rel, addr)) + { + case 2: + gelfx_update_rel (dso->elf, data, ndx, &rel); + break; + case 0: + break; + default: + return 1; + } + } + } + return 0; +} + +static int +undo_prelink_rela (DSO *dso, int n) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = dso->scn[n]; + GElf_Rela rela; + int sec; + + if (dso->arch->undo_prelink_rela == NULL) + return 0; + while ((data = elf_getdata (scn, data)) != NULL) + { + int ndx, maxndx; + GElf_Addr addr = dso->shdr[n].sh_addr + data->d_off; + + maxndx = data->d_size / dso->shdr[n].sh_entsize; + for (ndx = 0; ndx < maxndx; + ++ndx, addr += dso->shdr[n].sh_entsize) + { + gelfx_getrela (dso->elf, data, ndx, &rela); + sec = addr_to_sec (dso, rela.r_offset); + if (sec == -1) + continue; + + switch (dso->arch->undo_prelink_rela (dso, &rela, addr)) + { + case 2: + gelfx_update_rela (dso->elf, data, ndx, &rela); + break; + case 0: + break; + default: + return 1; + } + } + } + return 0; +} + +static int +remove_dynamic_prelink_tags (DSO *dso) +{ + Elf_Data *data; + Elf_Scn *scn; + GElf_Dyn dyn; + int ndx; + + assert (dso->shdr[dso->dynamic].sh_type == SHT_DYNAMIC); + scn = dso->scn[dso->dynamic]; + data = elf_getdata (scn, NULL); + assert (elf_getdata (scn, data) == NULL); + ndx = data->d_size / dso->shdr[dso->dynamic].sh_entsize; + while (--ndx >= 0) + { + gelfx_getdyn (dso->elf, data, ndx, &dyn); + switch (dyn.d_tag) + { + case DT_NULL: + continue; + case DT_CHECKSUM: + case DT_GNU_PRELINKED: + case DT_GNU_LIBLIST: + case DT_GNU_LIBLISTSZ: + case DT_GNU_CONFLICT: + case DT_GNU_CONFLICTSZ: + dyn.d_tag = DT_NULL; + dyn.d_un.d_val = 0; + gelfx_update_dyn (dso->elf, data, ndx, &dyn); + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + break; + default: + ndx = 0; + break; + } + } + return 0; +} + +int +undo_sections (DSO *dso, int undo, struct section_move *move, + struct reloc_info *rinfo, GElf_Ehdr *ehdr, + GElf_Phdr *phdr, GElf_Shdr *shdr) +{ + Elf_Data src, dst, *d; + Elf_Scn *scn; + int i, j; + + scn = dso->scn[undo]; + d = elf_getdata (scn, NULL); + assert (d != NULL && elf_getdata (scn, d) == NULL); + + src = *d; + src.d_type = ELF_T_EHDR; + src.d_align = dso->shdr[undo].sh_addralign; + src.d_size = gelf_fsize (dso->elf, ELF_T_EHDR, 1, EV_CURRENT); + dst = src; + if (src.d_size > d->d_size) + { + error (0, 0, "%s: .gnu.prelink_undo section too small", + dso->filename); + return 1; + } + switch (gelf_getclass (dso->elf)) + { + case ELFCLASS32: + dst.d_buf = alloca (dst.d_size); + break; + case ELFCLASS64: + dst.d_buf = ehdr; + break; + default: + return 1; + } + if (gelf_xlatetom (dso->elf, &dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + { + error (0, 0, "%s: Could not read .gnu.prelink_undo section", + dso->filename); + return 1; + } + if (gelf_getclass (dso->elf) == ELFCLASS32) + { + Elf32_Ehdr *ehdr32 = (Elf32_Ehdr *) dst.d_buf; + + memcpy (ehdr->e_ident, ehdr32->e_ident, sizeof (ehdr->e_ident)); +#define COPY(name) ehdr->name = ehdr32->name + COPY (e_type); + COPY (e_machine); + COPY (e_version); + COPY (e_entry); + COPY (e_phoff); + COPY (e_shoff); + COPY (e_flags); + COPY (e_ehsize); + COPY (e_phentsize); + COPY (e_phnum); + COPY (e_shentsize); + COPY (e_shnum); + COPY (e_shstrndx); +#undef COPY + } + + if (memcmp (ehdr->e_ident, dso->ehdr.e_ident, sizeof (ehdr->e_ident)) + || ehdr->e_type != dso->ehdr.e_type + || ehdr->e_machine != dso->ehdr.e_machine + || ehdr->e_version != dso->ehdr.e_version + || ehdr->e_flags != dso->ehdr.e_flags + || ehdr->e_ehsize != dso->ehdr.e_ehsize + || ehdr->e_phentsize != dso->ehdr.e_phentsize + || ehdr->e_shentsize != dso->ehdr.e_shentsize) + { + error (0, 0, "%s: ELF headers changed since prelinking", + dso->filename); + return 1; + } + + if (ehdr->e_phnum > dso->ehdr.e_phnum) + { + error (0, 0, "%s: Number of program headers is less than before prelinking", + dso->filename); + return 1; + } + + if (d->d_size != (src.d_size + + gelf_fsize (dso->elf, ELF_T_PHDR, ehdr->e_phnum, + EV_CURRENT) + + gelf_fsize (dso->elf, ELF_T_SHDR, ehdr->e_shnum - 1, + EV_CURRENT))) + { + error (0, 0, "%s: Incorrect size of .gnu.prelink_undo section", + dso->filename); + return 1; + } + + src.d_type = ELF_T_PHDR; + src.d_buf += src.d_size; + src.d_size = gelf_fsize (dso->elf, ELF_T_PHDR, ehdr->e_phnum, EV_CURRENT); + dst = src; + switch (gelf_getclass (dso->elf)) + { + case ELFCLASS32: + dst.d_buf = alloca (dst.d_size); + break; + case ELFCLASS64: + dst.d_buf = phdr; + break; + } + if (gelf_xlatetom (dso->elf, &dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + { + error (0, 0, "%s: Could not read .gnu.prelink_undo section", + dso->filename); + return 1; + } + + if (gelf_getclass (dso->elf) == ELFCLASS32) + { + Elf32_Phdr *phdr32 = (Elf32_Phdr *) dst.d_buf; + + for (i = 0; i < ehdr->e_phnum; ++i) + { +#define COPY(name) phdr[i].name = phdr32[i].name + COPY(p_type); + COPY(p_flags); + COPY(p_offset); + COPY(p_vaddr); + COPY(p_paddr); + COPY(p_filesz); + COPY(p_memsz); + COPY(p_align); +#undef COPY + } + } + + memset (shdr, 0, sizeof (GElf_Shdr)); + src.d_type = ELF_T_SHDR; + src.d_buf += src.d_size; + src.d_size = gelf_fsize (dso->elf, ELF_T_SHDR, ehdr->e_shnum - 1, EV_CURRENT); + dst = src; + switch (gelf_getclass (dso->elf)) + { + case ELFCLASS32: + dst.d_buf = alloca (dst.d_size); + break; + case ELFCLASS64: + dst.d_buf = shdr + 1; + break; + default: + return 1; + } + if (gelf_xlatetom (dso->elf, &dst, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + { + error (0, 0, "%s: Could not read .gnu.prelink_undo section", + dso->filename); + return 1; + } + + if (gelf_getclass (dso->elf) == ELFCLASS32) + { + Elf32_Shdr *shdr32 = (Elf32_Shdr *) dst.d_buf; + + for (i = 1; i < ehdr->e_shnum; ++i) + { +#define COPY(name) shdr[i].name = shdr32[i - 1].name + COPY (sh_name); + COPY (sh_type); + COPY (sh_flags); + COPY (sh_addr); + COPY (sh_offset); + COPY (sh_size); + COPY (sh_link); + COPY (sh_info); + COPY (sh_addralign); + COPY (sh_entsize); +#undef COPY + } + } + + move->new_shnum = ehdr->e_shnum; + for (i = 1; i < move->old_shnum; ++i) + move->old_to_new[i] = -1; + for (i = 1; i < move->new_shnum; ++i) + move->new_to_old[i] = -1; + + for (i = 1; i < move->old_shnum; ++i) + { + for (j = 1; j < move->new_shnum; ++j) + if (dso->shdr[i].sh_name == shdr[j].sh_name + && dso->shdr[i].sh_type == shdr[j].sh_type + && dso->shdr[i].sh_flags == shdr[j].sh_flags + && dso->shdr[i].sh_addralign == shdr[j].sh_addralign + && dso->shdr[i].sh_entsize == shdr[j].sh_entsize + && dso->shdr[i].sh_size == shdr[j].sh_size + && move->new_to_old[j] == -1) + break; + + if (j == move->new_shnum) + continue; + + move->old_to_new[i] = j; + move->new_to_old[j] = i; + } + + for (i = 1; i < move->old_shnum; ++i) + if (move->old_to_new[i] == -1) + { + const char *name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name); + + if (! strcmp (name, ".gnu.prelink_undo") + || ! strcmp (name, ".gnu.conflict") + || ! strcmp (name, ".gnu.liblist") + || ! strcmp (name, ".gnu.libstr") + || ((! strcmp (name, ".dynbss") || ! strcmp (name, ".sdynbss")) + && dso->ehdr.e_type == ET_EXEC)) + continue; + + if ((! strcmp (name, ".dynstr") && dso->ehdr.e_type == ET_EXEC) + || i == dso->ehdr.e_shstrndx) + { + for (j = 1; j < move->new_shnum; ++j) + if (dso->shdr[i].sh_name == shdr[j].sh_name + && dso->shdr[i].sh_type == shdr[j].sh_type + && dso->shdr[i].sh_flags == shdr[j].sh_flags + && dso->shdr[i].sh_addralign == shdr[j].sh_addralign + && dso->shdr[i].sh_entsize == shdr[j].sh_entsize + && dso->shdr[i].sh_size > shdr[j].sh_size + && move->new_to_old[j] == -1) + break; + + if (j < move->new_shnum) + { + move->old_to_new[i] = j; + move->new_to_old[j] = i; + continue; + } + } + + if (((i >= rinfo->first && i <= rinfo->last) || i == rinfo->plt) + && dso->shdr[i].sh_type == SHT_RELA) + { + for (j = 1; j < move->new_shnum; ++j) + if (dso->shdr[i].sh_name == shdr[j].sh_name + && shdr[j].sh_type == SHT_REL + && dso->shdr[i].sh_flags == shdr[j].sh_flags + && dso->shdr[i].sh_addralign == shdr[j].sh_addralign + && 2 * dso->shdr[i].sh_entsize == 3 * shdr[j].sh_entsize + && 2 * dso->shdr[i].sh_size == 3 * shdr[j].sh_size + && move->new_to_old[j] == -1) + break; + + if (j < move->new_shnum) + { + move->old_to_new[i] = j; + move->new_to_old[j] = i; + continue; + } + } + + if (! strcmp (name, ".bss") + || ! strcmp (name, ".sbss") + || ((! strcmp (name, ".plt") || ! strcmp (name, ".iplt")) + && dso->shdr[i].sh_type == SHT_PROGBITS)) + { + int is_plt = ! strcmp (name, ".plt"); + + for (j = 1; j < move->new_shnum; ++j) + if (dso->shdr[i].sh_name == shdr[j].sh_name + && dso->shdr[i].sh_flags == shdr[j].sh_flags + && dso->shdr[i].sh_addralign == shdr[j].sh_addralign + && (is_plt || dso->shdr[i].sh_entsize == shdr[j].sh_entsize) + && move->new_to_old[j] == -1) + { + if (is_plt) + { + if (dso->shdr[i].sh_size != shdr[j].sh_size) + continue; + if (shdr[j].sh_type == SHT_NOBITS + && dso->shdr[i].sh_entsize == shdr[j].sh_entsize) + break; + /* On Alpha prelink fixes bogus sh_entsize of .plt + sections. */ + if (shdr[j].sh_type == SHT_PROGBITS) + break; + } + else + { + const char *pname; + + if (dso->shdr[i].sh_type != shdr[j].sh_type + && (dso->shdr[i].sh_type != SHT_PROGBITS + || shdr[j].sh_type != SHT_NOBITS)) + continue; + + if (dso->shdr[i].sh_size == shdr[j].sh_size) + break; + + pname = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i - 1].sh_name); + if (strcmp (pname, ".dynbss") + && strcmp (pname, ".sdynbss")) + continue; + + if (dso->shdr[i].sh_size + dso->shdr[i - 1].sh_size + == shdr[j].sh_size) + break; + } + } + + if (j < move->new_shnum) + { + move->old_to_new[i] = j; + move->new_to_old[j] = i; + continue; + } + } + + error (0, 0, "%s: Section %s created after prelinking", + dso->filename, name); + return 1; + } + + for (i = 1; i < move->new_shnum; ++i) + if (move->new_to_old[i] == -1) + { + const char *name = strptr (dso, dso->ehdr.e_shstrndx, shdr[i].sh_name); + + error (0, 0, "%s: Section %s removed after prelinking", dso->filename, + name); + return 1; + } + + return 0; +} + +int +prelink_undo (DSO *dso) +{ + GElf_Ehdr ehdr; + GElf_Shdr shdr[dso->ehdr.e_shnum + 20], old_shdr[dso->ehdr.e_shnum]; + GElf_Phdr phdr[dso->ehdr.e_phnum]; + Elf_Scn *scn; + Elf_Data *d; + int undo, i; + struct section_move *move; + struct reloc_info rinfo; + + for (undo = 1; undo < dso->ehdr.e_shnum; ++undo) + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[undo].sh_name), + ".gnu.prelink_undo")) + break; + + if (undo == dso->ehdr.e_shnum) + { + if (undo_output) + return 0; + error (0, 0, "%s does not have .gnu.prelink_undo section", dso->filename); + return 1; + } + + memcpy (old_shdr, dso->shdr, sizeof (GElf_Shdr) * dso->ehdr.e_shnum); + move = init_section_move (dso); + if (move == NULL) + return 1; + + if (find_reloc_sections (dso, &rinfo)) + goto error_out; + + if (undo_sections (dso, undo, move, &rinfo, &ehdr, phdr, shdr)) + goto error_out; + + if (reopen_dso (dso, move, (undo_output && strcmp (undo_output, "-") == 0) + ? "/tmp/undo" : undo_output)) + goto error_out; + + if (find_reloc_sections (dso, &rinfo)) + goto error_out; + + for (i = 1; i < dso->ehdr.e_shnum; i++) + { + if (! (dso->shdr[i].sh_flags & SHF_ALLOC)) + continue; + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name), + ".gnu.conflict")) + continue; + switch (dso->shdr[i].sh_type) + { + case SHT_REL: + if (undo_prelink_rel (dso, i)) + goto error_out; + break; + case SHT_RELA: + if (undo_prelink_rela (dso, i)) + goto error_out; + break; + } + } + + if (dso->arch->arch_undo_prelink && dso->arch->arch_undo_prelink (dso)) + goto error_out; + + if (dso->ehdr.e_type == ET_DYN) + { + GElf_Addr adjust = 0, diff; + + for (i = dso->ehdr.e_shnum - 1; i > 0; --i) + if (shdr[i].sh_flags & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR)) + { + adjust = shdr[i].sh_addr - dso->shdr[i].sh_addr; + break; + } + while (i > 0) + { + int nsec = 1, j; + /* Change here PROGBITS .plt into NOBITS if needed. */ + + /* Convert RELA to REL if needed. */ + if (dso->shdr[i].sh_type == SHT_RELA && shdr[i].sh_type == SHT_REL) + { + assert (dso->arch->rela_to_rel != NULL); + if (i == rinfo.plt) + { + if (convert_rela_to_rel (dso, i)) + goto error_out; + dso->shdr[i].sh_size = shdr[i].sh_size; + } + else if (i == rinfo.last) + { + GElf_Addr start = dso->shdr[rinfo.first].sh_addr; + + for (j = rinfo.first; j <= rinfo.last; ++j) + { + if (convert_rela_to_rel (dso, j)) + goto error_out; + dso->shdr[j].sh_addr = start; + dso->shdr[j].sh_size = shdr[j].sh_size; + start += dso->shdr[j].sh_size; + } + nsec = rinfo.last - rinfo.first + 1; + i = rinfo.first; + } + else + { + error (0, 0, "%s: Cannot convert RELA to REL", dso->filename); + goto error_out; + } + } + diff = shdr[i].sh_addr - dso->shdr[i].sh_addr; + if (diff != adjust) + { + assert (diff >= adjust); + if (adjust_dso (dso, dso->shdr[i + nsec].sh_addr, adjust - diff)) + goto error_out; + adjust = diff; + } + --i; + } + if (adjust && adjust_dso (dso, 0, adjust)) + goto error_out; + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (shdr[i].sh_flags & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR)) + assert (shdr[i].sh_addr == dso->shdr[i].sh_addr); + } + else + { + /* Executable. */ + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + const char *name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name); + + if (dso->shdr[i].sh_type == SHT_PROGBITS + && shdr[i].sh_type == SHT_NOBITS) + { + assert (strcmp (name, ".bss") == 0 + || strcmp (name, ".sbss") == 0 + || strcmp (name, ".plt") == 0 + || strcmp (name, ".iplt") == 0); + scn = dso->scn[i]; + d = elf_getdata (scn, NULL); + assert (d != NULL && elf_getdata (scn, d) == NULL); + assert (d->d_size == 0 || d->d_buf != NULL); + assert (d->d_size == dso->shdr[i].sh_size); + free (d->d_buf); + d->d_buf = NULL; + dso->shdr[i].sh_type = SHT_NOBITS; + } + else if (dso->shdr[i].sh_type == SHT_RELA + && shdr[i].sh_type == SHT_REL) + { + if (convert_rela_to_rel (dso, i)) + goto error_out; + dso->shdr[i].sh_size = shdr[i].sh_size; + } + else + assert (dso->shdr[i].sh_type == shdr[i].sh_type); + if (dso->shdr[i].sh_size != shdr[i].sh_size) + { + /* This is handled in code below for both ET_DYN and ET_EXEC. */ + if (i == dso->ehdr.e_shstrndx) + continue; + assert (shdr[i].sh_type == SHT_NOBITS + || shdr[i].sh_size < dso->shdr[i].sh_size); + assert (strcmp (name, ".dynstr") == 0 + || strcmp (name, ".bss") == 0 + || strcmp (name, ".sbss") == 0); + scn = dso->scn[i]; + d = elf_getdata (scn, NULL); + assert (d != NULL && elf_getdata (scn, d) == NULL); + d->d_size = shdr[i].sh_size; + } + } + + if (update_dynamic_tags (dso, shdr, old_shdr, move)) + goto error_out; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (shdr[i].sh_flags & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR)) + dso->shdr[i].sh_addr = shdr[i].sh_addr; + } + + /* Clear .dynamic entries added by prelink, update others. */ + if (remove_dynamic_prelink_tags (dso) + || update_dynamic_rel (dso, &rinfo)) + goto error_out; + + /* Shrink .shstrtab. */ + i = dso->ehdr.e_shstrndx; + if (shdr[i].sh_size < dso->shdr[i].sh_size) + { + scn = dso->scn[i]; + d = elf_getdata (scn, NULL); + assert (d != NULL && elf_getdata (scn, d) == NULL); + assert (d->d_size == dso->shdr[i].sh_size); + d->d_size = shdr[i].sh_size; + } + + /* Now restore the rest. */ + for (i = 1; i < dso->ehdr.e_shnum; ++i) + dso->shdr[i] = shdr[i]; + if (dso->ehdr.e_phnum != ehdr.e_phnum) + { + assert (ehdr.e_phnum < dso->ehdr.e_phnum); + if (gelf_newphdr (dso->elf, ehdr.e_phnum) == 0) + { + error (0, 0, "Could not create new ELF headers"); + goto error_out; + } + } + for (i = 0; i < ehdr.e_phnum; ++i) + dso->phdr[i] = phdr[i]; + dso->permissive = 1; + assert (dso->ehdr.e_entry == ehdr.e_entry); + assert (dso->ehdr.e_shnum == ehdr.e_shnum); + assert (dso->ehdr.e_shstrndx == ehdr.e_shstrndx); + dso->ehdr.e_phoff = ehdr.e_phoff; + dso->ehdr.e_shoff = ehdr.e_shoff; + dso->ehdr.e_phnum = ehdr.e_phnum; + free (move); + return 0; + +error_out: + free (move); + return 1; +} diff --git a/src/undoall.c b/src/undoall.c new file mode 100644 index 0000000..99d2397 --- /dev/null +++ b/src/undoall.c @@ -0,0 +1,175 @@ +/* Copyright (C) 2002, 2005 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2002. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <alloca.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include "prelinktab.h" + +static int +undo_one (void **p, void *info) +{ + struct prelink_entry *ent = * (struct prelink_entry **) p; + DSO *dso; + struct stat64 st; + struct prelink_link *hardlink; + char *move = NULL, *move_temp; + size_t movelen = 0; + + if (ent->done != 2) + return 1; + + if (ent->type != ET_DYN + && (ent->type != ET_EXEC || libs_only)) + return 1; + + dso = open_dso (ent->canon_filename); + if (dso == NULL) + goto error_out; + + if (fstat64 (dso->fd, &st) < 0) + { + error (0, errno, "%s changed during prelinking", ent->filename); + goto error_out; + } + + if (st.st_dev != ent->dev || st.st_ino != ent->ino) + { + error (0, 0, "%s changed during prelinking", ent->filename); + goto error_out; + } + + if (verbose) + { + if (dry_run) + printf ("Would undo %s\n", ent->canon_filename); + else + printf ("Undoing %s\n", ent->canon_filename); + } + + if (prelink_undo (dso)) + goto error_out; + + if (dry_run) + close_dso (dso); + else + { + if (update_dso (dso, NULL)) + { + dso = NULL; + goto error_out; + } + } + + dso = NULL; + + /* Redo hardlinks. */ + for (hardlink = ent->hardlink; hardlink; hardlink = hardlink->next) + { + size_t len; + + if (wrap_lstat64 (hardlink->canon_filename, &st) < 0) + { + error (0, 0, "Could not stat %s (former hardlink to %s)", + hardlink->canon_filename, ent->canon_filename); + continue; + } + + if (st.st_dev != ent->dev || st.st_ino != ent->ino) + { + error (0, 0, "%s is no longer hardlink to %s", + hardlink->canon_filename, ent->canon_filename); + continue; + } + + if (verbose) + { + if (dry_run) + printf ("Would link %s to %s\n", hardlink->canon_filename, + ent->canon_filename); + else + printf ("Linking %s to %s\n", hardlink->canon_filename, + ent->canon_filename); + } + + len = strlen (hardlink->canon_filename); + if (len + sizeof (".#prelink#") > movelen) + { + movelen = len + sizeof (".#prelink#"); + move_temp = move; + move = realloc (move, movelen); + if (move == NULL) + { + free(move_temp); + error (0, ENOMEM, "Could not hardlink %s to %s", + hardlink->canon_filename, ent->canon_filename); + movelen = 0; + continue; + } + } + + memcpy (mempcpy (move, hardlink->canon_filename, len), ".#prelink#", + sizeof (".#prelink#")); + if (wrap_rename (hardlink->canon_filename, move) < 0) + { + error (0, errno, "Could not hardlink %s to %s", + hardlink->canon_filename, ent->canon_filename); + continue; + } + + if (wrap_link (ent->canon_filename, hardlink->canon_filename) < 0) + { + error (0, errno, "Could not hardlink %s to %s", + hardlink->canon_filename, ent->canon_filename); + + if (wrap_rename (move, hardlink->canon_filename) < 0) + { + error (0, errno, "Could not rename %s back to %s", + move, hardlink->canon_filename); + } + continue; + } + + if (wrap_unlink (move) < 0) + { + error (0, errno, "Could not unlink %s", move); + continue; + } + } + free (move); + return 1; + +error_out: + if (dso) + close_dso (dso); + (*(int *)info)++; + return 1; +} + +int +undo_all (void) +{ + int failures = 0; + htab_traverse (prelink_filename_htab, undo_one, &failures); + return failures != 0; +} diff --git a/src/verify.c b/src/verify.c new file mode 100644 index 0000000..d9f4d7d --- /dev/null +++ b/src/verify.c @@ -0,0 +1,458 @@ +/* Copyright (C) 2002, 2003, 2006, 2007, 2010 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2002. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <endian.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/mman.h> +#include "prelink.h" +#include "md5.h" +#include "sha.h" + +ssize_t +send_file (int outfd, int infd, off_t *poff, size_t count) +{ + char buf[65536], *b, *p, *q; + size_t todo = count, len; + ssize_t n; + + b = mmap (NULL, count, PROT_READ, MAP_PRIVATE, infd, *poff); + if (b != MAP_FAILED) + { + p = b; + q = p + count; + while (p != q) + { + n = TEMP_FAILURE_RETRY (write (outfd, p, q - p)); + if (n <= 0) + { + munmap (b, count); + return -1; + } + p += n; + } + munmap (b, count); + return count; + } + + if (lseek (infd, *poff, SEEK_SET) != *poff) + return -1; + while (todo > 0) + { + len = todo > sizeof (buf) ? sizeof (buf) : todo; + p = buf; + q = buf + len; + while (p != q) + { + n = TEMP_FAILURE_RETRY (read (infd, p, q - p)); + if (n <= 0) + return -1; + p += n; + } + p = buf; + while (p != q) + { + n = TEMP_FAILURE_RETRY (write (outfd, p, q - p)); + if (n <= 0) + return -1; + p += n; + } + todo -= len; + } + return count; +} + +static int +checksum_file (int fd, size_t count, + void (*sum) (const void *, size_t, void *), void *arg) +{ + char buf[65536+64], *b, *p, *q; + size_t todo = count, len; + ssize_t n; + + b = mmap (NULL, count, PROT_READ, MAP_PRIVATE, fd, 0); + if (b != MAP_FAILED) + { + sum (b, count, arg); + munmap (b, count); + return 0; + } + + b = (char *) (((uintptr_t) buf + 63) & ~(uintptr_t) 63); + while (todo > 0) + { + len = todo > 65536 ? 65536 : todo; + p = b; + q = b + len; + while (p != q) + { + n = TEMP_FAILURE_RETRY (read (fd, p, q - p)); + if (n < 0) + return 1; + p += n; + } + sum (b, len, arg); + todo -= len; + } + return 0; +} + +static int +handle_verify (int fd, const char *filename) +{ + off_t off; + size_t cnt; + struct stat64 st; + + if (fstat64 (fd, &st) < 0) + { + error (0, errno, "%s: couldn't fstat temporary file", filename); + return 1; + } + + if (verify_method == VERIFY_CONTENT) + { + off = 0; + if (send_file (1, fd, &off, st.st_size) != st.st_size) + { + error (0, errno, "Couldn't write file to standard output"); + return 1; + } + } + else if (verify_method == VERIFY_MD5) + { + struct md5_ctx ctx; + unsigned char bin_buffer[16]; + + md5_init_ctx (&ctx); + if (checksum_file (fd, st.st_size, + (void (*) (const void *, size_t, void *)) + md5_process_bytes, &ctx)) + { + error (0, errno, "%s: Couldn't read temporary file", filename); + return 1; + } + + md5_finish_ctx (&ctx, bin_buffer); + for (cnt = 0; cnt < 16; ++cnt) + printf ("%02x", bin_buffer[cnt]); + printf (" %s\n", filename); + } + else if (verify_method == VERIFY_SHA) + { + struct sha_ctx ctx; + unsigned char bin_buffer[20]; + + sha_init_ctx (&ctx); + if (checksum_file (fd, st.st_size, + (void (*) (const void *, size_t, void *)) + sha_process_bytes, &ctx)) + { + error (0, errno, "%s: Couldn't read temporary file", filename); + return 1; + } + + sha_finish_ctx (&ctx, bin_buffer); + for (cnt = 0; cnt < 20; ++cnt) + printf ("%02x", bin_buffer[cnt]); + printf (" %s\n", filename); + } + return 0; +} + +int +prelink_verify (const char *filename) +{ + DSO *dso = NULL, *dso2 = NULL; + int fd = -1, fdorig = -1, fdundone = -1, undo, ret; + struct stat64 st, st2; + struct prelink_entry *ent; + GElf_Addr base; + char buffer[32768], buffer2[32768]; + size_t count; + char *p, *q; + + if (wrap_stat64 (filename, &st) < 0) + error (EXIT_FAILURE, errno, "Couldn't stat %s", filename); + + dso = open_dso (filename); + if (dso == NULL) + goto not_prelinked; + + if (dso->ehdr.e_type != ET_DYN && dso->ehdr.e_type != ET_EXEC) + { + error (0, 0, "%s is not an ELF shared library nor binary", filename); + goto not_prelinked; + } + + for (undo = 1; undo < dso->ehdr.e_shnum; ++undo) + if (! strcmp (strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[undo].sh_name), + ".gnu.prelink_undo")) + break; + + if (undo == dso->ehdr.e_shnum) + goto not_prelinked; + + if (fstat64 (dso->fd, &st2) < 0) + { + error (0, errno, "Couldn't fstat %s", filename); + goto failure; + } + + if (st.st_dev != st2.st_dev || st.st_ino != st2.st_ino + || st.st_size != st2.st_size) + { + error (0, 0, "%s: changed during --verify", filename); + goto failure; + } + + if (read_config (prelink_conf)) + goto failure; + + if (gather_config ()) + goto failure; + + if (gather_object (filename, 0, 0)) + goto failure; + + ent = prelink_find_entry (filename, &st, 0); + if (ent == NULL) + { + error (0, 0, "%s disappeared while running --verify", filename); + goto failure; + } + + if (ent->done != 2) + { + error (0, 0, "%s: at least one of file's dependencies has changed since prelinking", + filename); + goto failure; + } + + base = dso->base; + ent->base = base; + + ret = prelink_undo (dso); + if (ret) + goto failure; + + switch (write_dso (dso)) + { + case 2: + error (0, 0, "Could not write temporary for %s: %s", filename, + elf_errmsg (-1)); + goto failure; + case 1: + goto failure; + case 0: + break; + } + + fd = wrap_open (dso->temp_filename, O_RDONLY); + if (fd < 0) + { + error (0, errno, "Could not verify %s", filename); + goto failure; + } + + fdorig = dup (dso->fdro); + if (fdorig < 0) + { + error (0, errno, "Could not verify %s", filename); + goto failure; + } + + ent->filename = dso->temp_filename; + dso->temp_filename = NULL; + close_dso (dso); + dso = NULL; + + fchmod (fd, 0700); + + dso2 = fdopen_dso (fd, filename); + if (dso2 == NULL) + goto failure_unlink; + fd = -1; + + if (prelink_prepare (dso2)) + goto failure_unlink; + + if (ent->type == ET_DYN && relocate_dso (dso2, base)) + goto failure_unlink; + + if (prelink (dso2, ent)) + goto failure_unlink; + + wrap_unlink (ent->filename); + + if (write_dso (dso2)) + goto failure; + + fd = dup (dso2->fd); + if (fd < 0) + { + error (0, errno, "Could not verify %s", filename); + goto failure; + } + + fdundone = dup (dso2->fdro); + if (fdundone < 0) + { + error (0, errno, "Could not verify %s", filename); + goto failure; + } + + close_dso (dso2); + dso2 = NULL; + + if (fstat64 (fdorig, &st2) < 0) + { + error (0, errno, "Couldn't fstat %s", filename); + goto failure; + } + + if (st.st_dev != st2.st_dev || st.st_ino != st2.st_ino + || st.st_size != st2.st_size) + { + error (0, 0, "%s: changed during --verify", filename); + goto failure; + } + + if (fstat64 (fd, &st2) < 0) + { + error (0, errno, "Couldn't fstat temporary file"); + goto failure; + } + + if (st.st_size != st2.st_size) + { + error (0, 0, "%s: prelinked file size differs", filename); + goto failure; + } + + q = MAP_FAILED; + p = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdorig, 0); + if (p != MAP_FAILED) + { + q = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (q == MAP_FAILED) + { + munmap (p, st.st_size); + p = MAP_FAILED; + } + } + if (p != MAP_FAILED) + { + int ret = memcmp (p, q, st.st_size); + + munmap (p, st.st_size); + munmap (q, st.st_size); + if (ret != 0) + { + error (0, 0, "%s: prelinked file was modified", filename); + goto failure; + } + } + else + { + if (lseek (fdorig, 0, SEEK_SET) != 0 + || lseek (fd, 0, SEEK_SET) != 0) + { + error (0, errno, "%s: couldn't seek to start of files", filename); + goto failure; + } + + count = st.st_size; + while (count > 0) + { + size_t len = sizeof (buffer); + + if (len > count) + len = count; + if (read (fdorig, buffer, len) != len) + { + error (0, errno, "%s: couldn't read file", filename); + goto failure; + } + if (read (fd, buffer2, len) != len) + { + error (0, errno, "%s: couldn't read temporary file", filename); + goto failure; + } + if (memcmp (buffer, buffer2, len) != 0) + { + error (0, 0, "%s: prelinked file was modified", filename); + goto failure; + } + count -= len; + } + } + + if (handle_verify (fdundone, filename)) + goto failure; + + fsync (fd); + fsync (fdorig); + fsync (fdundone); + close (fd); + close (fdorig); + close (fdundone); + return 0; + +failure_unlink: + unlink (ent->filename); +failure: + if (fd != -1) + { + fsync (fd); + close (fd); + } + if (fdorig != -1) + { + fsync (fdorig); + close (fdorig); + } + if (fdundone != -1) + { + fsync (fdundone); + close (fdundone); + } + if (dso) + close_dso (dso); + if (dso2) + close_dso (dso2); + return EXIT_FAILURE; + +not_prelinked: + if (dso) + close_dso (dso); + fd = wrap_open (filename, O_RDONLY); + if (fd < 0) + error (EXIT_FAILURE, errno, "Couldn't open %s", filename); + if (handle_verify (fd, filename)) + return EXIT_FAILURE; + fsync (fd); + close (fd); + return 0; +} diff --git a/src/wrap-file.c b/src/wrap-file.c new file mode 100644 index 0000000..f2cf305 --- /dev/null +++ b/src/wrap-file.c @@ -0,0 +1,399 @@ +/* Copyright (C) 2003 MontaVista Software, Inc. + Written by Daniel Jacobowitz <drow@mvista.com>, 2003. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <ftw.h> +#include <glob.h> +#include <stdarg.h> +#include <stddef.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/xattr.h> +#include <time.h> +#include <unistd.h> +#include <utime.h> +#include "prelink.h" + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#ifndef MAXSYMLINKS +#define MAXSYMLINKS 20 +#endif + +extern char *canon_filename (const char *name, int nested, struct stat64 *stp, + const char *chroot, int allow_last_link, + int allow_missing); + +const char *sysroot; + +char * +sysroot_file_name (const char *name, int allow_last_link) +{ + struct stat64 st; + char *ret; + + if (sysroot == NULL) + return (char *) name; + + ret = canon_filename (name, 0, &st, sysroot, allow_last_link, 1); + + if (ret == NULL) + /* That will have set errno. */ + return NULL; + + return ret; +} + +char * +unsysroot_file_name (const char *name) +{ + if (sysroot) + { + int sysroot_len = strlen (sysroot); + if (strncmp (name, sysroot, sysroot_len) == 0) + { + if (name[sysroot_len] == '/') + return strdup (name + sysroot_len); + else if (name[sysroot_len] == 0) + return strdup ("/"); + } + } + return (char *)name; +} + +static int +wrap_stat_body (const char *file, struct stat64 *buf, int lstat) +{ + char* file_copy; + char *tmpname; + int ret; + int len; + + tmpname = sysroot_file_name (file, lstat); + + if (tmpname == NULL) + return -1; + + file_copy = strdup (tmpname); + + if (tmpname != file) + free (tmpname); + + if (file_copy == NULL) + return -1; + + len = strlen (file_copy); + if (len && (file_copy[len - 1] == '/' || file_copy[len - 1] == '\\')) + file_copy[len - 1] = '\0'; + + ret = lstat ? lstat64 (file_copy, buf) : stat64 (file_copy, buf); + + free (file_copy); + + return ret; +} + +int +wrap_lstat64 (const char *file, struct stat64 *buf) +{ + return wrap_stat_body (file, buf, 1); +} + +int +wrap_stat64 (const char *file, struct stat64 *buf) +{ + return wrap_stat_body (file, buf, 0); +} + +int +wrap_rename (const char *old, const char *new) +{ + char *tmpold = sysroot_file_name (old, 1); + char *tmpnew; + int ret; + + if (tmpold == NULL) + return -1; + + tmpnew = sysroot_file_name (new, 1); + if (tmpnew == NULL) + return -1; + + ret = rename (tmpold, tmpnew); + + if (tmpold != old) + free (tmpold); + if (tmpnew != new) + free (tmpnew); + return ret; +} + +int +wrap_open (const char *name, int mode, ...) +{ + char *tmpname = sysroot_file_name (name, 0); + int ret; + + if (tmpname == NULL) + return -1; + + if (mode & O_CREAT) + { + va_list va; + int flags; + va_start (va, mode); + flags = va_arg (va, int); + va_end (va); + ret = open (tmpname, mode, flags); + } + else + ret = open (tmpname, mode); + + if (tmpname != name) + free (tmpname); + return ret; +} + +int +wrap_access (const char *name, int mode) +{ + char *tmpname = sysroot_file_name (name, 0); + int ret; + + if (tmpname == NULL) + return -1; + + ret = access (tmpname, mode); + + if (tmpname != name) + free (tmpname); + return ret; +} + +int +wrap_link (const char *old, const char *new) +{ + char *tmpold = sysroot_file_name (old, 1); + char *tmpnew; + int ret; + + if (tmpold == NULL) + return -1; + + tmpnew = sysroot_file_name (new, 1); + if (tmpnew == NULL) + return -1; + + ret = link (tmpold, tmpnew); + + if (tmpold != old) + free (tmpold); + if (tmpnew != new) + free (tmpnew); + return ret; +} + +/* Note that this isn't recursive safe, since nftw64 doesn't + pass an opaque object around to use. But that fits our needs + for now. */ + +static __nftw64_func_t nftw64_cur_func; + +static int +wrap_nftw64_func (const char *filename, const struct stat64 *status, + int flag, struct FTW *info) +{ + char *tmpname = unsysroot_file_name (filename); + int ret = nftw64_cur_func (tmpname, status, flag, info); + + if (tmpname != filename) + free (tmpname); + return ret; +} + +int +wrap_nftw64 (const char *dir, __nftw64_func_t func, + int descriptors, int flag) +{ + char *tmpdir = sysroot_file_name (dir, 1); + int ret; + + if (tmpdir == NULL) + return -1; + + nftw64_cur_func = func; + ret = nftw64 (tmpdir, wrap_nftw64_func, descriptors, flag); + + if (tmpdir != dir) + free (tmpdir); + return ret; +} + +int +wrap_utime (const char *file, struct utimbuf *file_times) +{ + char *tmpname = sysroot_file_name (file, 0); + int ret; + + if (tmpname == NULL) + return -1; + + ret = utime (tmpname, file_times); + + if (tmpname != file) + free (tmpname); + return ret; +} + +int +wrap_mkstemp (char *filename) +{ + char *tmpname = sysroot_file_name (filename, 1); + int ret; + + if (tmpname == NULL) + return -1; + + ret = mkstemp (tmpname); + + if (tmpname != filename) + { + strcpy (filename, tmpname + strlen (sysroot)); + free (tmpname); + } + return ret; +} + +int +wrap_unlink (const char *filename) +{ + char *tmpname = sysroot_file_name (filename, 1); + int ret; + + if (tmpname == NULL) + return -1; + + ret = unlink (tmpname); + + if (tmpname != filename) + free (tmpname); + return ret; +} + +int +wrap_readlink (const char *path, char *buf, int len) +{ + char *tmpname = sysroot_file_name (path, 1); + int ret; + + if (tmpname == NULL) + return -1; + + ret = readlink (tmpname, buf, len); + + if (tmpname != path) + free (tmpname); + return ret; +} + +int +wrap_setxattr (const char *path, const char *name, const void *value, + size_t size, int flags) +{ + char *tmpname = sysroot_file_name (path, 0); + int ret; + + if (tmpname == NULL) + return -1; + + ret = setxattr (tmpname, name, value, size, flags); + + if (tmpname != path) + free (tmpname); + return ret; +} + +ssize_t +wrap_getxattr (const char *path, const char *name, void *value, + size_t size) +{ + char *tmpname = sysroot_file_name (path, 0); + ssize_t ret; + + if (tmpname == NULL) + return -1; + + ret = getxattr (tmpname, name, value, size); + + if (tmpname != path) + free (tmpname); + return ret; +} + +ssize_t +wrap_listxattr (const char *path, char *list, size_t size) +{ + char *tmpname = sysroot_file_name (path, 0); + ssize_t ret; + + if (tmpname == NULL) + return -1; + + ret = listxattr (tmpname, list, size); + + if (tmpname != path) + free (tmpname); + return ret; +} + +int +wrap_glob (const char *pattern, int flags, + int (*errfunc) (const char *epath, int eerrno), + glob_t *pglob) +{ + char *tmp; + int ret; + + if (!sysroot) + return glob (pattern, flags, errfunc, pglob); + + asprintf (&tmp, "%s%s", sysroot, pattern); + + ret = glob(tmp, flags, errfunc, pglob); + if (!ret) + { + size_t n; + + for (n = 0; n < pglob->gl_pathc; ++n) + { + char *usname = unsysroot_file_name(pglob->gl_pathv[n]); + if (usname != pglob->gl_pathv[n]) + free(pglob->gl_pathv[n]); + pglob->gl_pathv[n] = usname; + } + } + + free(tmp); + return ret; +} |