From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id H3H2Ev0Au2jQdBcAWB0awg (envelope-from ) for ; Fri, 05 Sep 2025 11:25:49 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=KOdjvAq3; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 38A521E087; Fri, 05 Sep 2025 11:25:49 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-1.8 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_LOW,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=no autolearn_force=no version=4.0.1 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id BBCD51E023 for ; Fri, 05 Sep 2025 11:25:46 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3E6043857823 for ; Fri, 5 Sep 2025 15:25:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3E6043857823 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=KOdjvAq3 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id EE22C3858D37 for ; Fri, 5 Sep 2025 15:25:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EE22C3858D37 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org EE22C3858D37 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1757085901; cv=none; b=lHU0Da6sUVvUWLnzBbyjAIErklasQeeJ0bUSyvxzxt33RaGnG9sNMCYNDzyjk7cEhjXKzKiZpfm0qTIlomkP1U8AwOkWLwCOscfg/UEvzqoucCXks7XnaTBiwNl4aZ2b1i6HNHNFbUWff9QQPkFR+yCamVEgTHJSMmuh8KyWpFI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1757085901; c=relaxed/simple; bh=ahixIfJb4HeVWWAwWQz6xxl2QMwYf7q7MPIElvavmLk=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=X5Fo2D60+TOQIHE6Lo5/Mf4qnIai5RGBpZ36RDMH4NPOBY8sXGcKSu1lbl9jXfM1/NI+YEBQfxooqGH2EU4CSCseYzmFoIJVtPXay9ko8EP9kenO3i4Kta8QCDCOoC6eOrdjbv4DAaPuy6HUdnaflN+VFdyN2bjTUKyP6ZIxs0c= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org EE22C3858D37 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1757085900; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=I1KbGv0kJEPhWmNXUpkHhJKKWsI15cR4Yd8Bt+XvReY=; b=KOdjvAq3ciN/7Brs3bx0LWuJ+R05++zP0b2yyP3SHU0fniY3gEA98fSfOwuADGnwtpVgHW WPa1/3e0T0hf38eE4E8aFFcpn9XYToVNUVMXNNGT/anOgg6COyGIotBTN1Jf09SC6wX+2F yckMdh7GbeW8AuvhM/xOj83+PQQl88s= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-392-ojzFJHTYO6aEvG_Tb9OKUA-1; Fri, 05 Sep 2025 11:24:58 -0400 X-MC-Unique: ojzFJHTYO6aEvG_Tb9OKUA-1 X-Mimecast-MFC-AGG-ID: ojzFJHTYO6aEvG_Tb9OKUA_1757085897 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-45dd62a0dd2so4918105e9.1 for ; Fri, 05 Sep 2025 08:24:58 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1757085897; x=1757690697; h=mime-version:message-id:date:references:in-reply-to:subject:cc:to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=I1KbGv0kJEPhWmNXUpkHhJKKWsI15cR4Yd8Bt+XvReY=; b=FJMMFWXg7qyNkXYBpvoQ+yrKuORn6O146m7cZftYU8jp3w9EY/0LRFGGr9YSOrFZsj jAog85wPMp5lwXkcv2OD8oOTGZWvG/wubL2sJsbVet9VbgGoaJiHZZ5eCvLoHXlFwHSp A3nMqGju/zxlmfQnqjQeusUg7Iwd+VJ2z51kZ+KX/Bhv1U+zbgcYud2aC0ap9HDWwQHv ya0OaiGeCV5aV7+Jigw2Fr4UZysZf6628Vv22zvRflpXIdKaxKWzNwC5Iz+0VORtIUce SI/Z5wvf1tAXj4Qv3Hotr1LFHAYvKOTetXXFTQ99KDKU7xLvPKSQmyIrq6wwGOrxMaIc azUA== X-Forwarded-Encrypted: i=1; AJvYcCXwbD4eNbrSS0INKYhjNrmheyBTF5jL/CsHjS0tdCw4jj6FP5z113NuQqN0k/Y0+pBF0A+YO2jz/udOXA==@sourceware.org X-Gm-Message-State: AOJu0YyF9XurKQ0y+980YGFGaQe5BpPhR2rB+FjOincl1kcpJketXXoY jHoJpm40GUa9gkz7XYDqrqnvRGKgM2e3HLrAq0crev3dMW6+9Jky932qqX8yuf0sWkNwl1GUL9l JEczc8PiYF7SWNFSjeaRhdEDkprkUT8viN3KuUBP2O6K4VaFUA+WXlOO6xbzFli8oir4i43c= X-Gm-Gg: ASbGncsysMCDR01KRTBcghf5820sJcoPIaeA+fK/RdfF1iNAh7bnlp3dHBCwbjPZDky lmZpSHTwnZqb1rST8Kfr1qZliBg2W9uHaPAe+mdpkmyBI+DDOfvNcJgNAxkHHEdhLsyo6RD72oM p4+nUvvNj0cq46bVfZgrTdj5jQ3o2NntMq7hU+2n0TMWv1lU6fYcoLGGkPYLuXB5z+VnMKX1q33 IsKQaawC77/wEEzlITD9l6NmpzSs70B7tT4bMllZfkYrbPbF+Rt0tSPZEGGt+0iyRx94erHPl7d 631CPMq6cjW1yu8NwyPmoVkeGYJhGcxOrPU= X-Received: by 2002:a05:600c:8706:b0:45c:b523:5a09 with SMTP id 5b1f17b1804b1-45dd5b52cacmr37265275e9.16.1757085896121; Fri, 05 Sep 2025 08:24:56 -0700 (PDT) X-Google-Smtp-Source: AGHT+IG0VAaMeL0MoA0Srztvz9lhVsMifqabNhXW3DmhDkAW4y/2rCaD6lhaUGTEMAqmPXtzU5jJZg== X-Received: by 2002:a05:600c:8706:b0:45c:b523:5a09 with SMTP id 5b1f17b1804b1-45dd5b52cacmr37264815e9.16.1757085895120; Fri, 05 Sep 2025 08:24:55 -0700 (PDT) Received: from localhost ([31.111.84.207]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-45dd296ed51sm74973595e9.3.2025.09.05.08.24.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 05 Sep 2025 08:24:54 -0700 (PDT) From: Andrew Burgess To: Kevin Buettner , gdb-patches@sourceware.org Cc: Kevin Buettner Subject: Re: [PATCH v2] gdb/dwarf2: Add symbols for function declarations In-Reply-To: <20250703194719.2254338-1-kevinb@redhat.com> References: <20250703194719.2254338-1-kevinb@redhat.com> Date: Fri, 05 Sep 2025 16:24:53 +0100 Message-ID: <87qzwkj516.fsf@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: tBgknNVeLMqta-3y30eEiZneibdxqLeK-ulnrvb_oOg_1757085897 X-Mimecast-Originator: redhat.com Content-Type: text/plain X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Kevin Buettner writes: > This commit was motivated by comments 3 and 4 for bug 31563: > > https://sourceware.org/bugzilla/show_bug.cgi?id=31563#c3 > > When a program is built with -g3, macro information is available to > GDB; for errno, the macro defined in /usr/include/errno.h (provided by > GLIBC) looks like this: > > # define errno (*__errno_location ()) > > However, up to now, GDB doesn't know the type of __errno_location, > despite (sometimes) having a DIE representing a declaration providing > its type. In any case, apparently not knowing the return type of > __errno_location, GDB was unable to perform the inferior function call > specified by the errno macro: > > (gdb) p errno > '__errno_location' has unknown return type; cast the call to its > declared return type > > But, for some compilers, GDB *should* be able to know the type. These > are the DIEs related to the __errno_location declaration from the > "macros" case for the gdb.base/errno.exp test: > > <1><37>: Abbrev Number: 2 (DW_TAG_subprogram) > <38> DW_AT_external : 1 > <38> DW_AT_name : (indirect string, offset: 0x20e4): > __errno_location > <3c> DW_AT_decl_file : 2 > <3d> DW_AT_decl_line : 37 > <3e> DW_AT_decl_column : 13 > <3f> DW_AT_prototyped : 1 > <3f> DW_AT_type : <0x43> > <43> DW_AT_declaration : 1 > <1><43>: Abbrev Number: 3 (DW_TAG_pointer_type) > <44> DW_AT_byte_size : 8 > <45> DW_AT_type : <0x49> > <1><49>: Abbrev Number: 4 (DW_TAG_base_type) > <4a> DW_AT_byte_size : 4 > <4b> DW_AT_encoding : 5 (signed) > <4c> DW_AT_name : int > > If you wish to see this for yourself, from your gdb build directory, > do: > > make check TESTS=gdb.base/errno.exp > readelf -w testsuite/outputs/gdb.base/errno/errno-macros | less > > With this commit in place, using gcc as the C compiler, 8 XFAILs in > gdb.base/errno.exp turn into PASSes. They are: > > XFAIL: gdb.base/errno.exp: macros: print (int) errno > XFAIL: gdb.base/errno.exp: macros: print errno > XFAIL: gdb.base/errno.exp: pthreads-macros: print (int) errno > XFAIL: gdb.base/errno.exp: pthreads-macros: print errno > XFAIL: gdb.base/errno.exp: pthreads-static-macros: print (int) errno > XFAIL: gdb.base/errno.exp: pthreads-static-macros: print errno > XFAIL: gdb.base/errno.exp: static-macros: print (int) errno > XFAIL: gdb.base/errno.exp: static-macros: print errno > > For the example shown earlier, GDB is now able to print the correct > value for errno. > > As mentioned earlier, it doesn't work for all compliers. In > particular, when clang is used instead, there's (currently) no change > in results in the errno.exp test since clang doesn't provide the > necessary declaration(s) in its DWARF output. > > Perhaps even more compelling is being able to call functions like > malloc() without having debug info for the C library. To demonstrate > this, I'll use the test program from gdb.base/break.exp. After > starting the program (and not letting debuginfod fetch GLIBC's > symbols), an unpatched GDB will show: > > (gdb) ptype malloc > type = () > (gdb) p malloc(4) > 'malloc' has unknown return type; cast the call to its declared > return type > > However, with this commit, we now see: > > (gdb) ptype malloc > type = void *(unsigned long) > (gdb) p malloc(4) > $1 = (void *) 0x4042a0 > > This commit changes the name of read_func_scope in gdb/dwarf2/read.c > to read_func_scope_or_decl, changing all callers. I also added a > comment for this function. > > It introduces a new function, die_is_func_decl_p and uses it in > read_func_scope_or_decl(). If the call to die_is_func_decl_p() > returns true, the code in read_func_scope_or_decl which attempts to > get the function bounds is skipped and, after existing code which > attempts to do some template related stuff happens, a new symbol with > address class LOC_UNRESOLVED will be added. > > If just this change alone is made and regression testing is performed, > there are quite a few regressions (well over 50, as I recall), mostly > due to the fact that the PLT symbol / declaration is now found in > various cases, perhaps ahead of the symbol for the function > definition. I'll go into depth regarding the various cases, below. > > Many of the regressions were fixed by making the LOC_UNRESOLVED case > in language_defn::read_var_value in gdb/findvar.c prefer "normal" > symbols over PLT symbols, though the PLT symbol will be used if no > normal symbol is found. > > This change contains a (perhaps) surprising addition to deal with GNU > ifunc symbols: > > if (bmsym.minsym->type () == mst_text_gnu_ifunc) > { > /* GNU ifunc code elsewhere in GDB depends > on the symbol's type being set as shown > below. But, coming into this function, > VAR might have an arguably better type > obtained from a declaration, i.e. > DW_AT_declaration. In this case, the > PLT (solib trampoline) symbol is > usually found first; see above. > Nevertheless, we change the type to > what the rest of GDB expects in order > for the rest of the GNU ifunc related > code in GDB to work. */ > type = builtin_type (objfile) > ->nodebug_text_gnu_ifunc_symbol; > } > > Hopefully, the comment adequately describes what this is about, but > I'll note that without this particular bit of code, we see the > following GNU ifunc related failures: > > FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0: > final_debug=0: gdb-command

> FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0: > final_debug=0: gdb-command

> FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0: > final_debug=0: p gnu_ifunc executing > FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0: > final_debug=0: p gnu_ifunc() > FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0: > final_debug=0: resolver received HWCAP > > There are 17 more, but they're essentially repeats of the above, with > varying resolver_attr, resolver_debug, and final_debug cases. > > The change to info_address_command in gdb/printcmd.c forces execution > into the minimal symbol lookup case when presented with a > LOC_UNRESOLVED function symbol. Without this change, there were 12 > falures in gdb.base/gnu-ifunc.exp, two of which look like this: > > FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0: > final_debug=0: info addr gnu_ifunc > FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0: > final_debug=0: info sym > > The remaining failures are similar, only differing in the values > for resolver_attr, resolver_debug, and final_debug. > > With regard to the failure itself, for the first one, the log output > looks like this: > > info addr gnu_ifunc > Symbol "gnu_ifunc" is static storage at address 0x7ffff7fbb389. > (gdb) FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0: > final_debug=0: info addr gnu_ifunc > > The expected message from "info addr gnu_ifunc" was: > > Symbol "gnu_ifunc" is at 0x7ffff7fbb389 in a file compiled without > debugging. > > I don't think that the FAILing message is wrong, but I think that the > PASSing message (regarding being in a file without debugging) is more > helpful to the user. > > It bothered me that the only tests which caught this problem were > in gdb.base/gnu-ifunc.exp. There is now an "info addr foo" test > in the new test case gdb.dwarf2/func-decl.exp which also performs > this test. > > With the above change in place, we then see these failures: > > FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: > final_debug=0: info addr gnu_ifunc > FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: > final_debug=1: info addr gnu_ifunc > > (There are two others for "info sym ".) > > In each case, we now see a message like this... > > Symbol "gnu_ifunc" is at 0x7ffff7fbb389 in a file compiled without > debugging. > > ...when we should in fact see: > > Symbol "gnu_ifunc" is a function at address 0x7ffff7fbb389. > > Note that this is for the resolver_debug=1 case; for this case, the > resolver library has symbols, so the latter message makes sense and > the "failing" message is just plain wrong. > > These new failures are fixed by the change to > lookup_global_or_static_symbol in gdb/symtab.c. In this change, > normal function symbols are preferred to those whose address class is > LOC_UNRESOLVED. I used a similar approach to that for > language_defn::read_var_value, discussed earlier. > > Again, it seemed to me that there should be a non-gnu-ifunc test > for this, so I added one; it'll be tested by: > > gdb.dwarf2/func-decl.exp: lib_debug: info addr foo > > There were also regressions in gdb.base/info-fun.exp and > gdb.mi/mi-sym-info.exp: > > FAIL: gdb.base/info-fun.exp: n_flag=0: IN: info fun foo > FAIL: gdb.base/info-fun.exp: n_flag=0: NO: info fun foo > FAIL: gdb.base/info-fun.exp: n_flag=0: SEP: info fun foo > FAIL: gdb.base/info-fun.exp: n_flag=1: IN: info fun -n foo > FAIL: gdb.base/info-fun.exp: n_flag=1: NO: info fun -n foo > FAIL: gdb.base/info-fun.exp: n_flag=1: SEP: info fun -n foo > FAIL: gdb.mi/mi-sym-info.exp: List all functions matching pattern f3 > (unexpected output) > > For each of these failures, there was more output than expected. For > example, for one of the failing cases... > > (gdb) info fun foo > All functions matching regular expression "foo": > > File .../gdb/testsuite/gdb.base/info-fun.c: > 16: int foo(void); > > Non-debugging symbols: > 0x0000000000400370 foo@plt > 0x00007ffff7fbb389 foo > (gdb) FAIL: gdb.base/info-fun.exp: n_flag=0: NO: info fun foo > > The "passing" output looks like this: > > (gdb) info fun foo > All functions matching regular expression "foo": > > Non-debugging symbols: > 0x0000000000400370 foo@plt > 0x00007ffff7fbb389 foo > (gdb) PASS: gdb.base/info-fun.exp: n_flag=0: NO: info fun foo > > At first glance, the "failing" output looks useful; perhaps it could > be, but I'll note that the extra lines being output are for a > declaration for a function which is not in the CU where the function > is defined. I have a hunch that we might be overwhelmed by extra > output in a program with many libraries - it's conceivable that for > some symbols, each library would have its own declaration. > > In any case, I was able to obtain the original / passing behavior > by discarding LOC_UNRESOLVED symbols when searching in the > function domain in global_symbol_searcher::add_matching_symbols. > > Finally, there were two regressions in gdb.base/completion.exp: > > FAIL: gdb.base/completion.exp: complete break break.c:ma > FAIL: gdb.base/completion.exp: tab complete break break.c:ma (timeout) > > The log file for these failing tests is not especially helpful, but I > debugged it by throwing a "gdb_interact" into the test to see what > was going on. As I recall, when trying to complete "break.c:ma", > "marker1", "marker2", "marker3", "marker4", and "malloc" were all > being found in addition to "main", which is what the what the testcase > was expecting to be the sole completion. > > This problem was fixed by adjusting completion_skip_symbol in > symtab.h. > > The new test case, gdb.dwarf2/func-decl.exp, contains, in addition to > the tests already discussed, two tests which will fail in a GDB built > without this commit and pass in a GDB built with it... > > PASS: gdb.dwarf2/func-decl.exp: no_lib_debug: > gdb-command > PASS: gdb.dwarf2/func-decl.exp: no_lib_debug: ptype foo > > The remaining tests in gdb.dwarf2/func-decl.exp should all pass in a > GDB built with or without this commit. They will only fail if one of > the relevant changes discussed above is missing or becomes broken for > some reason (perhaps due to some future change to this area of the > code). > > Regarding the use of the DWARF assembler in the test... Using some > version(s) of GNU C, it's possible to write a test which causes a > suitable declaration DIE to be placed in the DWARF output. In fact, I > originally wrote most of the new test without the DWARF assembler. > But not all compilers do this, e.g. clang does not, and I wanted a > test which would test this functionality regardless of whether the > compiler generates the DWARF required for this test. > > I've tested on Fedora 42 w/ architectures x86_64, aarch64, riscv, > s390x, and ppc64le. On x86_64 Fedora 42, I've also tested with > --target_board=unix/-m32, --target_board=native-gdbserver, and > --target_board=native-extended-gdbserver. No regressions found. > > After skimming version 1 of this commit, Tom Tromey suggested that > there should also be changes to the indexer. This version 2 commit > adds that by making DW_TAG_subprogram declarations "interesting" to > the indexer. The changes which do this are in gdb/dwarf2/abbrev.c > and gdb/dwarf2/cooked-indexer.c. I also added a test to the new > test case which attempts to do "ptype foo" prior to starting the > program. This failed when using version 1 of this commit, but > passes now. > > Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31563 I'm not really super familiar with all of the symbol management logic, ideally Tom would take a look through this. But for what it's worth, after having a look through, this all made sense to me. It's a shame about the ifunc related block in findvar.c, I did play for a while to see if I could figure out a way to avoid that code, but I couldn't come up with anything better. What we'd need I believe, is for the declaration's type to be marked by a call to 'type->set_is_gnu_ifunc (true);'. If, as a total hack, we mark all declaration types that way in dwarf2/read.c when they are created, then the findvar.c ifunc block is no longer needed. The problem, of course, is figuring out in dwarf2/read.c which types should be marked, and which shouldn't, and I don't think you know the answer to that until you've looked at the objfile that contains the function implementation... As I said, I don't consider myself expert enough to give an approval tag, so this will have to do: Reviewed-By: Andrew Burgess Thanks, Andrew > --- > gdb/dwarf2/abbrev.c | 3 + > gdb/dwarf2/cooked-indexer.c | 5 + > gdb/dwarf2/read.c | 108 ++++++++++---- > gdb/findvar.c | 46 +++++- > gdb/printcmd.c | 4 +- > gdb/symtab.c | 43 ++++-- > gdb/symtab.h | 20 ++- > gdb/testsuite/gdb.dwarf2/func-decl-lib.c | 24 +++ > gdb/testsuite/gdb.dwarf2/func-decl.c | 35 +++++ > gdb/testsuite/gdb.dwarf2/func-decl.exp | 182 +++++++++++++++++++++++ > 10 files changed, 427 insertions(+), 43 deletions(-) > create mode 100644 gdb/testsuite/gdb.dwarf2/func-decl-lib.c > create mode 100644 gdb/testsuite/gdb.dwarf2/func-decl.c > create mode 100644 gdb/testsuite/gdb.dwarf2/func-decl.exp > > diff --git a/gdb/dwarf2/abbrev.c b/gdb/dwarf2/abbrev.c > index 5cfff69cc3b..2ca80ab8a6a 100644 > --- a/gdb/dwarf2/abbrev.c > +++ b/gdb/dwarf2/abbrev.c > @@ -240,6 +240,9 @@ abbrev_table::read (struct dwarf2_section_info *section, > the correct scope. */ > cur_abbrev->interesting = true; > } > + else if (has_hardcoded_declaration > + && cur_abbrev->tag == DW_TAG_subprogram) > + cur_abbrev->interesting = true; > else if (has_hardcoded_declaration > && (cur_abbrev->tag != DW_TAG_variable || !has_external)) > cur_abbrev->interesting = false; > diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c > index c093984bae0..710ef82ed0d 100644 > --- a/gdb/dwarf2/cooked-indexer.c > +++ b/gdb/dwarf2/cooked-indexer.c > @@ -301,6 +301,11 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu, > || abbrev->tag == DW_TAG_namespace) > && abbrev->has_children) > *flags |= IS_TYPE_DECLARATION; > + else if (abbrev->tag == DW_TAG_subprogram) > + { > + /* We want to index function declarations - do nothing in order > + to avoid nulling out *name, below. */ > + } > else > { > *linkage_name = nullptr; > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c > index 5e18e452061..ea6f508d96e 100644 > --- a/gdb/dwarf2/read.c > +++ b/gdb/dwarf2/read.c > @@ -842,7 +842,7 @@ static void read_file_scope (struct die_info *, struct dwarf2_cu *); > > static void read_type_unit_scope (struct die_info *, struct dwarf2_cu *); > > -static void read_func_scope (struct die_info *, struct dwarf2_cu *); > +static void read_func_scope_or_decl (struct die_info *, struct dwarf2_cu *); > > static void read_lexical_block_scope (struct die_info *, struct dwarf2_cu *); > > @@ -5012,7 +5012,7 @@ process_die (struct die_info *die, struct dwarf2_cu *cu) > /* Fall through. */ > case DW_TAG_entry_point: > case DW_TAG_inlined_subroutine: > - read_func_scope (die, cu); > + read_func_scope_or_decl (die, cu); > break; > case DW_TAG_lexical_block: > case DW_TAG_try_block: > @@ -8366,8 +8366,42 @@ fixup_low_high_pc (struct dwarf2_cu *cu, struct die_info *die, CORE_ADDR *low_pc > } > } > > +/* Return true if DIE represents a prototyped function declaration > + with a return type and the function named NAME has a minimal > + symbol in the CU's objfile. (NAME has already been extracted > + from the DIE.) Return false otherwise. */ > + > +static bool > +die_is_func_decl_p (struct die_info *die, struct dwarf2_cu *cu, > + const char * name) > +{ > + if (die->tag != DW_TAG_subprogram) > + return false; > + > + attribute *attr = dwarf2_attr (die, DW_AT_declaration, cu); > + if (attr == nullptr || !attr->as_boolean ()) > + return false; > + > + attr = dwarf2_attr (die, DW_AT_type, cu); > + if (attr == nullptr) > + return false; > + > + attr = dwarf2_attr (die, DW_AT_prototyped, cu); > + if (attr == nullptr || !attr->as_boolean ()) > + return false; > + > + bound_minimal_symbol mfunsym > + = lookup_minimal_symbol (current_program_space, name, > + cu->per_objfile->objfile); > + > + return (mfunsym.minsym != nullptr); > +} > + > +/* Record symbol and related info for a function defined in > + CU or for certain declarations (defined elsewhere). */ > + > static void > -read_func_scope (struct die_info *die, struct dwarf2_cu *cu) > +read_func_scope_or_decl (struct die_info *die, struct dwarf2_cu *cu) > { > dwarf2_per_objfile *per_objfile = cu->per_objfile; > struct objfile *objfile = per_objfile->objfile; > @@ -8417,37 +8451,44 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) > return; > } > > - /* Ignore functions with missing or invalid low and high pc attributes. */ > - unrelocated_addr unrel_low, unrel_high; > - if (dwarf2_get_pc_bounds (die, &unrel_low, &unrel_high, cu, nullptr, nullptr) > - <= PC_BOUNDS_INVALID) > + bool is_decl = die_is_func_decl_p (die, cu, name); > + > + /* If not a declaration, try to find the function bounds. */ > + if (!is_decl) > { > - if (have_complaint ()) > + /* Ignore functions with missing or invalid low and high pc > + attributes. */ > + unrelocated_addr unrel_low, unrel_high; > + if ((dwarf2_get_pc_bounds (die, &unrel_low, &unrel_high, cu, > + nullptr, nullptr) <= PC_BOUNDS_INVALID)) > { > - attr = dwarf2_attr (die, DW_AT_external, cu); > - bool external_p = attr != nullptr && attr->as_boolean (); > - attr = dwarf2_attr (die, DW_AT_inline, cu); > - bool inlined_p = false; > - if (attr != nullptr) > + if (have_complaint ()) > { > - std::optional value = attr->unsigned_constant (); > - inlined_p = (value.has_value () > - && (*value == DW_INL_inlined > - || *value == DW_INL_declared_inlined)); > + attr = dwarf2_attr (die, DW_AT_external, cu); > + bool external_p = attr != nullptr && attr->as_boolean (); > + attr = dwarf2_attr (die, DW_AT_inline, cu); > + bool inlined_p = false; > + if (attr != nullptr) > + { > + std::optional value = attr->unsigned_constant (); > + inlined_p = (value.has_value () > + && (*value == DW_INL_inlined > + || *value == DW_INL_declared_inlined)); > + } > + attr = dwarf2_attr (die, DW_AT_declaration, cu); > + bool decl_p = attr != nullptr && attr->as_boolean (); > + if (!external_p && !inlined_p && !decl_p) > + complaint (_("cannot get low and high bounds " > + "for subprogram DIE at %s"), > + sect_offset_str (die->sect_off)); > } > - attr = dwarf2_attr (die, DW_AT_declaration, cu); > - bool decl_p = attr != nullptr && attr->as_boolean (); > - if (!external_p && !inlined_p && !decl_p) > - complaint (_("cannot get low and high bounds " > - "for subprogram DIE at %s"), > - sect_offset_str (die->sect_off)); > + return; > } > - return; > - } > > - lowpc = per_objfile->relocate (unrel_low); > - highpc = per_objfile->relocate (unrel_high); > - fixup_low_high_pc (cu, die, &lowpc, &highpc); > + lowpc = per_objfile->relocate (unrel_low); > + highpc = per_objfile->relocate (unrel_high); > + fixup_low_high_pc (cu, die, &lowpc, &highpc); > + } > > /* If we have any template arguments, then we must allocate a > different sort of symbol. */ > @@ -8462,6 +8503,15 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) > } > } > > + /* If it's a declaration, record the symbol and return. */ > + if (is_decl) > + { > + struct symbol *sym = > + new_symbol (die, read_type_die (die, cu), cu, templ_func); > + sym->set_aclass_index (LOC_UNRESOLVED); > + return; > + } > + > gdb_assert (cu->get_builder () != nullptr); > newobj = cu->get_builder ()->push_context (0, lowpc); > newobj->name = new_symbol (die, read_type_die (die, cu), cu, templ_func); > @@ -11259,7 +11309,7 @@ handle_struct_member_die (struct die_info *child_die, struct type *type, > However, it does emit ordinary functions as children > of a struct DIE. */ > if (cu->lang () == language_rust) > - read_func_scope (child_die, cu); > + read_func_scope_or_decl (child_die, cu); > else > { > /* C++ member function. */ > diff --git a/gdb/findvar.c b/gdb/findvar.c > index 9da5c4831a6..4934cf4c2a4 100644 > --- a/gdb/findvar.c > +++ b/gdb/findvar.c > @@ -444,19 +444,59 @@ language_defn::read_var_value (struct symbol *var, > { > struct obj_section *obj_section; > bound_minimal_symbol bmsym; > + bound_minimal_symbol bmsym_solib_tramp; > > gdbarch_iterate_over_objfiles_in_search_order > (var->arch (), > - [var, &bmsym] (objfile *objfile) > + [var, &bmsym, &bmsym_solib_tramp, &type] (objfile *objfile) > { > bmsym = lookup_minimal_symbol (current_program_space, > var->linkage_name (), objfile); > > - /* Stop if a match is found. */ > - return bmsym.minsym != nullptr; > + if (bmsym.minsym != nullptr) > + { > + if (bmsym.minsym->type () == mst_solib_trampoline) > + { > + /* Stash the trampoline symbol in case no > + better symbol is found. */ > + if (bmsym_solib_tramp.minsym == nullptr) > + bmsym_solib_tramp = bmsym; > + /* Keep searching... */ > + bmsym = {}; > + return false; > + } > + else > + { > + if (bmsym.minsym->type () == mst_text_gnu_ifunc) > + { > + /* GNU ifunc code elsewhere in GDB depends > + on the symbol's type being set as shown > + below. But, coming into this function, > + VAR might have an arguably better type > + obtained from a declaration, i.e. > + DW_AT_declaration. In this case, the > + PLT (solib trampoline) symbol is > + usually found first; see above. > + Nevertheless, we change the type to > + what the rest of GDB expects in order > + for the rest of the GNU ifunc related > + code in GDB to work. */ > + type = builtin_type (objfile) > + ->nodebug_text_gnu_ifunc_symbol; > + } > + return true; > + } > + } > + else > + return false; > }, > var->objfile ()); > > + /* Use the solib trampoline symbol if an alternative (non trampline) > + symbol wasn't found. */ > + if (bmsym.minsym == nullptr) > + bmsym = bmsym_solib_tramp; > + > /* If we can't find the minsym there's a problem in the symbol info. > The symbol exists in the debug info, but it's missing in the minsym > table. */ > diff --git a/gdb/printcmd.c b/gdb/printcmd.c > index 19fbc20074e..30be62587bd 100644 > --- a/gdb/printcmd.c > +++ b/gdb/printcmd.c > @@ -1585,7 +1585,9 @@ info_address_command (const char *exp, int from_tty) > > sym = lookup_symbol (exp, get_selected_block (&context_pc), SEARCH_VFT, > &is_a_field_of_this).symbol; > - if (sym == NULL) > + if (sym == NULL > + || (sym->aclass () == LOC_UNRESOLVED > + && symbol_is_function_or_method (sym))) > { > if (is_a_field_of_this.type != NULL) > { > diff --git a/gdb/symtab.c b/gdb/symtab.c > index 7d1a0b066c7..e71837cb16e 100644 > --- a/gdb/symtab.c > +++ b/gdb/symtab.c > @@ -2654,15 +2654,33 @@ lookup_global_or_static_symbol (const char *name, > > /* Do a global search (of global blocks, heh). */ > if (result.symbol == NULL) > - gdbarch_iterate_over_objfiles_in_search_order > - (objfile != NULL ? objfile->arch () : current_inferior ()->arch (), > - [&result, block_index, name, domain] (struct objfile *objfile_iter) > - { > - result = lookup_symbol_in_objfile (objfile_iter, block_index, > - name, domain); > - return result.symbol != nullptr; > - }, > - objfile); > + { > + struct block_symbol result_unresolved = {}; > + gdbarch_iterate_over_objfiles_in_search_order > + (objfile != NULL ? objfile->arch () : current_inferior ()->arch (), > + [&result, &result_unresolved, block_index, name, domain] > + (struct objfile *objfile_iter) > + { > + result = lookup_symbol_in_objfile (objfile_iter, block_index, > + name, domain); > + /* If RESULT is an unresolved function or method, keep track > + of it in case no other symbols are found, but prefer > + other matches over this one. */ > + if (result.symbol != nullptr > + && result.symbol->aclass () == LOC_UNRESOLVED > + && symbol_is_function_or_method (result.symbol)) > + { > + result_unresolved = result; > + result = {}; > + return false; > + } > + return result.symbol != nullptr; > + }, > + objfile); > + > + if (result.symbol == nullptr) > + result = result_unresolved; > + } > > if (result.symbol != NULL) > symbol_cache_mark_found (bsc, slot, objfile, result.symbol, result.block, > @@ -5019,6 +5037,13 @@ global_symbol_searcher::add_matching_symbols > && !treg_matches_sym_type_name (*treg, sym))) > continue; > > + if ((kind & SEARCH_FUNCTION_DOMAIN) != 0) > + { > + /* Don't include unresolved function symbols. */ > + if (sym->aclass () == LOC_UNRESOLVED) > + continue; > + } > + > if ((kind & SEARCH_VAR_DOMAIN) != 0) > { > if (sym->aclass () == LOC_UNRESOLVED > diff --git a/gdb/symtab.h b/gdb/symtab.h > index 0a57be5ed80..461ca9d3a1e 100644 > --- a/gdb/symtab.h > +++ b/gdb/symtab.h > @@ -2494,6 +2494,23 @@ extern bool symbol_is_function_or_method (symbol *sym); > > extern bool symbol_is_function_or_method (minimal_symbol *msymbol); > > +/* Return whether SYM is an unresolved symbol. */ > + > +static inline bool > +completion_symbol_is_unresolved (symbol *sym) > +{ > + return sym->aclass () == LOC_UNRESOLVED; > +} > + > +/* For the purposes of skipping symbols for completion, return whether > + MSYMBOL is unresolved. */ > + > +static inline bool > +completion_symbol_is_unresolved (minimal_symbol *msymbol) > +{ > + return false; > +} > + > /* Return whether SYM should be skipped in completion mode MODE. In > linespec mode, we're only interested in functions/methods. */ > > @@ -2502,7 +2519,8 @@ static bool > completion_skip_symbol (complete_symbol_mode mode, Symbol *sym) > { > return (mode == complete_symbol_mode::LINESPEC > - && !symbol_is_function_or_method (sym)); > + && (!symbol_is_function_or_method (sym) > + || completion_symbol_is_unresolved (sym))); > } > > /* symtab.c */ > diff --git a/gdb/testsuite/gdb.dwarf2/func-decl-lib.c b/gdb/testsuite/gdb.dwarf2/func-decl-lib.c > new file mode 100644 > index 00000000000..4aad59be7ab > --- /dev/null > +++ b/gdb/testsuite/gdb.dwarf2/func-decl-lib.c > @@ -0,0 +1,24 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2025 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 . */ > + > +#include > + > +int > +foo (char *s, int n) > +{ > + return ((int) strlen (s) + n); > +} > diff --git a/gdb/testsuite/gdb.dwarf2/func-decl.c b/gdb/testsuite/gdb.dwarf2/func-decl.c > new file mode 100644 > index 00000000000..62c49d5c456 > --- /dev/null > +++ b/gdb/testsuite/gdb.dwarf2/func-decl.c > @@ -0,0 +1,35 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2025 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 . */ > + > +extern int foo (char *s, int n); > + > +volatile int data; > + > +void > +use_it (int a) > +{ > + data = a; > +} > + > +int > +main (int argc, char **argv) > +{ > + asm ("main_label: .globl main_label"); > + int i; > + i = foo ("foo", 2); > + use_it (i); > +} > diff --git a/gdb/testsuite/gdb.dwarf2/func-decl.exp b/gdb/testsuite/gdb.dwarf2/func-decl.exp > new file mode 100644 > index 00000000000..316dfe8d04c > --- /dev/null > +++ b/gdb/testsuite/gdb.dwarf2/func-decl.exp > @@ -0,0 +1,182 @@ > +# Copyright 2025 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. > + > +# Test GDB's ability to access declarations for function symbols. On > +# Linux, using GCC, compiling the main program with -g and the shared > +# lib source file without -g can be used to create a compelling test > +# case without needing the DWARF assembler. However, we don't want to > +# count on the fact that the compiler will place a declaration for the > +# shared library function in the DWARF info for the main program. (E.g. > +# when using CLANG/LLVM, these DIEs are omitted.) Therefore, we use the > +# DWARF assembler to create the requisite DWARF info for this test. > + > +load_lib dwarf.exp > +require dwarf2_support > + > +standard_testfile .c -dw.S > + > +set asm_file [standard_output_file $srcfile2] > +set libsrc "${srcdir}/${subdir}/${testfile}-lib.c" > +set libobj [standard_output_file "${testfile}-lib.so"] > + > +# We need to know the size of integer and address types in order to > +# write some of the debugging info we'd like to generate. > +# > +# For that, we ask GDB by debugging our test program. Any program > +# would do, but since we already have program written specifically for > +# this testcase, we might as well use that. Note that we need to > +# also build the shared library that the test program uses. > + > +set session_options [list debug shlib=${libobj}] > +if { [gdb_compile_shlib $libsrc $libobj [list nodebug]] != "" } { > + untested "failed to compile shared object" > + return -1 > +} > + > +if { [prepare_for_testing "failed to prepare" ${binfile} \ > + [list $srcfile] $session_options] } { > + return -1 > +} > + > +with_test_prefix "first session" { > + if ![runto_main] { > + return > + } > + > + with_shared_gdb { > + # Rather than start a new session, declare the current session the > + # shared one. Otherwise, get_func_info would compile an executable > + # in a temp dir; due to implementation details, this means that the > + # shared lib won't be found. > + share_gdb ${srcdir}/${subdir}/$srcfile $session_options > + > + get_func_info main $session_options > + > + # Using the running GDB session, determine sizes of several types. > + set int_size [get_sizeof "int" -1] > + set char_ptr_size [get_sizeof "char *" 8] > + } > +} > + > +Dwarf::assemble $asm_file { > + cu {} { > + DW_TAG_compile_unit { > + {DW_AT_language @DW_LANG_C99} > + {DW_AT_name $::srcfile} > + } { > + declare_labels int_label char_label char_ptr_label > + > + int_label: DW_TAG_base_type { > + {DW_AT_byte_size ${::int_size} DW_FORM_udata} > + {DW_AT_encoding @DW_ATE_signed} > + {DW_AT_name "int"} > + } > + > + char_label: DW_TAG_base_type { > + {byte_size 1 sdata} > + {encoding @DW_ATE_signed_char} > + {name "char"} > + } > + > + char_ptr_label: DW_TAG_pointer_type { > + {DW_AT_byte_size ${::char_ptr_size} DW_FORM_sdata} > + {DW_AT_type :$char_label} > + } > + > + DW_TAG_subprogram { > + {DW_AT_external 1 flag} > + {DW_AT_name foo} > + {DW_AT_prototyped 1 DW_FORM_flag_present} > + {DW_AT_type :$int_label} > + {DW_AT_declaration 1 flag} > + } { > + DW_TAG_formal_parameter { > + {DW_AT_type :$char_ptr_label} > + } > + DW_TAG_formal_parameter { > + {DW_AT_type :$int_label} > + } > + } > + > + DW_TAG_subprogram { > + {DW_AT_name main} > + {DW_AT_low_pc ${::main_start} DW_FORM_addr} > + {DW_AT_high_pc ${::main_end} DW_FORM_addr} > + {DW_AT_type :$int_label} > + } > + } > + } > +} > + > +# Test against a shared library built with no debugging symbols. Due > +# to the DWARF info provided by the DWARF assembler above, there will > +# be a declaration for the shared lib symbol "foo" in the main > +# program. Thus, due to the lack of DWARF info in the shared library, > +# GDB can't know the type from the shared library. Instead, it must > +# rely on the declaration of foo from the main program. > +# > +# Due to that declaration, it should be possible to examine its type > +# as well as make an inferior function call. We expect "info addr foo" > +# to provide the address of the actual function instead of foo's PLT > +# in the main program. > + > +with_test_prefix no_lib_debug { > + if { [gdb_compile_shlib $libsrc $libobj [list nodebug]] != "" } { > + untested "failed to compile shared object" > + return -1 > + } > + > + if { [prepare_for_testing "failed to prepare" ${binfile} \ > + [list $srcfile $asm_file] [list nodebug shlib=${libobj}]] } { > + return -1 > + } > + > + with_test_prefix "before program start" { > + # Verify that the type of foo is available prior to starting > + # the program. > + gdb_test "ptype foo" "^type = int \\(char \\*, int\\)" > + } > + > + clean_restart $binfile > + > + if ![runto_main] { > + return > + } > + > + gdb_test "ptype foo" "^type = int \\(char \\*, int\\)" > + gdb_test "print foo \(\"abc\", 5\)" "= 8" > + gdb_test "info addr foo" "Symbol \"foo\" is at $::hex in a file compiled without debugging\\." > +} > + > +# Test again with a library built with debugging symbols. The > +# "info addr foo" test can fail if PLT symbols are preferred over > +# normal symbols when looking up a global or static symbol. > + > +with_test_prefix lib_debug { > + set binfile $binfile-debug > + set libobj [standard_output_file "${testfile}-lib-debug.so"] > + > + if { [gdb_compile_shlib $libsrc $libobj [list debug]] != "" } { > + untested "failed to compile shared object" > + return -1 > + } > + > + if { [prepare_for_testing "failed to prepare" ${binfile} \ > + [list $srcfile $asm_file] [list nodebug shlib=${libobj}]] } { > + return -1 > + } > + > + if ![runto_main] { > + return > + } > + > + gdb_test "info addr foo" "Symbol \"foo\" is a function at address $::hex\\." > +} > -- > 2.50.0