* config/default.exp: Define objdump if it is not defined.
authorIan Lance Taylor <ian@airs.com>
Fri, 2 Dec 1994 22:29:20 +0000 (22:29 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 2 Dec 1994 22:29:20 +0000 (22:29 +0000)
* ld-empic/*: New tests to test -membedded-pic code.

ld/testsuite/.Sanitize
ld/testsuite/ld-empic/empic.exp [new file with mode: 0644]
ld/testsuite/ld-empic/relax.t [new file with mode: 0644]
ld/testsuite/ld-empic/relax1.c [new file with mode: 0644]
ld/testsuite/ld-empic/relax2.c [new file with mode: 0644]
ld/testsuite/ld-empic/relax3.c [new file with mode: 0644]
ld/testsuite/ld-empic/relax4.c [new file with mode: 0644]
ld/testsuite/ld-empic/run.c [new file with mode: 0644]
ld/testsuite/ld-empic/runtest1.c [new file with mode: 0644]
ld/testsuite/ld-empic/runtest2.c [new file with mode: 0644]
ld/testsuite/ld-empic/runtesti.s [new file with mode: 0644]

index 0c4d0b2cb3ad6be6273ed1ccf2ba7bc63ea714aa..b42c1a49b52eb7ed30229d706e29421d194d1c73 100644 (file)
@@ -28,6 +28,7 @@ config
 lib
 ld-bootstrap
 ld-cdtest
+ld-empic
 ld-scripts
 ld-shared
 
diff --git a/ld/testsuite/ld-empic/empic.exp b/ld/testsuite/ld-empic/empic.exp
new file mode 100644 (file)
index 0000000..a103cb3
--- /dev/null
@@ -0,0 +1,242 @@
+# Expect script for ld-empic tests
+#   Copyright (C) 1994 Free Software Foundation
+#
+# This file 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+#
+# Written by Ian Lance Taylor (ian@cygnus.com)
+#
+
+# Test the handling of MIPS embedded PIC code.  This test essentially
+# tests the compiler and assembler as well as the linker, since MIPS
+# embedded PIC is a GNU enhancement to standard MIPS tools.
+
+# Embedded PIC is only supported for MIPS ECOFF targets.
+if ![istarget mips*-*-ecoff*] { return }
+
+# Test that relaxation works correctly.  This testsuite was composed
+# (by experimentation) to force the linker to relax twice--that is,
+# the first relaxation pass will force another call to be out of
+# range, requiring a second relaxation pass.
+if ![ld_compile "$CC $CFLAGS -membedded-pic" $srcdir$subdir/relax1.c tmpdir/relax1.o] {
+    return
+}
+if ![ld_compile "$CC $CFLAGS -membedded-pic" $srcdir$subdir/relax2.c tmpdir/relax2.o] {
+    return
+}
+if ![ld_compile "$CC $CFLAGS -membedded-pic" $srcdir$subdir/relax3.c tmpdir/relax3.o] {
+    return
+}
+if ![ld_compile "$CC $CFLAGS -membedded-pic" $srcdir$subdir/relax4.c tmpdir/relax4.o] {
+    return
+}
+
+if ![ld_simple_link $ld tmpdir/relax "--relax -T $srcdir$subdir/relax.t tmpdir/relax1.o tmpdir/relax2.o tmpdir/relax3.o tmpdir/relax4.o"] {
+    fail relax
+} else {
+    # Check that the relaxation produced the correct result.  Check
+    # each bal instruction.  Some will go directly to the start of a
+    # function, which is OK.  Some will form part of the five
+    # instruction expanded call sequence, in which case we compute the
+    # real destination and make sure it is the start of a function.
+    # Some bal instructions are used to locate the start of the
+    # function in order to do position independent addressing into the
+    # text section, in which case we just check that it correctly
+    # computes the start of the function.
+
+    # Get the symbol table.
+    if ![ld_nm $nm tmpdir/relax] {
+       return
+    }
+
+    # Get a disassembly.
+    send_log "$objdump -d tmpdir/relax >tmpdir/relax.dis\n"
+    verbose "$objdump -d tmpdir/relax >tmpdir/relax.dis"
+    catch "exec $objdump -d tmpdir/relax >tmpdir/relax.dis" exec_output
+    if ![string match "" $exec_output] {
+       send_log "$exec_output\n"
+       verbose $exec_output
+       perror "tmpdir/relax: objdump failed"
+       return
+    }
+
+    set balcnt 0
+    set file [open tmpdir/relax.dis r]
+    while { [gets $file line] != -1 } {
+       verbose "$line" 2
+
+       if ![string match "*bal*" $line] {
+           continue
+       }
+
+       verbose "$line"
+
+       incr balcnt
+
+       if ![regexp "^(\[0-9a-fA-F\]+) (<\[a-z+0-9A-Z\]+>)? bal (\[0-9a-fA-F\]+)" $line whole addr label dest] {
+           perror "unrecognized format for $line"
+           return
+       }
+
+       if "0x$addr + 8 != 0x$dest" {
+           # This is a straight function call.  All function calls in
+           # this example are to either foo or bar.
+           if "0x$dest != $nm_output(foo) && 0x$dest != $nm_output(bar)" {
+               send_log "$line\n"
+               fail "relax (bad direct function call)"
+               return
+           }
+       } else {
+           # Pick up the next line.  If it is sll, this is a switch
+           # prologue, and there is not much we can do to test it.
+           # Otherwise, it should be lui, and the next instruction
+           # should be an addiu, followed by an addu to $31.
+           if { [gets $file l] == -1 } {
+               send_log "$line\n"
+               fail "relax (unexpected EOF after bal)"
+               return
+           }
+           verbose $l
+
+           if [string match "*sll*" $l] {
+               continue
+           }
+           if ![regexp "lui (\[\$a-z0-9\]+),(\[0-9\]+)" $l whole reg upper] {
+               send_log "$line\n"
+               send_log "$l\n"
+               fail "relax (could not find expected lui)"
+               return
+           }
+
+           if { [gets $file l] == -1 } {
+               send_log "$line\n"
+               fail "relax (unexpected EOF after lui)"
+               return
+           }
+           verbose "$l"
+           if ![regexp "addiu \\$reg,\\$reg,(\[-0-9\]+)" $l whole lower] {
+               send_log "$line\n"
+               send_log "$l\n"
+               send_log "addiu \\$reg,\\$reg,(\[-0-9\]+)\n"
+               fail "relax (could not find expected addiu)"
+               return
+           }
+
+           if { [gets $file l] == -1 } {
+               send_log "$line\n"
+               fail "relax (unexpected EOF after addiu)"
+               return
+           }
+           verbose "$l"
+           if ![regexp "addu \\$reg,\\$reg,\\\$ra" $l] {
+               send_log "$line\n"
+               send_log "$l\n"
+               fail "relax (could not find expected addu)"
+               return
+           }
+
+           # The next line will be jalr in the case of an expanded
+           # call.  Otherwise, the code is getting the start of the
+           # function, and the next line can be anything.
+
+           if { [gets $file l] == -1 } {
+               send_log "$line\n"
+               fail "relax (unexpected EOF after addu)"
+               return
+           }
+
+           if [string match "*jalr*" $l] {
+               set dest [expr 0x$addr + 8 + ($upper << 16) + $lower]
+               if { $dest != $nm_output(foo) && $dest != $nm_output(bar) } {
+                   send_log "$line\n"
+                   fail "relax (bad expanded function call)"
+                   return
+               }
+           } else {
+               set dest [expr ($upper << 16) + $lower]
+               if ![regexp "<\[a-z\]+\\+(\[0-9a-fA-F\]+)>" $label whole offset] {
+                   send_log "$line\n"
+                   fail "relax (unrecognized label)"
+                   return
+               }
+               if "0x$offset + 8 != - $dest" {
+                   send_log "$line\n"
+                   fail "relax (bad function start: 0x$offset + 8 != - $dest)"
+                   return
+               }
+           }
+       }
+    }
+
+    close $file
+
+    if {$balcnt < 10} {
+       fail "relax (not enough branches)"
+    } else {
+       verbose "$balcnt bal instructions"
+       pass relax
+    }
+}
+
+# We now test actually running embedded MIPS PIC code.  This can only
+# be done on a MIPS host with the same endianness as our target.
+if [istarget mipsel-*-*] {
+    if ![ishost mips*-*-ultrix*] {
+       return
+    }
+} else {
+    if ![ishost mips*-*-irix*] {
+       return
+    }
+}
+
+# Compile the program which will run the test.  This code must be
+# compiled for the host, not the target.
+send_log "$CC_FOR_HOST $CFLAGS_FOR_HOST -o tmpdir/run $srcdir$subdir/run.c\n"
+verbose "$CC_FOR_HOST $CFLAGS_FOR_HOST -o tmpdir/run $srcdir$subdir/run.c"
+catch "exec $CC_FOR_HOST $CFLAGS_FOR_HOST -o tmpdir/run $srcdir$subdir/run.c" exec_output
+if ![string match "" $exec_output] {
+    send_log "$exec_output\n"
+    verbose "$exec_output"
+    perror "$srcdir$subdir/run.c: compilation failed"
+    return
+}
+
+# Compile and link the test.
+if ![ld_compile "$CC $CFLAGS -membedded-pic" $srcdir$subdir/runtesti.s tmpdir/runtesti.o] {
+    return
+}
+if ![ld_compile "$CC $CFLAGS -membedded-pic" $srcdir$subdir/runtest1.c tmpdir/runtest1.o] {
+    return
+}
+if ![ld_compile "$CC $CFLAGS -membedded-pic" $srcdir$subdir/runtest2.c tmpdir/runtest2.o] {
+    return
+}
+if ![ld_simple_link $ld tmpdir/runtest "--embedded-relocs tmpdir/runtesti.o tmpdir/runtest1.o tmpdir/runtest2.o"] {
+    fail "run embedded PIC code (link)"
+} else {
+    # Now run the test.
+    send_log "tmpdir/run tmpdir/runtest\n"
+    verbose "tmpdir/run tmpdir/runtest"
+    catch "exec tmpdir/run tmpdir/runtest" exec_output
+    if [string match "*ran and returned 0*" $exec_output] {
+       send_log "$exec_output\n"
+       verbose "$exec_output"
+       pass "run embedded PIC code"
+    } else {
+       send_log "$exec_output\n"
+       verbose "$exec_output"
+       fail "run embedded PIC code"
+    }
+}
diff --git a/ld/testsuite/ld-empic/relax.t b/ld/testsuite/ld-empic/relax.t
new file mode 100644 (file)
index 0000000..8c18b69
--- /dev/null
@@ -0,0 +1,49 @@
+OUTPUT_FORMAT("ecoff-bigmips")
+SECTIONS
+{
+  .foo 0x30 : {
+    tmpdir/relax3.o(.text)
+    tmpdir/relax1.o(.text)
+  }
+  .text  0x20000 : {
+     _ftext = . ;
+    *(.init)
+     eprol  =  .;
+    tmpdir/relax4.o(.text)
+    *(.text)
+    *(.fini)
+     etext  =  .;
+     _etext  =  .;
+  }
+  .rdata  . : {
+    *(.rdata)
+  }
+   _fdata = .;
+  .data  . : {
+    *(.data)
+    CONSTRUCTORS
+  }
+   _gp = . + 0x8000;
+  .lit8  . : {
+    *(.lit8)
+  }
+  .lit4  . : {
+    *(.lit4)
+  }
+  .sdata  . : {
+    *(.sdata)
+  }
+   edata  =  .;
+   _edata  =  .;
+   _fbss = .;
+  .sbss  . : {
+    *(.sbss)
+    *(.scommon)
+  }
+  .bss  . : {
+    *(.bss)
+    *(COMMON)
+  }
+   end = .;
+   _end = .;
+}
diff --git a/ld/testsuite/ld-empic/relax1.c b/ld/testsuite/ld-empic/relax1.c
new file mode 100644 (file)
index 0000000..20ec39e
--- /dev/null
@@ -0,0 +1,22 @@
+/* First source file in relaxation test.  */
+
+extern int bar ();
+static int foo2 ();
+
+int foo (int i)
+{
+  switch (i)
+    {
+    case 0: bar (0); break;
+    case 1: bar (1); break;
+    case 2: bar (2); break;
+    case 3: bar (3); break;
+    case 4: bar (foo2); break;
+    case 5: bar (bar); break;
+    }
+  while (1)
+    if (i)
+      return bar ();
+}
+
+static int foo2 () { }
diff --git a/ld/testsuite/ld-empic/relax2.c b/ld/testsuite/ld-empic/relax2.c
new file mode 100644 (file)
index 0000000..58854a0
--- /dev/null
@@ -0,0 +1,19 @@
+/* Second source file in relaxation test.  */
+
+int bar2 ()
+{
+  int i;
+
+  for (i = 0; i < 100; i++)
+    foo ();
+  return foo () + foo () + foo () + foo ();
+}
+
+int bar (int i)
+{
+  while (1)
+    if (i)
+      return foo ();
+    else
+      return foo ();
+}
diff --git a/ld/testsuite/ld-empic/relax3.c b/ld/testsuite/ld-empic/relax3.c
new file mode 100644 (file)
index 0000000..1aaa532
--- /dev/null
@@ -0,0 +1,3 @@
+/* Third source file in relaxation test.  */
+
+int quux () { return 0; }
diff --git a/ld/testsuite/ld-empic/relax4.c b/ld/testsuite/ld-empic/relax4.c
new file mode 100644 (file)
index 0000000..21cfb05
--- /dev/null
@@ -0,0 +1,3 @@
+/* Fourth source file in relaxation test.  */
+
+int xyzzy () { return 0; }
diff --git a/ld/testsuite/ld-empic/run.c b/ld/testsuite/ld-empic/run.c
new file mode 100644 (file)
index 0000000..9a0377e
--- /dev/null
@@ -0,0 +1,160 @@
+/* Load and run a MIPS position independent ECOFF file.
+   Written by Ian Lance Taylor <ian@cygnus.com>
+   Public domain.  */
+
+/* This program will load an ECOFF file into memory and execute it.
+   The file must have been compiled using the GNU -membedded-pic
+   switch to produce position independent code.  This will only work
+   if this program is run on a MIPS system with the same endianness as
+   the ECOFF file.  The ECOFF file must be complete.  System calls may
+   not work correctly.
+
+   There are further restrictions on the file (they could be removed
+   by doing some additional programming).  The file must be aligned
+   such that it does not require any gaps introduced in the data
+   segment; the GNU linker produces such files by default.  However,
+   the file must not assume that the text or data segment is aligned
+   on a page boundary.  The start address must be at the start of the
+   text segment.
+
+   The ECOFF file is run by calling it as though it were a function.
+   The address of the data segment is passed as the only argument.
+   The file is expected to return an integer value, which will be
+   printed.  */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* Structures used in ECOFF files.  We assume that a short is two
+   bytes and an int is four bytes.  This is not much of an assumption,
+   since we already assume that we are running on a MIPS host with the
+   same endianness as the file we are examining.  */
+
+struct ecoff_filehdr {
+  unsigned short f_magic;      /* magic number                 */
+  unsigned short f_nscns;      /* number of sections           */
+  unsigned int f_timdat;       /* time & date stamp            */
+  unsigned int f_symptr;       /* file pointer to symtab       */
+  unsigned int f_nsyms;                /* number of symtab entries     */
+  unsigned short f_opthdr;     /* sizeof(optional hdr)         */
+  unsigned short f_flags;      /* flags                        */
+};
+
+struct ecoff_aouthdr
+{
+  unsigned short magic;                /* type of file                         */
+  unsigned short vstamp;       /* version stamp                        */
+  unsigned int tsize;          /* text size in bytes, padded to FW bdry*/
+  unsigned int dsize;          /* initialized data "  "                */
+  unsigned int bsize;          /* uninitialized data "   "             */
+  unsigned int entry;          /* entry pt.                            */
+  unsigned int text_start;     /* base of text used for this file */
+  unsigned int data_start;     /* base of data used for this file */
+  unsigned int bss_start;      /* base of bss used for this file */
+  unsigned int gprmask;                /* ?? */
+  unsigned int cprmask[4];     /* ?? */
+  unsigned int gp_value;       /* value for gp register */
+};
+
+#define ECOFF_SCNHDR_SIZE (40)
+
+static void
+die (s)
+     char *s;
+{
+  perror (s);
+  exit (1);
+}
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  FILE *f;
+  struct stat s;
+  char *z;
+  struct ecoff_filehdr *fh;
+  struct ecoff_aouthdr *ah;
+  unsigned int toff;
+  char *t, *d;
+  int (*pfn) ();
+  int ret;
+
+  if (argc != 2)
+    {
+      fprintf (stderr, "Usage: %s file\n", argv[0]);
+      exit (1);
+    }
+
+  f = fopen (argv[1], "r");
+  if (f == NULL)
+    die (argv[1]);
+
+  if (stat (argv[1], &s) < 0)
+    die ("stat");
+
+  z = (char *) malloc (s.st_size);
+  if (z == NULL)
+    die ("malloc");
+
+  if (fread (z, 1, s.st_size, f) != s.st_size)
+    die ("fread");
+
+  /* We need to figure out the start of the text segment, which is the
+     location we are going to call, and the start of the data segment,
+     which we are going to pass as an argument.  We also need the size
+     and start address of the bss segment.  This information is all in
+     the ECOFF a.out header.  */
+
+  fh = (struct ecoff_filehdr *) z;
+  if (fh->f_opthdr != sizeof (struct ecoff_aouthdr))
+    {
+      fprintf (stderr, "%s: unexpected opthdr size: is %u, want %u\n",
+              argv[1], (unsigned int) fh->f_opthdr,
+              (unsigned int) sizeof (struct ecoff_aouthdr));
+      exit (1);
+    }
+
+  ah = (struct ecoff_aouthdr *) (z + sizeof (struct ecoff_filehdr));
+  if (ah->magic != 0413)
+    {
+      fprintf (stderr, "%s: bad aouthdr magic number 0%o (want 0413)\n",
+              argv[1], (unsigned int) ah->magic);
+      exit (1);
+    }
+
+  /* We should clear the bss segment at this point.  This is the
+     ah->bsize bytes starting at ah->bss_start, To do this correctly,
+     we would have to make sure our memory block is large enough.  It
+     so happens that our test case does not have any additional pages
+     for the bss segment--it is contained within the data segment.
+     So, we don't bother.  */
+  if (ah->bsize != 0)
+    {
+      fprintf (stderr,
+              "%s: bss segment is %u bytes; non-zero sizes not supported\n",
+              argv[1], ah->bsize);
+      exit (1);
+    }
+
+  /* The text section starts just after all the headers, rounded to a
+     16 byte boundary.  */
+  toff = (sizeof (struct ecoff_filehdr) + sizeof (struct ecoff_aouthdr)
+         + fh->f_nscns * ECOFF_SCNHDR_SIZE);
+  toff += 15;
+  toff &=~ 15;
+  t = z + toff;
+
+  /* The tsize field gives us the start of the data segment.  */
+  d = z + ah->tsize;
+
+  /* Call the code as a function.  */
+  pfn = (int (*) ()) t;
+  ret = (*pfn) (d);
+
+  printf ("%s ran and returned %d\n", argv[1], ret);
+
+  exit (0);
+}
diff --git a/ld/testsuite/ld-empic/runtest1.c b/ld/testsuite/ld-empic/runtest1.c
new file mode 100644 (file)
index 0000000..f9ab6eb
--- /dev/null
@@ -0,0 +1,117 @@
+/* First C source file for actual execution test.  */
+
+/* The main point of this test is to make sure that the code and data
+   are truly position independent.  We statically initialize several
+   global variables, and make sure that they are correctly adjusted at
+   runtime.  */
+
+int i = 1;
+int j = 0;
+extern int k;
+int l;
+char small_buf[] = "aaaa";
+char *small_pointer = small_buf;
+char big_buf[] = "aaaaaaaaaaaaaaaa";
+char *big_pointer = big_buf;
+
+extern int bar ();
+int (*pbar) () = bar;
+
+static int
+foo2 (arg)
+     int arg;
+{
+  l = arg;
+  return i + j;
+}
+
+int (*pfoo2) () = foo2;
+
+int
+chkstr (z, c)
+     char *z;
+     int c;
+{
+  /* Switch statements need extra effort to be position independent,
+     so we run one here, even though most of the cases will never be
+     taken.  */
+  switch (c)
+    {
+    case 1:
+    case 2:
+    case 3:
+      return i - 1;
+    case 4:
+      break;
+    case 5:
+    case 6:
+    case 7:
+    case 8:
+    case 9:
+      return i * j;
+    case 10:
+    case 11:
+    case 12:
+    case 13:
+    case 14:
+    case 15:
+      return j;
+    case 16:
+      break;
+    default:
+      return 0;
+    }
+
+  while (c-- != 0)
+    if (*z++ != 'a')
+      return 0;
+
+  return *z == '\0';
+}
+
+/* This function is called by the assembler startup routine.  It tries
+   to test that everything was correctly initialized.  It returns 0 on
+   success, something else on failure.  */
+
+int
+foo ()
+{
+  if (i != 1)
+    return 1;
+  if (j != 0)
+    return 2;
+  if (! chkstr (small_buf, 4))
+    return 3;
+  if (! chkstr (small_pointer, 4))
+    return 4;
+  if (! chkstr (big_buf, 16))
+    return 5;
+  if (! chkstr (big_pointer, 16))
+    return 6;
+
+  if (l != 0)
+    return 7;
+  if (foo2 (1) != 1)
+    return 8;
+  if (l != 1)
+    return 9;
+  if ((*pfoo2) (2) != 1)
+    return 10;
+  if (l != 2)
+    return 11;
+
+  if (bar (1) != 0)
+    return 12;
+  if (bar (-1) != 1)
+    return 13;
+  if ((*pbar) (0xa5a5a5a5) != -1)
+    return 14;
+  if (k != 0xa5a5a5a5)
+    return 15;
+  if ((*pbar) (0) != 0xa5a5a5a5)
+    return 16;
+  if (k != 0)
+    return 17;
+
+  return 0;
+}
diff --git a/ld/testsuite/ld-empic/runtest2.c b/ld/testsuite/ld-empic/runtest2.c
new file mode 100644 (file)
index 0000000..000525f
--- /dev/null
@@ -0,0 +1,26 @@
+/* Second C source file for actual execution test.  */
+
+int k;
+extern int i;
+extern int j;
+extern char small_buf[];
+extern char *small_pointer;
+
+extern int chkstr ();
+
+int
+bar (n)
+     int n;
+{
+  int r;
+
+  if (i != 1
+      || j != 0
+      || ! chkstr (small_buf, 4)
+      || ! chkstr (small_pointer, 4))
+    return k + 1;
+
+  r = k;
+  k = n;
+  return r;
+}
diff --git a/ld/testsuite/ld-empic/runtesti.s b/ld/testsuite/ld-empic/runtesti.s
new file mode 100644 (file)
index 0000000..efa1953
--- /dev/null
@@ -0,0 +1,94 @@
+# Assembler initialization code for actual execution test.
+       
+# This code becomes the start of the execution test program.  It is
+# responsible for initializing the static data, invoking the C code,
+# and returning the result.  It is called as though it were a C
+# function with an argument of the address of the data segment.
+
+# We need to know the value of _ftext and _fdata at link time, but we
+# have no way to actually get that at runtime.  This is because when
+# this code is compiled with -membedded-pic, the la instruction will
+# be turned into an addiu $gp instruction.  We work around this by
+# storing the information in words in the .data section.  We then load
+# the values of these words *before* doing the runtime relocation.
+       .sdata
+text_start:
+       .word   _ftext
+data_start:
+       .word   _fdata
+
+       .globl  start
+       .text
+start:
+       # Grab some space on the stack, just as though we were a real
+       # function.
+       addiu   $sp,$sp,-8
+       sw      $31,0($sp)
+
+       # Save the $gp register, and set it up for our data section.
+       sw      $gp,4($sp)
+
+       addu    $gp,$4,0x8000           # macro
+
+       # The start of the data segment is in $4.
+
+       # Get the address of start into $5 in a position independent
+       # fashion.
+       .set    noreorder
+       $LF1 = . + 8
+       bal     $LF1
+       la      $5,start-$LF1           # macro
+       .set    reorder
+       addu    $5,$5,$31
+
+       # Now get the address of _ftext into $6.
+       la      $6,_ftext-start         # macro
+       addu    $6,$6,$5
+
+       # Get the value of _ftext used to link into $7.
+       lw      $7,text_start           # macro
+
+       # Get the value of _fdata used to link into $8.
+       lw      $8,data_start           # macro
+
+       # Get the address of __runtime_reloc_start into $9.
+       la      $9,__runtime_reloc_start-start  # macro
+       addu    $9,$9,$5
+
+       # Get the address of __runtime_reloc_stop into $10.
+       la      $10,__runtime_reloc_stop-start  # macro
+       addu    $10,$10,$5
+
+       # The words between $9 and $10 are the runtime initialization
+       # instructions.  Step through and relocate them.  First set
+       # $11 and $12 to the values to add to text and data sections,
+       # respectively.
+       subu    $11,$6,$7
+       subu    $12,$4,$8
+
+1:     
+       bge     $9,$10,3f               # macro
+       lw      $13,0($9)
+       and     $14,$13,0xfffffffe      # macro
+       move    $15,$11
+       beq     $13,$14,2f
+       move    $15,$12
+2:     
+       addu    $14,$14,$4
+       lw      $24,0($14)
+       addu    $24,$24,$15
+       sw      $24,0($14)
+       addiu   $9,$9,4
+       b       1b
+3:     
+
+       # Now the statically initialized data has been relocated
+       # correctly, and we can call the C code which does the actual
+       # testing.
+       bal     foo
+
+       # We return the value returned by the C code.
+       lw      $31,0($sp)
+       lw      $gp,4($sp)
+       addu    $sp,$sp,8
+       j       $31