summaryrefslogtreecommitdiffstats
path: root/ld/mips.c
diff options
context:
space:
mode:
Diffstat (limited to 'ld/mips.c')
-rw-r--r--ld/mips.c403
1 files changed, 403 insertions, 0 deletions
diff --git a/ld/mips.c b/ld/mips.c
new file mode 100644
index 0000000..59a9221
--- /dev/null
+++ b/ld/mips.c
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 2015 Serge Vakulenko
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "ld.h"
+#include "ld_arch.h"
+#include "ld_dynamic.h"
+#include "ld_input.h"
+#include "ld_output.h"
+#include "ld_reloc.h"
+#include "ld_symbols.h"
+#include "ld_utils.h"
+#include "mips.h"
+
+#define EF_MIPS_ABI2 0x00000020 /* n32 abi */
+#define EF_MIPS_ABI 0x00007000 /* Application binary interface */
+#define E_MIPS_ABI_O32 0x00001000 /* MIPS 32 bit ABI (UCODE) */
+#define E_MIPS_ABI_O64 0x00002000 /* UCODE MIPS 64 bit ABI */
+#define E_MIPS_ABI_EABI32 0x00003000 /* Embedded ABI for 32-bit */
+#define E_MIPS_ABI_EABI64 0x00004000 /* Embedded ABI for 64-bit */
+
+#define EF_MIPS_ASE_MDMX 0x08000000 /* MDMX multimedia extensions */
+#define EF_MIPS_ASE_M16 0x04000000 /* MIPS16e ISA extensions */
+#define EF_MIPS_ASE_MICROMIPS 0x02000000 /* MicroMIPS architecture */
+
+#define EF_MIPS_ARCH_1 0x00000000 /* MIPS I instruction set */
+#define EF_MIPS_ARCH_2 0x10000000 /* MIPS II instruction set */
+#define EF_MIPS_ARCH_3 0x20000000 /* MIPS III instruction set */
+#define EF_MIPS_ARCH_4 0x30000000 /* MIPS IV instruction set */
+#define EF_MIPS_ARCH_5 0x40000000 /* Never introduced */
+#define EF_MIPS_ARCH_32 0x50000000 /* Mips32 Revision 1 */
+#define EF_MIPS_ARCH_64 0x60000000 /* Mips64 Revision 1 */
+#define EF_MIPS_ARCH_32R2 0x70000000 /* Mips32 Revision 2 */
+#define EF_MIPS_ARCH_64R2 0x80000000 /* Mips64 Revision 2 */
+
+ELFTC_VCSID("$Id$");
+
+static void
+_scan_reloc(struct ld *ld, struct ld_input_section *is,
+ struct ld_reloc_entry *lre)
+{
+
+ (void) is;
+
+ switch (lre->lre_type) {
+
+ case R_MIPS_NONE:
+ case R_MIPS_32:
+ case R_MIPS_26:
+ case R_MIPS_PC16:
+ case R_MIPS_GPREL16:
+ case R_MIPS_HI16:
+ case R_MIPS_LO16:
+ break;
+
+ default:
+ ld_warn(ld, "can not handle relocation %ju",
+ lre->lre_type);
+ break;
+ }
+}
+
+static void
+_process_reloc(struct ld *ld, struct ld_input_section *is,
+ struct ld_reloc_entry *lre, struct ld_symbol *lsb, uint8_t *buf)
+{
+ struct ld_output *lo = ld->ld_output;
+ uint32_t pc, s;
+ int32_t a, v, la;
+ static uint64_t gp;
+ static char gp_name[] = "_gp";
+
+ assert(lo != NULL);
+
+ pc = lre->lre_offset + is->is_output->os_addr + is->is_reloff;
+ s = (uint32_t) lsb->lsb_value;
+ READ_32(buf + lre->lre_offset, a);
+
+ switch (lre->lre_type) {
+
+ case R_MIPS_NONE:
+ break;
+
+ case R_MIPS_32:
+ /* 32-bit byte address. */
+ v = s + a;
+ WRITE_32(buf + lre->lre_offset, v);
+ break;
+
+ case R_MIPS_26:
+ /* Word address at lower 26 bits. */
+ s += (a & 0x3ffffff) << 2;
+ v = (a & ~0x3ffffff) | ((s >> 2) & 0x3ffffff);
+ WRITE_32(buf + lre->lre_offset, v);
+ break;
+
+ case R_MIPS_PC16:
+ /* PC-relative word address at lower 16 bits. */
+ s += ((a & 0xffff) << 2) - pc;
+ v = (a & ~0xffff) | ((s >> 2) & 0xffff);
+ WRITE_32(buf + lre->lre_offset, v);
+ break;
+
+ case R_MIPS_GPREL16:
+ /* GP-relative byte address at lower 16 bits. */
+ if (! gp && ld_symbols_get_value(ld, gp_name, &gp) < 0)
+ ld_fatal(ld, "symbol _gp is undefined");
+
+ s += (int16_t)(a & 0xffff) - gp;
+ v = (a & ~0xffff) | (s & 0xffff);
+ WRITE_32(buf + lre->lre_offset, v);
+ break;
+
+ case R_MIPS_HI16:
+ /* 16-bit high part of address pair. */
+ if (! STAILQ_NEXT(lre, lre_next) ||
+ STAILQ_NEXT(lre, lre_next)->lre_type != R_MIPS_LO16)
+ ld_fatal(ld, "no LO16 after HI16 relocation");
+ READ_32(buf + STAILQ_NEXT(lre, lre_next)->lre_offset, la);
+ s += (a << 16) + (int16_t)la;
+ v = (a & ~0xffff) | (((s - (int16_t)s) >> 16) & 0xffff);
+ WRITE_32(buf + lre->lre_offset, v);
+ break;
+
+ case R_MIPS_LO16:
+ /* 16-bit low part of address pair. */
+ s += (int16_t)a;
+ v = (a & ~0xffff) | (s & 0xffff);
+ WRITE_32(buf + lre->lre_offset, v);
+ break;
+
+ default:
+ ld_fatal(ld, "Relocation %d not supported", lre->lre_type);
+ break;
+ }
+}
+
+/*
+ * Map flags into a valid MIPS architecture level value.
+ */
+static unsigned
+_map_arch(unsigned flags)
+{
+ flags &= EF_MIPS_ARCH;
+
+ switch (flags) {
+ default:
+ case EF_MIPS_ARCH_1:
+ return EF_MIPS_ARCH_1;
+ case EF_MIPS_ARCH_2:
+ case EF_MIPS_ARCH_3:
+ case EF_MIPS_ARCH_4:
+ case EF_MIPS_ARCH_5:
+ case EF_MIPS_ARCH_32:
+ case EF_MIPS_ARCH_64:
+ case EF_MIPS_ARCH_32R2:
+ case EF_MIPS_ARCH_64R2:
+ return flags;
+ }
+}
+
+/*
+ * Merge architecture levels of two files.
+ */
+static unsigned
+_merge_arch(unsigned old_arch, unsigned new_arch)
+{
+ unsigned base, extended;
+
+ if (old_arch < new_arch) {
+ base = old_arch;
+ extended = new_arch;
+ } else if (old_arch > new_arch) {
+ base = new_arch;
+ extended = old_arch;
+ } else
+ return old_arch;
+
+ switch (extended) {
+ default:
+ case EF_MIPS_ARCH_1:
+ case EF_MIPS_ARCH_2:
+ case EF_MIPS_ARCH_3:
+ case EF_MIPS_ARCH_4:
+ case EF_MIPS_ARCH_5:
+ return extended;
+
+ case EF_MIPS_ARCH_32:
+ if (base <= EF_MIPS_ARCH_2)
+ return EF_MIPS_ARCH_32;
+ return EF_MIPS_ARCH_64;
+
+ case EF_MIPS_ARCH_64:
+ return EF_MIPS_ARCH_64;
+
+ case EF_MIPS_ARCH_32R2:
+ if (base <= EF_MIPS_ARCH_2 || base == EF_MIPS_ARCH_32)
+ return EF_MIPS_ARCH_32R2;
+ return EF_MIPS_ARCH_64R2;
+
+ case EF_MIPS_ARCH_64R2:
+ return EF_MIPS_ARCH_64R2;
+ }
+}
+
+static const char*
+_abi_name(int flags)
+{
+ switch (flags & EF_MIPS_ABI) {
+ case 0:
+ return (flags & EF_MIPS_ABI2) ? "N32" : "none";
+ case E_MIPS_ABI_O32:
+ return "O32";
+ case E_MIPS_ABI_O64:
+ return "O64";
+ case E_MIPS_ABI_EABI32:
+ return "EABI32";
+ case E_MIPS_ABI_EABI64:
+ return "EABI64";
+ default:
+ return "Unknown";
+ }
+}
+
+/*
+ * Merge options of application binary interface.
+ */
+static unsigned
+_merge_abi(struct ld *ld, unsigned new_flags)
+{
+ int old = ld->ld_arch->flags & EF_MIPS_ABI;
+ int new = new_flags & EF_MIPS_ABI;
+
+ if (old == 0)
+ return new;
+
+ if (new != old && new != 0)
+ ld_fatal(ld, "ABI mismatch: linking '%s' module with previous '%s' modules",
+ _abi_name(new_flags), _abi_name(ld->ld_arch->flags));
+
+ return old;
+}
+
+/*
+ * Merge options of application-specific extensions.
+ */
+static unsigned
+_merge_ase(struct ld *ld, unsigned new_flags)
+{
+ int old_micro = ld->ld_arch->flags & EF_MIPS_ASE_MICROMIPS;
+ int new_micro = new_flags & EF_MIPS_ASE_MICROMIPS;
+ int old_m16 = ld->ld_arch->flags & EF_MIPS_ASE_M16;
+ int new_m16 = new_flags & EF_MIPS_ASE_M16;
+
+ if ((old_m16 && new_micro) || (old_micro && new_m16))
+ ld_fatal(ld, "ASE mismatch: linking '%s' module with previous '%s' modules",
+ new_m16 ? "MIPS16" : "microMIPS",
+ old_micro ? "microMIPS" : "MIPS16");
+ return old_micro | new_micro | old_m16 | new_m16;
+}
+
+/*
+ * Merge architecture-specific flags of the file to be linked
+ * into a resulting value for output file.
+ */
+static void
+_merge_flags(struct ld *ld, unsigned new_flags)
+{
+ struct ld_arch *la = ld->ld_arch;
+ unsigned value;
+
+ /* At least one .noreorder directive appeared in the source. */
+ la->flags |= new_flags & EF_MIPS_NOREORDER;
+
+ /* Merge position-independent flags. */
+ if (((new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0) !=
+ ((la->flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0))
+ ld_warn(ld, "linking PIC files with non-PIC files");
+ if (new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC))
+ la->flags |= EF_MIPS_CPIC;
+ if (! (new_flags & EF_MIPS_PIC))
+ la->flags &= ~EF_MIPS_PIC;
+
+ /* Merge architecture level. */
+ value = _merge_arch(_map_arch(la->flags), _map_arch(new_flags));
+ la->flags &= ~EF_MIPS_ARCH;
+ la->flags |= value;
+
+ /* Merge ABI options. */
+ value = _merge_abi(ld, new_flags);
+ la->flags &= ~EF_MIPS_ABI;
+ la->flags |= value;
+
+ /* Merge application-specific extensions. */
+ value = _merge_ase(ld, new_flags);
+ la->flags &= ~EF_MIPS_ARCH_ASE;
+ la->flags |= value;
+}
+
+static uint64_t
+_get_max_page_size(struct ld *ld)
+{
+
+ (void) ld;
+
+ return 0x1000;
+}
+
+static uint64_t
+_get_common_page_size(struct ld *ld)
+{
+
+ (void) ld;
+
+ return 0x1000;
+}
+
+static int
+_is_absolute_reloc(uint64_t r)
+{
+ if (r == R_MIPS_32)
+ return 1;
+
+ return 0;
+}
+
+static int
+_is_relative_reloc(uint64_t r)
+{
+ if (r == R_MIPS_REL32)
+ return 1;
+
+ return 0;
+}
+
+void
+mips_register(struct ld *ld)
+{
+ struct ld_arch *mips_little_endian, *mips_big_endian;
+
+ if ((mips_little_endian = calloc(1, sizeof(*mips_little_endian))) == NULL)
+ ld_fatal_std(ld, "calloc");
+ if ((mips_big_endian = calloc(1, sizeof(*mips_big_endian))) == NULL)
+ ld_fatal_std(ld, "calloc");
+
+ /*
+ * Little endian.
+ */
+ snprintf(mips_little_endian->name, sizeof(mips_little_endian->name), "%s", "littlemips");
+
+ mips_little_endian->script = littlemips_script;
+ mips_little_endian->get_max_page_size = _get_max_page_size;
+ mips_little_endian->get_common_page_size = _get_common_page_size;
+ mips_little_endian->scan_reloc = _scan_reloc;
+ mips_little_endian->process_reloc = _process_reloc;
+ mips_little_endian->is_absolute_reloc = _is_absolute_reloc;
+ mips_little_endian->is_relative_reloc = _is_relative_reloc;
+ mips_little_endian->merge_flags = _merge_flags;
+ mips_little_endian->reloc_is_64bit = 0;
+ mips_little_endian->reloc_is_rela = 0;
+ mips_little_endian->reloc_entsize = sizeof(Elf32_Rel);
+
+ /*
+ * Big endian.
+ */
+ snprintf(mips_big_endian->name, sizeof(mips_big_endian->name), "%s", "bigmips");
+
+ mips_big_endian->script = bigmips_script;
+ mips_big_endian->get_max_page_size = _get_max_page_size;
+ mips_big_endian->get_common_page_size = _get_common_page_size;
+ mips_big_endian->scan_reloc = _scan_reloc;
+ mips_big_endian->process_reloc = _process_reloc;
+ mips_big_endian->is_absolute_reloc = _is_absolute_reloc;
+ mips_big_endian->is_relative_reloc = _is_relative_reloc;
+ mips_little_endian->merge_flags = _merge_flags;
+ mips_big_endian->reloc_is_64bit = 0;
+ mips_big_endian->reloc_is_rela = 0;
+ mips_big_endian->reloc_entsize = sizeof(Elf32_Rel);
+
+ HASH_ADD_STR(ld->ld_arch_list, name, mips_little_endian);
+ HASH_ADD_STR(ld->ld_arch_list, name, mips_big_endian);
+}
OpenPOWER on IntegriCloud