summaryrefslogtreecommitdiff
path: root/ldso
diff options
context:
space:
mode:
authorMarkos Chandras <markos.chandras@imgtec.com>2008-02-05 14:51:48 +0000
committerBernhard Reutner-Fischer <rep.dot.nop@gmail.com>2013-03-14 22:45:15 +0100
commit22686a1383c4a4a319eaaa6b16b1a9540114bd66 (patch)
tree04e12086f9187c150ba6a33db0368b9903e50363 /ldso
parent37439e66a31f251eba39604885f57099a43d943d (diff)
Add support for the Meta architecture
Meta cores are 32-bit, hardware multithreaded, general purpose, embedded processors which also feature a DSP instruction set, and can be found in many digital radios. They are capable of running different operating systems on different hardware threads, for example a digital radio might run RTOSes for DAB decoding and audio decoding on 3 hardware threads, and run Linux on the 4th hardware thread to manage the user interface, networking etc. HTPs are also capable of running SMP Linux on multiple hardware threads. Signed-off-by: Markos Chandras <markos.chandras@imgtec.com> Signed-off-by: Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Diffstat (limited to 'ldso')
-rw-r--r--ldso/ldso/metag/dl-debug.h27
-rw-r--r--ldso/ldso/metag/dl-startup.h63
-rw-r--r--ldso/ldso/metag/dl-syscalls.h6
-rw-r--r--ldso/ldso/metag/dl-sysdep.h98
-rw-r--r--ldso/ldso/metag/elfinterp.c299
-rw-r--r--ldso/ldso/metag/resolve.S51
6 files changed, 544 insertions, 0 deletions
diff --git a/ldso/ldso/metag/dl-debug.h b/ldso/ldso/metag/dl-debug.h
new file mode 100644
index 000000000..5981d7c73
--- /dev/null
+++ b/ldso/ldso/metag/dl-debug.h
@@ -0,0 +1,27 @@
+/*
+ * Meta ELF shared library loader support.
+ *
+ * Program to load an elf binary on a linux system, and run it.
+ * References to symbols in sharable libraries can be resolved
+ * by either an ELF sharable library or a linux style of shared
+ * library.
+ *
+ * Copyright (C) 2013, Imagination Technologies Ltd.
+ *
+ * Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
+ */
+
+static const char *_dl_reltypes_tab[] = {
+ [0] "R_METAG_HIADDR16", "R_METAG_LOADDR16", "R_METAG_ADDR32",
+ [3] "R_METAG_NONE", "R_METAG_RELBRANCH", "R_METAG_GETSETOFF",
+ [6] "R_METAG_REG32OP1", "R_METAG_REG32OP2", "R_METAG_REG32OP3",
+ [9] "R_METAG_REG16OP1", "R_METAG_REG16OP2", "R_METAG_REG16OP3",
+ [12] "R_METAG_REG32OP4", "R_METAG_HIOG", "R_METAG_LOOG",
+ [30] "R_METAG_VTINHERIT", "R_METAG_VTENTRY",
+ [32] "R_METAG_HI16_GOTOFF", "R_METAG_LO16_GOTOFF",
+ [34] "R_METAG_GETSET_GOTOFF", "R_METAG_GETSET_GOT",
+ [36] "R_METAG_HI16_GOTPC", "R_METAG_LO16_GOTPC",
+ [38] "R_METAG_HI16_PLT", "R_METAG_LO16_PLT",
+ [40] "R_METAG_RELBRANCH_PLT", "R_METAG_GOTOFF",
+ [42] "R_METAG_PLT", "R_METAG_COPY", "R_METAG_JMP_SLOT",
+};
diff --git a/ldso/ldso/metag/dl-startup.h b/ldso/ldso/metag/dl-startup.h
new file mode 100644
index 000000000..8dbf747e1
--- /dev/null
+++ b/ldso/ldso/metag/dl-startup.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 Imagination Technologies Ltd.
+ *
+ * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
+ */
+
+/*
+ * This code fixes the stack pointer so that the dynamic linker
+ * can find argc, argv and auxvt (Auxillary Vector Table).
+ */
+
+__asm__ (
+" .text\n"
+" .global __start\n"
+" .type __start,@function\n"
+" .hidden __start\n"
+"_start:\n"
+"__start:\n"
+" MSETL [A0StP++],D0Ar4,D0Ar2\n"
+" MOV D1Ar1,D0Ar2\n"
+" CALLR D1RtP,__dl_start\n"
+" GETL D0Ar2,D1Ar1,[A0StP+#-(1*8)]\n"
+" GETL D0Ar4,D1Ar3,[A0StP+#-(2*8)]\n"
+" SUB A0StP,A0StP,#(2*8)\n"
+" MOV PC,D0Re0\n"
+" .size __start,.-__start\n"
+" .previous\n"
+);
+
+
+/*
+ * Get a pointer to the argv array. On many platforms this can be just
+ * the address if the first argument, on other platforms we need to
+ * do something a little more subtle here.
+ */
+
+#define GET_ARGV(ARGVP, ARGS) ARGVP = (((unsigned long *) ARGS))
+
+
+/* Handle relocation of the symbols in the dynamic loader. */
+static inline
+void PERFORM_BOOTSTRAP_RELOC(ELF_RELOC *rpnt, unsigned long *reloc_addr,
+ unsigned long symbol_addr, unsigned long load_addr, Elf32_Sym *symtab)
+{
+ switch (ELF32_R_TYPE(rpnt->r_info)) {
+ case R_METAG_GLOB_DAT:
+ case R_METAG_JMP_SLOT:
+ case R_METAG_ADDR32:
+ *reloc_addr = symbol_addr;
+ break;
+ case R_METAG_RELATIVE:
+ *reloc_addr = load_addr + rpnt->r_addend;
+ break;
+ case R_METAG_RELBRANCH:
+ *reloc_addr = symbol_addr + rpnt->r_addend - *reloc_addr - 4;
+ break;
+ case R_METAG_NONE:
+ break;
+ default:
+ _dl_exit(1);
+ break;
+ }
+}
diff --git a/ldso/ldso/metag/dl-syscalls.h b/ldso/ldso/metag/dl-syscalls.h
new file mode 100644
index 000000000..70ceab10e
--- /dev/null
+++ b/ldso/ldso/metag/dl-syscalls.h
@@ -0,0 +1,6 @@
+/* stub for arch-specific syscall issues
+ *
+ * Copyright (C) 2013, Imagination Technologies Ltd.
+ *
+ * Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
+ */
diff --git a/ldso/ldso/metag/dl-sysdep.h b/ldso/ldso/metag/dl-sysdep.h
new file mode 100644
index 000000000..55e8d1f66
--- /dev/null
+++ b/ldso/ldso/metag/dl-sysdep.h
@@ -0,0 +1,98 @@
+/*
+ * Meta can never use Elf32_Rel relocations.
+ *
+ * Copyright (C) 2013, Imagination Technologies Ltd.
+ *
+ * Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
+ */
+
+#define ELF_USES_RELOCA
+
+#include <elf.h>
+
+/* Initialization sequence for the GOT. */
+#define INIT_GOT(GOT_BASE,MODULE) \
+{ \
+ GOT_BASE[1] = (unsigned long) MODULE; \
+ GOT_BASE[2] = (unsigned long) _dl_linux_resolve; \
+}
+
+/* Maximum unsigned GOT [GS]ETD offset size, ie. 2^(11+2). */
+#define GOT_REG_OFFSET 0x2000
+
+/* Defined some magic numbers that this ld.so should accept. */
+#define MAGIC1 EM_METAG
+#undef MAGIC2
+#define ELF_TARGET "META"
+
+/* Need bootstrap relocations */
+#define ARCH_NEEDS_BOOTSTRAP_RELOCS
+
+struct elf_resolve;
+extern unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry);
+
+/* Variable page size. */
+#define ADDR_ALIGN (_dl_pagesize - 1)
+#define PAGE_ALIGN (~ADDR_ALIGN)
+#define OFFS_ALIGN (PAGE_ALIGN & ~(1ul << (sizeof(_dl_pagesize) * 8 - 1)))
+
+/* The union of reloc-type-classes where the reloc TYPE is a member.
+
+ TYPE is in the class ELF_RTYPE_CLASS_NOCOPY if it should not be allowed
+ to resolve to one of the main executable's symbols, as for a COPY
+ reloc. */
+#define elf_machine_type_class(type) \
+ (((((type) == R_METAG_JMP_SLOT)) * ELF_RTYPE_CLASS_PLT) \
+ | (((type) == R_METAG_COPY) * ELF_RTYPE_CLASS_COPY))
+
+static inline Elf32_Addr
+elf_machine_dynamic(Elf32_Ehdr *header)
+{
+ Elf32_Addr *got;
+
+ __asm__ ("MOV %0,A1LbP" : "=r" (got));
+
+ if (header->e_ident[EI_ABIVERSION] >= 1) {
+ /* GOT register offset was introduced with ABI v1 */
+ got = (Elf32_Addr*)((void*)got - GOT_REG_OFFSET);
+ }
+ return *got;
+}
+
+#define DL_BOOT_COMPUTE_GOT(GOT) \
+ ((GOT) = elf_machine_dynamic(header))
+
+static inline Elf32_Addr
+elf_machine_load_address(void)
+{
+ Elf32_Addr addr;
+ __asm__ ("MOV D1Ar1,A1LbP\n"
+ "ADDT D1Ar1,D1Ar1,#HI(__dl_start@GOTOFF)\n"
+ "ADD D1Ar1,D1Ar1,#LO(__dl_start@GOTOFF)\n"
+ "ADDT D0Ar2,D0Ar2,#HI(__dl_start_addr@GOTOFF)\n"
+ "ADD D0Ar2,D0Ar2,#LO(__dl_start_addr@GOTOFF)\n"
+ "GETD D0Ar2,[D0Ar2]\n"
+ "SUB %0,D1Ar1,D0Ar2\n"
+ ".section .data\n"
+ "__dl_start_addr: .long __dl_start\n"
+ ".previous\n"
+ : "=d" (addr) : : "D1Ar1", "D0Ar2");
+ return addr;
+}
+
+static inline void
+elf_machine_relative(Elf32_Addr load_off, const Elf32_Addr rel_addr,
+ Elf32_Word relative_count)
+{
+ Elf32_Rela *rpnt = (void *)rel_addr;
+
+ --rpnt;
+ do {
+ Elf32_Addr *const reloc_addr =
+ (void *)(load_off + (++rpnt)->r_offset);
+
+ *reloc_addr = load_off + rpnt->r_addend;
+ } while (--relative_count);
+}
+
+#define DL_MALLOC_ALIGN 8
diff --git a/ldso/ldso/metag/elfinterp.c b/ldso/ldso/metag/elfinterp.c
new file mode 100644
index 000000000..78434167b
--- /dev/null
+++ b/ldso/ldso/metag/elfinterp.c
@@ -0,0 +1,299 @@
+/*
+ * Meta ELF shared library loader support.
+ *
+ * Program to load an elf binary on a linux system, and run it.
+ * References to symbols in sharable libraries can be resolved
+ * by either an ELF sharable library or a linux style of shared
+ * library.
+ *
+ * Copyright (C) 2013, Imagination Technologies Ltd.
+ *
+ * Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
+ */
+
+#include "ldso.h"
+
+/* Defined in resolve.S. */
+extern int _dl_linux_resolve(void);
+
+static inline unsigned long __get_unaligned_reloc(unsigned long *addr)
+{
+ char *rel_addr = (char *)addr;
+ unsigned long val;
+
+ val = *rel_addr++ & 0xff;
+ val |= (*rel_addr++ << 8) & 0x0000ff00;
+ val |= (*rel_addr++ << 16) & 0x00ff0000;
+ val |= (*rel_addr++ << 24) & 0xff000000;
+
+ return val;
+}
+
+static inline void __put_unaligned_reloc(unsigned long *addr,
+ unsigned long val)
+{
+ char *rel_addr = (char *)addr;
+
+ *rel_addr++ = (val & 0x000000ff);
+ *rel_addr++ = ((val & 0x0000ff00) >> 8);
+ *rel_addr++ = ((val & 0x00ff0000) >> 16);
+ *rel_addr++ = ((val & 0xff000000) >> 24);
+}
+
+unsigned long
+_dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
+{
+ int reloc_type;
+ int symtab_index;
+ char *strtab;
+ char *symname;
+ char *new_addr;
+ char *rel_addr;
+ char **got_addr;
+ Elf32_Sym *symtab;
+ ELF_RELOC *this_reloc;
+ unsigned long instr_addr;
+
+ rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
+
+ this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
+ reloc_type = ELF32_R_TYPE(this_reloc->r_info);
+ symtab_index = ELF32_R_SYM(this_reloc->r_info);
+
+ symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
+ strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
+ symname = strtab + symtab[symtab_index].st_name;
+
+ if (unlikely(reloc_type != R_METAG_JMP_SLOT)) {
+ _dl_dprintf(2, "%s: Incorrect relocation type in jump relocations\n",
+ _dl_progname);
+ _dl_exit(1);
+ }
+
+ /* Address of the jump instruction to fix up. */
+ instr_addr = ((unsigned long)this_reloc->r_offset +
+ (unsigned long)tpnt->loadaddr);
+ got_addr = (char **)instr_addr;
+
+ /* Get the address of the GOT entry. */
+ new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt,
+ ELF_RTYPE_CLASS_PLT, NULL);
+ if (unlikely(!new_addr)) {
+ _dl_dprintf(2, "%s: Can't resolve symbol '%s'\n", _dl_progname, symname);
+ _dl_exit(1);
+ }
+
+#if defined (__SUPPORT_LD_DEBUG__)
+ if (_dl_debug_bindings) {
+ _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
+ if (_dl_debug_detail)
+ _dl_dprintf(_dl_debug_file,
+ "\n\tpatched: %x ==> %x @ %x\n",
+ *got_addr, new_addr, got_addr);
+ }
+ if (!_dl_debug_nofixups) {
+ *got_addr = new_addr;
+ }
+#else
+ *got_addr = new_addr;
+#endif
+
+ return (unsigned long)new_addr;
+}
+
+static int
+_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
+ unsigned long rel_addr, unsigned long rel_size,
+ int (*reloc_fnc)(struct elf_resolve *tpnt, struct dyn_elf *scope,
+ ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
+{
+ int symtab_index;
+ unsigned int i;
+ char *strtab;
+ Elf32_Sym *symtab;
+ ELF_RELOC *rpnt;
+
+ /* Parse the relocation information. */
+ rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
+ rel_size /= sizeof(ELF_RELOC);
+
+ symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
+ strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
+
+ for (i = 0; i < rel_size; i++, rpnt++) {
+ int res;
+
+ symtab_index = ELF32_R_SYM(rpnt->r_info);
+
+ debug_sym(symtab, strtab, symtab_index);
+ debug_reloc(symtab, strtab, rpnt);
+
+ /* Pass over to actual relocation function. */
+ res = reloc_fnc(tpnt, scope, rpnt, symtab, strtab);
+
+ if (res == 0)
+ continue;
+
+ _dl_dprintf(2, "\n%s: ", _dl_progname);
+
+ if (symtab_index)
+ _dl_dprintf(2, "symbol '%s': ",
+ strtab + symtab[symtab_index].st_name);
+
+ if (unlikely(res < 0)) {
+ int reloc_type = ELF32_R_TYPE(rpnt->r_info);
+
+#if defined (__SUPPORT_LD_DEBUG__)
+ _dl_dprintf(2, "can't handle reloc type %s\n",
+ _dl_reltypes(reloc_type));
+#else
+ _dl_dprintf(2, "can't handle reloc type %x\n",
+ reloc_type);
+#endif
+ _dl_exit(-res);
+ } else if (unlikely(res > 0)) {
+ _dl_dprintf(2, "can't resolve symbol\n");
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+static int
+_dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
+ ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
+{
+ int reloc_type;
+ int symtab_index;
+ char *symname = NULL;
+ unsigned long *reloc_addr;
+ unsigned long symbol_addr;
+#if defined (__SUPPORT_LD_DEBUG__)
+ unsigned long old_val;
+#endif
+
+ reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
+ reloc_type = ELF32_R_TYPE(rpnt->r_info);
+ symtab_index = ELF32_R_SYM(rpnt->r_info);
+ symbol_addr = 0;
+ symname = strtab + symtab[symtab_index].st_name;
+
+ if (symtab_index) {
+ if (symtab[symtab_index].st_shndx != SHN_UNDEF &&
+ ELF32_ST_BIND(symtab[symtab_index].st_info) == STB_LOCAL) {
+ symbol_addr = (unsigned long)tpnt->loadaddr;
+ } else {
+ symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt,
+ elf_machine_type_class(reloc_type), NULL);
+ }
+
+ if (unlikely(!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) {
+ _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
+ _dl_exit(1);
+ };
+
+ symbol_addr += rpnt->r_addend;
+ }
+
+#if defined (__SUPPORT_LD_DEBUG__)
+ if (reloc_type != R_METAG_NONE)
+ old_val = __get_unaligned_reloc(reloc_addr);
+#endif
+
+ switch (reloc_type) {
+ case R_METAG_NONE:
+ break;
+ case R_METAG_GLOB_DAT:
+ case R_METAG_JMP_SLOT:
+ case R_METAG_ADDR32:
+ __put_unaligned_reloc(reloc_addr, symbol_addr);
+ break;
+ case R_METAG_COPY:
+#if defined (__SUPPORT_LD_DEBUG__)
+ if (_dl_debug_move)
+ _dl_dprintf(_dl_debug_file,
+ "\t%s move %d bytes from %x to %x\n",
+ symname, symtab[symtab_index].st_size,
+ symbol_addr, reloc_addr);
+#endif
+
+ _dl_memcpy((char *)reloc_addr,
+ (char *)symbol_addr,
+ symtab[symtab_index].st_size);
+ break;
+ case R_METAG_RELATIVE:
+ __put_unaligned_reloc(reloc_addr,
+ (unsigned long)tpnt->loadaddr +
+ rpnt->r_addend);
+ break;
+ default:
+ return -1; /* Calls _dl_exit(1). */
+ }
+
+#if defined (__SUPPORT_LD_DEBUG__)
+ if (_dl_debug_reloc && _dl_debug_detail &&
+ (reloc_type != R_METAG_NONE)) {
+ unsigned long new_val = __get_unaligned_reloc(reloc_addr);
+ _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
+ old_val, new_val, reloc_addr);
+ }
+#endif
+
+ return 0;
+}
+
+static int
+_dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope,
+ ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
+{
+ int reloc_type;
+ unsigned long *reloc_addr;
+#if defined (__SUPPORT_LD_DEBUG__)
+ unsigned long old_val;
+#endif
+
+ reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
+ reloc_type = ELF32_R_TYPE(rpnt->r_info);
+
+#if defined (__SUPPORT_LD_DEBUG__)
+ old_val = *reloc_addr;
+#endif
+
+ switch (reloc_type) {
+ case R_METAG_NONE:
+ break;
+ case R_METAG_JMP_SLOT:
+ *reloc_addr += (unsigned long)tpnt->loadaddr;
+ break;
+ default:
+ return -1; /* Calls _dl_exit(1). */
+ }
+
+#if defined (__SUPPORT_LD_DEBUG__)
+ if (_dl_debug_reloc && _dl_debug_detail)
+ _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n",
+ old_val, *reloc_addr, reloc_addr);
+#endif
+
+ return 0;
+}
+
+/* External interface to the generic part of the dynamic linker. */
+
+void
+_dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
+ unsigned long rel_addr,
+ unsigned long rel_size)
+{
+ _dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
+}
+
+int
+_dl_parse_relocation_information(struct dyn_elf *rpnt,
+ unsigned long rel_addr,
+ unsigned long rel_size)
+{
+ return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr,
+ rel_size, _dl_do_reloc);
+}
diff --git a/ldso/ldso/metag/resolve.S b/ldso/ldso/metag/resolve.S
new file mode 100644
index 000000000..8f23a340a
--- /dev/null
+++ b/ldso/ldso/metag/resolve.S
@@ -0,0 +1,51 @@
+/*
+ * Meta dynamic resolver
+ *
+ * Copyright (C) 2013 Imagination Technologies Ltd.
+ *
+ * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
+ *
+ * This function is _not_ called directly. It is jumped to (so no return
+ * address is on the stack) when attempting to use a symbol that has not yet
+ * been resolved. The first time a jump symbol (such as a function call inside
+ * a shared library) is used (before it gets resolved) it will jump here to
+ * _dl_linux_resolve. When we get called the stack looks like this:
+ * reloc_entry
+ * tpnt
+ *
+ * This function saves all the registers then makes the function call
+ * _dl_linux_resolver(tpnt, reloc_entry). _dl_linux_resolver() figures out
+ * where the jump symbol is _really_ supposed to have jumped to and returns
+ * that to us. Once we have that, we overwrite tpnt with this fixed up
+ * address. We then clean up after ourselves, put all the registers back how we
+ * found them, then we jump to the fixed up address, which is where the jump
+ * symbol that got us here really wanted to jump to in the first place.
+ * -Erik Andersen
+ */
+
+ .text
+ .global __dl_linux_resolve
+ .type __dl_linux_resolve,@function
+
+__dl_linux_resolve:
+ !! Save registers on the stack. Do we need to save any more here?
+ MSETL [A0StP++],D0Ar6,D0Ar4,D0Ar2,D0FrT
+ SETL [A0StP++],A0FrP,A1LbP
+ !! Get the args for _dl_linux_resolver off the stack
+ GETL D0Re0,D1Re0,[A0StP+#-(6*8)]
+ GETD D1Ar1,[D0Re0]
+ MOV D0Ar2,D1Re0
+ !! Multiply plt_index by sizeof(Elf32_Rela)
+ MULW D0Ar2,D0Ar2,#12
+ !! Call the resolver
+ CALLR D1RtP,__dl_linux_resolver
+ !! Restore the registers from the stack
+ SUB A0.2,A0StP,#(1*8)
+ GETL A0FrP,A1LbP,[A0.2]
+ SUB A0.2,A0.2,#(4*8)
+ MGETL D0Ar6,D0Ar4,D0Ar2,D0FrT,[A0.2]
+ !! Also take into account args pushed by PLT
+ SUB A0StP,A0StP,#(6*8)
+ !! Jump to the resolved address
+ MOV PC,D0Re0
+ .size __dl_linux_resolve, .-__dl_linux_resolve