* ld-empic/*: New tests to test -membedded-pic code.
lib
ld-bootstrap
ld-cdtest
+ld-empic
ld-scripts
ld-shared
--- /dev/null
+# 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"
+ }
+}
--- /dev/null
+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 = .;
+}
--- /dev/null
+/* 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 () { }
--- /dev/null
+/* 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 ();
+}
--- /dev/null
+/* Third source file in relaxation test. */
+
+int quux () { return 0; }
--- /dev/null
+/* Fourth source file in relaxation test. */
+
+int xyzzy () { return 0; }
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+# 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