diff options
author | jake <jake@FreeBSD.org> | 2002-09-27 23:12:53 +0000 |
---|---|---|
committer | jake <jake@FreeBSD.org> | 2002-09-27 23:12:53 +0000 |
commit | c03e93ab3a4cf602e9fad9ef7bc7537a0cf96e8c (patch) | |
tree | ba3c4f5add825cc1d7164df7492430782b2070c4 | |
parent | d08d61cf7e649a1706432628cc516edea8878dd9 (diff) | |
download | FreeBSD-src-c03e93ab3a4cf602e9fad9ef7bc7537a0cf96e8c.zip FreeBSD-src-c03e93ab3a4cf602e9fad9ef7bc7537a0cf96e8c.tar.gz |
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
-rw-r--r-- | sys/kern/link_elf.c | 10 | ||||
-rw-r--r-- | sys/kern/link_elf_obj.c | 10 | ||||
-rw-r--r-- | sys/sparc64/sparc64/elf_machdep.c | 47 | ||||
-rw-r--r-- | sys/sys/linker.h | 1 |
4 files changed, 60 insertions, 8 deletions
diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c index 9980005..ea4f0dd 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 9980005..ea4f0dd 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 272b823..4889bc9 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 0090a5f..bd45a69 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 |