/* Copyright (C) 2001, 2002, 2003, 2004, 2009, 2011 Red Hat, Inc. Written by Jakub Jelinek , 2001. * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #include #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 };