aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJake Burkholder <jake@FreeBSD.org>2002-09-27 23:12:53 +0000
committerJake Burkholder <jake@FreeBSD.org>2002-09-27 23:12:53 +0000
commit169d513cb4a13db57920364f5ca33cf6b2c223c6 (patch)
treeba3c4f5add825cc1d7164df7492430782b2070c4
parent2eaa80698f6d027c66f2eed0516cae1c4eef97f0 (diff)
downloadsrc-169d513cb4a13db57920364f5ca33cf6b2c223c6.tar.gz
src-169d513cb4a13db57920364f5ca33cf6b2c223c6.zip
Add a workaround for what seems to be confusion between binutils and the
sparc v9 ABI. The Elf_Rela records for local symbols appear to already have the symbol's value added in to the addend field, even though the ABI specifies we need to lookup the symbol and add its value too. This breaks text relocations in klds because the symbol's value is added twice, and the resulting address points off into nowhere land, so for now just use the addend. Tested by: rwatson
Notes
Notes: svn path=/head/; revision=104072
-rw-r--r--sys/kern/link_elf.c10
-rw-r--r--sys/kern/link_elf_obj.c10
-rw-r--r--sys/sparc64/sparc64/elf_machdep.c47
-rw-r--r--sys/sys/linker.h1
4 files changed, 60 insertions, 8 deletions
diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c
index 998000551076..ea4f0dd2d772 100644
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -1188,6 +1188,16 @@ link_elf_get_gp(linker_file_t lf)
}
#endif
+const Elf_Sym *
+elf_get_sym(linker_file_t lf, Elf_Word symidx)
+{
+ elf_file_t ef = (elf_file_t)lf;
+
+ if (symidx >= ef->nchains)
+ return (NULL);
+ return (ef->symtab + symidx);
+}
+
/*
* Symbol lookup function that can be used when the symbol index is known (ie
* in relocations). It uses the symbol index instead of doing a fully fledged
diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c
index 998000551076..ea4f0dd2d772 100644
--- a/sys/kern/link_elf_obj.c
+++ b/sys/kern/link_elf_obj.c
@@ -1188,6 +1188,16 @@ link_elf_get_gp(linker_file_t lf)
}
#endif
+const Elf_Sym *
+elf_get_sym(linker_file_t lf, Elf_Word symidx)
+{
+ elf_file_t ef = (elf_file_t)lf;
+
+ if (symidx >= ef->nchains)
+ return (NULL);
+ return (ef->symtab + symidx);
+}
+
/*
* Symbol lookup function that can be used when the symbol index is known (ie
* in relocations). It uses the symbol index instead of doing a fully fledged
diff --git a/sys/sparc64/sparc64/elf_machdep.c b/sys/sparc64/sparc64/elf_machdep.c
index 272b82338157..4889bc9ab881 100644
--- a/sys/sparc64/sparc64/elf_machdep.c
+++ b/sys/sparc64/sparc64/elf_machdep.c
@@ -131,10 +131,10 @@ static int reloc_target_flags[] = {
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* DISP_32 */
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_30 */
_RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_22 */
- _RF_A| _RF_B|_RF_SZ(32) | _RF_RS(10), /* HI22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* HI22 */
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 22 */
_RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 13 */
- _RF_A| _RF_B|_RF_SZ(32) | _RF_RS(0), /* LO10 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LO10 */
_RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT10 */
_RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT13 */
_RF_G| _RF_SZ(32) | _RF_RS(10), /* GOT22 */
@@ -144,8 +144,8 @@ static int reloc_target_flags[] = {
_RF_SZ(32) | _RF_RS(0), /* COPY */
_RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* GLOB_DAT */
_RF_SZ(32) | _RF_RS(0), /* JMP_SLOT */
- _RF_A| _RF_B|_RF_SZ(64) | _RF_RS(0), /* RELATIVE */
- _RF_S|_RF_A| _RF_U|_RF_SZ(32) | _RF_RS(0), /* UA_32 */
+ _RF_A| _RF_B| _RF_SZ(64) | _RF_RS(0), /* RELATIVE */
+ _RF_S|_RF_A| _RF_U| _RF_SZ(32) | _RF_RS(0), /* UA_32 */
_RF_A| _RF_SZ(32) | _RF_RS(0), /* PLT32 */
_RF_A| _RF_SZ(32) | _RF_RS(10), /* HIPLT22 */
@@ -181,6 +181,22 @@ static int reloc_target_flags[] = {
_RF_S|_RF_A| _RF_U| _RF_SZ(16) | _RF_RS(0), /* UA16 */
};
+#if 0
+static const char *reloc_names[] = {
+ "NONE", "RELOC_8", "RELOC_16", "RELOC_32", "DISP_8",
+ "DISP_16", "DISP_32", "WDISP_30", "WDISP_22", "HI22",
+ "22", "13", "LO10", "GOT10", "GOT13",
+ "GOT22", "PC10", "PC22", "WPLT30", "COPY",
+ "GLOB_DAT", "JMP_SLOT", "RELATIVE", "UA_32", "PLT32",
+ "HIPLT22", "LOPLT10", "LOPLT10", "PCPLT22", "PCPLT32",
+ "10", "11", "64", "OLO10", "HH22",
+ "HM10", "LM22", "PC_HH22", "PC_HM10", "PC_LM22",
+ "WDISP16", "WDISP19", "GLOB_JMP", "7", "5", "6",
+ "DISP64", "PLT64", "HIX22", "LOX10", "H44", "M44",
+ "L44", "REGISTER", "UA64", "UA16"
+};
+#endif
+
#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
@@ -224,6 +240,7 @@ int
elf_reloc(linker_file_t lf, const void *data, int type)
{
const Elf_Rela *rela;
+ const Elf_Sym *sym;
Elf_Addr relocbase;
Elf_Half *where32;
Elf_Addr *where;
@@ -255,10 +272,24 @@ elf_reloc(linker_file_t lf, const void *data, int type)
value = rela->r_addend;
if (RELOC_RESOLVE_SYMBOL(rtype)) {
- addr = elf_lookup(lf, symidx, 1);
- if (addr == 0)
- return (-1);
- value += addr;
+ /*
+ * Work around what appears to be confusion between binutils
+ * and the v9 ABI. LO10 and HI22 relocations are listed as
+ * S + A, but for STB_LOCAL symbols it seems that the value
+ * in the Elf_Sym refered to by the symbol index is wrong,
+ * instead the value is in the addend field of the Elf_Rela
+ * record. So if the symbol is local don't look it up, just
+ * use the addend as its value and add in the relocbase.
+ */
+ sym = elf_get_sym(lf, symidx);
+ if (ELF_ST_BIND(sym->st_info) == STB_LOCAL)
+ value += relocbase;
+ else {
+ addr = elf_lookup(lf, symidx, 1);
+ if (addr == 0)
+ return (-1);
+ value += addr;
+ }
}
if (RELOC_PC_RELATIVE(rtype))
diff --git a/sys/sys/linker.h b/sys/sys/linker.h
index 0090a5f7d7af..bd45a698438e 100644
--- a/sys/sys/linker.h
+++ b/sys/sys/linker.h
@@ -230,6 +230,7 @@ extern int kld_debug;
/* Support functions */
int elf_reloc(linker_file_t _lf, const void *_rel, int _type);
Elf_Addr elf_lookup(linker_file_t, Elf_Word, int);
+const Elf_Sym *elf_get_sym(linker_file_t _lf, Elf_Word _symidx);
/* values for type */
#define ELF_RELOC_REL 1