aboutsummaryrefslogtreecommitdiffstats
path: root/src/arch-mips.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch-mips.c')
-rw-r--r--src/arch-mips.c1444
1 files changed, 1444 insertions, 0 deletions
diff --git a/src/arch-mips.c b/src/arch-mips.c
new file mode 100644
index 0000000..ccb1834
--- /dev/null
+++ b/src/arch-mips.c
@@ -0,0 +1,1444 @@
+/* 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
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* GENERAL NOTES
+
+ The psABI defines R_MIPS_REL32 as A - EA + S, where the value of EA
+ depends on the symbol index. If the index is less than DT_MIPS_GOTSYM,
+ EA is the symbol's st_value "plus displacement". If the index is greater
+ than or equal to DT_MIPS_GOTSYM, EA is the original value of the
+ associated GOT entry.
+
+ However, glibc's dynamic linker implements a different definition.
+ If the index is less than DT_MIPS_GOTSYM, the dynamic linker adds the
+ symbol's st_value and the base address to the addend. If the index
+ is greater than or equal to DT_MIPS_GOTSYM, the dynamic linker adds
+ the final symbol value to the addend.
+
+ MIPS GOTs are divided into three parts:
+
+ - Reserved entries (of which GNU objects have 2)
+ - Local entries
+ - Global entries
+
+ DT_MIPS_LOCAL_GOTNO gives the total number of reserved and local
+ entries. The local entries all hold virtual addresses and the
+ dynamic linker will add the base address to each one.
+
+ Unlike most other architectures, the MIPS ABI does not use
+ relocations to initialize the global GOT entries. Instead, global
+ GOT entry X is mapped to dynamic symbol DT_MIPS_GOTSYM + X, and there
+ are a total of DT_MIPS_SYMTABNO - DT_MIPS_GOTSYM global GOT entries.
+
+ The interpretation of a global GOT entry depends on the symbol entry
+ and the initial GOT contents. The psABI lists the following cases:
+
+ st_shndx st_type st_value initial GOT value
+ -------- ------- -------- -----------------
+ A: SHN_UNDEF STT_FUNC 0 st_value (== 0) / QS
+ B: SHN_UNDEF STT_FUNC stub address st_value / QS
+ C: SHN_UNDEF all others 0 st_value (== 0) / QS
+ D: SHN_COMMON any alignment 0 / QS
+ E: all others STT_FUNC value st_value / stub address
+ F: all others all others value st_value
+
+ (wording slightly modified from the psABI table). Here, QS denotes
+ Quickstart values.
+
+ The dynamic linker treats each case as follows:
+
+ - [A, B when not binding lazily, C, D, E when not binding lazily, F]
+ Resolve the symbol and store its value in the GOT.
+
+ - [B when binding lazily] Set the GOT entry to the st_value plus
+ the base address.
+
+ - [E when binding lazily] If the GOT entry is different from the st_value,
+ add the base addreess to the GOT entry. Otherwise resolve the symbol
+ and store its value in the GOT (as for A, C, etc).
+
+ As the table shows, we can install Quickstart values for types A-D.
+ Installing Quickstart values for type F should be a no-op, because the
+ GOT should already hold the desired value. Installing Quickstart values
+ for type E would either be a no-op (if the GOT entry already contains
+ st_value) or would lose the address of the lazy binding stub. */
+
+#include <config.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <locale.h>
+#include <error.h>
+#include <argp.h>
+#include <stdlib.h>
+
+#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
+
+/* Dynamic thread vector pointers point 0x8000 past the start of each
+ TLS block. */
+#define TLS_DTV_OFFSET 0x8000
+
+/* The number of reserved entries at the beginning of the GOT.
+ The dynamic linker points entry 0 to the resolver function
+ and entry 1 to the link_map. */
+#define RESERVED_GOTNO 2
+
+/* A structure for iterating over local GOT entries. */
+struct mips_local_got_iterator {
+ /* The DSO containing the GOT. */
+ DSO *dso;
+
+ /* The size of a GOT entry. */
+ GElf_Word entry_size;
+
+ /* The index of the current GOT entry. */
+ GElf_Word got_index;
+
+ /* A pointer to the current GOT entry. */
+ unsigned char *got_entry;
+
+ /* True if we failed to read an entry correctly. */
+ int failed;
+
+ /* Used internally to obtain GOT_ENTRY. */
+ 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. */
+
+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_ADDR, 1, EV_CURRENT);
+ lgi->got_index = RESERVED_GOTNO - 1;
+ lgi->failed = 0;
+ init_data_iterator (&lgi->got_iterator, dso,
+ dso->info[DT_PLTGOT]
+ + (lgi->got_index + 1) * lgi->entry_size);
+}
+
+/* Return true if LGI has not reached the end of the GOT and if the next
+ entry can be accessed. When returning true, use LGI's fields to
+ describe the next entry. */
+
+static inline int
+mips_get_local_got_entry (struct mips_local_got_iterator *lgi)
+{
+ lgi->got_index++;
+ if (lgi->got_index >= lgi->dso->info_DT_MIPS_LOCAL_GOTNO)
+ return 0;
+
+ lgi->got_entry = get_data_from_iterator (&lgi->got_iterator,
+ lgi->entry_size);
+ if (lgi->got_entry == NULL)
+ {
+ error (0, 0, "%s: Malformed local GOT\n", lgi->dso->filename);
+ lgi->failed = 1;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* A structure for iterating over global GOT entries. */
+struct mips_global_got_iterator {
+ /* The DSO containing the GOT. */
+ DSO *dso;
+
+ /* The size of a GOT entry. */
+ GElf_Word entry_size;
+
+ /* The virtual address of the current GOT entry. */
+ GElf_Addr got_addr;
+
+ /* The index of the associated entry in the dynamic symbol table. */
+ GElf_Word sym_index;
+
+ /* A pointer to the current GOT entry. */
+ unsigned char *got_entry;
+
+ /* The symbol associated with the current GOT entry. */
+ GElf_Sym sym;
+
+ /* True if we failed to read an entry correctly. */
+ int failed;
+
+ /* Used internally to obtain GOT_ENTRY and SYM. */
+ struct data_iterator got_iterator;
+ struct data_iterator sym_iterator;
+};
+
+/* Set up GGI to iterate over DSO's global GOT. The caller should use
+ mips_get_global_got_entry to read the first entry. */
+
+static inline void
+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_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;
+ ggi->failed = 0;
+
+ sym_size = gelf_fsize (dso->elf, ELF_T_SYM, 1, EV_CURRENT);
+ init_data_iterator (&ggi->got_iterator, dso,
+ ggi->got_addr + ggi->entry_size);
+ init_data_iterator (&ggi->sym_iterator, dso,
+ dso->info[DT_SYMTAB] + (ggi->sym_index + 1) * sym_size);
+}
+
+/* Return true if GGI has not reached the end of the GOT and if the next
+ entry can be accessed. When returning true, use GGI's fields to
+ describe the next entry. */
+
+static inline int
+mips_get_global_got_entry (struct mips_global_got_iterator *ggi)
+{
+ ggi->sym_index++;
+ ggi->got_addr += ggi->entry_size;
+ if (ggi->sym_index >= ggi->dso->info_DT_MIPS_SYMTABNO)
+ return 0;
+
+ ggi->got_entry = get_data_from_iterator (&ggi->got_iterator,
+ ggi->entry_size);
+ if (ggi->got_entry == NULL
+ || !get_sym_from_iterator (&ggi->sym_iterator, &ggi->sym))
+ {
+ error (0, 0, "%s: Malformed global GOT\n", ggi->dso->filename);
+ ggi->failed = 1;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+mips_arch_adjust (DSO *dso, GElf_Addr start, GElf_Addr adjust)
+{
+ struct mips_local_got_iterator lgi;
+ struct mips_global_got_iterator ggi;
+ GElf_Addr value;
+
+ if (dso->info[DT_PLTGOT] == 0)
+ return 0;
+
+ /* Adjust every local GOT entry by ADJUST. Every adjustment moves
+ the code and data, so we do not need to check START here. */
+ mips_init_local_got_iterator (&lgi, dso);
+ while (mips_get_local_got_entry (&lgi))
+ {
+ 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:
+
+ For [A, B, C]: Adjust the GOT entry if it contains st_value
+ and if the symbol's value will be adjusted.
+
+ For [D]: Do nothing. SHN_COMMON entries never need adjusting.
+
+ For [E, F]: Adjust the GOT entry if it does not contain st_value
+ -- in other words, if it is a type E entry that points to a lazy
+ binding stub -- or if the symbol's value will also be adjusted. */
+ mips_init_global_got_iterator (&ggi, dso);
+ while (mips_get_global_got_entry (&ggi))
+ {
+ 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))
+ mips_buf_write_addr (dso, ggi.got_entry, value + adjust);
+ }
+
+ return lgi.failed || ggi.failed;
+}
+
+static int
+mips_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
+ GElf_Addr adjust)
+{
+ switch (dyn->d_tag)
+ {
+ case DT_MIPS_TIME_STAMP:
+ case DT_MIPS_ICHECKSUM:
+ case DT_MIPS_IVERSION:
+ case DT_MIPS_CONFLICT:
+ case DT_MIPS_CONFLICTNO:
+ case DT_MIPS_LIBLIST:
+ case DT_MIPS_LIBLISTNO:
+ error (0, 0, "%s: File contains QuickStart information", dso->filename);
+ return 1;
+
+ case DT_MIPS_BASE_ADDRESS:
+ case DT_MIPS_RLD_MAP:
+ case DT_MIPS_OPTIONS:
+ if (dyn->d_un.d_ptr >= start)
+ dyn->d_un.d_ptr += adjust;
+ return 1;
+
+ case DT_MIPS_LOCAL_GOTNO:
+ case DT_MIPS_UNREFEXTNO:
+ case DT_MIPS_SYMTABNO:
+ case DT_MIPS_HIPAGENO:
+ case DT_MIPS_GOTSYM:
+ /* We don't change the layout of the GOT or symbol table. */
+ return 1;
+
+ case DT_MIPS_RLD_VERSION:
+ case DT_MIPS_FLAGS:
+ /* We don't change these properties. */
+ return 1;
+ }
+ return 0;
+}
+
+/* Read the addend for a relocation in DSO. If RELA is nonnull,
+ use its r_addend, otherwise read a 32-bit in-place addend from
+ address R_OFFSET. */
+
+static inline uint32_t
+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_32bit_addend, but change the addend to VALUE. */
+
+static inline void
+mips_write_32bit_addend (DSO *dso, GElf_Addr r_offset, GElf_Rela *rela,
+ uint32_t value)
+{
+ if (rela)
+ rela->r_addend = (int32_t) value;
+ else
+ 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,
+ otherwise it is null. */
+
+static int
+mips_adjust_reloc (DSO *dso, GElf_Addr r_offset, GElf_Xword r_info,
+ GElf_Addr start, GElf_Addr adjust, GElf_Rela *rela)
+{
+ GElf_Addr value;
+ GElf_Word r_sym;
+
+ if (reloc_r_type (dso, r_info) == R_MIPS_REL32)
+ {
+ 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
+ base address to the addend. It therefore treats all symbols
+ as being relative, even if they would normally be considered
+ absolute. For example, the special null symbol should always
+ have the value zero, even when the base address is nonzero,
+ but R_MIPS_REL32 relocations against the null symbol must
+ nevertheles be adjusted as if that symbol were relative.
+ The same would apply to SHN_ABS symbols too.
+
+ Thus the result of the relocation calculation must always
+ be adjusted by ADJUST. (We do not need to check START because
+ every adjustment requested by the caller will affect all
+ legitimate local relocation values.) This means that we
+ should add ADJUST to the addend if and only if the symbol's
+ value is not being adjusted.
+
+ In general, we can only check whether a symbol's value is
+ being adjusted by reading its entry in the dynamic symbol
+ table and then querying adjust_symbol_p. However, this
+ generality is fortunately not needed. Modern versions
+ of binutils will never generate R_MIPS_REL32 relocations
+ against symbols in the range [1, DT_MIPS_GOTSYM), so we
+ only need to handle relocations against the null symbol. */
+ if (r_sym != 0)
+ {
+ error (0, 0, "%s: The prelinker does not support R_MIPS_REL32"
+ " relocs against local symbols", dso->filename);
+ return 1;
+ }
+ 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;
+}
+
+static int
+mips_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, GElf_Addr adjust)
+{
+ return mips_adjust_reloc (dso, rel->r_offset, rel->r_info,
+ start, adjust, NULL);
+}
+
+static int
+mips_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, GElf_Addr adjust)
+{
+ return mips_adjust_reloc (dso, rela->r_offset, rela->r_info,
+ start, adjust, rela);
+}
+
+/* Calculate relocation RELA as A + VALUE and store the result in DSO. */
+
+static void
+mips_prelink_32bit_reloc (DSO *dso, GElf_Rela *rela, GElf_Addr value)
+{
+ assert (rela != NULL);
+ 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,
+ otherwise it is null. */
+
+static int
+mips_prelink_reloc (struct prelink_info *info, GElf_Addr r_offset,
+ GElf_Xword r_info, GElf_Rela *rela)
+{
+ DSO *dso;
+ GElf_Addr value;
+ GElf_Word r_sym;
+ int r_type;
+
+ dso = info->dso;
+ r_sym = reloc_r_sym (dso, r_info);
+ r_type = reloc_r_type (dso, r_info);
+ switch (r_type)
+ {
+ case R_MIPS_NONE:
+ break;
+
+ case R_MIPS_REL32:
+ /* An in-place R_MIPS_REL32 relocation against symbol 0 needs no
+ adjustment. */
+ if (rela != NULL || r_sym != 0)
+ {
+ value = 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);
+ mips_prelink_64bit_reloc (dso, rela, value);
+ }
+ else
+ mips_prelink_32bit_reloc (dso, rela, value);
+ }
+ break;
+
+ case R_MIPS_GLOB_DAT:
+ 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:
+ write_ne32 (dso, r_offset, info->resolve (info, r_sym, r_type));
+ break;
+
+ case R_MIPS_TLS_DTPMOD32:
+ case R_MIPS_TLS_DTPMOD64:
+ /* 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)
+ {
+ struct prelink_tls *tls = info->symbols[r_sym].u.tls;
+
+ if (tls == NULL)
+ break;
+ value = tls->modid;
+ if (r_type == R_MIPS_TLS_DTPMOD32)
+ mips_prelink_32bit_reloc (dso, rela, value);
+ else
+ mips_prelink_64bit_reloc (dso, rela, value);
+ }
+ break;
+
+ case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_DTPREL64:
+ value = info->resolve (info, r_sym, r_type);
+ 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;
+ if (r_type == R_MIPS_TLS_TPREL32)
+ mips_prelink_32bit_reloc (dso, rela, value);
+ else
+ mips_prelink_64bit_reloc (dso, rela, value);
+ }
+ break;
+
+ case R_MIPS_COPY:
+ if (dso->ehdr.e_type == ET_EXEC)
+ /* COPY relocs are handled specially in generic code. */
+ return 0;
+ error (0, 0, "%s: R_MIPS_COPY reloc in shared library?", dso->filename);
+ return 1;
+
+ default:
+ error (0, 0, "%s: Unknown MIPS relocation type %d",
+ dso->filename, (int) reloc_r_type (dso, r_info));
+ return 1;
+ }
+ return 0;
+}
+
+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;
+ 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)
+ {
+ 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);
+}
+
+static int
+mips_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
+ GElf_Addr relaaddr)
+{
+ return mips_prelink_reloc (info, rela->r_offset, rela->r_info, rela);
+}
+
+/* CONFLICT is a conflict returned by prelink_conflict for a symbol
+ belonging to DSO. Set *TLS_OUT to the associated TLS information.
+ Return 1 on failure. */
+
+static int
+mips_get_tls (DSO *dso, struct prelink_conflict *conflict,
+ struct prelink_tls **tls_out)
+{
+ if (conflict->reloc_class != RTYPE_CLASS_TLS
+ || conflict->lookup.tls == NULL)
+ {
+ error (0, 0, "%s: R_MIPS_TLS not resolving to STT_TLS symbol",
+ dso->filename);
+ return 1;
+ }
+
+ *tls_out = conflict->lookup.tls;
+ return 0;
+}
+
+/* There is a relocation of type R_INFO against address R_OFFSET in DSO.
+ See if the relocation field must be adjusted by a conflict when DSO
+ is used in the context described by INFO. Add a conflict entry if so.
+ If the relocation is in a RELA section, RELA points to the relocation,
+ otherwise it is null. */
+
+static int
+mips_prelink_conflict_reloc (DSO *dso, struct prelink_info *info,
+ GElf_Addr r_offset, GElf_Xword r_info,
+ GElf_Rela *rela)
+{
+ GElf_Addr value;
+ struct prelink_conflict *conflict;
+ 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);
+ if (conflict == NULL)
+ {
+ 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;
+ /* A relocation against symbol 0. A shared library cannot
+ know what the final module IDs or TP-relative offsets are,
+ so the executable must always have a conflict for them. */
+ value = 0;
+ break;
+ default:
+ 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;
+ 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;
+
+ value = conflict_lookup_value (conflict);
+ }
+ /* VALUE now contains the final symbol value. Change it to the
+ value we want to store at R_OFFSET. */
+ switch (r_type)
+ {
+ case R_MIPS_REL32:
+ 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:
+ break;
+
+ case R_MIPS_COPY:
+ error (0, 0, "R_MIPS_COPY should not be present in shared libraries");
+ 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_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;
+ 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,
+ r_type);
+ return 1;
+ }
+ /* Create and initialize a conflict entry. */
+ entry = prelink_conflict_add_rela (info);
+ if (entry == NULL)
+ return 1;
+ entry->r_offset = r_offset;
+ 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;
+}
+
+static int
+mips_prelink_conflict_rel (DSO *dso, struct prelink_info *info,
+ GElf_Rel *rel, GElf_Addr reladdr)
+{
+ return mips_prelink_conflict_reloc (dso, info, rel->r_offset,
+ rel->r_info, NULL);
+}
+
+static int
+mips_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
+ GElf_Rela *rela, GElf_Addr relaaddr)
+{
+ return mips_prelink_conflict_reloc (dso, info, rela->r_offset,
+ rela->r_info, rela);
+}
+
+static int
+mips_arch_prelink_conflict (DSO *dso, struct prelink_info *info)
+{
+ struct mips_global_got_iterator ggi;
+ GElf_Addr value;
+ struct prelink_conflict *conflict;
+ GElf_Rela *entry;
+
+ if (info->dso == dso || dso->info[DT_PLTGOT] == 0)
+ return 0;
+
+ /* Add a conflict for every global GOT entry that does not hold the
+ right value, either because of a conflict, or because the DSO has
+ a lazy binding stub for a symbol that it also defines. */
+ mips_init_global_got_iterator (&ggi, dso);
+ while (mips_get_global_got_entry (&ggi))
+ {
+ conflict = prelink_conflict (info, ggi.sym_index, R_MIPS_REL32);
+ if (conflict != NULL)
+ value = conflict_lookup_value (conflict);
+ else if (ggi.sym.st_shndx != SHN_UNDEF
+ && ggi.sym.st_shndx != SHN_COMMON)
+ value = ggi.sym.st_value;
+ else
+ continue;
+ if (mips_buf_read_addr (dso, ggi.got_entry) != value)
+ {
+ entry = prelink_conflict_add_rela (info);
+ if (entry == NULL)
+ return 1;
+ entry->r_offset = ggi.got_addr;
+ 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 ggi.failed;
+}
+
+static int
+mips_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
+ char *buf, GElf_Addr dest_addr)
+{
+ DSO *dso;
+
+ dso = info->dso;
+ switch (reloc_r_type (dso, rela->r_info))
+ {
+ case R_MIPS_REL32:
+ 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:
+ buf_write_ne32 (info->dso, buf, rela->r_addend);
+ break;
+
+ default:
+ abort ();
+ }
+ return 0;
+}
+
+/* BUF points to a 32-bit field in DSO that is subject to relocation.
+ If the relocation is in a RELA section, RELA points to the relocation,
+ otherwise it is null. Add the addend to ADJUSTMENT and install the
+ result. */
+
+static inline void
+mips_apply_adjustment (DSO *dso, GElf_Rela *rela, char *buf,
+ GElf_Addr adjustment)
+{
+ if (rela)
+ adjustment += rela->r_addend;
+ else
+ 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;
+ 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;
+
+ case R_MIPS_JUMP_SLOT:
+ buf_write_ne32 (info->dso, buf, value);
+ break;
+
+ case R_MIPS_COPY:
+ abort ();
+
+ case R_MIPS_REL32:
+ 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:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+mips_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf)
+{
+ return mips_apply_reloc (info, rel->r_info, NULL, buf);
+}
+
+static int
+mips_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf)
+{
+ return mips_apply_reloc (info, rela->r_info, rela, 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 (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,
+ r_type);
+ return 1;
+ }
+ return 0;
+}
+
+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 (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_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,
+ r_type);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+mips_need_rel_to_rela (DSO *dso, int first, int last)
+{
+ Elf_Data *data;
+ Elf_Scn *scn;
+ 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)
+ {
+ 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;
+
+ 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;
+}
+
+static int
+mips_reloc_size (int reloc_type)
+{
+ return 4;
+}
+
+static int
+mips_reloc_class (int reloc_type)
+{
+ switch (reloc_type)
+ {
+ case R_MIPS_COPY:
+ return RTYPE_CLASS_COPY;
+ 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;
+ }
+}
+
+static int
+mips_arch_prelink (struct prelink_info *info)
+{
+ struct mips_global_got_iterator ggi;
+ DSO *dso;
+ GElf_Addr value;
+ int i;
+
+ dso = info->dso;
+
+ if (dso->info_DT_MIPS_PLTGOT)
+ {
+ /* Write address of .plt into gotplt[1]. This is in each
+ normal gotplt entry unless prelinking. */
+ int sec = addr_to_sec (dso, dso->info_DT_MIPS_PLTGOT);
+ Elf32_Addr data;
+
+ if (sec == -1)
+ return 1;
+
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ if (dso->shdr[i].sh_type == SHT_PROGBITS
+ && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name),
+ ".plt"))
+ break;
+
+ if (i == dso->ehdr.e_shnum)
+ return 0;
+ data = dso->shdr[i].sh_addr;
+ write_ne32 (dso, dso->info_DT_MIPS_PLTGOT + 4, data);
+ }
+
+ if (dso->info[DT_PLTGOT] == 0)
+ return 0;
+
+ /* Install Quickstart values for all global GOT entries of type A-D
+ in the table above. */
+ mips_init_global_got_iterator (&ggi, dso);
+ while (mips_get_global_got_entry (&ggi))
+ {
+ value = info->resolve (info, ggi.sym_index, R_MIPS_REL32);
+ if (ggi.sym.st_shndx == SHN_UNDEF
+ || ggi.sym.st_shndx == SHN_COMMON)
+ mips_buf_write_addr (dso, ggi.got_entry, value);
+ else
+ {
+ /* Type E and F in the table above. We cannot install Quickstart
+ values for type E, but we should never need to in executables,
+ because an executable should not use lazy binding stubs for
+ symbols it defines itself. Although we could in theory just
+ discard any such stub address, it goes against the principle
+ that prelinking should be reversible.
+
+ When type E entries occur in shared libraries, we can fix
+ them up using conflicts.
+
+ Type F entries should never need a Quickstart value -- the
+ current value should already be correct. However, the conflict
+ 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 != 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);
+ return 1;
+ }
+ }
+ }
+ return ggi.failed;
+}
+
+static int
+mips_arch_undo_prelink (DSO *dso)
+{
+ struct mips_global_got_iterator ggi;
+ int i;
+
+ if (dso->info_DT_MIPS_PLTGOT)
+ {
+ /* Clear gotplt[1] if it contains the address of .plt. */
+ int sec = addr_to_sec (dso, dso->info_DT_MIPS_PLTGOT);
+ Elf32_Addr data;
+
+ if (sec == -1)
+ return 1;
+
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ if (dso->shdr[i].sh_type == SHT_PROGBITS
+ && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name),
+ ".plt"))
+ break;
+
+ if (i == dso->ehdr.e_shnum)
+ return 0;
+ data = read_une32 (dso, dso->info_DT_MIPS_PLTGOT + 4);
+ if (data == dso->shdr[i].sh_addr)
+ write_ne32 (dso, dso->info_DT_MIPS_PLTGOT + 4, 0);
+ }
+
+ if (dso->info[DT_PLTGOT] == 0)
+ return 0;
+
+ mips_init_global_got_iterator (&ggi, dso);
+ while (mips_get_global_got_entry (&ggi))
+ if (ggi.sym.st_shndx == SHN_UNDEF)
+ /* Types A-C in the table above. */
+ 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. */
+ mips_buf_write_addr (dso, ggi.got_entry, 0);
+ return ggi.failed;
+}
+
+static int
+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
+ these changes in the undo section, but in the absence of that,
+ 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. */
+ 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)
+ {
+ 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 (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);
+ if (sec == -1 || strcmp (name, ".got.plt"))
+ {
+ error (0, 0,
+ "%s: R_MIPS_JUMP_SLOT not pointing into .got.plt section",
+ dso->filename);
+ return 1;
+ }
+ else
+ {
+ Elf32_Addr data = read_une32 (dso, dso->shdr[sec].sh_addr + 4);
+
+ assert (rel->r_offset >= dso->shdr[sec].sh_addr + 8);
+ assert (((rel->r_offset - dso->shdr[sec].sh_addr) & 3) == 0);
+ write_ne32 (dso, rel->r_offset, data);
+ }
+ }
+
+ return 0;
+}
+
+PL_ARCH(mips) = {
+ .name = "MIPS",
+ .class = ELFCLASS32,
+ .machine = EM_MIPS,
+ .max_reloc_size = 4,
+ .dynamic_linker = "/lib/ld.so.1",
+ .dynamic_linker_alt = "/lib32/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,
+ .rtype_class_valid = RTYPE_CLASS_VALID,
+ .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 0x2aaa8000, 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 = 0x2c000000,
+ .mmap_end = 0x3c000000,
+ .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,
+ .rtype_class_valid = RTYPE_CLASS_VALID,
+ .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
+};