* config/default.exp: Define objdump if it is not defined.

* ld-empic/*: New tests to test -membedded-pic code.
This commit is contained in:
Ian Lance Taylor 1994-12-02 22:29:20 +00:00
parent dc5df17b8a
commit 3c2476067c
11 changed files with 736 additions and 0 deletions

View File

@ -28,6 +28,7 @@ config
lib
ld-bootstrap
ld-cdtest
ld-empic
ld-scripts
ld-shared

View File

@ -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"
}
}

View File

@ -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 = .;
}

View File

@ -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 () { }

View File

@ -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 ();
}

View File

@ -0,0 +1,3 @@
/* Third source file in relaxation test. */
int quux () { return 0; }

View File

@ -0,0 +1,3 @@
/* Fourth source file in relaxation test. */
int xyzzy () { return 0; }

160
ld/testsuite/ld-empic/run.c Normal file
View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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