blob: b611c33b274a329254b8e71f4a18b1d9ad6b1e86 [file] [log] [blame]
# Copyright 2022-2024 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 <http://www.gnu.org/licenses/>.
# 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.
require allow_shlib_tests is_elf_target
# 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