diff options
author | Andrew Burgess <aburgess@redhat.com> | 2024-12-15 16:06:02 +0000 |
---|---|---|
committer | Andrew Burgess <aburgess@redhat.com> | 2024-12-16 16:22:10 +0000 |
commit | f1b4718cbdfc3332b737fbea8612448586f5184b (patch) | |
tree | b36938bc271d503c8834b19fb5da00595c1a3eab | |
parent | ea17e06b50ecc045c311c348d28404f711ac3737 (diff) | |
download | gdb-f1b4718cbdfc3332b737fbea8612448586f5184b.zip gdb-f1b4718cbdfc3332b737fbea8612448586f5184b.tar.gz gdb-f1b4718cbdfc3332b737fbea8612448586f5184b.tar.bz2 |
gdb/testsuite: rename test source file to avoid glibc clash
After posting this series:
https://inbox.sourceware.org/gdb-patches/cover.1733742925.git.aburgess@redhat.com
I got a failure report from the Linaro CI system. I eventually
tracked the issue down to a filename clash with glibc. I was able to
reproduce the issue when I installed the glibc debug information on to
my local machine, and ran the gdb.base/dlmopen.exp test as updated in
the above series.
Here's what's happening:
There is a file called dlmopen.c within glibc, within the glibc source
tree the file can be found at ./dlfcn/dlmopen.c. When this file is
compiled it appears that the glibc build system first enters the dlfcn
directory, and then compiles the file using the relative path
./dlmopen.c, here's a snippet of the DWARF:
<0><d5d27>: Abbrev Number: 12 (DW_TAG_compile_unit)
<d5d28> DW_AT_producer : (alt indirect string, offset: 0x16433) t
<d5d2c> DW_AT_language : 29 (C11)
<d5d2d> DW_AT_name : (indirect line string, offset: 0x5c8f): dlmopen.c
<d5d31> DW_AT_comp_dir : (indirect line string, offset: 0xb478): /usr/src/debug/glibc-2.38-19.fc39.x86_64/dlfcn
<d5d35> DW_AT_low_pc : 0x8a4c0
<d5d3d> DW_AT_high_pc : 408
<d5d3f> DW_AT_stmt_list : 0x68ec1
The important thing here is the DW_AT_name, which is just "dlmopen.c".
The gdb.base/dlmopen.exp test also has a source file called
"dlmopen.c".
The dlmopen.exp test makes use of the clean_restart TCL proc, which
calls gdb_reinitialize_dir, which resets the source directories search
path to '$cdir:$cwd', and then prepends the test source directory to
the front of the list, so the source directory search path will look
something like:
/tmp/src/gdb/testsuite/gdb.base/gdb.base:$cdir:$cwd
In the existing test we try to place a breakpoint on 'dlmopen.c:64'.
This is the line tagged 'bp.main' in the source file. This currently
works fine. GDB searches through the symtabs and finds two matches,
the test dlmopen.c, and the glibc dlmopen.c. For each GDB tries to
convert line 64 into an address.
For the testsuite source file this is fine, we get the address of the
line tagged 'bp.main' from the source, and the breakpoint is created.
For the glibc source file though, at least, for the version available
to me, line 64 happens to be the closing '}' of a function, and there
isn't a line table entry for this exact line. So GDB searches forward
looking for the next line in order to place a breakpoint there. The
next line GDB finds is the start of the next function, and so GDB
rejects this location due to commit:
commit dcaa85e58c4ef50a92908e071ded631ce48c971c
Date: Wed May 1 10:47:47 2024 +0100
gdb: reject inserting breakpoints between functions
So we managed to avoid creating two breakpoint locations in this case,
but only by pure good luck.
In my updates to the test though I try to create a breakpoint at line
61 in addition to the breakpoint at line 64. So now the breakpoint
spec is 'dlmopen.c:61'.
Just as before, GDB identifies the 'dlmopen.c' could mean two files,
and searches for line 61 in both. The test source works as expected
and the breakpoint is created in the desired location.
But this time, line 61 in the glibc source file is an actual line,
with actual code, and so GDB places a breakpoint at this location.
This second breakpoint, in glibc is entirely unexpected (by the
dlmopen.exp test script). Unfortunately, the inferior hits this
second glibc breakpoint before it hits the actual breakpoint within
the main test executable, this throws the test off and causes some
failures.
In trying to fix this, I did wonder if I could just specify the full
path to the source file, instead of using just 'dlmopen.c:61'.
However, this doesn't work.
Remember that the glibc source file is recorded as just 'dlmopen.c'.
So, when GDB tries to figure out the absolute path to this source
file, the source directory search path is used. In this case, the
first entry in the source directory search path is the gdb.base/
directory in the GDB source tree. GDB looks in this directory and
finds a dlmopen.c, and so GDB assumes that this is the file in
question.
Thus, GDB actually thinks that both files _are_ the same source file.
Indeed, when GDB stops at the incorrect (glibc) breakpoint, and lists
the source code, it actually lists the source code from the correct
file. This confused me to begin with, GDB reported the wrong
function (the glibc function), but listed code from the correct file
and line.
Now on my machine I have installed the package that provides the glibc
source code. If I change the source directory search path so that
$cdir is first instead of the gdb.base/ from the GDB source tree, this
fixes the listing the wrong file problem. GDB does not realise that
the files are different, and if I create the breakpoint using the
absolute path then only a single breakpoint location is created.
However, this relies on the developer having both the glibc debug
information, and the glibc source package installed, this doesn't seem
like a great requirement to have in place.
So instead, I propose that we just take the easy way out, rename the
test source file. By doing this all the issues are avoided. The test
now creates a breakpoint at 'dlmopen-main.c:61', and there is only one
file with this name found, so we only get a single breakpoint location
created.
I renamed the source file, but not the dlmopen.exp file because the
test already makes use of multiple source files, so having a range of
different names didn't feel that bad, but if this bothers people, I
could rename both the .exp and main .c file, just let me know.
If you want to explore this issue for yourself then try with
installing the glibc debug information for your system, and ensure
that your GDBs under test are able to find the glibc debug
information. You can then either apply the series I linked above, or,
you can modify the existing test source so that the line tagged as
'bp.main' becomes line 61, I just deleted 3 lines from the big comment
at the head of the file.
Of course, reproducing this does depend on how glibc is compiled,
which could change from system to system, or overtime. I reproduced
this issue on Fedora 39 with glibc-2.38-19.
With this patch applied I no longer see any regressions when I apply
the above linked series.
While making these changes I took the opportunity to update the test
script to make better use of standard_testfile and build_executable.
Reviewed-By: Keith Seitz <keiths@redhat.com>
Approved-By: Tom Tromey <tom@tromey.com>
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen-main.c (renamed from gdb/testsuite/gdb.base/dlmopen.c) | 0 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen.exp | 56 |
2 files changed, 42 insertions, 14 deletions
diff --git a/gdb/testsuite/gdb.base/dlmopen.c b/gdb/testsuite/gdb.base/dlmopen-main.c index 196d13a..196d13a 100644 --- a/gdb/testsuite/gdb.base/dlmopen.c +++ b/gdb/testsuite/gdb.base/dlmopen-main.c diff --git a/gdb/testsuite/gdb.base/dlmopen.exp b/gdb/testsuite/gdb.base/dlmopen.exp index 264c518..dee046a 100644 --- a/gdb/testsuite/gdb.base/dlmopen.exp +++ b/gdb/testsuite/gdb.base/dlmopen.exp @@ -23,30 +23,58 @@ require allow_dlmopen_tests -standard_testfile +# Don't use 'dlmopen.c' as the source file name, glibc also has a file +# with that name. Within our tests, we set the source directory search +# path order to: +# +# (1) the test source directory, +# (2) the compilation directory, and then +# (3) the current working directory. +# +# Because (1) is first when we try to place a breakpoint on +# 'dlmopen.c', if the test source file has that name, then GDB will +# find both the test source file, and the source file from glibc. +# +# We could work around this by making (2) first in the source +# directory list, but that only works when the glibc source is +# installed. If it isn't then GDB will try the compilation directory, +# fail to find the source, then try the test source directory, get a +# hit, and so still confuse the two files. +# +# You might think the problem can be solved by specifying the absolute +# path to the source file. This doesn't work because the glibc file +# has its filename recorded as just "dlmopen.c", as such GDB has to +# figure out an absolute path to the file (if possible). The absolute +# path is figured out based on where GDB can find a matching file in +# the source directory list, and because of the confusion above, GDB +# will usually think the test 'dlmopen.c' and the glibc 'dlmopen.c' +# are actually the same file. +# +# The conclusion is that it is just easier to rename the test source +# file to avoid conflicts with glibc. + +standard_testfile -main.c -lib.c -lib-dep.c set basename_lib dlmopen-lib -set srcfile_lib $srcdir/$subdir/$basename_lib.c +set srcfile_lib $srcfile2 set binfile_lib1 [standard_output_file $basename_lib.1.so] set binfile_lib2 [standard_output_file $basename_lib.2.so] -set srcfile_lib_dep $srcdir/$subdir/$basename_lib-dep.c +set srcfile_lib_dep $srcfile3 set binfile_lib_dep [standard_output_file $basename_lib-dep.so] -if { [gdb_compile_shlib $srcfile_lib_dep $binfile_lib_dep {debug}] != "" } { - untested "failed to prepare shlib" - return -1 +if { [build_executable "build shlib dep" $binfile_lib_dep $srcfile_lib_dep \ + {debug shlib}] == -1 } { + return } -if { [gdb_compile_shlib $srcfile_lib $binfile_lib1 \ - [list debug shlib_load libs=$binfile_lib_dep]] != "" } { - untested "failed to prepare shlib" - return -1 +if { [build_executable "build shlib" $binfile_lib1 $srcfile_lib \ + [list debug shlib_load shlib libs=$binfile_lib_dep]] == -1 } { + return } -if { [gdb_compile_shlib $srcfile_lib $binfile_lib2 \ - [list debug shlib_load libs=$binfile_lib_dep]] != "" } { - untested "failed to prepare shlib" - return -1 +if { [build_executable "build shlib" $binfile_lib2 $srcfile_lib \ + [list debug shlib_load shlib libs=$binfile_lib_dep]] == -1 } { + return } if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ |