From patchwork Wed Sep 6 13:31:57 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 22677 Received: (qmail 61438 invoked by alias); 6 Sep 2017 13:32:03 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 61406 invoked by uid 89); 6 Sep 2017 13:32:02 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=afford, painful X-Spam-User: qpsmtpd, 2 recipients X-HELO: mx1.redhat.com DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com BD2DA86647 Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=palves@redhat.com Subject: Re: [RFC PATCH 0/3] Pretty-printing for errno To: Zack Weinberg References: <20170622224456.1358-1-zackw@panix.com> <3a7946e9-d178-f878-9774-64ff44bcf5df@redhat.com> <9490d183-a57b-b336-3131-6580e4773818@redhat.com> <2f28f69b-406f-65e5-40e1-ae65632ea4f0@redhat.com> <1d38297f-f430-ca73-6d3f-a67144d08eea@redhat.com> <7348d7d9-b339-b14f-3dea-31d17c996a2a@redhat.com> <4ed368f7-4469-4a49-c4e3-0c3afc18c121@redhat.com> Cc: GNU C Library , gdb@sourceware.org From: Pedro Alves Message-ID: Date: Wed, 6 Sep 2017 14:31:57 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.4.0 MIME-Version: 1.0 In-Reply-To: On 09/06/2017 02:05 PM, Zack Weinberg wrote: > On Tue, Sep 5, 2017 at 6:31 PM, Pedro Alves wrote: >> On 09/05/2017 10:15 PM, Zack Weinberg wrote: >>> >>> I don't understand why thread-local variables are inaccessible on my >>> perfectly ordinary x86_64-unknown-linux-gnu workstation (the base OS >>> is Debian 'stretch'). Do you have any idea what might be wrong? >> >> I assume your test program isn't actually linked with -pthread? > > That is correct. > >> When you do "print errno" in this situation, because there's no >> "#define errno ..." in sight, gdb ends up finding the real "errno" symbol, >> which, even though the program isn't threaded, is a TLS symbol, and as such has >> a DWARF location expression describing its location as an offset into the >> thread-local block for the current thread. GDB needs to resolve that address, and >> for threaded programs that is normally done with assistance from libthread_db.so. >> The problem is then that libthread_db.so only works with programs that >> link with libpthread.so, and if your test program is actually non-threaded, >> it doesn't link with libpthread.so. > > I am not familiar with the glibc-side TLS implementation, nor with > libthread_db.so, nor the code in GDB that uses libthread_db.so. > However, reading the implementation of td_thr_tls_get_addr leads me to > believe that that function is *supposed* to work even if libpthread.so > has not been loaded into the 'inferior'. If it doesn't, perhaps that > is a bug on our side. Do you know if GDB even tries? It's not obvious > to me looking at linux-thread-db.c. GDB only tries to load libthread_db.so if it detects libpthread.so loaded in the inferior. gdb/linux-thread-db.c:thread_db_new_objfile is called for every shared library found in the inferior. However, if we hack gdb like this to force it to always try to load libthread_db.so: we get: (gdb) set debug libthread-db 1 (gdb) nosharedlibrary (gdb) sharedlibrary Reading symbols from /lib64/libc.so.6...Reading symbols from /usr/lib/debug/usr/lib64/libc-2.22.so.debug...done. done. Trying host libthread_db library: libthread_db.so.1. Host libthread_db.so.1 resolved to: /lib64/libthread_db.so.1. td_ta_new failed: application not linked with libthread thread_db_load_search returning 0 ... That "td_ta_new failed: application not linked with libthread" error is output by thread_db_err_str in linux-thread-db.c. It's just pretty-printing TD_NOLIBTHREAD. I.e., opening a connection to libthread_db.so fails: /* Now attempt to open a connection to the thread library. */ err = info->td_ta_new_p (&info->proc_handle, &info->thread_agent); if (err != TD_OK) { Because lithread_db.so itself "rejects" the inferior. If we repeat the exercise with a breakpoint on gdb's ps_pglobal_lookup (from proc-service.c, which implements the libthread_db.so -> gdb callback interface), we see: [note to self: IWBN to make "set debug libthread-db 1" print these look ups] ... (gdb) sharedlibrary Reading symbols from /lib64/libc.so.6...Reading symbols from /usr/lib/debug/usr/lib64/libc-2.22.so.debug...done. done. Trying host libthread_db library: libthread_db.so.1. Host libthread_db.so.1 resolved to: /lib64/libthread_db.so.1. Breakpoint 3, ps_pglobal_lookup (ph=0x1f04100, obj=0x7fffe58c50f8 "libpthread.so.0", name=0x7fffe58c5221 "nptl_version", sym_addr=0x7fffffffcf78) at src/gdb/proc-service.c:113 113 struct inferior *inf = find_inferior_ptid (ph->ptid); (top-gdb) c Continuing. td_ta_new failed: application not linked with libthread thread_db_load_search returning 0 ... So in order to check whether the inferior is running a matching glibc version, libthread_db.so looks for a "nptl_version" symbol, which is only defined in libpthread.so, so not found in the inferior. I did not try to hack gdb to return some address that happens to point at a value that happens to match the expected version. I think that for that experiment, hacking libthread_db.so itself to skip the checks would likely be easier [and I can't afford to do it right now]. > >> A workaround specifically for errno, and only for live-process debugging [*] >> is the "macro define" trick I had suggested before: >> >> (gdb) macro define errno (*__errno_location ()) >> >> After that, "p errno" ends up calling __errno_location just >> like when you compile the test program with -g3. > > Again, is it possible to do (the equivalent of) this from the > initialization code of a pretty-printer module? Specifically, there > already exists a Python function that's doing this: > > def register(objfile): > """Register pretty printers for the current objfile.""" > > printer = gdb.printing.RegexpCollectionPrettyPrinter("glibc-errno") > printer.add_printer('error_t', r'^(?:__)?error_t', ErrnoPrinter) > > if objfile == None: > objfile = gdb > > gdb.printing.register_pretty_printer(objfile, printer) > > called when the module is loaded; what would I need to add to that so > that the macro is defined (if it isn't already)? I'm hoping that other people more experienced with the gdb Python API can chime in. My idea was just to call gdb.execute ("macro define errno (*(int *) __errno_location ())") somewhere around your Python code. Thanks, Pedro Alves diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index 6d98135..6cf634e 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -1000,8 +1000,11 @@ thread_db_new_objfile (struct objfile *objfile) For dynamically linked executables, libpthread can be near the end of the list of shared libraries to load, and in an app of several thousand shared libraries, this can otherwise be painful. */ +#if 0 && ((objfile->flags & OBJF_MAINLINE) != 0 - || libpthread_name_p (objfile_name (objfile)))) + || libpthread_name_p (objfile_name (objfile))) +#endif + ) check_for_thread_db (); }