From 3c2476067c4b9c7db494af355e575b607fededee Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 2 Dec 1994 22:29:20 +0000 Subject: [PATCH] * config/default.exp: Define objdump if it is not defined. * ld-empic/*: New tests to test -membedded-pic code. --- ld/testsuite/.Sanitize | 1 + ld/testsuite/ld-empic/empic.exp | 242 +++++++++++++++++++++++++++++++ ld/testsuite/ld-empic/relax.t | 49 +++++++ ld/testsuite/ld-empic/relax1.c | 22 +++ ld/testsuite/ld-empic/relax2.c | 19 +++ ld/testsuite/ld-empic/relax3.c | 3 + ld/testsuite/ld-empic/relax4.c | 3 + ld/testsuite/ld-empic/run.c | 160 ++++++++++++++++++++ ld/testsuite/ld-empic/runtest1.c | 117 +++++++++++++++ ld/testsuite/ld-empic/runtest2.c | 26 ++++ ld/testsuite/ld-empic/runtesti.s | 94 ++++++++++++ 11 files changed, 736 insertions(+) create mode 100644 ld/testsuite/ld-empic/empic.exp create mode 100644 ld/testsuite/ld-empic/relax.t create mode 100644 ld/testsuite/ld-empic/relax1.c create mode 100644 ld/testsuite/ld-empic/relax2.c create mode 100644 ld/testsuite/ld-empic/relax3.c create mode 100644 ld/testsuite/ld-empic/relax4.c create mode 100644 ld/testsuite/ld-empic/run.c create mode 100644 ld/testsuite/ld-empic/runtest1.c create mode 100644 ld/testsuite/ld-empic/runtest2.c create mode 100644 ld/testsuite/ld-empic/runtesti.s diff --git a/ld/testsuite/.Sanitize b/ld/testsuite/.Sanitize index 0c4d0b2cb3..b42c1a49b5 100644 --- a/ld/testsuite/.Sanitize +++ b/ld/testsuite/.Sanitize @@ -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 index 0000000000..a103cb31ea --- /dev/null +++ b/ld/testsuite/ld-empic/empic.exp @@ -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 index 0000000000..8c18b69a86 --- /dev/null +++ b/ld/testsuite/ld-empic/relax.t @@ -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 index 0000000000..20ec39efc9 --- /dev/null +++ b/ld/testsuite/ld-empic/relax1.c @@ -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 index 0000000000..58854a03aa --- /dev/null +++ b/ld/testsuite/ld-empic/relax2.c @@ -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 index 0000000000..1aaa532bc2 --- /dev/null +++ b/ld/testsuite/ld-empic/relax3.c @@ -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 index 0000000000..21cfb05b67 --- /dev/null +++ b/ld/testsuite/ld-empic/relax4.c @@ -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 index 0000000000..9a0377e02e --- /dev/null +++ b/ld/testsuite/ld-empic/run.c @@ -0,0 +1,160 @@ +/* Load and run a MIPS position independent ECOFF file. + Written by Ian Lance Taylor + 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 +#include +#include + +/* 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 index 0000000000..f9ab6eb668 --- /dev/null +++ b/ld/testsuite/ld-empic/runtest1.c @@ -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 index 0000000000..000525f11e --- /dev/null +++ b/ld/testsuite/ld-empic/runtest2.c @@ -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 index 0000000000..efa195301f --- /dev/null +++ b/ld/testsuite/ld-empic/runtesti.s @@ -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