)]}'
{
  "commit": "2dd4e9dbc5e82d3101ad184c430e7bdea805628a",
  "tree": "eac509e417df94b0c4b5cb4c307e38e654686e4f",
  "parents": [
    "de364c377b7416e15ba8aeada451757db9c541cc"
  ],
  "author": {
    "name": "Andrew Burgess",
    "email": "aburgess@redhat.com",
    "time": "Fri Nov 14 15:19:58 2025 +0000"
  },
  "committer": {
    "name": "Andrew Burgess",
    "email": "aburgess@redhat.com",
    "time": "Wed Jan 07 13:10:10 2026 +0000"
  },
  "message": "gdb: test for misplaced symtab causing file not found error\n\nThis patch adds a new test that checks for a bug that was, if not\nfixed, then at least, worked around, by commit:\n\n  commit a736ff7d886dbcc85026264c3ce11c125a8409b2\n  Date:   Sat Sep 27 22:29:24 2025 -0600\n\n      Clean up iterate_over_symtabs\n\nThe bug was reported against Fedora GDB which, at the time the bug was\nreported, is based off GDB 16, and so doesn\u0027t include the above\ncommit.  The bug report can be found here:\n\n  https://bugzilla.redhat.com/show_bug.cgi?id\u003d2403580\n\nTo summarise the bug report: a user is inspecting an application\nbacktrace.  The original bug report was from a core file, but the same\nissue will trigger for a live inferior.  It\u0027s the inspection of the\nstack frames which is important.  The user moves up the stack with the\n\u0027up\u0027 command and eventually finds an interesting frame.  They use\n\u0027list\u0027 to view the source code at the current location, this works and\ndisplays lines 6461 to 6470 from the source file \u0027../glib/gmain.c\u0027.\nThe user then does \u0027list 6450\u0027 to try and display some earlier lines\nfrom the same source file, at which point GDB gives the message:\n\n   warning: 6445  ../glib/gmain.c: No such file or directory\n\nSo GDB initially manages to find the source file, but for the very\nnext command, GDB now claims that the source file doesn\u0027t exist.\n\nAs I said, commit a736ff7d886d appears to fix this issue, but it\nwasn\u0027t clear to me (from the commit message) if this commit was\nintended to fix any bugs, or if the bug was being hidden by this\ncommit.  I\u0027ve spent some time trying to understand what\u0027s going on,\nand have come up with this test case.\n\nI think there might still be an issue in GDB, but I do think that the\nabove commit really is making it so that the issue (if it is an issue)\ndoesn\u0027t occur in that particular situation any more, so I think we can\nconsider the above commit a fix, and testing for this bug is worth\nwhile to ensure it doesn\u0027t get reintroduced.\n\nIn order to trigger this bug we need these high level requirements:\n\n  1. Multiple shared libraries compiled from the same source tree.  In\n     this case it was glib, but the test in this commit uses a much\n     smaller library.\n\n  2. Common DWARF must be pulled from the libraries using the \u0027dwz\u0027\n     tool.\n\n  3. Debuginfod must be in use for at least downloading the source\n     code.  In the original bug, and in the test presented here,\n     debuginfod is used for fetching both the debug info, and the\n     source code for the library.\n\nThere are some additional specific requirements for the DWARF in order\nto trigger the bug, but to make discussing this easier, lets look at\nthe structure of the test presented here.  When discussing the source\nfiles I\u0027ll drop the solib-with-dwz- prefix, e.g. when I mention\n\u0027foo.c\u0027 I really mean \u0027solib-with-dwz-foo.c\u0027.\n\nThere are three shared libraries built for this test, libbar.so,\nlibfoo.so, and libfoo-2.so.  The source file bar.c is used to create\nlibbar.so, and foo.c is used to create libfoo.so and libfoo-2.so.\n\nThe main test executable is built from main.c, and links against\nlibbar.so and libfoo.so.  libfoo-2.so is not used by the main\nexecutable, and just exists to trigger some desired behaviour from the\ndwz tool.\n\nThe debug information for each shared library is extracted into a\ncorresponding .debug file, and the dwz tool is used to extract common\ndebug from the three .debug files into a file called \u0027common.dwz\u0027.\n\nGiven all this then, in order to trigger the bug, the following\nadditional requirements must be met:\n\n  4. libbar.so must NOT make use of foo.c.  In this test libbar.so is\n     built from bar.c (and some headers) only.\n\n  5. A reference to foo.c must be placed into common.dwz.  This is why\n     libfoo-2.so exists, as this library is almost identical to\n     libfoo.so, there is lots of shared DWARF between libfoo.so and\n     libfoo-2.so which can be moved into common.dwz, this shared DWARF\n     includes references to foo.c, so an entry for foo.c is added to\n     the file table list in common.dwz.\n\n  6. There must be a DWARF construct within libbar.so.debug that\n     references common.dwz, and which causes GDB to parse the line\n     table from within common.dwz.  For more details on this, see\n     below.\n\n  7. We need libbar.so to appear before libfoo.so in GDB\u0027s\n     comunit_symtab lists.  This means that GDB will scan the symtabs\n     for libbar.so before checking the symtabs of libfoo.so.  I\n     achieve this by mentioning libbar.so first when building the\n     executable, but this is definitely the most fragile part of the\n     test.\n\nTo satisfy requirement (6) the inline function \u0027add_some_int\u0027 is added\nto the test.  This function appears in both libbar.so and libfoo.so,\nthis means that the DW_TAG_subprogram representing the abstract\ninstance tree will be moved into common.dwz.  However, as this is an\ninline function, the DW_TAG_inlined_subroutine DIEs for each concrete\ninstance, will be left in libbar.so.debug and libfoo.so.debug, with a\nDW_AT_abstract_origin that points into common.dwz.\n\nWhen GDB parses libbar.so.debug it finds the DW_TAG_inlined_subroutine\nand begins processing it.  It sees the DW_AT_abstract_origin and so\njumps into common.dwz to read the DIEs that define the inline\nfunction.  Here is the DWARF from libbar.so.debug for the inlined\ninstance:\n\n \u003c2\u003e\u003c91\u003e: Abbrev Number: 3 (DW_TAG_inlined_subroutine)\n    \u003c92\u003e   DW_AT_abstract_origin: \u003calt 0x1b\u003e\n    \u003c96\u003e   DW_AT_low_pc      : 0x1121\n    \u003c9e\u003e   DW_AT_high_pc     : 31\n    \u003c9f\u003e   DW_AT_call_file   : 1\n    \u003ca0\u003e   DW_AT_call_line   : 26\n    \u003ca1\u003e   DW_AT_call_column : 15\n \u003c3\u003e\u003ca2\u003e: Abbrev Number: 5 (DW_TAG_formal_parameter)\n    \u003ca3\u003e   DW_AT_abstract_origin: \u003calt 0x2c\u003e\n    \u003ca7\u003e   DW_AT_location    : 2 byte block: 91 68      (DW_OP_fbreg: -24)\n \u003c3\u003e\u003caa\u003e: Abbrev Number: 5 (DW_TAG_formal_parameter)\n    \u003cab\u003e   DW_AT_abstract_origin: \u003calt 0x25\u003e\n    \u003caf\u003e   DW_AT_location    : 2 byte block: 91 6c      (DW_OP_fbreg: -20)\n\nAnd here\u0027s the DWARF from common.dwz for the abstract instance tree:\n\n \u003c1\u003e\u003c1b\u003e: Abbrev Number: 7 (DW_TAG_subprogram)\n    \u003c1c\u003e   DW_AT_name        : (indirect string, offset: 0x18a): add_some_int\n    \u003c20\u003e   DW_AT_decl_file   : 1\n    \u003c21\u003e   DW_AT_decl_line   : 24\n    \u003c22\u003e   DW_AT_decl_column : 1\n    \u003c23\u003e   DW_AT_prototyped  : 1\n    \u003c23\u003e   DW_AT_type        : \u003c0x14\u003e\n    \u003c24\u003e   DW_AT_inline      : 3        (declared as inline and inlined)\n \u003c2\u003e\u003c25\u003e: Abbrev Number: 8 (DW_TAG_formal_parameter)\n    \u003c26\u003e   DW_AT_name        : a\n    \u003c28\u003e   DW_AT_decl_file   : 1\n    \u003c29\u003e   DW_AT_decl_line   : 24\n    \u003c2a\u003e   DW_AT_decl_column : 19\n    \u003c2b\u003e   DW_AT_type        : \u003c0x14\u003e\n \u003c2\u003e\u003c2c\u003e: Abbrev Number: 8 (DW_TAG_formal_parameter)\n    \u003c2d\u003e   DW_AT_name        : b\n    \u003c2f\u003e   DW_AT_decl_file   : 1\n    \u003c30\u003e   DW_AT_decl_line   : 24\n    \u003c31\u003e   DW_AT_decl_column : 26\n    \u003c32\u003e   DW_AT_type        : \u003c0x14\u003e\n\nWhile processing the common.dwz DIEs GDB sees the DW_AT_decl_file\nattributes, and this triggers a read of the file table within\ncommon.dwz, which creates symtabs for any files mentioned, if the\nsymtabs don\u0027t already exist.\n\nBut, and this is the important bit, when doing this, GDB is creating a\ncompunit_symtab for libbar.so.debug, so any symtabs created will be\nattached to the libbar.so.debug objfile.\n\nRemember requirement (5), the file list in common.dwz mentions\n\u0027foo.c\u0027, so even though libbar.so doesn\u0027t use \u0027foo.c\u0027 we end up with a\nsymtab for \u0027foo.c\u0027 created within the compunit_symtab for\nlibbar.so.debug!\n\nI don\u0027t think this is ideal.  This wastes memory and time; we have\nmore symtabs to search through even if, as I\u0027ll discuss below, we\nusually end up ignoring these symtabs.\n\nThe exact path that triggers this weird symtab creation starts with a\ncall to \u0027new_symbol\u0027 (dwarf2/read.c) for the DW_TAG_formal_parameter\nin the abstract instance tree.  These include DW_AT_decl_file, which\nis read in \u0027new_symbol\u0027.  In \u0027new_symbol\u0027 GDB spots that the\nline_header has not yet been read in, so handle_DW_AT_stmt_list is\ncalled which reads the file/line table and then calls\n\u0027dwarf_decode_lines\u0027 (line_program.c), which then creates symtabs for\nall the files mentioned.\n\nThis symtab creation issue still exists today in GDB, though I\u0027ve not\nbeen able to find any real issues that this is causing after commit\na736ff7d886d fixed the issue I\u0027m discussing here.\n\nSo, having tricked GDB into creating a misplaced symtab, what problem\ndid this cause prior to commit a736ff7d886d?\n\nTo answer this, we need to take a diversion to understand how a\ncommand like \u0027list 6450\u0027 works.  The two interesting functions are\ncreate_sals_line_offset and decode_digits_list_mode, which is called\nfrom the former.  The create_sals_line_offset is called indirectly\nfrom list_command via the initial call to decode_line_1.\n\nIn create_sals_line_offset, if the incoming linespec doesn\u0027t specify a\nspecific symtab, then GDB uses the name of the default symtab to\nlookup every symtab with a matching name, this is done with the line:\n\n  ls-\u003efile_symtabs\n    \u003d collect_symtabs_from_filename (self-\u003edefault_symtab-\u003efilename (),\n                                     self-\u003esearch_pspace);\n\nIn our case, when the default symtab is \u0027foo.c\u0027, this is going to\nreturn multiple symtabs, these will include the correct \u0027foo.c\u0027 symtab\nfrom libfoo.so, but will also include the misplaced \u0027foo.c\u0027 symtab\nfrom libbar.so.  This is where the ordering is important.  As list\nwill only ever list one file, at a later point in this process we\u0027re\ngoing to toss out everything except the first result.  So, to trigger\nthe bug, it is critical that the FIRST result returned here be the\nmisplaced \u0027foo.c\u0027 symtab from libbar.so.  In the test I try to ensure\nthis by mentioning libbar.so before libfoo.so when building the\nexecutable, which currently means we get back the misplaced symtab\nfirst, but this could change in the future and wouldn\u0027t necessarily\nmean that the problem has gone away.\n\nHaving got the symtab list GDB then calls decode_digits_list_mode\nwhich iterates over the symtabs and converts them into symtab_and_line\nobjects, at the heart of which is a call to find_line_symtab, which\nchecks if a given symtab has a line table entry for the desired line.\nIf it does then the symtab is returned.  If it doesn\u0027t then GDB looks\nfor another symtab with the same name that does have a line table\nentry.  If no suitably named symtab has an exact match, then the\nsymtab with the closest line above the required line is returned.  If\nno symtab has a matching line table entry then find_line_symtab\nreturns NULL.\n\nRemember, the misplaced symtab was only created as a side effect of\ntrying to attach the DW_TAG_formal_parameter symbol to a symtab.\nThe actual line table for libbar.so (in libbar.so.debug) has no line\ntable entries for \u0027foo.c\u0027.  What this means is that the line table for\n\u0027foo.c\u0027 attached to libbar.so.debug is empty.  So normally what\nhappens is that find_line_symtab will instead find a line table entry\nfor \u0027foo.c\u0027 in libfoo.so.debug that does have a suitable line table\nentry, and will switch GDB back to that symtab, effectively avoiding\nthe problem.  However, that is not what happens in the bug case.  In\nthe bug case find_line_symtab returns NULL, which means that\ndecode_digits_list_mode just uses the original symtab, in this case\nthe symtab for \u0027foo.c\u0027 from libbar.so.debug.\n\nIn the original bug, the code is compiled with -O2, and this\noptimisation has left the line table covering the problem file pretty\nsparse.  In fact, there are no line table entries for any line after\nthe line that the user is trying to list.  This is why\nfind_line_symtab doesn\u0027t find a better alternative symtab, and instead\njust returns NULL.\n\nIn the test I\u0027ve replicated this by having a comment at the end of the\nsource file, and asking GDB to list a line within this comment.  The\nresult is that there are no line table entries for that line in any\n\u0027foo.c\u0027 symtab, and so find_line_symtab returns NULL.\n\nAfter decode_digits_list_mode sees the NULL from find_line_symtab, it\njust uses the initial symtab.\n\nAfter this we eventually return back to list_command (cli/cli-cmds.c)\nwith a list of symtab_and_line objects.  The first entry in this list\nis for the symtab \u0027foo.c\u0027 from libbar.so.  In list_command we call\nfilter_sals which throws away everything but the first entry as all\nthe symtabs have the same filename (and are in the same program\nspace).\n\nUsing the symtab we build an absolute path to the source file.\n\nNow, if the source is installed locally, GDB performs no additional\nchecks; we found a symtab, the symtab gave us a source filename, if\nthe source file exists on disk, then the requires lines are listed for\nthe user.\n\nBut if the source file doesn\u0027t exist on disk, then we are going to ask\ndebuginfod for the source file.  To do that we use two pieces of\ninformation; the absolute path to the source file, which we have; and\nthe build-id of an objfile, this is the objfile that owns the symtab\nwe are trying to get the source for.  In this case libbar.so.  And so\nwe send the build-id and filename to debuginfod.\n\nNow debuginfod isn\u0027t going to just serve any file to anyone, that\nwould be a security issue for the server.  Instead, debuginfod scans\nthe DWARF and builds up its own model of which objfiles use which\nsource files, and for a given build-id, debuginfod will only serve\nback files that the objfile matching that build-id, actually uses.\nSo, in this case, when we ask for \u0027foo.c\u0027 from libbar.so, debuginfod\ncorrectly realises the \u0027foo.c\u0027 is not part of libbar.so, and refuses\nto send the file back.\n\nAnd this is how the original bug occurred.\n\nSo, why does commit a736ff7d886d fix this problem?  The answer is in\niterate_over_symtabs, which is used by collect_symtabs_from_filename\nto find the matching symtabs.\n\nPrior to this commit, iterate_over_symtabs had two phases, first a\ncall to iterate_over_some_symtabs which walks over compunit_symtabs\nthat already exist looking for matches, during this phase only the\nsymtab filenames are considered.  The second phase uses\nobjfile::map_symtabs_matching_filename to look through the objfiles\nand expand new symtabs that match the required name.  In our case, by\nthe time iterate_over_symtabs is called, all of the interesting\nsymtabs have already been expanded, so we only perform the filename\ncheck in iterate_over_some_symtabs, this passes, and so \u0027foo.c\u0027 from\nlibbar.so is considered a suitable symtab.\n\nAfter commit a736ff7d886d the initial call to\niterate_over_some_symtabs has been removed from iterate_over_symtabs,\nand only the objfile::map_symtabs_matching_filename call remains.\nThis ends up in cooked_index_functions::search (dwarf2/read.c) to\nsearch for matching symtabs.\n\nThe first think cooked_index_functions::search does is setup a vector\nof CUs to skip by calling dw_search_file_matcher, this then calls\ndw2_get_file_names to get the file and line table for a CU, this\nfunction in turn creates a cutu_reader object, passing true for the\n\u0027skip_partial\u0027 argument to its constructor.\n\nAs our \u0027foo.c\u0027 symtab was created from within the dwz extracted DWARF,\nthen it is associated with the DW_TAG_partial_unit that held the\nDW_TAG_subprogram DIEs that were being processed when the misplaced\nsymtab was original created; this is a partial unit.  As this is a\npartial unit, and the skip_partial flag was passed true, the\ncutu_reader::is_dummy function will return true.\n\nBack in dw2_get_file_names, if cutu_reader::is_dummy is true then\ndw2_get_file_names_reader is never called, and the file names are\nnever read.  This means that back in dw_search_file_matcher, the file\ndata, returned from dw2_get_file_names is NULL, and so this CU is\nmarked to be skipped.  Which is exactly what we want, this misplaced\nsymtab, which was created for a partial unit and associated with\nlibbar.so, is skipped and never considered as a possible match.\n\nThere is a remaining problem, which is marked in the test with an\nxfail.  That is, when the test does the \u0027list LINENO\u0027, GDB still tries\nto download the source for \u0027foo.c\u0027 from libbar.so.  The reason for\nthis is that, while it is true that the initial\ncollect_symtabs_from_filename call no longer returns \u0027foo.c\u0027 from\nlibbar.so, when decode_digits_list_mode calls find_line_symtab for the\ncorrect \u0027foo.c\u0027 from libfoo.so, it is still the case that there is no\nexact match for LINENO in that symtabs line table.\n\nAs a result, GDB looks through all the other symtabs for \u0027foo.c\u0027 to\nsee if any are a better match.  Checking if another symtab is a\npossible better match requires a full comparison of the symtabs source\nfile name, which in this case triggers an attempt to download the\nsource file from debuginfod.  Here\u0027s the backtrace at the time of the\nrogue source download request, which appears as an xfail in the test\npresented here:\n\n  #0  debuginfod_source_query (build_id\u003d..., build_id_len\u003d..., srcpath\u003d..., destname\u003d...) at ../../src/gdb/debuginfod-support.c:332\n  #1  0x0000000000f0bb3b in open_source_file (s\u003d...) at ../../src/gdb/source.c:1152\n  #2  0x0000000000f0be42 in symtab_to_fullname (s\u003d...) at ../../src/gdb/source.c:1214\n  #3  0x0000000000f6dc40 in find_line_symtab (sym_tab\u003d..., line\u003d..., index\u003d...) at ../../src/gdb/symtab.c:3314\n  #4  0x0000000000aea319 in decode_digits_list_mode (self\u003d..., ls\u003d..., line\u003d...) at ../../src/gdb/linespec.c:3939\n  #5  0x0000000000ae4684 in create_sals_line_offset (self\u003d..., ls\u003d...) at ../../src/gdb/linespec.c:2039\n  #6  0x0000000000ae557f in convert_linespec_to_sals (state\u003d..., ls\u003d...) at ../../src/gdb/linespec.c:2289\n  #7  0x0000000000ae6546 in parse_linespec (parser\u003d..., arg\u003d..., match_type\u003d...) at ../../src/gdb/linespec.c:2647\n  #8  0x0000000000ae7605 in location_spec_to_sals (parser\u003d..., locspec\u003d...) at ../../src/gdb/linespec.c:3045\n  #9  0x0000000000ae7c7f in decode_line_1 (locspec\u003d..., flags\u003d..., search_pspace\u003d..., default_symtab\u003d..., default_line\u003d...) at ../../src.dev-m/gdb/linespec.c:3167\n\nI think that this might not be what we really want to do here.  After\ndownloading the source file we\u0027ll end up with a filename within the\ndebuginfod download cache, which will be different for each\nobjfile (the cache partitions downloads based on build-id).  So if two\nsymtabs originate from the same source file, but are in two different\nobjfiles, then, when the source is on disk, the filenames for these\nsymtabs will be identical, and the symtabs will be considered\nequivalent by find_line_symtab.  But when debuginfod is downloading\nthe source the source paths will be different, and find_line_symtab\nwill consider the symtabs different.  This doesn\u0027t seem right to me.\nBut I\u0027m going to leave worrying about that for another day.\n\nGiven this last bug, I am of the opinion that the misplaced symtab is\nlikely a bug, though after commit a736ff7d886d, the only issue I can\nfind is the extra debuginfod download request, which isn\u0027t huge.  But\nstill, maybe just reducing the number of symtabs would be worth it?\n\nBut this patch isn\u0027t about fixing any bugs, it\u0027s about adding a test\ncase for an issue that was a problem, but isn\u0027t any longer.\n\nApproved-By: Tom Tromey \u003ctom@tromey.com\u003e\n",
  "tree_diff": [
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "bd70ff524587a03c55d4929718a9a5ca678d5d2f",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.debuginfod/solib-with-dwz-bar.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "d7b014bc90ad3e59150f5cdd60b52f04333ddd2d",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.debuginfod/solib-with-dwz-bar.h"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "653d5d497c399f3b95d446544a56b1ac1c99e07b",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.debuginfod/solib-with-dwz-common.h"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "145a2e29c86ad271ae7965dc8cdaa8fee2578c9b",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.debuginfod/solib-with-dwz-foo.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "65eb58ae28cbcfdf730136272e85d8ad573a5458",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.debuginfod/solib-with-dwz-foo.h"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "10214a7e40acce59504faa337b0e41f2934cb087",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.debuginfod/solib-with-dwz-main.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "d30707d54338ebaecd4a17dd61a1974319594f02",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.debuginfod/solib-with-dwz.exp"
    },
    {
      "type": "modify",
      "old_id": "bee758962a64ec9aefbcffa4705f387013ed6ca6",
      "old_mode": 33188,
      "old_path": "gdb/testsuite/lib/gdb.exp",
      "new_id": "68fd2b55ee73c13d3dafaeec80d5889b2a797157",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/lib/gdb.exp"
    }
  ]
}
