summaryrefslogtreecommitdiffstats
path: root/trunk/src/arch-mips.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/arch-mips.c')
-rw-r--r--trunk/src/arch-mips.c550
1 files changed, 419 insertions, 131 deletions
diff --git a/trunk/src/arch-mips.c b/trunk/src/arch-mips.c
index cf583aa..b85616e 100644
--- a/trunk/src/arch-mips.c
+++ b/trunk/src/arch-mips.c
@@ -1,5 +1,6 @@
-/* Copyright (C) 2006 CodeSourcery
+/* Copyright (C) 2006, 2008 CodeSourcery.
Written by Richard Sandiford <richard@codesourcery.com>, 2006
+ Updated by Maciej W. Rozycki <macro@codesourcery.com>, 2008.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -90,6 +91,7 @@
#include "prelink.h"
#include "layout.h"
+#include "reloc-info.h"
/* The thread pointer points 0x7000 past the first static TLS block. */
#define TLS_TP_OFFSET 0x7000
@@ -124,6 +126,28 @@ struct mips_local_got_iterator {
struct data_iterator got_iterator;
};
+/* Read native-endian address-type data. */
+
+static uint64_t
+mips_buf_read_addr (DSO *dso, unsigned char *data)
+{
+ if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ return buf_read_une64 (dso, data);
+ else
+ return buf_read_une32 (dso, data);
+}
+
+/* Write native-endian address-type data. */
+
+static void
+mips_buf_write_addr (DSO *dso, unsigned char *data, uint64_t val)
+{
+ if (dso->ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ buf_write_ne64 (dso, data, val);
+ else
+ buf_write_ne32 (dso, data, val);
+}
+
/* Set up LGI to iterate over DSO's local GOT. The caller should use
mips_get_local_got_entry to read the first entry. */
@@ -131,7 +155,7 @@ static inline void
mips_init_local_got_iterator (struct mips_local_got_iterator *lgi, DSO *dso)
{
lgi->dso = dso;
- lgi->entry_size = gelf_fsize (dso->elf, ELF_T_WORD, 1, EV_CURRENT);
+ lgi->entry_size = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT);
lgi->got_index = RESERVED_GOTNO - 1;
lgi->failed = 0;
init_data_iterator (&lgi->got_iterator, dso,
@@ -199,7 +223,7 @@ mips_init_global_got_iterator (struct mips_global_got_iterator *ggi, DSO *dso)
GElf_Word sym_size;
ggi->dso = dso;
- ggi->entry_size = gelf_fsize (dso->elf, ELF_T_WORD, 1, EV_CURRENT);
+ ggi->entry_size = gelf_fsize (dso->elf, ELF_T_ADDR, 1, EV_CURRENT);
ggi->got_addr = (dso->info[DT_PLTGOT]
+ (dso->info_DT_MIPS_LOCAL_GOTNO - 1) * ggi->entry_size);
ggi->sym_index = dso->info_DT_MIPS_GOTSYM - 1;
@@ -252,8 +276,8 @@ mips_arch_adjust (DSO *dso, GElf_Addr start, GElf_Addr adjust)
mips_init_local_got_iterator (&lgi, dso);
while (mips_get_local_got_entry (&lgi))
{
- value = buf_read_une32 (dso, lgi.got_entry);
- buf_write_ne32 (dso, lgi.got_entry, value + adjust);
+ value = mips_buf_read_addr (dso, lgi.got_entry);
+ mips_buf_write_addr (dso, lgi.got_entry, value + adjust);
}
/* Adjust every global GOT entry. Referring to the table above:
@@ -269,13 +293,13 @@ mips_arch_adjust (DSO *dso, GElf_Addr start, GElf_Addr adjust)
mips_init_global_got_iterator (&ggi, dso);
while (mips_get_global_got_entry (&ggi))
{
- value = buf_read_une32 (dso, ggi.got_entry);
+ value = mips_buf_read_addr (dso, ggi.got_entry);
if (ggi.sym.st_shndx != SHN_COMMON
&& value >= start
&& (value == ggi.sym.st_value
? adjust_symbol_p (dso, &ggi.sym)
: ggi.sym.st_shndx != SHN_UNDEF))
- buf_write_ne32 (dso, ggi.got_entry, value + adjust);
+ mips_buf_write_addr (dso, ggi.got_entry, value + adjust);
}
return lgi.failed || ggi.failed;
@@ -325,16 +349,16 @@ mips_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
address R_OFFSET. */
static inline uint32_t
-mips_read_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela)
+mips_read_32bit_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela)
{
return rela ? rela->r_addend : read_une32 (dso, r_offset);
}
-/* Like mips_read_addend, but change the addend to VALUE. */
+/* Like mips_read_32bit_addend, but change the addend to VALUE. */
static inline void
-mips_write_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela,
- uint32_t value)
+mips_write_32bit_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela,
+ uint32_t value)
{
if (rela)
rela->r_addend = (int32_t) value;
@@ -342,6 +366,26 @@ mips_write_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela,
write_ne32 (dso, r_offset, value);
}
+/* Like mips_read_32bit_addend, but 64-bit. */
+
+static inline uint64_t
+mips_read_64bit_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela)
+{
+ return rela ? rela->r_addend : read_une64 (dso, r_offset);
+}
+
+/* Like mips_read_64bit_addend, but change the addend to VALUE. */
+
+static inline void
+mips_write_64bit_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela,
+ uint64_t value)
+{
+ if (rela)
+ rela->r_addend = value;
+ else
+ write_ne64 (dso, r_offset, value);
+}
+
/* There is a relocation of type R_INFO against address R_OFFSET in DSO.
Adjust it so that virtual addresses >= START are increased by ADJUST
If the relocation is in a RELA section, RELA points to the relocation,
@@ -354,9 +398,9 @@ mips_adjust_reloc (DSO *dso, GElf_Addr r_offset, GElf_Xword r_info,
GElf_Addr value;
GElf_Word r_sym;
- if (GELF_R_TYPE (r_info) == R_MIPS_REL32)
+ if (reloc_r_type (dso, r_info) == R_MIPS_REL32)
{
- r_sym = GELF_R_SYM (r_info);
+ r_sym = reloc_r_sym (dso, r_info);
if (r_sym < dso->info_DT_MIPS_GOTSYM)
{
/* glibc's dynamic linker adds the symbol's st_value and the
@@ -388,8 +432,18 @@ mips_adjust_reloc (DSO *dso, GElf_Addr r_offset, GElf_Xword r_info,
" relocs against local symbols", dso->filename);
return 1;
}
- value = mips_read_addend (dso, r_offset, rela);
- mips_write_addend (dso, r_offset, rela, value + adjust);
+ if (reloc_r_type2 (dso, r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF);
+ value = mips_read_64bit_addend (dso, r_offset, rela);
+ mips_write_64bit_addend (dso, r_offset, rela, value + adjust);
+ }
+ else
+ {
+ value = mips_read_32bit_addend (dso, r_offset, rela);
+ mips_write_32bit_addend (dso, r_offset, rela, value + adjust);
+ }
}
}
return 0;
@@ -418,6 +472,13 @@ mips_prelink_32bit_reloc (DSO *dso, GElf_Rela *rela, GElf_Addr value)
write_ne32 (dso, rela->r_offset, value + rela->r_addend);
}
+static void
+mips_prelink_64bit_reloc (DSO *dso, GElf_Rela *rela, GElf_Addr value)
+{
+ assert (rela != NULL);
+ write_ne64 (dso, rela->r_offset, value + rela->r_addend);
+}
+
/* There is a relocation of type R_INFO against address R_OFFSET in DSO.
Prelink the relocation field, using INFO to look up symbol values.
If the relocation is in a RELA section, RELA points to the relocation,
@@ -433,8 +494,8 @@ mips_prelink_reloc (struct prelink_info *info, GElf_Addr r_offset,
int r_type;
dso = info->dso;
- r_sym = GELF_R_SYM (r_info);
- r_type = GELF_R_TYPE (r_info);
+ r_sym = reloc_r_sym (dso, r_info);
+ r_type = reloc_r_type (dso, r_info);
switch (r_type)
{
case R_MIPS_NONE:
@@ -443,15 +504,29 @@ mips_prelink_reloc (struct prelink_info *info, GElf_Addr r_offset,
case R_MIPS_REL32:
/* An in-place R_MIPS_REL32 relocation against symbol 0 needs no
adjustment. */
- if (rela != NULL || GELF_R_SYM (r_info) != 0)
+ if (rela != NULL || r_sym != 0)
{
value = info->resolve (info, r_sym, r_type);
- mips_prelink_32bit_reloc (dso, rela, value);
+ if (reloc_r_type2 (dso, r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF);
+ mips_prelink_64bit_reloc (dso, rela, value);
+ }
+ else
+ mips_prelink_32bit_reloc (dso, rela, value);
}
break;
case R_MIPS_GLOB_DAT:
- write_ne32 (dso, r_offset, info->resolve (info, r_sym, r_type));
+ if (reloc_r_type2 (dso, r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF);
+ write_ne64 (dso, r_offset, info->resolve (info, r_sym, r_type));
+ }
+ else
+ write_ne32 (dso, r_offset, info->resolve (info, r_sym, r_type));
break;
case R_MIPS_JUMP_SLOT:
@@ -459,29 +534,32 @@ mips_prelink_reloc (struct prelink_info *info, GElf_Addr r_offset,
break;
case R_MIPS_TLS_DTPMOD32:
- if (dso->ehdr.e_type == ET_EXEC)
- {
- error (0, 0, "%s: R_MIPS_TLS_DTPMOD32 reloc in executable?",
- dso->filename);
- return 1;
- }
+ case R_MIPS_TLS_DTPMOD64:
/* These relocations will be resolved using a conflict. We need
not change the field value here. */
break;
case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_DTPREL64:
value = info->resolve (info, r_sym, r_type);
- mips_prelink_32bit_reloc (dso, rela, value - TLS_DTV_OFFSET);
+ if (r_type == R_MIPS_TLS_DTPREL32)
+ mips_prelink_32bit_reloc (dso, rela, value - TLS_DTV_OFFSET);
+ else
+ mips_prelink_64bit_reloc (dso, rela, value - TLS_DTV_OFFSET);
break;
case R_MIPS_TLS_TPREL32:
+ case R_MIPS_TLS_TPREL64:
/* Relocations in a shared library will be resolved using a conflict.
We need not change the relocation field here. */
if (dso->ehdr.e_type == ET_EXEC)
{
value = info->resolve (info, r_sym, r_type);
value += info->resolvetls->offset - TLS_TP_OFFSET;
- mips_prelink_32bit_reloc (dso, rela, value);
+ if (r_type == R_MIPS_TLS_TPREL32)
+ mips_prelink_32bit_reloc (dso, rela, value);
+ else
+ mips_prelink_64bit_reloc (dso, rela, value);
}
break;
@@ -494,7 +572,7 @@ mips_prelink_reloc (struct prelink_info *info, GElf_Addr r_offset,
default:
error (0, 0, "%s: Unknown MIPS relocation type %d",
- dso->filename, (int) GELF_R_TYPE (r_info));
+ dso->filename, (int) reloc_r_type (dso, r_info));
return 1;
}
return 0;
@@ -503,20 +581,41 @@ mips_prelink_reloc (struct prelink_info *info, GElf_Addr r_offset,
static int
mips_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr)
{
+ GElf_Xword r_info;
+ GElf_Word r_sym;
+ int r_type;
DSO *dso;
/* Convert R_MIPS_REL32 relocations against global symbols into
R_MIPS_GLOB_DAT if the addend is zero. */
dso = info->dso;
- if (GELF_R_TYPE (rel->r_info) == R_MIPS_REL32
- && GELF_R_SYM (rel->r_info) >= dso->info_DT_MIPS_GOTSYM
- && read_une32 (dso, rel->r_offset) == 0)
+ r_sym = reloc_r_sym (dso, rel->r_info);
+ r_type = reloc_r_type (dso, rel->r_info);
+ if (r_type == R_MIPS_REL32 && r_sym >= dso->info_DT_MIPS_GOTSYM)
{
- rel->r_info = GELF_R_INFO (GELF_R_SYM (rel->r_info), R_MIPS_GLOB_DAT);
- write_ne32 (dso, rel->r_offset,
- info->resolve (info, GELF_R_SYM (rel->r_info),
- GELF_R_TYPE (rel->r_info)));
- return 2;
+ r_type = R_MIPS_GLOB_DAT;
+ r_info = reloc_r_info_ext (dso, r_sym, reloc_r_ssym (dso, rel->r_info),
+ r_type,
+ reloc_r_type2 (dso, rel->r_info),
+ reloc_r_type3 (dso, rel->r_info));
+ if (reloc_r_type2 (dso, rel->r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, rel->r_info) == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, rel->r_info) == RSS_UNDEF);
+ if (read_une64 (dso, rel->r_offset) == 0)
+ {
+ rel->r_info = r_info;
+ write_ne64 (dso, rel->r_offset,
+ info->resolve (info, r_sym, r_type));
+ return 2;
+ }
+ }
+ else if (read_une32 (dso, rel->r_offset) == 0)
+ {
+ rel->r_info = r_info;
+ write_ne32 (dso, rel->r_offset, info->resolve (info, r_sym, r_type));
+ return 2;
+ }
}
return mips_prelink_reloc (info, rel->r_offset, rel->r_info, NULL);
}
@@ -563,15 +662,20 @@ mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info,
struct prelink_conflict *conflict;
struct prelink_tls *tls;
GElf_Rela *entry;
+ GElf_Word r_sym;
+ int r_type;
- conflict = prelink_conflict (info, GELF_R_SYM (r_info),
- GELF_R_TYPE (r_info));
+ r_sym = reloc_r_sym (dso, r_info);
+ r_type = reloc_r_type (dso, r_info);
+ conflict = prelink_conflict (info, r_sym, r_type);
if (conflict == NULL)
{
- switch (GELF_R_TYPE (r_info))
+ switch (r_type)
{
case R_MIPS_TLS_DTPMOD32:
+ case R_MIPS_TLS_DTPMOD64:
case R_MIPS_TLS_TPREL32:
+ case R_MIPS_TLS_TPREL64:
tls = info->curtls;
if (tls == NULL)
return 0;
@@ -586,10 +690,11 @@ mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info,
}
else
{
- /* DTPREL32 relocations just involve the symbol value; no other
- TLS information is needed. Ignore conflicts created from a
- lookup of type RTYPE_CLASS_TLS if no real conflict exists. */
- if (GELF_R_TYPE (r_info) == R_MIPS_TLS_DTPREL32
+ /* DTPREL32/DTPREL64 relocations just involve the symbol value;
+ no other TLS information is needed. Ignore conflicts created
+ from a lookup of type RTYPE_CLASS_TLS if no real conflict
+ exists. */
+ if ((r_type == R_MIPS_TLS_DTPREL32 || r_type == R_MIPS_TLS_DTPREL64)
&& conflict->lookup.tls == conflict->conflict.tls
&& conflict->lookupval == conflict->conflictval)
return 0;
@@ -598,10 +703,17 @@ mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info,
}
/* VALUE now contains the final symbol value. Change it to the
value we want to store at R_OFFSET. */
- switch (GELF_R_TYPE (r_info))
+ switch (r_type)
{
case R_MIPS_REL32:
- value += mips_read_addend (dso, r_offset, rela);
+ if (reloc_r_type2 (dso, r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF);
+ value += mips_read_64bit_addend (dso, r_offset, rela);
+ }
+ else
+ value += mips_read_32bit_addend (dso, r_offset, rela);
break;
case R_MIPS_GLOB_DAT:
@@ -612,34 +724,46 @@ mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info,
return 1;
case R_MIPS_TLS_DTPMOD32:
+ case R_MIPS_TLS_DTPMOD64:
if (conflict != NULL && mips_get_tls (dso, conflict, &tls) == 1)
return 1;
value = tls->modid;
break;
case R_MIPS_TLS_DTPREL32:
- value += mips_read_addend (dso, r_offset, rela) - TLS_DTV_OFFSET;
+ value += mips_read_32bit_addend (dso, r_offset, rela) - TLS_DTV_OFFSET;
+ break;
+ case R_MIPS_TLS_DTPREL64:
+ value += mips_read_64bit_addend (dso, r_offset, rela) - TLS_DTV_OFFSET;
break;
case R_MIPS_TLS_TPREL32:
+ case R_MIPS_TLS_TPREL64:
if (conflict != NULL && mips_get_tls (dso, conflict, &tls) == 1)
return 1;
- value += (mips_read_addend (dso, r_offset, rela)
- + tls->offset - TLS_TP_OFFSET);
+ if (r_type == R_MIPS_TLS_TPREL32)
+ value += mips_read_32bit_addend (dso, r_offset, rela);
+ else
+ value += mips_read_64bit_addend (dso, r_offset, rela);
+ value += tls->offset - TLS_TP_OFFSET;
break;
default:
error (0, 0, "%s: Unknown MIPS relocation type %d", dso->filename,
- (int) GELF_R_TYPE (r_info));
+ r_type);
return 1;
}
/* Create and initialize a conflict entry. */
entry = prelink_conflict_add_rela (info);
if (entry == NULL)
return 1;
- entry->r_addend = (int32_t) value;
entry->r_offset = r_offset;
- entry->r_info = GELF_R_INFO (0, R_MIPS_REL32);
+ entry->r_info = reloc_r_info_ext (dso, 0, RSS_UNDEF,
+ R_MIPS_REL32, R_MIPS_64, R_MIPS_NONE);
+ if (reloc_r_type2 (dso, entry->r_info) == R_MIPS_64)
+ entry->r_addend = value;
+ else
+ entry->r_addend = (int32_t) value;
return 0;
}
@@ -684,14 +808,19 @@ mips_arch_prelink_conflict (DSO *dso, struct prelink_info *info)
value = ggi.sym.st_value;
else
continue;
- if (buf_read_une32 (dso, ggi.got_entry) != value)
+ if (mips_buf_read_addr (dso, ggi.got_entry) != value)
{
entry = prelink_conflict_add_rela (info);
if (entry == NULL)
return 1;
- entry->r_addend = (int32_t) value;
entry->r_offset = ggi.got_addr;
- entry->r_info = GELF_R_INFO (0, R_MIPS_REL32);
+ entry->r_info = reloc_r_info_ext (dso, 0, RSS_UNDEF,
+ R_MIPS_REL32, R_MIPS_64,
+ R_MIPS_NONE);
+ if (reloc_r_type2 (dso, entry->r_info) == R_MIPS_64)
+ entry->r_addend = value;
+ else
+ entry->r_addend = (int32_t) value;
}
}
@@ -702,10 +831,20 @@ static int
mips_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
char *buf)
{
- switch (GELF_R_TYPE (rela->r_info))
+ DSO *dso;
+
+ dso = info->dso;
+ switch (reloc_r_type (dso, rela->r_info))
{
case R_MIPS_REL32:
- buf_write_ne32 (info->dso, buf, rela->r_addend);
+ if (reloc_r_type2 (dso, rela->r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_ssym (dso, rela->r_info) == RSS_UNDEF);
+ assert (reloc_r_type3 (dso, rela->r_info) == R_MIPS_NONE);
+ buf_write_ne64 (info->dso, buf, rela->r_addend);
+ }
+ else
+ buf_write_ne32 (info->dso, buf, rela->r_addend);
break;
case R_MIPS_JUMP_SLOT:
@@ -730,18 +869,24 @@ mips_apply_adjustment (DSO *dso, GElf_Rela *rela, char *buf,
if (rela)
adjustment += rela->r_addend;
else
- adjustment += buf_read_une32 (dso, buf);
- buf_write_ne32 (dso, buf, adjustment);
+ adjustment += mips_buf_read_addr (dso, buf);
+ mips_buf_write_addr (dso, buf, adjustment);
}
static int
mips_apply_reloc (struct prelink_info *info, GElf_Xword r_info,
GElf_Rela *rela, char *buf)
{
+ GElf_Addr value;
+ GElf_Word r_sym;
+ int r_type;
DSO *dso;
dso = info->dso;
- switch (GELF_R_TYPE (r_info))
+ r_sym = reloc_r_sym (dso, r_info);
+ r_type = reloc_r_type (dso, r_info);
+ value = info->resolve (info, r_sym, r_type);
+ switch (r_type)
{
case R_MIPS_NONE:
break;
@@ -756,9 +901,12 @@ mips_apply_reloc (struct prelink_info *info, GElf_Xword r_info,
abort ();
case R_MIPS_REL32:
- mips_apply_adjustment (dso, rela, buf,
- info->resolve (info, GELF_R_SYM (r_info),
- GELF_R_TYPE (r_info)));
+ if (reloc_r_type2 (dso, r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, r_info) == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, r_info) == RSS_UNDEF);
+ }
+ mips_apply_adjustment (dso, rela, buf, value);
break;
default:
@@ -782,28 +930,50 @@ mips_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf)
static int
mips_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela)
{
+ GElf_Word r_sym;
+ int r_type;
+
+ r_sym = reloc_r_sym (dso, rel->r_info);
+ r_type = reloc_r_type (dso, rel->r_info);
rela->r_offset = rel->r_offset;
rela->r_info = rel->r_info;
- switch (GELF_R_TYPE (rel->r_info))
+ switch (r_type)
{
case R_MIPS_REL32:
+ /* This relocation has an in-place addend. */
+ if (reloc_r_type2 (dso, rel->r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, rel->r_info) == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, rel->r_info) == RSS_UNDEF);
+ rela->r_addend = read_une64 (dso, rel->r_offset);
+ }
+ else
+ rela->r_addend = (int32_t) read_une32 (dso, rel->r_offset);
+ break;
+
case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_TPREL32:
/* These relocations have an in-place addend. */
rela->r_addend = (int32_t) read_une32 (dso, rel->r_offset);
break;
+ case R_MIPS_TLS_DTPREL64:
+ case R_MIPS_TLS_TPREL64:
+ /* These relocations have an in-place addend. */
+ rela->r_addend = read_une64 (dso, rel->r_offset);
+ break;
case R_MIPS_NONE:
case R_MIPS_COPY:
case R_MIPS_GLOB_DAT:
case R_MIPS_TLS_DTPMOD32:
+ case R_MIPS_TLS_DTPMOD64:
/* These relocations have no addend. */
rela->r_addend = 0;
break;
default:
error (0, 0, "%s: Unknown MIPS relocation type %d", dso->filename,
- (int) GELF_R_TYPE (rel->r_info));
+ r_type);
return 1;
}
return 0;
@@ -812,30 +982,60 @@ mips_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela)
static int
mips_rela_to_rel (DSO *dso, GElf_Rela *rela, GElf_Rel *rel)
{
+ GElf_Sxword r_addend;
+ GElf_Word r_sym;
+ int r_type;
+
+ r_sym = reloc_r_sym (dso, rela->r_info);
+ r_type = reloc_r_type (dso, rela->r_info);
+ r_addend = rela->r_addend;
rel->r_offset = rela->r_offset;
rel->r_info = rela->r_info;
- switch (GELF_R_TYPE (rela->r_info))
+ switch (r_type)
{
case R_MIPS_NONE:
case R_MIPS_COPY:
break;
+ case R_MIPS_GLOB_DAT:
+ /* This relocation has no addend. */
+ r_addend = 0;
+ /* FALLTHROUGH */
case R_MIPS_REL32:
+ /* This relocation has an in-place addend. */
+ if (reloc_r_type2 (dso, rel->r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, rel->r_info) == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, rel->r_info) == RSS_UNDEF);
+ write_ne64 (dso, rela->r_offset, rela->r_addend);
+ }
+ else
+ write_ne32 (dso, rela->r_offset, rela->r_addend);
+ break;
+
+ case R_MIPS_TLS_DTPMOD32:
+ /* This relocation has no addend. */
+ r_addend = 0;
+ /* FALLTHROUGH */
case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_TPREL32:
/* These relocations have an in-place addend. */
write_ne32 (dso, rela->r_offset, rela->r_addend);
break;
-
- case R_MIPS_GLOB_DAT:
- case R_MIPS_TLS_DTPMOD32:
- /* These relocations have no addend. */
- write_ne32 (dso, rela->r_offset, 0);
+ case R_MIPS_TLS_DTPMOD64:
+ /* This relocation has no addend. */
+ r_addend = 0;
+ /* FALLTHROUGH */
+ case R_MIPS_TLS_DTPREL64:
+ case R_MIPS_TLS_TPREL64:
+ /* These relocations have an in-place addend. */
+ write_ne64 (dso, rela->r_offset, rela->r_addend);
+ break;
break;
default:
error (0, 0, "%s: Unknown MIPS relocation type %d", dso->filename,
- (int) GELF_R_TYPE (rela->r_info));
+ r_type);
return 1;
}
return 0;
@@ -846,66 +1046,91 @@ mips_need_rel_to_rela (DSO *dso, int first, int last)
{
Elf_Data *data;
Elf_Scn *scn;
- Elf32_Rel *rel, *relend;
+ GElf_Shdr shdr;
+ GElf_Rel rel;
+ GElf_Word r_sym;
+ int r_type;
+ int count;
+ int i;
int n;
for (n = first; n <= last; n++)
{
data = NULL;
scn = dso->scn[n];
+ gelfx_getshdr (dso->elf, scn, &shdr);
while ((data = elf_getdata (scn, data)) != NULL)
{
- rel = (Elf32_Rel *) data->d_buf;
- relend = rel + data->d_size / sizeof (Elf32_Rel);
- for (; rel < relend; rel++)
- switch (ELF32_R_TYPE (rel->r_info))
- {
- case R_MIPS_NONE:
- case R_MIPS_COPY:
- case R_MIPS_JUMP_SLOT:
- break;
-
- case R_MIPS_REL32:
- /* The SVR4 definition was designed to allow exactly the
- sort of prelinking we want to do here, in combination
- with Quickstart. Unfortunately, glibc's definition
- makes it impossible for relocations against anything
- other than the null symbol. We get around this for
- zero addends by using a R_MIPS_GLOB_DAT relocation
- instead, where R_MIPS_GLOB_DAT is a GNU extension
- added specifically for this purpose. */
- if (ELF32_R_SYM (rel->r_info) != 0
- && (ELF32_R_SYM (rel->r_info) < dso->info_DT_MIPS_GOTSYM
- || read_une32 (dso, rel->r_offset) != 0))
- return 1;
- break;
-
- case R_MIPS_GLOB_DAT:
- /* This relocation has no addend. */
- break;
-
- case R_MIPS_TLS_DTPMOD32:
- /* The relocation will be resolved using a conflict. */
- break;
-
- case R_MIPS_TLS_DTPREL32:
- /* We can prelink these fields, and the addend is relative
- to the symbol value. A RELA entry is needed. */
- return 1;
-
- case R_MIPS_TLS_TPREL32:
- /* Relocations in shared libraries will be resolved by a
- conflict. Relocations in executables will not, and the
- addend is relative to the symbol value. */
- if (dso->ehdr.e_type == ET_EXEC)
+ count = data->d_size / shdr.sh_entsize;
+ for (i = 0; i < count; i++)
+ {
+ gelfx_getrel (dso->elf, data, i, &rel);
+ r_type = reloc_r_type (dso, rel.r_info);
+ r_sym = reloc_r_sym (dso, rel.r_info);
+ switch (r_type)
+ {
+ case R_MIPS_NONE:
+ case R_MIPS_COPY:
+ case R_MIPS_JUMP_SLOT:
+ break;
+
+ case R_MIPS_REL32:
+ /* The SVR4 definition was designed to allow exactly the
+ sort of prelinking we want to do here, in combination
+ with Quickstart. Unfortunately, glibc's definition
+ makes it impossible for relocations against anything
+ other than the null symbol. We get around this for
+ zero addends by using a R_MIPS_GLOB_DAT relocation
+ instead, where R_MIPS_GLOB_DAT is a GNU extension
+ added specifically for this purpose. */
+ if (r_sym != 0)
+ {
+ if (r_sym < dso->info_DT_MIPS_GOTSYM)
+ return 1;
+ if (reloc_r_type2 (dso, rel.r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, rel.r_info)
+ == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, rel.r_info)
+ == RSS_UNDEF);
+ if (read_une64 (dso, rel.r_offset) != 0)
+ return 1;
+ }
+ else if (read_une32 (dso, rel.r_offset) != 0)
+ return 1;
+ }
+ break;
+
+ case R_MIPS_GLOB_DAT:
+ /* This relocation has no addend. */
+ break;
+
+ case R_MIPS_TLS_DTPMOD32:
+ case R_MIPS_TLS_DTPMOD64:
+ /* The relocation will be resolved using a conflict. */
+ break;
+
+ case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_DTPREL64:
+ /* We can prelink these fields, and the addend is relative
+ to the symbol value. A RELA entry is needed. */
return 1;
- break;
-
- default:
- error (0, 0, "%s: Unknown MIPS relocation type %d",
- dso->filename, (int) GELF_R_TYPE (rel->r_info));
+
+ case R_MIPS_TLS_TPREL32:
+ case R_MIPS_TLS_TPREL64:
+ /* Relocations in shared libraries will be resolved by a
+ conflict. Relocations in executables will not, and the
+ addend is relative to the symbol value. */
+ if (dso->ehdr.e_type == ET_EXEC)
+ return 1;
+ break;
+
+ default:
+ error (0, 0, "%s: Unknown MIPS relocation type %d",
+ dso->filename, r_type);
return 1;
- }
+ }
+ }
}
}
return 0;
@@ -927,8 +1152,11 @@ mips_reloc_class (int reloc_type)
case R_MIPS_JUMP_SLOT:
return RTYPE_CLASS_PLT;
case R_MIPS_TLS_DTPMOD32:
+ case R_MIPS_TLS_DTPMOD64:
case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_DTPREL64:
case R_MIPS_TLS_TPREL32:
+ case R_MIPS_TLS_TPREL64:
return RTYPE_CLASS_TLS;
default:
return RTYPE_CLASS_VALID;
@@ -944,6 +1172,7 @@ mips_arch_prelink (struct prelink_info *info)
int i;
dso = info->dso;
+
if (dso->info_DT_MIPS_PLTGOT)
{
/* Write address of .plt into gotplt[1]. This is in each
@@ -960,6 +1189,7 @@ mips_arch_prelink (struct prelink_info *info)
dso->shdr[i].sh_name),
".plt"))
break;
+
if (i == dso->ehdr.e_shnum)
return 0;
data = dso->shdr[i].sh_addr;
@@ -977,7 +1207,7 @@ mips_arch_prelink (struct prelink_info *info)
value = info->resolve (info, ggi.sym_index, R_MIPS_REL32);
if (ggi.sym.st_shndx == SHN_UNDEF
|| ggi.sym.st_shndx == SHN_COMMON)
- buf_write_ne32 (dso, ggi.got_entry, value);
+ mips_buf_write_addr (dso, ggi.got_entry, value);
else
{
/* Type E and F in the table above. We cannot install Quickstart
@@ -995,7 +1225,7 @@ mips_arch_prelink (struct prelink_info *info)
code will cope correctly with malformed type F entries in
shared libraries, so we only complain about executables here. */
if (dso->ehdr.e_type == ET_EXEC
- && value != buf_read_une32 (dso, ggi.got_entry))
+ && value != mips_buf_read_addr (dso, ggi.got_entry))
{
error (0, 0, "%s: The global GOT entries for defined symbols"
" do not match their st_values\n", dso->filename);
@@ -1042,10 +1272,10 @@ mips_arch_undo_prelink (DSO *dso)
while (mips_get_global_got_entry (&ggi))
if (ggi.sym.st_shndx == SHN_UNDEF)
/* Types A-C in the table above. */
- buf_write_ne32 (dso, ggi.got_entry, ggi.sym.st_value);
+ mips_buf_write_addr (dso, ggi.got_entry, ggi.sym.st_value);
else if (ggi.sym.st_shndx == SHN_COMMON)
/* Type D in the table above. */
- buf_write_ne32 (dso, ggi.got_entry, 0);
+ mips_buf_write_addr (dso, ggi.got_entry, 0);
return ggi.failed;
}
@@ -1054,6 +1284,8 @@ mips_undo_prelink_rel (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr)
{
int sec;
const char *name;
+ GElf_Word r_sym;
+ int r_type;
/* Convert R_MIPS_GLOB_DAT relocations back into R_MIPS_REL32
relocations. Ideally we'd have some mechanism for recording
@@ -1061,13 +1293,26 @@ mips_undo_prelink_rel (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr)
it's better to assume that the original relocation was
R_MIPS_REL32; R_MIPS_GLOB_DAT was added specifically for the
prelinker and shouldn't be used in non-prelinked binaries. */
- if (GELF_R_TYPE (rel->r_info) == R_MIPS_GLOB_DAT)
+ r_sym = reloc_r_sym (dso, rel->r_info);
+ r_type = reloc_r_type (dso, rel->r_info);
+ if (r_type == R_MIPS_GLOB_DAT)
{
- write_ne32 (dso, rel->r_offset, 0);
- rel->r_info = GELF_R_INFO (GELF_R_SYM (rel->r_info), R_MIPS_REL32);
+ if (reloc_r_type2 (dso, rel->r_info) == R_MIPS_64)
+ {
+ assert (reloc_r_type3 (dso, rel->r_info) == R_MIPS_NONE);
+ assert (reloc_r_ssym (dso, rel->r_info) == RSS_UNDEF);
+ write_ne64 (dso, rel->r_offset, 0);
+ }
+ else
+ write_ne32 (dso, rel->r_offset, 0);
+ rel->r_info = reloc_r_info_ext (dso,
+ r_sym, reloc_r_ssym (dso, rel->r_info),
+ R_MIPS_REL32,
+ reloc_r_type2 (dso, rel->r_info),
+ reloc_r_type3 (dso, rel->r_info));
return 2;
}
- else if (GELF_R_TYPE (rel->r_info) == R_MIPS_JUMP_SLOT)
+ else if (r_type == R_MIPS_JUMP_SLOT)
{
sec = addr_to_sec (dso, rel->r_offset);
name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name);
@@ -1091,7 +1336,7 @@ mips_undo_prelink_rel (DSO *dso, GElf_Rel *rel, GElf_Addr reladdr)
return 0;
}
-PL_ARCH = {
+PL_ARCH(mips) = {
.name = "MIPS",
.class = ELFCLASS32,
.machine = EM_MIPS,
@@ -1133,3 +1378,46 @@ PL_ARCH = {
.max_page_size = 0x10000,
.page_size = 0x1000
};
+
+PL_ARCH(mips64) = {
+ .name = "MIPS64",
+ .class = ELFCLASS64,
+ .machine = EM_MIPS,
+ .max_reloc_size = 8,
+ .dynamic_linker = "/lib/ld.so.1",
+ .dynamic_linker_alt = "/lib64/ld.so.1",
+ .R_COPY = R_MIPS_COPY,
+ .R_JMP_SLOT = R_MIPS_JUMP_SLOT,
+ /* R_MIPS_REL32 relocations against symbol 0 do act as relative relocs,
+ but those against other symbols don't. */
+ .R_RELATIVE = ~0U,
+ .arch_adjust = mips_arch_adjust,
+ .adjust_dyn = mips_adjust_dyn,
+ .adjust_rel = mips_adjust_rel,
+ .adjust_rela = mips_adjust_rela,
+ .prelink_rel = mips_prelink_rel,
+ .prelink_rela = mips_prelink_rela,
+ .prelink_conflict_rel = mips_prelink_conflict_rel,
+ .prelink_conflict_rela = mips_prelink_conflict_rela,
+ .arch_prelink_conflict = mips_arch_prelink_conflict,
+ .apply_conflict_rela = mips_apply_conflict_rela,
+ .apply_rel = mips_apply_rel,
+ .apply_rela = mips_apply_rela,
+ .rel_to_rela = mips_rel_to_rela,
+ .rela_to_rel = mips_rela_to_rel,
+ .need_rel_to_rela = mips_need_rel_to_rela,
+ .reloc_size = mips_reloc_size,
+ .reloc_class = mips_reloc_class,
+ .arch_prelink = mips_arch_prelink,
+ .arch_undo_prelink = mips_arch_undo_prelink,
+ .undo_prelink_rel = mips_undo_prelink_rel,
+ /* Although TASK_UNMAPPED_BASE is 0x5555556000, we leave some
+ area so that mmap of /etc/ld.so.cache and ld.so's malloc
+ does not take some library's VA slot.
+ Also, if this guard area isn't too small, typically
+ even dlopened libraries will get the slots they desire. */
+ .mmap_base = 0x5800000000LL,
+ .mmap_end = 0x9800000000LL,
+ .max_page_size = 0x10000,
+ .page_size = 0x1000
+};