From patchwork Fri Dec 6 07:50:33 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 297610 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 4BA6F2C00A1 for ; Fri, 6 Dec 2013 18:50:54 +1100 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:reply-to:mime-version :content-type; q=dns; s=default; b=ZTw2PFX+XDYm+6Sqgusb3AboGi1XE t8v00gGNho6kToBv3jDeTUU+SN9mXZNBE+eaZ5NetyAq7UTfI4Xud2rpWj9TUmg8 WRSTNXg3CjPsSz5XC03zG8vdm7ucXZSgpiEAoGLc2uoXDa/4LTKmbtfKbuj2Rv1H 9ULz1/Xc5TXsA4= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:reply-to:mime-version :content-type; s=default; bh=kWndRmrQeg07hQMI3ifPADIDMKg=; b=gDY nTZt6Ycn6pXYe4OnCm62J7wO2APp27OPDb3wZdwFN5RJDbkKt+P+5/5n1UjLS6Xe LSi9cyKDujJzySRjy7LV73g6+AkC4TXHCKhK8ESxBLFODxKsVGxptQecx+aQCwlr aNezko3T2Yb/mPe+Js0C9T36v0cef9JkvVGS4MlY= Received: (qmail 21749 invoked by alias); 6 Dec 2013 07:50:47 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 21736 invoked by uid 89); 6 Dec 2013 07:50:46 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.8 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from Unknown (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 06 Dec 2013 07:50:43 +0000 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id rB67oagf027597 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 6 Dec 2013 02:50:36 -0500 Received: from tucnak.zalov.cz (vpn1-5-16.ams2.redhat.com [10.36.5.16]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id rB67oYx0028044 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 6 Dec 2013 02:50:35 -0500 Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.14.7/8.14.7) with ESMTP id rB67oX1j025531; Fri, 6 Dec 2013 08:50:33 +0100 Received: (from jakub@localhost) by tucnak.zalov.cz (8.14.7/8.14.7/Submit) id rB67oXpN025530; Fri, 6 Dec 2013 08:50:33 +0100 Date: Fri, 6 Dec 2013 08:50:33 +0100 From: Jakub Jelinek To: Ian Lance Taylor , Konstantin Serebryany , Dmitry Vyukov Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] Handle PIEs in libbacktrace Message-ID: <20131206075033.GE892@tucnak.redhat.com> Reply-To: Jakub Jelinek MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Hi! With the new tsan tests, I've noticed that libbacktrace symbolization doesn't work when the binary is a PIE. The problem is that in that case we obviously can't use base_address of 0, the PIE typically will not have 0 bias, that is actually the sole point of PIEs that their base address is randomized. So, in that case we need to wait until dl_iterate_phdr to determine the proper bias. The executable is always the first in the search scope and/or list of loaded modules, so we just check if the first callback gives us a dlpi_name == NULL entry and assume it is the PIE. Regtested on x86_64-linux, ok for trunk? The rest of this mail except the libbacktrace patch is for the sanitizer folks. With this patch check-gcc tsan.exp passes fully again, but check-g++ does not, there seems to be important differences between tsan symbolization and say asan symbolization. First of all, if symbolizer fails (as without this patch for tsan because the executables are PIEs), we get something like: WARNING: ThreadSanitizer: data race (pid=18998) Write of size 4 at 0x7f4a88f0a08c by thread T1: #0 :0 (tiny_race+0x000000000b19) Previous write of size 4 at 0x7f4a88f0a08c by main thread: #0 :0 (tiny_race+0x000000000b7a) #1 __libc_start_main :0 (libc.so.6+0x003c08c21b74) As if synchronized via sleep: #0 sleep ../../../../libsanitizer/tsan/tsan_interceptors.cc:239 (libtsan.so.0+0x000000048c65) #1 :0 (tiny_race+0x000000000b0a) Location is global '' of size 0 at 0x000000000000 (tiny_race+0x00000020208c) Thread T1 (tid=19000, running) created by main thread at: #0 pthread_create ../../../../libsanitizer/tsan/tsan_interceptors.cc:877 (libtsan.so.0+0x0000000461b3) #1 :0 (tiny_race+0x000000000b6b) #2 __libc_start_main :0 (libc.so.6+0x003c08c21b74) The :0 or :0 look very ugly, e.g. asan I think just prints the exe/dso name + offset in that case. And the reason why check-g++ tsan.exp fails even with this patch is that apparently tsan doesn't try to demangle the symbol names, so we get e.g.: FAIL: c-c++-common/tsan/atomic_stack.c -O0 output pattern test, is ================== WARNING: ThreadSanitizer: data race (pid=22075) Atomic write of size 4 at 0x7fe8c9b630ac by thread T1: #0 __tsan_atomic32_fetch_add ../../../../libsanitizer/tsan/tsan_interface_atomic.cc:468 (libtsan.so.0+0x00000001ec7e) #1 _Z7Thread1Pv /usr/src/gcc/gcc/testsuite/c-c++-common/tsan/atomic_stack.c:11 (atomic_stack.exe+0x000000000d90) Previous write of size 4 at 0x7fe8c9b630ac by thread T2: #0 _Z7Thread2Pv /usr/src/gcc/gcc/testsuite/c-c++-common/tsan/atomic_stack.c:16 (atomic_stack.exe+0x000000000dde) As if synchronized via sleep: #0 sleep ../../../../libsanitizer/tsan/tsan_interceptors.cc:239 (libtsan.so.0+0x000000048c65) #1 _Z7Thread1Pv /usr/src/gcc/gcc/testsuite/c-c++-common/tsan/atomic_stack.c:10 (atomic_stack.exe+0x000000000d7a) Location is global 'Global' of size 4 at 0x7fe8c9b630ac (atomic_stack.exe+0x0000002020ac) Thread T1 (tid=22080, running) created by main thread at: #0 pthread_create ../../../../libsanitizer/tsan/tsan_interceptors.cc:877 (libtsan.so.0+0x0000000461b3) #1 main /usr/src/gcc/gcc/testsuite/c-c++-common/tsan/atomic_stack.c:22 (atomic_stack.exe+0x000000000e2a) Thread T2 (tid=22081, finished) created by main thread at: #0 pthread_create ../../../../libsanitizer/tsan/tsan_interceptors.cc:877 (libtsan.so.0+0x0000000461b3) #1 main /usr/src/gcc/gcc/testsuite/c-c++-common/tsan/atomic_stack.c:23 (atomic_stack.exe+0x000000000e4b) Is the non-demangling intentional? If yes, I guess we'd need to adjust the testcases to cope with both mangled and non-mangled names, otherwise please change tsan to demangle, the symbolizer has functions for it. 2013-12-06 Jakub Jelinek * elf.c (ET_DYN): Undefine and define again. (elf_add): Add exe argument, if true and ehdr.e_type is ET_DYN, return early -1 without closing the descriptor. (struct phdr_data): Add exe_descriptor. (phdr_callback): If pd->exe_descriptor is not -1, for very first call if dlpi_name is NULL just call elf_add with the exe_descriptor, otherwise backtrace_close the exe_descriptor if not -1. Adjust call to elf_add. (backtrace_initialize): Adjust call to elf_add. If it returns -1, set pd.exe_descriptor to descriptor, otherwise set it to -1. Jakub --- libbacktrace/elf.c.jj 2013-11-19 08:47:37.000000000 +0100 +++ libbacktrace/elf.c 2013-12-06 08:26:53.273602062 +0100 @@ -96,6 +96,7 @@ dl_iterate_phdr (int (*callback) (struct #undef ELFDATA2LSB #undef ELFDATA2MSB #undef EV_CURRENT +#undef ET_DYN #undef SHN_LORESERVE #undef SHN_XINDEX #undef SHN_UNDEF @@ -171,6 +172,8 @@ typedef struct { #define EV_CURRENT 1 +#define ET_DYN 3 + typedef struct { b_elf_word sh_name; /* Section name, index in string tbl */ b_elf_word sh_type; /* Type of section */ @@ -512,7 +515,7 @@ elf_syminfo (struct backtrace_state *sta static int elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, backtrace_error_callback error_callback, void *data, - fileline *fileline_fn, int *found_sym, int *found_dwarf) + fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe) { struct backtrace_view ehdr_view; b_elf_ehdr ehdr; @@ -591,6 +594,12 @@ elf_add (struct backtrace_state *state, goto fail; } + /* If the executable is ET_DYN, it is either a PIE, or we are running + directly a shared library with .interp. We need to wait for + dl_iterate_phdr in that case to determine the actual base_address. */ + if (exe && ehdr.e_type == ET_DYN) + return -1; + shoff = ehdr.e_shoff; shnum = ehdr.e_shnum; shstrndx = ehdr.e_shstrndx; @@ -847,6 +856,7 @@ struct phdr_data fileline *fileline_fn; int *found_sym; int *found_dwarf; + int exe_descriptor; }; /* Callback passed to dl_iterate_phdr. Load debug info from shared @@ -862,17 +872,32 @@ phdr_callback (struct dl_phdr_info *info fileline elf_fileline_fn; int found_dwarf; - /* There is not much we can do if we don't have the module name. */ + /* There is not much we can do if we don't have the module name, + unless executable is ET_DYN, where we expect the very first + phdr_callback to be for the PIE. */ if (info->dlpi_name == NULL || info->dlpi_name[0] == '\0') - return 0; + { + if (pd->exe_descriptor == -1) + return 0; + descriptor = pd->exe_descriptor; + pd->exe_descriptor = -1; + } + else + { + if (pd->exe_descriptor != -1) + { + backtrace_close (pd->exe_descriptor, pd->error_callback, pd->data); + pd->exe_descriptor = -1; + } - descriptor = backtrace_open (info->dlpi_name, pd->error_callback, pd->data, - &does_not_exist); - if (descriptor < 0) - return 0; + descriptor = backtrace_open (info->dlpi_name, pd->error_callback, + pd->data, &does_not_exist); + if (descriptor < 0) + return 0; + } if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback, - pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf)) + pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, 0)) { if (found_dwarf) { @@ -893,13 +918,15 @@ backtrace_initialize (struct backtrace_s backtrace_error_callback error_callback, void *data, fileline *fileline_fn) { + int ret; int found_sym; int found_dwarf; fileline elf_fileline_fn; struct phdr_data pd; - if (!elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn, - &found_sym, &found_dwarf)) + ret = elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn, + &found_sym, &found_dwarf, 1); + if (!ret) return 0; pd.state = state; @@ -908,6 +935,7 @@ backtrace_initialize (struct backtrace_s pd.fileline_fn = &elf_fileline_fn; pd.found_sym = &found_sym; pd.found_dwarf = &found_dwarf; + pd.exe_descriptor = ret < 0 ? descriptor : -1; dl_iterate_phdr (phdr_callback, (void *) &pd);