* lib/ld-lib.exp (run_dump_test): Add -L$srcdir/$subdir.
[deliverable/binutils-gdb.git] / ld / testsuite / lib / ld-lib.exp
index 6c0c7f1daa084c56ca4cf75a15dcb63d44b09142..418c00013db3d66bcaffe9b07f2055cb3bc50faf 100644 (file)
@@ -1,3 +1,21 @@
+# Support routines for LD testsuite.
+#   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
 #
 # default_ld_version 
 #      extract and print the version number of ld
@@ -265,7 +283,7 @@ proc default_ld_assemble { as source object } {
 # default_ld_nm
 #      run nm on a file, putting the result in the array nm_output
 #
-proc default_ld_nm { nm object } {
+proc default_ld_nm { nm nmflags object } {
     global NMFLAGS
     global nm_output
     global host_triplet
@@ -281,15 +299,15 @@ proc default_ld_nm { nm object } {
 
     if ![info exists NMFLAGS] { set NMFLAGS "" }
 
-    verbose -log "$nm $NMFLAGS $object >tmpdir/nm.out"
+    verbose -log "$nm $NMFLAGS $nmflags $object >tmpdir/nm.out"
 
-    catch "exec $nm $NMFLAGS $object >tmpdir/nm.out" exec_output
+    catch "exec $nm $NMFLAGS $nmflags $object >tmpdir/nm.out" exec_output
     set exec_output [prune_warnings $exec_output]
     if [string match "" $exec_output] then {
        set file [open tmpdir/nm.out r]
        while { [gets $file line] != -1 } {
            verbose "$line" 2
-           if [regexp "^(\[0-9a-fA-F\]+) \[a-zA-Z0-9\] (.+)$" $line whole value name] {
+           if [regexp "^(\[0-9a-fA-F\]+) \[a-zA-Z0-9\] \\.*(.+)$" $line whole value name] {
                 set name [string trimleft $name "_"]
                verbose "Setting nm_output($name) to 0x$value" 2
                set nm_output($name) 0x$value
@@ -304,6 +322,29 @@ proc default_ld_nm { nm object } {
     }
 }
 
+#
+# is_elf_format
+#      true if the object format is known to be ELF
+#
+proc is_elf_format {} {
+    if { ![istarget *-*-sysv4*] \
+        && ![istarget *-*-unixware*] \
+        && ![istarget *-*-elf*] \
+        && ![istarget *-*-eabi*] \
+        && ![istarget *-*-linux*] \
+        && ![istarget *-*-irix5*] \
+        && ![istarget *-*-irix6*] \
+        && ![istarget *-*-solaris2*] } {
+       return 0
+    }
+
+    if { [istarget *-*-linux*aout*] \
+        || [istarget *-*-linux*oldld*] } {
+       return 0
+    }
+    return 1
+}
+
 #
 # simple_diff
 #      compares two files line-by-line
@@ -375,6 +416,541 @@ proc simple_diff { file_1 file_2 } {
     }
 }
 
+# run_dump_test FILE 
+# Copied from gas testsuite, tweaked and further extended.
+#
+# Assemble a .s file, then run some utility on it and check the output.
+# 
+# There should be an assembly language file named FILE.s in the test
+# suite directory, and a pattern file called FILE.d.  `run_dump_test'
+# will assemble FILE.s, run some tool like `objdump', `objcopy', or
+# `nm' on the .o file to produce textual output, and then analyze that
+# with regexps.  The FILE.d file specifies what program to run, and
+# what to expect in its output.
+#
+# The FILE.d file begins with zero or more option lines, which specify
+# flags to pass to the assembler, the program to run to dump the
+# assembler's output, and the options it wants.  The option lines have
+# the syntax:
+# 
+#         # OPTION: VALUE
+# 
+# OPTION is the name of some option, like "name" or "objdump", and
+# VALUE is OPTION's value.  The valid options are described below.
+# Whitespace is ignored everywhere, except within VALUE.  The option
+# list ends with the first line that doesn't match the above syntax
+# (hmm, not great for error detection).
+#
+# The interesting options are:
+# 
+#   name: TEST-NAME
+#      The name of this test, passed to DejaGNU's `pass' and `fail'
+#       commands.  If omitted, this defaults to FILE, the root of the
+#       .s and .d files' names.
+# 
+#   as: FLAGS
+#      When assembling, pass FLAGS to the assembler.
+#       If assembling several files, you can pass different assembler
+#       options in the "source" directives.  See below.
+#
+#   ld: FLAGS
+#       Link assembled files using FLAGS, in the order of the "source"
+#       directives, when using multiple files.
+#
+#   objcopy_linked_file: FLAGS
+#      Run objcopy on the linked file with the specified flags.
+#      This lets you transform the linked file using objcopy, before the
+#      result is analyzed by an analyzer program specified below (which
+#      may in turn *also* be objcopy).
+#
+#   PROG: PROGRAM-NAME
+#       The name of the program to run to analyze the .o file produced
+#       by the assembler or the linker output.  This can be omitted;
+#       run_dump_test will guess which program to run by seeing which of
+#       the flags options below is present.
+#
+#   objdump: FLAGS
+#   nm: FLAGS
+#   objcopy: FLAGS
+#      Use the specified program to analyze the assembler or linker
+#       output file, and pass it FLAGS, in addition to the output name.
+#
+#   source: SOURCE [FLAGS]
+#      Assemble the file SOURCE.s using the flags in the "as" directive
+#       and the (optional) FLAGS.  If omitted, the source defaults to
+#       FILE.s.
+#       This is useful if several .d files want to share a .s file.
+#       More than one "source" directive can be given, which is useful
+#       when testing linking.
+#
+#   xfail: TARGET
+#       The test is expected to fail on TARGET.  This may occur more than
+#       once.
+#
+#   target: TARGET
+#       Only run the test for TARGET.  This may occur more than once; the
+#       target being tested must match at least one.
+#
+#   notarget: TARGET
+#       Do not run the test for TARGET.  This may occur more than once;
+#       the target being tested must not match any of them.
+#
+#   error: REGEX
+#      An error with message matching REGEX must be emitted for the test
+#      to pass.  The PROG, objdump, nm and objcopy options have no
+#      meaning and need not supplied if this is present.
+#
+# Each option may occur at most once unless otherwise mentioned.
+#
+# After the option lines come regexp lines.  `run_dump_test' calls
+# `regexp_diff' to compare the output of the dumping tool against the
+# regexps in FILE.d.  `regexp_diff' is defined later in this file; see
+# further comments there.
+
+proc run_dump_test { name } {
+    global subdir srcdir
+    global OBJDUMP NM AS OBJCOPY READELF LD
+    global OBJDUMPFLAGS NMFLAGS ASFLAGS OBJCOPYFLAGS READELFFLAGS LDFLAGS
+    global host_triplet runtests
+
+    if [string match "*/*" $name] {
+       set file $name
+       set name [file tail $name]
+    } else {
+       set file "$srcdir/$subdir/$name"
+    }
+
+    if ![runtest_file_p $runtests $name] then {
+       return
+    }
+
+    set opt_array [slurp_options "${file}.d"]
+    if { $opt_array == -1 } {
+       perror "error reading options from $file.d"
+       unresolved $subdir/$name
+       return
+    }
+    set dumpfile tmpdir/dump.out
+    set run_ld 0
+    set run_objcopy 0
+    set opts(as) {}
+    set opts(ld) {}
+    set opts(xfail) {}
+    set opts(target) {}
+    set opts(notarget) {}
+    set opts(objdump) {}
+    set opts(nm) {}
+    set opts(objcopy) {}
+    set opts(readelf) {}
+    set opts(name) {}
+    set opts(PROG) {}
+    set opts(source) {}
+    set opts(error) {}
+    set opts(objcopy_linked_file) {}
+    set asflags(${file}.s) {}
+
+    foreach i $opt_array {
+       set opt_name [lindex $i 0]
+       set opt_val [lindex $i 1]
+       if ![info exists opts($opt_name)] {
+           perror "unknown option $opt_name in file $file.d"
+           unresolved $subdir/$name
+           return
+       }
+
+       switch -- $opt_name {
+           xfail {}
+           target {}
+           notarget {}
+           source {
+               # Move any source-specific as-flags to a separate array to
+               # simplify processing.
+               if { [llength $opt_val] > 1 } {
+                   set asflags([lindex $opt_val 0]) [lrange $opt_val 1 end]
+                   set opt_val [lindex $opt_val 0]
+               } else {
+                   set asflags($opt_val) {}
+               }
+           }
+           default {
+               if [string length $opts($opt_name)] {
+                   perror "option $opt_name multiply set in $file.d"
+                   unresolved $subdir/$name
+                   return
+               }
+
+               # A single "# ld:" with no options should do the right thing.
+               if { $opt_name == "ld" } {
+                   set run_ld 1
+               }
+               # Likewise objcopy_linked_file.
+               if { $opt_name == "objcopy_linked_file" } {
+                   set run_objcopy 1
+               }
+           }
+       }
+       set opts($opt_name) [concat $opts($opt_name) $opt_val]
+    }
+
+    # Decide early whether we should run the test for this target.
+    if { [llength $opts(target)] > 0 } {
+       set targmatch 0
+       foreach targ $opts(target) {
+           if [istarget $targ] {
+               set targmatch 1
+               break
+           }
+       }
+       if { $targmatch == 0 } {
+           return
+       }
+    }
+    foreach targ $opts(notarget) {
+       if [istarget $targ] {
+           return
+       }
+    }
+
+    if {$opts(PROG) != ""} {
+       switch -- $opts(PROG) {
+           objdump
+               { set program objdump }
+           nm
+               { set program nm }
+           objcopy
+               { set program objcopy }
+           readelf
+               { set program readelf }
+           default
+               { perror "unrecognized program option $opts(PROG) in $file.d"
+                 unresolved $subdir/$name
+                 return }
+       }
+    } elseif { $opts(error) != "" } {
+       # It's meaningless to require an output-testing method when we
+       # expect an error.  For simplicity, we fake an arbitrary method.
+       set program "nm"
+    } else {
+       # Guess which program to run, by seeing which option was specified.
+       set program ""
+       foreach p {objdump objcopy nm readelf} {
+           if {$opts($p) != ""} {
+               if {$program != ""} {
+                   perror "ambiguous dump program in $file.d"
+                   unresolved $subdir/$name
+                   return
+               } else {
+                   set program $p
+               }
+           }
+       }
+       if {$program == ""} {
+           perror "dump program unspecified in $file.d"
+           unresolved $subdir/$name
+           return
+       }
+    }
+
+    set progopts1 $opts($program)
+    eval set progopts \$[string toupper $program]FLAGS
+    eval set binary \$[string toupper $program]
+    if { $opts(name) == "" } {
+       set testname "$subdir/$name"
+    } else {
+       set testname $opts(name)
+    }
+
+    if { $opts(source) == "" } {
+       set sourcefiles [list ${file}.s]
+    } else {
+       set sourcefiles {}
+       foreach sf $opts(source) {
+           lappend sourcefiles "$srcdir/$subdir/$sf"
+           # Must have asflags indexed on source name.
+           set asflags($srcdir/$subdir/$sf) $asflags($sf)
+       }
+    }
+
+    # Time to setup xfailures.
+    foreach targ $opts(xfail) {
+       setup_xfail $targ
+    }
+
+    # Assemble each file.
+    set objfiles {}
+    for { set i 0 } { $i < [llength $sourcefiles] } { incr i } {
+       set sourcefile [lindex $sourcefiles $i]
+
+       set objfile "tmpdir/dump$i.o"
+       lappend objfiles $objfile
+       set cmd "$AS $ASFLAGS $opts(as) $asflags($sourcefile) -o $objfile $sourcefile"
+
+       send_log "$cmd\n"
+       set cmdret [catch "exec $cmd" comp_output]
+       set comp_output [prune_warnings $comp_output]
+
+       # We accept errors at assembly stage too, unless we're supposed to
+       # link something.
+       if { $cmdret != 0 || ![string match "" $comp_output] } then {
+           send_log "$comp_output\n"
+           verbose "$comp_output" 3
+           if { $opts(error) != "" && $run_ld == 0 } {
+               if [regexp $opts(error) $comp_output] {
+                   pass $testname
+                   return
+               }
+           }
+           fail $testname
+           return
+       }
+    }
+
+    # Perhaps link the file(s).
+    if { $run_ld } {
+       set objfile "tmpdir/dump"
+
+       # Add -L$srcdir/$subdir so that the linker command can use
+       # linker scripts in the source directory.
+       set cmd "$LD $LDFLAGS -L$srcdir/$subdir \
+                  $opts(ld) -o $objfile $objfiles"
+
+       send_log "$cmd\n"
+       set cmdret [catch "exec $cmd" comp_output]
+       set comp_output [prune_warnings $comp_output]
+
+       if { $cmdret != 0 || ![string match "" $comp_output] } then {
+           verbose -log "failed with: <$comp_output>, expected: <$opts(error)>"
+           send_log "$comp_output\n"
+           verbose "$comp_output" 3
+           if { $opts(error) != "" && $run_objcopy == 0 } {
+               if [regexp $opts(error) $comp_output] {
+                   pass $testname
+                   return
+               }
+           }
+           fail $testname
+           return
+       }
+
+       if { $run_objcopy } {
+           set infile $objfile
+           set objfile "tmpdir/dump1"
+
+           # Note that we don't use OBJCOPYFLAGS here; any flags must be
+           # explicitly specified.
+           set cmd "$OBJCOPY $opts(objcopy_linked_file) $infile $objfile"
+
+           send_log "$cmd\n"
+           set cmdret [catch "exec $cmd" comp_output]
+           set comp_output [prune_warnings $comp_output]
+
+           if { $cmdret != 0 || ![string match "" $comp_output] } then {
+               verbose -log "failed with: <$comp_output>, expected: <$opts(error)>"
+               send_log "$comp_output\n"
+               verbose "$comp_output" 3
+               if { $opts(error) != "" } {
+                   if [regexp $opts(error) $comp_output] {
+                       pass $testname
+                       return
+                   }
+               }
+               fail $testname
+               return
+           }
+       }
+    } else {
+       set objfile "tmpdir/dump0.o"
+    }
+
+    # We must not have expected failure if we get here.
+    if { $opts(error) != "" } {
+       fail $testname
+       return
+    }
+
+    if { [which $binary] == 0 } {
+       untested $testname
+       return
+    }
+
+    if { $progopts1 == "" } { set $progopts1 "-r" }
+    verbose "running $binary $progopts $progopts1" 3
+
+    # Objcopy, unlike the other two, won't send its output to stdout,
+    # so we have to run it specially.
+    if { $program == "objcopy" } {
+       set cmd "$binary $progopts $progopts1 $objfile $dumpfile"
+       send_log "$cmd\n"
+       catch "exec $cmd" comp_output
+       set comp_output [prune_warnings $comp_output]
+       if ![string match "" $comp_output] then {
+           send_log "$comp_output\n"
+           fail $testname
+           return
+       }
+    } else {
+       set cmd "$binary $progopts $progopts1 $objfile > $dumpfile"
+       send_log "$cmd\n"
+       catch "exec $cmd" comp_output
+       set comp_output [prune_warnings $comp_output]
+       if ![string match "" $comp_output] then {
+           send_log "$comp_output\n"
+           fail $testname
+           return
+       }
+    }
+
+    verbose_eval {[file_contents $dumpfile]} 3
+    if { [regexp_diff $dumpfile "${file}.d"] } then {
+       fail $testname
+       verbose "output is [file_contents $dumpfile]" 2
+       return
+    }
+
+    pass $testname
+}
+
+proc slurp_options { file } {
+    if [catch { set f [open $file r] } x] {
+       #perror "couldn't open `$file': $x"
+       perror "$x"
+       return -1
+    }
+    set opt_array {}
+    # whitespace expression
+    set ws  {[         ]*}
+    set nws {[^        ]*}
+    # whitespace is ignored anywhere except within the options list;
+    # option names are alphabetic plus underscore only.
+    set pat "^#${ws}(\[a-zA-Z_\]*)$ws:${ws}(.*)$ws\$"
+    while { [gets $f line] != -1 } {
+       set line [string trim $line]
+       # Whitespace here is space-tab.
+       if [regexp $pat $line xxx opt_name opt_val] {
+           # match!
+           lappend opt_array [list $opt_name $opt_val]
+       } else {
+           break
+       }
+    }
+    close $f
+    return $opt_array
+}
+
+# regexp_diff, copied from gas, based on simple_diff above.
+#      compares two files line-by-line
+#      file1 contains strings, file2 contains regexps and #-comments
+#      blank lines are ignored in either file
+#      returns non-zero if differences exist
+#
+proc regexp_diff { file_1 file_2 } {
+
+    set eof -1
+    set end_1 0
+    set end_2 0
+    set differences 0
+    set diff_pass 0
+
+    if [file exists $file_1] then {
+       set file_a [open $file_1 r]
+    } else {
+       warning "$file_1 doesn't exist"
+       return 1
+    }
+
+    if [file exists $file_2] then {
+       set file_b [open $file_2 r]
+    } else {
+       fail "$file_2 doesn't exist"
+       close $file_a
+       return 1
+    }
+
+    verbose " Regexp-diff'ing: $file_1 $file_2" 2
+
+    while { 1 } {
+       set line_a ""
+       set line_b ""
+       while { [string length $line_a] == 0 } {
+           if { [gets $file_a line_a] == $eof } {
+               set end_1 1
+               break
+           }
+       }
+       while { [string length $line_b] == 0 || [string match "#*" $line_b] } {
+           if [ string match "#pass" $line_b ] {
+               set end_2 1
+               set diff_pass 1
+               break
+           } elseif [ string match "#..." $line_b ] {
+               if { [gets $file_b line_b] == $eof } {
+                   set end_2 1
+                   break
+               }
+               verbose "looking for \"^$line_b$\"" 3
+               while { ![regexp "^$line_b$" "$line_a"] } {
+                   verbose "skipping    \"$line_a\"" 3
+                   if { [gets $file_a line_a] == $eof } {
+                       set end_1 1
+                       break
+                   }
+               }
+               break
+           }
+           if { [gets $file_b line_b] == $eof } {
+               set end_2 1
+               break
+           }
+       }
+
+        if { $diff_pass } { 
+            break 
+        } elseif { $end_1 && $end_2 } { 
+            break
+        } elseif { $end_1 } {
+            send_log "extra regexps in $file_2 starting with \"^$line_b$\"\nEOF from $file_1\n"
+            verbose "extra regexps in $file_2 starting with \"^$line_b$\"\nEOF from $file_1" 3
+            set differences 1
+            break
+        } elseif { $end_2 } {
+            send_log "extra lines in $file_1 starting with \"^$line_a$\"\nEOF from $file_2\n"
+            verbose "extra lines in $file_1 starting with \"^$line_a$\"\nEOF from $file_2\n" 3
+            set differences 1
+            break
+        } else {
+            verbose "regexp \"^$line_b$\"\nline   \"$line_a\"" 3
+            if ![regexp "^$line_b$" "$line_a"] {
+               send_log "regexp_diff match failure\n"
+               send_log "regexp \"^$line_b$\"\nline   \"$line_a\"\n"
+               set differences 1
+            }
+        }
+    }
+
+    if { $differences == 0 && !$diff_pass && [eof $file_a] != [eof $file_b] } {
+       send_log "$file_1 and $file_2 are different lengths\n"
+       verbose "$file_1 and $file_2 are different lengths" 3
+       set differences 1
+    }
+
+    close $file_a
+    close $file_b
+
+    return $differences
+}
+
+proc file_contents { filename } {
+    set file [open $filename r]
+    set contents [read $file]
+    close $file
+    return $contents
+}
+
+proc verbose_eval { expr { level 1 } } {
+    global verbose
+    if $verbose>$level then { eval verbose "$expr" $level }
+}
+
 # This definition is taken from an unreleased version of DejaGnu.  Once
 # that version gets released, and has been out in the world for a few
 # months at least, it may be safe to delete this copy.
This page took 0.029166 seconds and 4 git commands to generate.