+
+# True if the ELF target supports STB_GNU_UNIQUE with the ELF header's
+# OSABI field set to ELFOSABI_GNU.
+#
+# This generally depends on the target OS only, however there are a
+# number of exceptions for bare metal targets as follows. The MSP430
+# and Visium targets set OSABI to ELFOSABI_STANDALONE and cannot
+# support STB_GNU_UNIQUE. Likewise non-EABI ARM targets set OSABI to
+# ELFOSABI_ARM, and TI C6X targets to ELFOSABI_C6000_*. Finally
+# rather than `bfd_elf_final_link' AM33/2.0, D30V, DLX, and
+# picoJava targets use `_bfd_generic_final_link', which does not
+# support STB_GNU_UNIQUE symbol binding causing assertion failures.
+#
+proc supports_gnu_unique {} {
+ if { [istarget *-*-gnu*]
+ || [istarget *-*-linux*]
+ || [istarget *-*-nacl*] } {
+ return 1
+ }
+ if { [istarget "arm*-*-*eabi*"] } {
+ return 1
+ }
+ if { [istarget "wasm32*-*-*"] } {
+ return 1
+ }
+ if { ![istarget "*-*-elf*"] } {
+ return 0
+ }
+ if { [istarget "arm*-*-*"]
+ || [istarget "msp430-*-*"]
+ || [istarget "tic6x-*-*"]
+ || [istarget "visium-*-*"] } {
+ return 0
+ }
+ if { [istarget "am33_2.0-*-*"]
+ || [istarget "d30v-*-*"]
+ || [istarget "dlx-*-*"]
+ || [istarget "pj*-*-*"] } {
+ return 0
+ }
+ return 1
+}
+
+# True for targets that do not sort .symtab as per the ELF standard.
+# ie. any that have mips_elf32_be_vec, mips_elf32_le_vec,
+# mips_elf32_n_be_vec or mips_elf32_n_le_vec as the primary bfd target
+# vector in config.bfd. When syncing with config.bfd, don't forget that
+# earlier case-matches trump later ones.
+proc is_bad_symtab {} {
+ if { ![istarget "mips*-*-*"] } {
+ return 0;
+ }
+ if { [istarget "*-*-chorus*"]
+ || [istarget "*-*-irix5*"]
+ || [istarget "*-*-irix6*"]
+ || [istarget "*-*-none"]
+ || [istarget "*-*-rtems*"]
+ || [istarget "*-*-windiss"] } {
+ return 1;
+ }
+ if { [istarget "*-*-elf*"]
+ && ![istarget "*-sde-*"]
+ && ![istarget "*-mti-*"]
+ && ![istarget "*-img-*"] } {
+ return 1;
+ }
+ if { [istarget "*-*-openbsd*"]
+ && ![istarget "mips64*-*-*"] } {
+ return 1;
+ }
+ return 0;
+}
+
+# Compare two files line-by-line. FILE_1 is the actual output and FILE_2
+# is the expected output. Ignore blank lines in either file.
+#
+# FILE_2 is a series of regexps, comments and # directives. The directives
+# are:
+#
+# #pass
+# Treat the test as a PASS if everything up till this point has
+# matched. Ignore any remaining lines in either FILE_1 or FILE_2.
+#
+# #failif
+# Reverse the sense of the test: expect differences to exist.
+#
+# #...
+# REGEXP
+# Skip all lines in FILE_1 until the first that matches REGEXP.
+#
+# Other # lines are comments. Regexp lines starting with the `!' character
+# specify inverse matching (use `\!' for literal matching against a leading
+# `!'). Skip empty lines in both files.
+#
+# The first optional argument is a list of regexp substitutions of the form:
+#
+# EXP1 SUBSPEC1 EXP2 SUBSPEC2 ...
+#
+# This tells the function to apply each regexp substitution EXPi->SUBSPECi
+# in order to every line of FILE_2.
+#
+# Return nonzero if differences exist.
+proc regexp_diff { file_1 file_2 args } {
+ set eof -1
+ set end_1 0
+ set end_2 0
+ set differences 0
+ set diff_pass 0
+ set fail_if_match 0
+ set ref_subst ""
+ if { [llength $args] > 0 } {
+ set ref_subst [lindex $args 0]
+ }
+ if { [llength $args] > 1 } {
+ perror "Too many arguments to regexp_diff"
+ return 1
+ }
+
+ if [file exists $file_1] then {
+ set file_a [open $file_1 r]
+ } else {
+ perror "$file_1 doesn't exist"
+ return 1
+ }
+
+ if [file exists $file_2] then {
+ set file_b [open $file_2 r]
+ } else {
+ perror "$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 } {
+ # Ignore blank line in FILE_1.
+ 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 "#failif" $line_b] } {
+ send_log "fail if no difference\n"
+ verbose "fail if no difference" 3
+ set fail_if_match 1
+ } elseif { [string match "#..." $line_b] } {
+ if { [gets $file_b line_b] == $eof } {
+ set end_2 1
+ set diff_pass 1
+ break
+ }
+ set negated [expr { [string index $line_b 0] == "!" }]
+ set line_bx [string range $line_b $negated end]
+ set n [expr { $negated ? "! " : "" }]
+ # Substitute on the reference.
+ foreach {name value} $ref_subst {
+ regsub -- $name $line_bx $value line_bx
+ }
+ verbose "looking for $n\"^$line_bx$\"" 3
+ while { [expr [regexp "^$line_bx$" "$line_a"] == $negated] } {
+ 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 {
+ set negated [expr { [string index $line_b 0] == "!" }]
+ set line_bx [string range $line_b $negated end]
+ set n [expr { $negated ? "! " : "" }]
+ set s [expr { $negated ? " " : "" }]
+ # Substitute on the reference.
+ foreach {name value} $ref_subst {
+ regsub -- $name $line_bx $value line_bx
+ }
+ verbose "regexp $n\"^$line_bx$\"\nline \"$line_a\"" 3
+ if { [expr [regexp "^$line_bx$" "$line_a"] == $negated] } {
+ send_log "regexp_diff match failure\n"
+ send_log "regexp $n\"^$line_bx$\"\nline $s\"$line_a\"\n"
+ verbose "regexp_diff match failure\n" 3
+ 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
+ }
+
+ if { $fail_if_match } {
+ if { $differences == 0 } {
+ set differences 1
+ } else {
+ set differences 0
+ }
+ }
+
+ close $file_a
+ close $file_b
+
+ return $differences
+}