diff options
Diffstat (limited to 'trunk/src')
-rw-r--r-- | trunk/src/Makefile.am | 4 | ||||
-rw-r--r-- | trunk/src/Makefile.in | 4 | ||||
-rw-r--r-- | trunk/src/arch-alpha.c | 20 | ||||
-rw-r--r-- | trunk/src/arch-arm.c | 35 | ||||
-rw-r--r-- | trunk/src/arch-cris.c | 14 | ||||
-rw-r--r-- | trunk/src/arch-i386.c | 52 | ||||
-rw-r--r-- | trunk/src/arch-ia64.c | 14 | ||||
-rw-r--r-- | trunk/src/arch-mips.c | 16 | ||||
-rw-r--r-- | trunk/src/arch-ppc.c | 28 | ||||
-rw-r--r-- | trunk/src/arch-ppc64.c | 25 | ||||
-rw-r--r-- | trunk/src/arch-s390.c | 16 | ||||
-rw-r--r-- | trunk/src/arch-s390x.c | 24 | ||||
-rw-r--r-- | trunk/src/arch-sh.c | 26 | ||||
-rw-r--r-- | trunk/src/arch-sparc.c | 20 | ||||
-rw-r--r-- | trunk/src/arch-sparc64.c | 28 | ||||
-rw-r--r-- | trunk/src/arch-x86_64.c | 40 | ||||
-rw-r--r-- | trunk/src/conflict.c | 177 | ||||
-rw-r--r-- | trunk/src/cxx.c | 490 | ||||
-rw-r--r-- | trunk/src/doit.c | 60 | ||||
-rw-r--r-- | trunk/src/dso.c | 1 | ||||
-rw-r--r-- | trunk/src/dwarf2.c | 74 | ||||
-rw-r--r-- | trunk/src/dwarf2.h | 25 | ||||
-rw-r--r-- | trunk/src/fptr.c | 15 | ||||
-rw-r--r-- | trunk/src/gather.c | 113 | ||||
-rw-r--r-- | trunk/src/get.c | 119 | ||||
-rw-r--r-- | trunk/src/main.c | 9 | ||||
-rw-r--r-- | trunk/src/prelink.c | 36 | ||||
-rw-r--r-- | trunk/src/prelink.h | 43 | ||||
-rw-r--r-- | trunk/src/space.c | 350 | ||||
-rw-r--r-- | trunk/src/verify.c | 7 |
30 files changed, 1308 insertions, 577 deletions
diff --git a/trunk/src/Makefile.am b/trunk/src/Makefile.am index 64dad8f..387b6ef 100644 --- a/trunk/src/Makefile.am +++ b/trunk/src/Makefile.am @@ -5,8 +5,8 @@ AUTOMAKE_OPTIONS = 1.4 gnu PKGVERSION = "\"@PKGVERSION@\"" REPORT_BUGS_TO = "\"@REPORT_BUGS_TO@\"" -DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -Wall -AM_CFLAGS = -Wall +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)'`\"" \ diff --git a/trunk/src/Makefile.in b/trunk/src/Makefile.in index 7dbe941..85cf845 100644 --- a/trunk/src/Makefile.in +++ b/trunk/src/Makefile.in @@ -97,8 +97,8 @@ install_sh = @install_sh@ AUTOMAKE_OPTIONS = 1.4 gnu -DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -Wall -AM_CFLAGS = -Wall +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -Wall -Wno-pointer-sign +AM_CFLAGS = -Wall -Wno-pointer-sign AM_CPPFLAGS = -DSBINDIR='"@sbindir@"' INCLUDES = @GELFINCLUDE@ diff --git a/trunk/src/arch-alpha.c b/trunk/src/arch-alpha.c index f35ee18..7802a3e 100644 --- a/trunk/src/arch-alpha.c +++ b/trunk/src/arch-alpha.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* 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 @@ -30,14 +30,14 @@ static int alpha_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { return 0; } static int alpha_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { error (0, 0, "%s: Alpha doesn't support REL relocs", dso->filename); return 1; @@ -45,7 +45,7 @@ alpha_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, static int alpha_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (GELF_R_TYPE (rela->r_info) == R_ALPHA_RELATIVE || GELF_R_TYPE (rela->r_info) == R_ALPHA_JMP_SLOT) @@ -195,7 +195,7 @@ alpha_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int alpha_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (GELF_R_TYPE (rela->r_info) & 0xff) { @@ -260,7 +260,8 @@ alpha_prelink_conflict_rela (DSO *dso, struct prelink_info *info, GElf_Rela *ret; if (GELF_R_TYPE (rela->r_info) == R_ALPHA_RELATIVE - || GELF_R_TYPE (rela->r_info) == R_ALPHA_NONE) + || 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), @@ -280,6 +281,12 @@ alpha_prelink_conflict_rela (DSO *dso, struct prelink_info *info, } 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 @@ -467,6 +474,7 @@ PL_ARCH(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, diff --git a/trunk/src/arch-arm.c b/trunk/src/arch-arm.c index 0dddaac..ba53929 100644 --- a/trunk/src/arch-arm.c +++ b/trunk/src/arch-arm.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2004 Red Hat, Inc. +/* 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 @@ -36,7 +36,7 @@ static int arm_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (dyn->d_tag == DT_PLTGOT) { @@ -73,7 +73,7 @@ arm_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, static int arm_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { Elf32_Addr data; switch (GELF_R_TYPE (rel->r_info)) @@ -90,7 +90,7 @@ arm_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, static int arm_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { Elf32_Addr data; @@ -186,7 +186,7 @@ arm_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) static int arm_prelink_rela (struct prelink_info *info, GElf_Rela *rela, - GElf_Addr relaaddr) + GElf_Addr relaaddr) { DSO *dso; GElf_Addr value; @@ -254,7 +254,7 @@ arm_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int arm_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (GELF_R_TYPE (rela->r_info)) { @@ -354,7 +354,7 @@ arm_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) static int arm_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, - GElf_Addr reladdr) + GElf_Addr reladdr) { GElf_Addr value; struct prelink_conflict *conflict; @@ -362,7 +362,8 @@ arm_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, GElf_Rela *ret; if (GELF_R_TYPE (rel->r_info) == R_ARM_RELATIVE - || GELF_R_TYPE (rel->r_info) == R_ARM_NONE) + || GELF_R_TYPE (rel->r_info) == R_ARM_NONE + || info->dso == dso) /* Fast path: nothing to do. */ return 0; conflict = prelink_conflict (info, GELF_R_SYM (rel->r_info), @@ -384,6 +385,12 @@ arm_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, } 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 @@ -452,7 +459,7 @@ arm_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, static int arm_prelink_conflict_rela (DSO *dso, struct prelink_info *info, - GElf_Rela *rela, GElf_Addr relaaddr) + GElf_Rela *rela, GElf_Addr relaaddr) { GElf_Addr value; struct prelink_conflict *conflict; @@ -461,7 +468,8 @@ arm_prelink_conflict_rela (DSO *dso, struct prelink_info *info, Elf32_Sword val; if (GELF_R_TYPE (rela->r_info) == R_ARM_RELATIVE - || GELF_R_TYPE (rela->r_info) == R_ARM_NONE) + || GELF_R_TYPE (rela->r_info) == R_ARM_NONE + || info->dso == dso) /* Fast path: nothing to do. */ return 0; conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), @@ -484,6 +492,12 @@ arm_prelink_conflict_rela (DSO *dso, struct prelink_info *info, } 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 @@ -823,6 +837,7 @@ PL_ARCH(arm) = { .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.2", .dynamic_linker_alt = "/lib/ld-linux.so.3", .adjust_dyn = arm_adjust_dyn, diff --git a/trunk/src/arch-cris.c b/trunk/src/arch-cris.c index b110539..3272779 100644 --- a/trunk/src/arch-cris.c +++ b/trunk/src/arch-cris.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2004 Red Hat, Inc. +/* 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 @@ -159,7 +159,7 @@ cris_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int cris_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (GELF_R_TYPE (rela->r_info)) { @@ -247,13 +247,20 @@ cris_prelink_conflict_rela (DSO *dso, struct prelink_info *info, GElf_Rela *ret; if (GELF_R_TYPE (rela->r_info) == R_CRIS_RELATIVE - || GELF_R_TYPE (rela->r_info) == R_CRIS_NONE) + || 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) @@ -375,6 +382,7 @@ PL_ARCH(cris) = { .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, diff --git a/trunk/src/arch-i386.c b/trunk/src/arch-i386.c index 37b5c67..5661dcb 100644 --- a/trunk/src/arch-i386.c +++ b/trunk/src/arch-i386.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* 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 @@ -75,6 +75,7 @@ i386_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, { 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); @@ -92,6 +93,7 @@ i386_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, 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; @@ -117,6 +119,7 @@ i386_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) 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; @@ -187,6 +190,7 @@ i386_prelink_rela (struct prelink_info *info, GElf_Rela *rela, 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; @@ -244,8 +248,10 @@ i386_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int i386_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { + GElf_Rela *ret; + switch (GELF_R_TYPE (rela->r_info)) { case R_386_GLOB_DAT: @@ -253,6 +259,16 @@ i386_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, 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 (); } @@ -339,20 +355,25 @@ i386_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, GELF_R_TYPE (rel->r_info)); if (conflict == NULL) { - if (info->curtls == NULL) - return 0; 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 @@ -376,6 +397,11 @@ i386_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, /* 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: @@ -441,20 +467,25 @@ i386_prelink_conflict_rela (DSO *dso, struct prelink_info *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_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 @@ -477,11 +508,16 @@ i386_prelink_conflict_rela (DSO *dso, struct prelink_info *info, 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); @@ -539,6 +575,7 @@ i386_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) /* 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: @@ -672,6 +709,7 @@ i386_undo_prelink_rel (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr) { 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); @@ -746,6 +784,7 @@ i386_rela_to_rel (DSO *dso, GElf_Rela *rela, GElf_Rel *rel) 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: @@ -1052,6 +1091,7 @@ PL_ARCH(i386) = { .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, diff --git a/trunk/src/arch-ia64.c b/trunk/src/arch-ia64.c index 89df85e..6039115 100644 --- a/trunk/src/arch-ia64.c +++ b/trunk/src/arch-ia64.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* 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 @@ -196,7 +196,7 @@ ia64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int ia64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { if ((GELF_R_TYPE (rela->r_info) & ~1) == R_IA64_IPLTMSB) { @@ -312,13 +312,20 @@ ia64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, GElf_Rela *ret; if ((GELF_R_TYPE (rela->r_info) & ~3) == R_IA64_REL32MSB - || GELF_R_TYPE (rela->r_info) == R_IA64_NONE) + || 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) @@ -493,6 +500,7 @@ PL_ARCH(ia64) = { .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, diff --git a/trunk/src/arch-mips.c b/trunk/src/arch-mips.c index b85616e..2e0ea4c 100644 --- a/trunk/src/arch-mips.c +++ b/trunk/src/arch-mips.c @@ -660,11 +660,14 @@ mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info, { GElf_Addr value; struct prelink_conflict *conflict; - struct prelink_tls *tls; + 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); @@ -688,6 +691,12 @@ mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info, 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; @@ -791,7 +800,7 @@ mips_arch_prelink_conflict (DSO *dso, struct prelink_info *info) struct prelink_conflict *conflict; GElf_Rela *entry; - if (dso->info[DT_PLTGOT] == 0) + if (info->dso == dso || dso->info[DT_PLTGOT] == 0) return 0; /* Add a conflict for every global GOT entry that does not hold the @@ -829,7 +838,7 @@ mips_arch_prelink_conflict (DSO *dso, struct prelink_info *info) static int mips_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { DSO *dso; @@ -1348,6 +1357,7 @@ PL_ARCH(mips) = { /* 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, diff --git a/trunk/src/arch-ppc.c b/trunk/src/arch-ppc.c index 4bf5aca..99dbea5 100644 --- a/trunk/src/arch-ppc.c +++ b/trunk/src/arch-ppc.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2009 Red Hat, Inc. Written by Jakub Jelinek <jakub@redhat.com>, 2001. This program is free software; you can redistribute it and/or modify @@ -37,7 +37,7 @@ static int ppc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (dyn->d_tag == DT_PPC_GOT) { @@ -116,7 +116,7 @@ ppc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, static int ppc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { error (0, 0, "%s: PowerPC doesn't support REL relocs", dso->filename); return 1; @@ -124,7 +124,7 @@ ppc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, static int ppc_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE) { @@ -143,7 +143,7 @@ ppc_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, static int ppc_prelink_rel (struct prelink_info *info, GElf_Rel *rel, - GElf_Addr reladdr) + GElf_Addr reladdr) { error (0, 0, "%s: PowerPC doesn't support REL relocs", info->dso->filename); return 1; @@ -201,7 +201,7 @@ ppc_fixup_plt (DSO *dso, GElf_Rela *rela, GElf_Addr value) static int ppc_prelink_rela (struct prelink_info *info, GElf_Rela *rela, - GElf_Addr relaaddr) + GElf_Addr relaaddr) { DSO *dso = info->dso; GElf_Addr value; @@ -330,7 +330,7 @@ ppc_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int ppc_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (GELF_R_TYPE (rela->r_info)) { @@ -418,7 +418,7 @@ ppc_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) static int ppc_prelink_conflict_rel (DSO *dso, struct prelink_info *info, - GElf_Rel *rel, GElf_Addr reladdr) + GElf_Rel *rel, GElf_Addr reladdr) { error (0, 0, "%s: PowerPC doesn't support REL relocs", dso->filename); return 1; @@ -426,7 +426,7 @@ ppc_prelink_conflict_rel (DSO *dso, struct prelink_info *info, static int ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info, - GElf_Rela *rela, GElf_Addr relaaddr) + GElf_Rela *rela, GElf_Addr relaaddr) { GElf_Addr value; struct prelink_conflict *conflict; @@ -435,7 +435,8 @@ ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info, int r_type; if (GELF_R_TYPE (rela->r_info) == R_PPC_RELATIVE - || GELF_R_TYPE (rela->r_info) == R_PPC_NONE) + || GELF_R_TYPE (rela->r_info) == R_PPC_NONE + || info->dso == dso) /* Fast path: nothing to do. */ return 0; conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), @@ -459,6 +460,12 @@ ppc_prelink_conflict_rela (DSO *dso, struct prelink_info *info, } value = 0; } + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on PowerPC yet", + dso->filename); + return 1; + } else { /* DTPREL wants to see only real conflicts, not lookups @@ -1122,6 +1129,7 @@ PL_ARCH(ppc) = { .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, diff --git a/trunk/src/arch-ppc64.c b/trunk/src/arch-ppc64.c index ec28ee8..6f9f644 100644 --- a/trunk/src/arch-ppc64.c +++ b/trunk/src/arch-ppc64.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002, 2003, 2004 Red Hat, Inc. +/* 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 @@ -60,7 +60,7 @@ ppc64_adjust_section (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) static int ppc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (dyn->d_tag == DT_PPC64_GLINK && dyn->d_un.d_ptr >= start) { @@ -73,7 +73,7 @@ ppc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, static int ppc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { error (0, 0, "%s: PowerPC64 doesn't support REL relocs", dso->filename); return 1; @@ -81,7 +81,7 @@ ppc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, static int ppc64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE) { @@ -306,7 +306,7 @@ ppc64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int ppc64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (GELF_R_TYPE (rela->r_info)) { @@ -439,7 +439,8 @@ ppc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, int r_type; if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE - || GELF_R_TYPE (rela->r_info) == R_PPC64_NONE) + || GELF_R_TYPE (rela->r_info) == R_PPC64_NONE + || info->dso == dso) /* Fast path: nothing to do. */ return 0; conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), @@ -463,6 +464,12 @@ ppc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, } value = 0; } + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on PowerPC64 yet", + dso->filename); + return 1; + } else { /* DTPREL wants to see only real conflicts, not lookups @@ -771,13 +778,12 @@ ppc64_reloc_class (int reloc_type) { switch (reloc_type) { - case R_PPC64_COPY: return RTYPE_CLASS_COPY; - case R_PPC64_ADDR24: return RTYPE_CLASS_PLT; + 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_VALID; + return RTYPE_CLASS_PLT; } } @@ -824,6 +830,7 @@ PL_ARCH(ppc64) = { .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, diff --git a/trunk/src/arch-s390.c b/trunk/src/arch-s390.c index f2ca7e1..7907e83 100644 --- a/trunk/src/arch-s390.c +++ b/trunk/src/arch-s390.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* 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 @@ -67,7 +67,7 @@ s390_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, static int s390_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename); return 1; @@ -168,7 +168,7 @@ s390_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int s390_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (GELF_R_TYPE (rela->r_info)) { @@ -238,7 +238,8 @@ s390_prelink_conflict_rela (DSO *dso, struct prelink_info *info, GElf_Rela *ret; if (GELF_R_TYPE (rela->r_info) == R_390_RELATIVE - || GELF_R_TYPE (rela->r_info) == R_390_NONE) + || GELF_R_TYPE (rela->r_info) == R_390_NONE + || info->dso == dso) /* Fast path: nothing to do. */ return 0; conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), @@ -258,6 +259,12 @@ s390_prelink_conflict_rela (DSO *dso, struct prelink_info *info, } value = 0; } + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on S390 yet", + dso->filename); + return 1; + } else { /* DTPOFF wants to see only real conflicts, not lookups @@ -482,6 +489,7 @@ PL_ARCH(s390) = { .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, diff --git a/trunk/src/arch-s390x.c b/trunk/src/arch-s390x.c index e1c213b..8676fff 100644 --- a/trunk/src/arch-s390x.c +++ b/trunk/src/arch-s390x.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* 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 @@ -67,7 +67,7 @@ s390x_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, static int s390x_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename); return 1; @@ -75,7 +75,7 @@ s390x_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, static int s390x_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { Elf64_Addr addr; @@ -108,7 +108,7 @@ s390x_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) static int s390x_prelink_rela (struct prelink_info *info, GElf_Rela *rela, - GElf_Addr relaaddr) + GElf_Addr relaaddr) { DSO *dso = info->dso; GElf_Addr value; @@ -191,7 +191,7 @@ s390x_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int s390x_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (GELF_R_TYPE (rela->r_info)) { @@ -276,7 +276,7 @@ s390x_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) static int s390x_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, - GElf_Addr reladdr) + GElf_Addr reladdr) { error (0, 0, "%s: S390 doesn't support REL relocs", dso->filename); return 1; @@ -284,7 +284,7 @@ s390x_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, static int s390x_prelink_conflict_rela (DSO *dso, struct prelink_info *info, - GElf_Rela *rela, GElf_Addr relaaddr) + GElf_Rela *rela, GElf_Addr relaaddr) { GElf_Addr value; struct prelink_conflict *conflict; @@ -293,7 +293,8 @@ s390x_prelink_conflict_rela (DSO *dso, struct prelink_info *info, int r_type; if (GELF_R_TYPE (rela->r_info) == R_390_RELATIVE - || GELF_R_TYPE (rela->r_info) == R_390_NONE) + || GELF_R_TYPE (rela->r_info) == R_390_NONE + || info->dso == dso) /* Fast path: nothing to do. */ return 0; conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), @@ -313,6 +314,12 @@ s390x_prelink_conflict_rela (DSO *dso, struct prelink_info *info, } value = 0; } + else if (conflict->ifunc) + { + error (0, 0, "%s: STT_GNU_IFUNC not handled on S390x yet", + dso->filename); + return 1; + } else { /* DTPOFF wants to see only real conflicts, not lookups @@ -598,6 +605,7 @@ PL_ARCH(s390x) = { .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, diff --git a/trunk/src/arch-sh.c b/trunk/src/arch-sh.c index d851a4d..1b11312 100644 --- a/trunk/src/arch-sh.c +++ b/trunk/src/arch-sh.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* 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 @@ -30,7 +30,7 @@ static int sh_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (dyn->d_tag == DT_PLTGOT) { @@ -67,7 +67,7 @@ sh_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, static int sh_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { error (0, 0, "%s: SH doesn't support REL relocs", dso->filename); return 1; @@ -75,7 +75,7 @@ sh_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, static int sh_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { Elf32_Addr data; @@ -107,7 +107,7 @@ sh_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) static int sh_prelink_rela (struct prelink_info *info, GElf_Rela *rela, - GElf_Addr relaaddr) + GElf_Addr relaaddr) { DSO *dso; GElf_Addr value; @@ -151,7 +151,7 @@ sh_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int sh_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (GELF_R_TYPE (rela->r_info)) { @@ -206,7 +206,7 @@ sh_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) static int sh_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, - GElf_Addr reladdr) + GElf_Addr reladdr) { error (0, 0, "%s: SH doesn't support REL relocs", dso->filename); return 1; @@ -214,20 +214,27 @@ sh_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, static int sh_prelink_conflict_rela (DSO *dso, struct prelink_info *info, - GElf_Rela *rela, GElf_Addr relaaddr) + 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) + || 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) @@ -414,6 +421,7 @@ PL_ARCH(sh) = { .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, diff --git a/trunk/src/arch-sparc.c b/trunk/src/arch-sparc.c index 6594c8f..e016a79 100644 --- a/trunk/src/arch-sparc.c +++ b/trunk/src/arch-sparc.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2004 Red Hat, Inc. +/* 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 @@ -30,7 +30,7 @@ static int sparc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (dyn->d_tag == DT_PLTGOT) { @@ -55,7 +55,7 @@ sparc_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, static int sparc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename); return 1; @@ -63,7 +63,7 @@ sparc_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, static int sparc_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (GELF_R_TYPE (rela->r_info) == R_SPARC_RELATIVE) { @@ -235,7 +235,7 @@ sparc_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int sparc_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (GELF_R_TYPE (rela->r_info)) { @@ -333,7 +333,8 @@ sparc_prelink_conflict_rela (DSO *dso, struct prelink_info *info, int r_type; if (GELF_R_TYPE (rela->r_info) == R_SPARC_RELATIVE - || GELF_R_TYPE (rela->r_info) == R_SPARC_NONE) + || 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), @@ -355,6 +356,12 @@ sparc_prelink_conflict_rela (DSO *dso, struct prelink_info *info, } 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 @@ -607,6 +614,7 @@ PL_ARCH(sparc) = { .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, diff --git a/trunk/src/arch-sparc64.c b/trunk/src/arch-sparc64.c index 70d3fd7..aee4601 100644 --- a/trunk/src/arch-sparc64.c +++ b/trunk/src/arch-sparc64.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2004 Red Hat, Inc. +/* 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 @@ -32,7 +32,7 @@ static int sparc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (dyn->d_tag == DT_PLTGOT) { @@ -57,7 +57,7 @@ sparc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, static int sparc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename); return 1; @@ -65,7 +65,7 @@ sparc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, static int sparc64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_RELATIVE) { @@ -84,7 +84,7 @@ sparc64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, static int sparc64_prelink_rel (struct prelink_info *info, GElf_Rel *rel, - GElf_Addr reladdr) + GElf_Addr reladdr) { error (0, 0, "%s: Sparc doesn't support REL relocs", info->dso->filename); return 1; @@ -166,7 +166,7 @@ sparc64_fixup_plt (DSO *dso, GElf_Rela *rela, GElf_Addr value) static int sparc64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, - GElf_Addr relaaddr) + GElf_Addr relaaddr) { DSO *dso = info->dso; GElf_Addr value; @@ -309,7 +309,7 @@ sparc64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int sparc64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { switch (SPARC64_R_TYPE (rela->r_info)) { @@ -425,7 +425,7 @@ sparc64_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) static int sparc64_prelink_conflict_rel (DSO *dso, struct prelink_info *info, - GElf_Rel *rel, GElf_Addr reladdr) + GElf_Rel *rel, GElf_Addr reladdr) { error (0, 0, "%s: Sparc doesn't support REL relocs", dso->filename); return 1; @@ -433,7 +433,7 @@ sparc64_prelink_conflict_rel (DSO *dso, struct prelink_info *info, static int sparc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, - GElf_Rela *rela, GElf_Addr relaaddr) + GElf_Rela *rela, GElf_Addr relaaddr) { GElf_Addr value; struct prelink_conflict *conflict; @@ -442,7 +442,8 @@ sparc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, int r_type; if (SPARC64_R_TYPE (rela->r_info) == R_SPARC_RELATIVE - || SPARC64_R_TYPE (rela->r_info) == R_SPARC_NONE) + || 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), @@ -464,6 +465,12 @@ sparc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, } 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 @@ -807,6 +814,7 @@ PL_ARCH(sparc64) = { .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, diff --git a/trunk/src/arch-x86_64.c b/trunk/src/arch-x86_64.c index 8846672..83286e9 100644 --- a/trunk/src/arch-x86_64.c +++ b/trunk/src/arch-x86_64.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004, 2006 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2004, 2006, 2009 Red Hat, Inc. Written by Jakub Jelinek <jakub@redhat.com>, 2001. This program is free software; you can redistribute it and/or modify @@ -30,7 +30,7 @@ static int x86_64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { if (dyn->d_tag == DT_PLTGOT) { @@ -75,7 +75,7 @@ x86_64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, static int x86_64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, - GElf_Addr adjust) + GElf_Addr adjust) { Elf64_Addr addr; @@ -89,6 +89,10 @@ x86_64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, 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_ule64 (dso, rela->r_offset); if (addr >= start) @@ -113,7 +117,8 @@ x86_64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, GElf_Addr value; dso = info->dso; - if (GELF_R_TYPE (rela->r_info) == R_X86_64_NONE) + 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) { @@ -169,8 +174,9 @@ x86_64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, static int x86_64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, - char *buf) + char *buf, GElf_Addr dest_addr) { + GElf_Rela *ret; switch (GELF_R_TYPE (rela->r_info)) { case R_X86_64_GLOB_DAT: @@ -178,6 +184,16 @@ x86_64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, 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; @@ -252,19 +268,24 @@ x86_64_prelink_conflict_rela (DSO *dso, struct prelink_info *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_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 @@ -288,7 +309,10 @@ x86_64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, /* 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; @@ -422,6 +446,7 @@ x86_64_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) { 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); @@ -505,6 +530,7 @@ PL_ARCH(x86_64) = { .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, diff --git a/trunk/src/conflict.c b/trunk/src/conflict.c index cb0ba80..3181de1 100644 --- a/trunk/src/conflict.c +++ b/trunk/src/conflict.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. +/* 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. @@ -36,8 +36,12 @@ prelink_conflict (struct prelink_info *info, GElf_Word r_sym, 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; - for (conflict = info->curconflicts; conflict; conflict = conflict->next) + 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; @@ -278,7 +282,7 @@ conflict_rela_cmp (const void *A, const void *B) int get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr, - char *buf, GElf_Word size) + char *buf, GElf_Word size, GElf_Addr dest_addr) { int sec = addr_to_sec (dso, addr), j; Elf_Scn *scn; @@ -323,7 +327,7 @@ get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr, reloc offset. */ for (j = 0; j < info->conflict_rela_size; ++j) { - int reloc_type, reloc_size; + int reloc_type, reloc_size, ret; off_t off; if (info->conflict_rela[j].r_offset >= addr + size) @@ -345,8 +349,12 @@ get_relocated_mem (struct prelink_info *info, DSO *dso, GElf_Addr addr, return 2; /* Note that apply_conflict_rela shouldn't rely on R_SYM field of conflict to be 0. */ - dso->arch->apply_conflict_rela (info, info->conflict_rela + j, - buf + off); + ret + = dso->arch->apply_conflict_rela (info, info->conflict_rela + j, + buf + off, + dest_addr ? dest_addr + off : 0); + if (ret) + return ret; } } else @@ -469,15 +477,17 @@ prelink_build_conflicts (struct prelink_info *info) } } - for (i = 1; i < ndeps; ++i) + for (i = 0; i < ndeps; ++i) { + int j, sec, first_conflict, maxidx; + struct prelink_conflict *conflict; + dso = info->dsos[i]; - ent = info->ent->depends[i - 1]; + ent = i ? info->ent->depends[i - 1] : info->ent; /* Verify .gnu.liblist sections of all dependent libraries. */ - if (ent->ndepends > 0) + if (i && ent->ndepends > 0) { - int j; const char *name; int nliblist; Elf32_Lib *liblist; @@ -541,83 +551,88 @@ prelink_build_conflicts (struct prelink_info *info) } } - if (info->conflicts[i] || info->tls[i].modid) + 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) { - int j, sec, first_conflict; - struct prelink_conflict *conflict; - - 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) { - error (0, 0, "Bad symtab"); - goto error_out; + 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; } - 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->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)) { - if (! (dso->shdr[j].sh_flags & SHF_ALLOC)) - continue; - switch (dso->shdr[j].sh_type) - { - case SHT_REL: - if (prelink_conflict_rel (dso, j, info)) - goto error_out; - break; - case SHT_RELA: - if (prelink_conflict_rela (dso, j, info)) - goto error_out; - break; - } + error (0, 0, "%s: Conflict %08llx not found in any relocation", + dso->filename, (unsigned long long) conflict->symoff); + ret = 1; } - if (dso->arch->arch_prelink_conflict - && dso->arch->arch_prelink_conflict (dso, info)) - goto error_out; + /* 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 + = GELF_R_INFO (i, GELF_R_TYPE (info->conflict_rela[j].r_info)); - for (conflict = info->curconflicts; conflict; - conflict = conflict->next) - if (! conflict->used) - { - error (0, 0, "%s: Conflict %08llx not found in any relocation", - dso->filename, (unsigned long long) conflict->symoff); - ret = 1; - } + 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; - /* 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; - } - } + 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; + } } } @@ -755,11 +770,13 @@ prelink_build_conflicts (struct prelink_info *info) 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); + - 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); + - info->dynbss_base, cr.rela[i].r_addend, + cr.rela[i].r_offset); switch (j) { diff --git a/trunk/src/cxx.c b/trunk/src/cxx.c index abb97a5..438cdfe 100644 --- a/trunk/src/cxx.c +++ b/trunk/src/cxx.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003 Red Hat, Inc. +/* 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 @@ -16,6 +16,7 @@ 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> @@ -27,24 +28,168 @@ #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 *fcs, int reloc_size, + struct find_cxx_sym_cache **cache) { int n, ndeps = info->ent->ndepends + 1; - int ndx, maxndx; + unsigned int hi, lo, mid; DSO *dso = NULL; - Elf_Scn *scn; + struct find_cxx_sym_cache *c; if (fcs->dso == NULL || addr < fcs->dso->base @@ -67,44 +212,76 @@ find_cxx_sym (struct prelink_info *info, GElf_Addr addr, } 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->symsec = addr_to_sec (dso, dso->info[DT_SYMTAB]); - if (fcs->symsec == -1) + 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) { - fcs->ent = NULL; - return -1; + 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; + } } - scn = dso->scn[fcs->symsec]; - fcs->symtab = elf_getdata (scn, NULL); - assert (elf_getdata (scn, fcs->symtab) == NULL); - fcs->strsec = addr_to_sec (dso, dso->info[DT_STRTAB]); - if (fcs->strsec == -1) + else { - fcs->ent = NULL; - return -1; + 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; + } } - scn = dso->scn[fcs->strsec]; - fcs->strtab = elf_getdata (scn, NULL); - assert (elf_getdata (scn, fcs->strtab) == NULL); } - else - dso = fcs->dso; - - maxndx = fcs->symtab->d_size / dso->shdr[fcs->symsec].sh_entsize; - for (ndx = 0; ndx < maxndx; ++ndx) + while (lo < hi) { - gelfx_getsym (dso->elf, fcs->symtab, ndx, &fcs->sym); - if (fcs->sym.st_value <= addr - && fcs->sym.st_value + fcs->sym.st_size >= addr + reloc_size) - break; + 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; } - if (ndx == maxndx) - return -1; - - return ndx; + return -1; } /* The idea here is that C++ virtual tables are always emitted @@ -117,7 +294,8 @@ int remove_redundant_cxx_conflicts (struct prelink_info *info) { int i, j, k, n, o, state, removed = 0; - int ndx, maxndx, sec; + int ndx, sec; + unsigned int hi, lo, mid; int reloc_type, reloc_size; struct find_cxx_sym fcs1, fcs2; char *mem1, *mem2; @@ -127,24 +305,10 @@ remove_redundant_cxx_conflicts (struct prelink_info *info) Elf_Data *binsymtab = NULL; int binsymtabsec; struct prelink_conflict *conflict; - static struct - { - unsigned char *prefix; - unsigned char prefix_len, st_info, check_pltref; - unsigned char *section; - } - specials[] = - { - /* G++ 3.0 ABI. */ - /* Virtual table. */ - { "_ZTV", 4, GELF_ST_INFO (STB_WEAK, STT_OBJECT), 1, ".data" }, - /* Typeinfo. */ - { "_ZTI", 4, GELF_ST_INFO (STB_WEAK, STT_OBJECT), 0, ".data" }, - /* G++ 2.96-RH ABI. */ - /* Virtual table. */ - { "__vt_", 5, GELF_ST_INFO (STB_WEAK, STT_OBJECT), 0, ".data" }, - { NULL, 0, 0, 0, NULL } - }; + 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) @@ -162,11 +326,19 @@ remove_redundant_cxx_conflicts (struct prelink_info *info) 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); @@ -186,12 +358,16 @@ remove_redundant_cxx_conflicts (struct prelink_info *info) } n = find_cxx_sym (info, info->conflict_rela[i].r_offset, - &fcs1, reloc_size); + &fcs1, reloc_size, cache); - name = (const char *) fcs1.strtab->d_buf + fcs1.sym.st_name; 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) @@ -201,31 +377,20 @@ remove_redundant_cxx_conflicts (struct prelink_info *info) 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 - && strcmp (secname, specials[k].section) == 0) + && strncmp (name, specials[k].prefix, specials[k].prefix_len) == 0) break; if (specials[k].prefix == NULL) continue; - /* Now check there are no other symbols pointing to it. */ - maxndx = fcs1.symtab->d_size / fcs1.dso->shdr[fcs1.symsec].sh_entsize; - for (ndx = 0; ndx < maxndx; ++ndx) - if (ndx != n) - { - GElf_Sym sym; - - gelfx_getsym (fcs1.dso->elf, fcs1.symtab, ndx, &sym); - if ((sym.st_value + sym.st_size > fcs1.sym.st_value - && sym.st_value < fcs1.sym.st_value + fcs1.sym.st_size) - || sym.st_value == fcs1.sym.st_value) - break; - } - - if (ndx < maxndx) + if (strcmp (secname, ".data") != 0 + && strcmp (secname, ".data.rel.ro") != 0 + && strcmp (secname, ".sdata") != 0) continue; if (specials[k].check_pltref) @@ -234,10 +399,13 @@ remove_redundant_cxx_conflicts (struct prelink_info *info) symtab_start = fcs1.dso->shdr[fcs1.symsec].sh_addr - fcs1.dso->base; symoff = symtab_start + n * fcs1.dso->shdr[fcs1.symsec].sh_entsize; - for (conflict = info->conflicts[fcs1.n]; conflict; + 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) + && conflict->reloc_class == rtype_class_valid) break; if (conflict == NULL) @@ -257,7 +425,13 @@ remove_redundant_cxx_conflicts (struct prelink_info *info) goto check_pltref; o = find_cxx_sym (info, conflict->lookup.ent->base + conflict->lookupval, - &fcs2, fcs1.sym.st_size); + &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 @@ -271,15 +445,16 @@ remove_redundant_cxx_conflicts (struct prelink_info *info) { error (0, ENOMEM, "%s: Could not compare %s arrays", info->dso->filename, name); - return 1; + 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) + fcs1.sym.st_size, 0) || get_relocated_mem (info, fcs2.dso, fcs2.sym.st_value, mem2, - fcs1.sym.st_size) + fcs1.sym.st_size, 0) || memcmp (mem1, mem2, fcs1.sym.st_size) != 0) { free (mem1); @@ -314,50 +489,133 @@ check_pltref: || binsymtab == NULL) continue; - maxndx = binsymtab->d_size / info->dso->shdr[binsymtabsec].sh_entsize; - for (ndx = 0; ndx < maxndx; ++ndx) + 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); - if (sym.st_value == info->conflict_rela[i].r_addend) + assert (sym.st_value == info->conflict_rela[i].r_addend); + if (sym.st_shndx == SHN_UNDEF && sym.st_value) { - 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) { - struct prelink_symbol *s; - - 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) - { - for (conflict = info->conflicts[fcs1.n]; 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) - { - 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; - } - break; - } + 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 = + GELF_R_INFO (1, GELF_R_TYPE (info->conflict_rela[i].r_info)); + ++removed; + goto pltref_check_done; + } + +pltref_check_done: break; } } @@ -375,5 +633,11 @@ check_pltref: info->conflict_rela_size = j; } - return 0; +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/trunk/src/doit.c b/trunk/src/doit.c index 2208ece..c94fc53 100644 --- a/trunk/src/doit.c +++ b/trunk/src/doit.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2003, 2004, 2005 Red Hat, Inc. +/* 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 @@ -38,6 +38,7 @@ 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; @@ -46,39 +47,14 @@ find_ents (void **p, void *info) } static void -clear_ent_marks (struct prelink_entry *ent) -{ - int i; - - ent->u.tmp = 0; - for (i = 0; i < ent->ndepends; ++i) - clear_ent_marks (ent->depends[i]); -} - -static struct prelink_entry * -find_unlisted_dependency (struct prelink_entry *ent) -{ - int i; - struct prelink_entry *ret; - - if (ent->u.tmp == 0) - return ent; - for (i = 0; i < ent->ndepends; ++i) - if ((ret = find_unlisted_dependency (ent->depends[i])) != NULL) - return ret; - return NULL; -} - -static void prelink_ent (struct prelink_entry *ent) { - int i; + int i, j; DSO *dso; struct stat64 st; struct prelink_link *hardlink; char *move = NULL; size_t movelen = 0; - struct prelink_entry *dep; for (i = 0; i < ent->ndepends; ++i) if (ent->depends[i]->done == 1) @@ -95,23 +71,31 @@ prelink_ent (struct prelink_entry *ent) ent->filename, ent->depends[i]->filename); return; } - else - clear_ent_marks (ent->depends[i]); ent->u.tmp = 1; for (i = 0; i < ent->ndepends; ++i) ent->depends[i]->u.tmp = 1; - - if ((dep = find_unlisted_dependency (ent)) != NULL) + for (i = 0; i < ent->ndepends; ++i) { - 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, dep->filename); - return; + 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) { diff --git a/trunk/src/dso.c b/trunk/src/dso.c index 3c61ff2..b3b6ee3 100644 --- a/trunk/src/dso.c +++ b/trunk/src/dso.c @@ -849,6 +849,7 @@ reopen_dso (DSO *dso, struct section_move *move, const char *temp_base) } else { + memset (&data, 0, sizeof data); data.d_type = ELF_T_NUM; data1 = NULL; while ((data1 = elf_getdata (dso->scn[j], data1)) diff --git a/trunk/src/dwarf2.c b/trunk/src/dwarf2.c index a0a89dd..e831a18 100644 --- a/trunk/src/dwarf2.c +++ b/trunk/src/dwarf2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2005, 2006 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2005, 2006, 2009 Red Hat, Inc. Written by Jakub Jelinek <jakub@redhat.com>, 2001. This program is free software; you can redistribute it and/or modify @@ -155,16 +155,18 @@ static struct #define DEBUG_LINE 2 #define DEBUG_ARANGES 3 #define DEBUG_PUBNAMES 4 -#define DEBUG_MACINFO 5 -#define DEBUG_LOC 6 -#define DEBUG_STR 7 -#define DEBUG_FRAME 8 -#define DEBUG_RANGES 9 +#define DEBUG_PUBTYPES 5 +#define DEBUG_MACINFO 6 +#define DEBUG_LOC 7 +#define DEBUG_STR 8 +#define DEBUG_FRAME 9 +#define DEBUG_RANGES 10 { ".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 }, @@ -191,6 +193,7 @@ struct cu_data { GElf_Addr cu_entry_pc; GElf_Addr cu_low_pc; + unsigned char cu_version; }; static hashval_t @@ -250,7 +253,7 @@ no_memory: } if (*slot != NULL) { - error (0, 0, "%s: Duplicate DWARF-2 abbreviation %d", dso->filename, + error (0, 0, "%s: Duplicate DWARF abbreviation %d", dso->filename, t->entry); free (t); htab_delete (h); @@ -270,7 +273,7 @@ no_memory: form = read_uleb128 (ptr); if (form == 2 || form > DW_FORM_indirect) { - error (0, 0, "%s: Unknown DWARF-2 DW_FORM_%d", dso->filename, form); + error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, form); htab_delete (h); return NULL; } @@ -280,7 +283,7 @@ no_memory: } if (read_uleb128 (ptr) != 0) { - error (0, 0, "%s: DWARF-2 abbreviation does not end with 2 zeros", + error (0, 0, "%s: DWARF abbreviation does not end with 2 zeros", dso->filename); htab_delete (h); return NULL; @@ -340,8 +343,11 @@ adjust_location_list (DSO *dso, unsigned char *ptr, size_t len, case DW_OP_reg0 ... DW_OP_reg31: case DW_OP_nop: case DW_OP_push_object_address: - case DW_OP_call_ref: + 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: @@ -360,6 +366,7 @@ adjust_location_list (DSO *dso, unsigned char *ptr, size_t len, case DW_OP_const4u: case DW_OP_const4s: case DW_OP_call4: + case DW_OP_call_ref: ptr += 4; break; case DW_OP_const8u: @@ -376,11 +383,17 @@ adjust_location_list (DSO *dso, unsigned char *ptr, size_t len, read_uleb128 (ptr); break; case DW_OP_bregx: + case DW_OP_bit_piece: read_uleb128 (ptr); read_uleb128 (ptr); break; + case DW_OP_implicit_value: + { + uint32_t len = read_uleb128 (ptr); + ptr += len; + } default: - error (0, 0, "%s: Unknown DWARF-2 DW_OP_%d", dso->filename, op); + error (0, 0, "%s: Unknown DWARF DW_OP_%d", dso->filename, op); return 1; } } @@ -596,6 +609,11 @@ adjust_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, 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: ptr += 4; break; @@ -622,7 +640,7 @@ adjust_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, assert (len < UINT_MAX); break; default: - error (0, 0, "%s: Unknown DWARF-2 DW_FORM_%d", dso->filename, + error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, form); return NULL; } @@ -645,7 +663,7 @@ adjust_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, || (t->attr[i].attr >= DW_AT_sf_names && t->attr[i].attr <= DW_AT_body_end)) break; - error (0, 0, "%s: Unknown DWARF-2 DW_AT_%d with block DW_FORM", + error (0, 0, "%s: Unknown DWARF DW_AT_%d with block DW_FORM", dso->filename, t->attr[i].attr); return NULL; } @@ -688,7 +706,7 @@ adjust_dwarf2_line (DSO *dso, GElf_Addr start, GElf_Addr adjust) } value = read_16 (ptr); - if (value != 2) + if (value != 2 && value != 3) { error (0, 0, "%s: DWARF version %d unhandled", dso->filename, value); @@ -836,7 +854,13 @@ adjust_dwarf2_frame (DSO *dso, GElf_Addr start, GElf_Addr adjust) if (value == 0xffffffff) { /* CIE. */ - ptr++; /* Skip version. */ + uint32_t version = *ptr++; + if (version != 1 && version != 3) + { + error (0, 0, "%s: unhandled .debug_frame version %d", + dso->filename, version); + return 1; + } if (*ptr != '\0') { error (0, 0, "%s: .debug_frame unhandled augmentation \"%s\"", @@ -846,7 +870,10 @@ adjust_dwarf2_frame (DSO *dso, GElf_Addr start, GElf_Addr adjust) ptr++; /* Skip augmentation. */ read_uleb128 (ptr); /* Skip code_alignment factor. */ read_uleb128 (ptr); /* Skip data_alignment factor. */ - read_uleb128 (ptr); /* Skip return_address_register. */ + if (version >= 3) + read_uleb128 (ptr); /* Skip return_address_register. */ + else + ptr++; } else { @@ -881,6 +908,8 @@ adjust_dwarf2_frame (DSO *dso, GElf_Addr start, GElf_Addr adjust) 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: @@ -907,6 +936,7 @@ adjust_dwarf2_frame (DSO *dso, GElf_Addr start, GElf_Addr adjust) ptr += 4; break; case DW_CFA_expression: + case DW_CFA_val_expression: read_uleb128 (ptr); /* FALLTHROUGH */ case DW_CFA_def_cfa_expression: @@ -1041,12 +1071,13 @@ adjust_dwarf2 (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) } value = read_16 (ptr); - if (value != 2) + if (value != 2 && value != 3) { error (0, 0, "%s: DWARF version %d unhandled", dso->filename, value); return 1; } + cu.cu_version = value; value = read_32 (ptr); if (value >= debug_sections[DEBUG_ABBREV].size) @@ -1054,7 +1085,7 @@ adjust_dwarf2 (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) if (debug_sections[DEBUG_ABBREV].data == NULL) error (0, 0, "%s: .debug_abbrev not present", dso->filename); else - error (0, 0, "%s: DWARF-2 CU abbrev offset too large", + error (0, 0, "%s: DWARF CU abbrev offset too large", dso->filename); return 1; } @@ -1074,14 +1105,14 @@ adjust_dwarf2 (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) } else { - error (0, 0, "%s: Invalid DWARF-2 pointer size %d", + error (0, 0, "%s: Invalid DWARF pointer size %d", dso->filename, ptr_size); return 1; } } else if (read_1 (ptr) != ptr_size) { - error (0, 0, "%s: DWARF-2 pointer size differs between CUs", + error (0, 0, "%s: DWARF pointer size differs between CUs", dso->filename); return 1; } @@ -1102,7 +1133,7 @@ adjust_dwarf2 (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) t = htab_find_with_hash (abbrev, &tag, tag.entry); if (t == NULL) { - error (0, 0, "%s: Could not find DWARF-2 abbreviation %d", + error (0, 0, "%s: Could not find DWARF abbreviation %d", dso->filename, tag.entry); htab_delete (abbrev); return 1; @@ -1138,6 +1169,7 @@ adjust_dwarf2 (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) /* .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. */ diff --git a/trunk/src/dwarf2.h b/trunk/src/dwarf2.h index 1f938f5..143a00b 100644 --- a/trunk/src/dwarf2.h +++ b/trunk/src/dwarf2.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2009 Red Hat, Inc. Written by Jakub Jelinek <jakub@redhat.com>, 2001. This program is free software; you can redistribute it and/or modify @@ -71,6 +71,8 @@ #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_MIPS_loop 0x4081 #define DW_TAG_format_label 0x4101 #define DW_TAG_function_template 0x4102 @@ -348,7 +350,14 @@ #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_lo_user 0xe0 #define DW_OP_hi_user 0xff @@ -362,6 +371,12 @@ #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 @@ -437,6 +452,9 @@ #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 @@ -468,6 +486,11 @@ #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_Mips_Assembler 0x8001 #define DW_LANG_lo_user 0x8000 #define DW_LANG_hi_user 0xffff diff --git a/trunk/src/fptr.c b/trunk/src/fptr.c index 7602d5e..cfe3aed 100644 --- a/trunk/src/fptr.c +++ b/trunk/src/fptr.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003 Red Hat, Inc. +/* 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 @@ -147,17 +147,21 @@ opd_init (struct prelink_info *info) 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]; conflict; + for (conflict = info->conflicts[i + 1].hash[symoff % maxidx]; conflict; conflict = conflict->next) { - if (conflict->symoff == ol->u.refs[j].symoff + if (conflict->symoff == symoff && conflict->reloc_class != RTYPE_CLASS_COPY && conflict->reloc_class != RTYPE_CLASS_TLS) break; @@ -183,6 +187,7 @@ opd_init (struct prelink_info *info) 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) @@ -190,7 +195,9 @@ opd_init (struct prelink_info *info) assert (k < info->ent->ndepends); - for (conflict = info->conflicts[k + 1]; conflict; + 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 diff --git a/trunk/src/gather.c b/trunk/src/gather.c index 958a8ad..16ed21b 100644 --- a/trunk/src/gather.c +++ b/trunk/src/gather.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. +/* 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 @@ -1077,6 +1077,8 @@ gather_object (const char *name, int deref, int onefs) ++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; @@ -1087,13 +1089,20 @@ gather_object (const char *name, int deref, int onefs) return gather_binlib (name, &st); } +static struct config_line +{ + struct config_line *next; + char line[1]; +} *config_lines, **config_end = &config_lines; + int -gather_config (const char *config) +read_config (const char *config) { FILE *file = fopen (config, "r"); char *line = NULL; - size_t len; + size_t len, llen; int ret = 0; + struct config_line *c; if (file == NULL) { @@ -1101,12 +1110,9 @@ gather_config (const char *config) return 1; } - implicit = 1; do { ssize_t i = getline (&line, &len, file); - int deref = 0; - int onefs = 0; char *p; if (i < 0) @@ -1120,6 +1126,62 @@ gather_config (const char *config) *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 (!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 == '-') { @@ -1127,7 +1189,7 @@ gather_config (const char *config) { case 'h': deref = 1; break; case 'l': onefs = 1; break; - case 'b': *p = '\0'; continue; + case 'b': p = ""; continue; default: error (0, 0, "Unknown directory option `%s'\n", p); break; @@ -1170,10 +1232,8 @@ gather_config (const char *config) break; } } - } while (!feof (file)); + } - free (line); - fclose (file); implicit = 0; return ret; } @@ -1351,39 +1411,18 @@ add_blacklist_ext (const char *ext) } int -blacklist_from_config (const char *config) +blacklist_from_config (void) { - FILE *file = fopen (config, "r"); - char *line = NULL; - size_t len; + struct config_line *c; int ret = 0; - if (file == NULL) - { - error (0, errno, "Can't open configuration file %s", config); - return 1; - } - implicit = 1; - do + for (c = config_lines; c; c = c->next) { - ssize_t i = getline (&line, &len, file); int deref = 0; int onefs = 0; int blacklist = 0; - 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"); + char *p = c->line; while (*p == '-') { @@ -1437,10 +1476,8 @@ blacklist_from_config (const char *config) break; } } - } while (!feof (file)); + } - free (line); - fclose (file); implicit = 0; return ret; } diff --git a/trunk/src/get.c b/trunk/src/get.c index 0ae418b..001bb1e 100644 --- a/trunk/src/get.c +++ b/trunk/src/get.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. +/* 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 @@ -39,6 +39,27 @@ is_ldso_soname (const char *soname) 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) @@ -208,18 +229,20 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, if (dso->ehdr.e_type == ET_EXEC || dso->arch->create_opd) { - info->conflicts = (struct prelink_conflict **) - calloc (sizeof (struct prelink_conflict *), ndeps); + 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; + int reloc_class, len, type = 1, ifunc = 0; char *symname; r = strchr (buffer, '\n'); @@ -253,14 +276,26 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, if (type) reloc_class = dso->arch->reloc_class (reloc_class); else - reloc_class |= RTYPE_CLASS_VALID; + { + 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 && info->conflicts)) + || ((reloc_class == RTYPE_CLASS_TLS || ifunc) + && info->conflicts)) { for (i = 0; i < ndeps; i++) if (deps[i].start == valstart[0]) @@ -289,7 +324,7 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, } } - if (symstart == deps[0].start) + 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) @@ -299,7 +334,8 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, goto error_out; } - if (ent == info->ent && reloc_class != RTYPE_CLASS_TLS) + 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) @@ -344,12 +380,14 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, s->next = NULL; } } - else if (reloc_class == RTYPE_CLASS_TLS && info->conflicts) + else if ((reloc_class == RTYPE_CLASS_TLS || ifunc) + && info->conflicts) { struct prelink_conflict *conflict; int symowner; + size_t idx; - for (symowner = 1; symowner < ndeps; symowner++) + for (symowner = 0; symowner < ndeps; symowner++) if (deps[symowner].start == symstart) break; if (symowner == ndeps) @@ -359,13 +397,20 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, goto error_out; } - for (conflict = info->conflicts[symowner]; conflict; + 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 (conflict->lookup.tls != tls - || conflict->conflict.tls != tls + 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]) { @@ -384,15 +429,27 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, goto error_out; } - conflict->next = info->conflicts[symowner]; - info->conflicts[symowner] = conflict; - conflict->lookup.tls = tls; - conflict->conflict.tls = tls; + 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; + if (++info->conflicts[symowner].count == 16) + conflict_hash_init (&info->conflicts[symowner]); } } } @@ -423,7 +480,18 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, if (type) reloc_class = dso->arch->reloc_class (reloc_class); else - reloc_class |= RTYPE_CLASS_VALID; + { + 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; @@ -439,6 +507,7 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, 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) @@ -480,7 +549,11 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, } } - for (conflict = info->conflicts[symowner]; conflict; + 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) @@ -509,8 +582,9 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, goto error_out; } - conflict->next = info->conflicts[symowner]; - info->conflicts[symowner] = conflict; + 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]; @@ -526,6 +600,9 @@ prelink_record_relocations (struct prelink_info *info, FILE *f, conflict->symoff = symoff; conflict->reloc_class = reloc_class; conflict->used = 0; + conflict->ifunc = ifunc; + if (++info->conflicts[symowner].count == 16) + conflict_hash_init (&info->conflicts[symowner]); } } } diff --git a/trunk/src/main.c b/trunk/src/main.c index 7c3e1a4..2c442e8 100644 --- a/trunk/src/main.c +++ b/trunk/src/main.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 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 @@ -453,13 +453,16 @@ main (int argc, char *argv[]) return failures; } - if (blacklist_from_config (prelink_conf)) + if (read_config (prelink_conf)) + return EXIT_FAILURE; + + if (blacklist_from_config ()) return EXIT_FAILURE; if (quick) prelink_load_cache (); - if (gather_config (prelink_conf)) + if (gather_config ()) return EXIT_FAILURE; while (remaining < argc) diff --git a/trunk/src/prelink.c b/trunk/src/prelink.c index 663a071..0798811 100644 --- a/trunk/src/prelink.c +++ b/trunk/src/prelink.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. +/* 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 @@ -817,15 +817,37 @@ free_info (struct prelink_info *info) { for (i = 0; i < info->ent->ndepends + 1; ++i) { - struct prelink_conflict *c = info->conflicts[i]; - void *f; + if (info->conflicts[i].hash == &info->conflicts[i].first) + { + struct prelink_conflict *c = info->conflicts[i].first; + void *f; - while (c != NULL) + while (c != NULL) + { + f = c; + c = c->next; + free (f); + } + } + else { - f = c; - c = c->next; - free (f); + 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); } diff --git a/trunk/src/prelink.h b/trunk/src/prelink.h index a50ddfe..78f775d 100644 --- a/trunk/src/prelink.h +++ b/trunk/src/prelink.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 + Red Hat, Inc. Written by Jakub Jelinek <jakub@redhat.com>, 2001. Updated by Maciej W. Rozycki <macro@codesourcery.com>, 2008. @@ -103,6 +104,20 @@ typedef uint8_t Elf64_Byte; #define RSS_UNDEF 0 #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 + struct prelink_entry; struct prelink_info; struct PLArch; @@ -180,6 +195,7 @@ struct PLArch 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, @@ -198,7 +214,7 @@ struct PLArch 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); + 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); @@ -416,6 +432,7 @@ struct prelink_symbol struct prelink_conflict { struct prelink_conflict *next; + struct prelink_conflict *next2; /* Object which it was relocated to. */ union { @@ -431,7 +448,16 @@ struct prelink_conflict /* Value it has in conflict.ent. */ GElf_Addr conflictval; int reloc_class; - int used; + unsigned char used; + unsigned char ifunc; +}; + +struct prelink_conflicts +{ + struct prelink_conflict *first; + struct prelink_conflict **hash; + struct prelink_conflict **hash2; + size_t count; }; #define conflict_lookup_value(cfl) \ @@ -444,8 +470,8 @@ struct prelink_info DSO **dsos; struct prelink_entry *ent; struct prelink_symbol *symbols; - struct prelink_conflict **conflicts; - struct prelink_conflict *curconflicts; + struct prelink_conflicts *conflicts; + struct prelink_conflicts *curconflicts; struct prelink_tls *tls, *curtls; const char **sonames; char *dynbss, *sdynbss; @@ -488,17 +514,18 @@ int prelink_undo (DSO *dso); int prelink_verify (const char *filename); int gather_object (const char *dir, int deref, int onefs); -int gather_config (const char *config); +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 (const char *config); +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); + char *buf, GElf_Word size, GElf_Addr dest_addr); int layout_libs (void); diff --git a/trunk/src/space.c b/trunk/src/space.c index 35bcfc7..4bd4760 100644 --- a/trunk/src/space.c +++ b/trunk/src/space.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004, 2006 Red Hat, Inc. +/* 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 @@ -412,14 +412,20 @@ find_readonly_space (DSO *dso, GElf_Shdr *add, GElf_Ehdr *ehdr, if (shdr[k].sh_addr > add->sh_addr) { /* Don't allow inserting in between reloc sections - if they are adjacent. */ - if (shdr[k].sh_type != SHT_REL - && shdr[k].sh_type != SHT_RELA) + 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 (shdr[k - 1].sh_type != SHT_REL - && shdr[k - 1].sh_type != SHT_RELA) + + if (sh_type1 != SHT_REL && sh_type1 != SHT_NOTE) break; - if (shdr[k - 1].sh_addr + shdr[k - 1].sh_size + if ((shdr[k - 1].sh_addr + + ((shdr[k - 1].sh_size + 3) & -4)) != shdr[k].sh_addr) break; } @@ -512,150 +518,208 @@ find_readonly_space (DSO *dso, GElf_Shdr *add, GElf_Ehdr *ehdr, { 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; - } - - 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; - } + { + 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; + } - 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) + 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) { - if (phdr[e].p_filesz != shdr[k].sh_size - || phdr[e].p_memsz != shdr[k].sh_size) + 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) { - error (0, 0, "%s: Non-PT_LOAD segment spanning more than one section", - dso->filename); - return 0; + minsize = endaddr - addstart - add->sh_size; + movesec = j; } - 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; + 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 (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) + 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; - 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; - } - } - } + } + + 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) diff --git a/trunk/src/verify.c b/trunk/src/verify.c index 8a3d89a..b04709b 100644 --- a/trunk/src/verify.c +++ b/trunk/src/verify.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002, 2003, 2006 Red Hat, Inc. +/* Copyright (C) 2002, 2003, 2006, 2007 Red Hat, Inc. Written by Jakub Jelinek <jakub@redhat.com>, 2002. This program is free software; you can redistribute it and/or modify @@ -227,7 +227,10 @@ prelink_verify (const char *filename) goto failure; } - if (gather_config (prelink_conf)) + if (read_config (prelink_conf)) + goto failure; + + if (gather_config ()) goto failure; if (gather_object (filename, 0, 0)) |