# Copyright 2022 Free Software Foundation, Inc. # This program 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 3 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, see . # Tools which use the BFD library will output error messages of the # form "BFD: some messsage" when a problem with the file upon which it # operating is found. E.g. an actual message (modulo some shortening # of the pathname) from this test is: # # BFD: bfd-errors-lib.so: invalid string offset 1154 >= 154 for section `.dynstr' # # For some problems with executable files or libraries, BFD will # attempt to output many identical messages. Code has been added to # GDB to suppress messages which are identical to earlier messages # that have already been printed. # # This test makes sure that (all but the first) identical BFD messages # are suppresssed and also that differing messages are output at least # once. # # To accomplish this, a shared object with at least four symbols is # created. The .dynsym section is extracted and offsets which should # refer to strings in the .dynstr section are changed to be # larger than the size of the .dynstr section. Only two (different) # offsets are used; thus BFD will attempt to output at least two pairs # of identical messages. (And it would do this too if not intercepted # by the hook placed by GDB.) After modifying the extracted section, # the mangled section is placed back into the shared object. # # This test then loads the shared library's symbol table (and other # debug info) using the 'add-symbol-file' command. While doing this, # the test observes and records the BFD errors that were output. # Finally, data collected while adding the shared library symbols are # examined to make sure that identical messages were suppressed while # also making sure that at least two messages have been printed. # This test can't be run on targets lacking shared library support # or for non-ELF targets. if { [skip_shlib_tests] || ![is_elf_target] } { return 0 } # Library file names and flags: set lib_basename ${::gdb_test_file_name}-lib set srcfile_lib ${srcdir}/${subdir}/${lib_basename}.c set binfile_lib [standard_output_file ${lib_basename}.so] set lib_flags debug # Compile shared library: if { [gdb_compile_shlib ${srcfile_lib} ${binfile_lib} $lib_flags] != "" } { untested "failed to compile" return -1 } # Open the shared library and determine some basic facts. The key # things that we need to learn are 1) whether the solib is 32-bit or # 64-bit ELF file, and 2) the endianness. set solib_fp [open ${binfile_lib} r] fconfigure $solib_fp -translation binary # Read and check EI_MAG to verify that it's really an ELF file. set data [read $solib_fp 4] if { ![string equal $data "\x7fELF"] } { close $solib_fp untested "shared library is not an ELF file" return -1 } # Read EI_CLASS for ELF32 versus ELF64. set data [read $solib_fp 1] set is_elf64 [string equal $data "\x02"] # Read EI_DATA to determine data encoding (byte order). set data [read $solib_fp 1] set is_big_endian [string equal $data "\x02"] close $solib_fp set objcopy_program [gdb_find_objcopy] # Extract the .dynsym and .dynstr section from the shared object. if { [catch "exec $objcopy_program \ --dump-section .dynsym=${binfile_lib}.dynsym \ --dump-section .dynstr=${binfile_lib}.dynstr \ ${binfile_lib}" output] } { untested "failed objcopy dump-section" verbose -log "objcopy output: $output" return -1 } # Determine length of .dynstr. We'll use the length for creating invalid # offsets into .dynstr. set dynstr_len [file size ${binfile_lib}.dynstr] # Open the file containing .dynsym and determine its length. In this # case, we want to know the length in order to compute the total number # of symbols that it contains. We also leave the file open for a # while so that we can write invalid offsets to it. set dynsym_fp [open ${binfile_lib}.dynsym r+] fconfigure $dynsym_fp -translation binary set dynsym_len [string length [read $dynsym_fp]] # SZ is the size of the Elf32_Sym / Elf64_Sym struct. OFF is the # offset into the file. CNT is one greater than the number of symbols # left to mangle. Note that, in the loop below, the first symbol is # skipped. This is intentional since the first symbol is defined by # the ELF specification to be the undefined symbol. set off 0 if { $is_elf64 } { set sz 24 } else { set sz 16 } set cnt [expr $dynsym_len / $sz] # Create 32-bit patterns (bad offsets) to write into the st_name area. if { $is_big_endian } { set pat(0) [binary format I [expr $dynstr_len + 1000]] set pat(1) [binary format I [expr $dynstr_len + 2000]] } else { set pat(0) [binary format i [expr $dynstr_len + 1000]] set pat(1) [binary format i [expr $dynstr_len + 2000]] } # Mangle st_name for the symbols following the first (STN_UNDEF) entry. while { [incr cnt -1] > 0 } { seek $dynsym_fp [incr off $sz] puts $dynsym_fp $pat([expr $cnt % 2]) } close $dynsym_fp # Replace .dynsym section in shared object with the mangled version. if { [catch "exec $objcopy_program \ --update-section .dynsym=${binfile_lib}.dynsym \ ${binfile_lib}" output] } { untested "failed objcopy update-section" verbose -log "objcopy output: $output" return -1 } clean_restart # Count number of distinct BFD error messages via 'bfd_error_count' # array while using add-symbol-file to "load" the shared library. gdb_test_multiple "add-symbol-file -readnow $binfile_lib" \ "load library with add-symbol-file" { -re "add symbol table from file.*\(y or n\)" { send_gdb "y\n" answer exp_continue } -re "(BFD:\[^\r\n\]*)\[\r\n\]+" { incr bfd_error_count($expect_out(1,string)) exp_continue } -re "Expanding full symbols from.*$gdb_prompt $" { pass $gdb_test_name } } # Examine counts recorded in the 'bfd_error_count' array to see if any # message was printed multiple times. set more_than_one 0 foreach index [array names bfd_error_count] { if { $bfd_error_count($index) > 1 } { incr more_than_one } } gdb_assert { $more_than_one == 0 } "consolidated bfd errors" # There should have been at least two distinct BFD errors printed. gdb_assert { [array size bfd_error_count] >= 2 } "print all unique bfd errors" gdb_exit return 0