From patchwork Wed Mar 19 22:31:15 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Kratochvil X-Patchwork-Id: 179 Return-Path: X-Original-To: siddhesh@wilcox.dreamhost.com Delivered-To: siddhesh@wilcox.dreamhost.com Received: from homiemail-mx20.g.dreamhost.com (caibbdcaaahc.dreamhost.com [208.113.200.72]) by wilcox.dreamhost.com (Postfix) with ESMTP id 8421536010F for ; Wed, 19 Mar 2014 15:31:46 -0700 (PDT) Received: by homiemail-mx20.g.dreamhost.com (Postfix, from userid 14314964) id 4721440FFB853; Wed, 19 Mar 2014 15:31:46 -0700 (PDT) X-Original-To: gdb@patchwork.siddhesh.in Delivered-To: x14314964@homiemail-mx20.g.dreamhost.com Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by homiemail-mx20.g.dreamhost.com (Postfix) with ESMTPS id 1FA2F408B4202 for ; Wed, 19 Mar 2014 15:31:46 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:subject:from:to:cc:date:message-id:in-reply-to :references:mime-version:content-type:content-transfer-encoding; q=dns; s=default; b=MX75Sovpipxa9zr5TAJaLULUvg0eunJcd2+EWqcX0Rs pQbSGjlqcRezwVMH1ZwjEjiLY+vQSWBNk9TKFD9UrvleFd1sw3nK8hyx0Qk9S5kA vYdvhh/U+lJTK7AEn4MqLgankgTijGQPuDCU1NljGJnAV3kQoRTn8/mDebTQaqwQ = DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:subject:from:to:cc:date:message-id:in-reply-to :references:mime-version:content-type:content-transfer-encoding; s=default; bh=GHZuXDhiLPnAOAbTmF+GyuGhAdg=; b=sifuZB1rMdSxcwG/r w27sNFsGo8vOt8Cb9IJSlqoyrBdxuLIuwVKxWqUCEt2fPWRNly0S4AYIk9elXoaN qXaJg/fiDnh1eTiFJUEnAJ6KGGoMzVLL5AzPjW6QgmlJ6rrtdUe16ikru5ZlUlXL YdMuASylVeSEy6vOCHiiklm9+M= Received: (qmail 20058 invoked by alias); 19 Mar 2014 22:31:25 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 19992 invoked by uid 89); 19 Mar 2014 22:31:24 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-4.1 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 19 Mar 2014 22:31:22 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s2JMVIb2006862 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 19 Mar 2014 18:31:18 -0400 Received: from host1.jankratochvil.net (ovpn-116-22.ams2.redhat.com [10.36.116.22]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s2JMVF10020032; Wed, 19 Mar 2014 18:31:16 -0400 Subject: [PATCH v5 6/8] gdbserver build-id attribute generator From: Jan Kratochvil To: gdb-patches@sourceware.org Cc: Aleksandar Ristovski Date: Wed, 19 Mar 2014 23:31:15 +0100 Message-ID: <20140319223115.14668.73456.stgit@host1.jankratochvil.net> In-Reply-To: <20140319223004.14668.20989.stgit@host1.jankratochvil.net> References: <20140319223004.14668.20989.stgit@host1.jankratochvil.net> User-Agent: StGit/0.17-dirty MIME-Version: 1.0 X-IsSubscribed: yes X-DH-Original-To: gdb@patchwork.siddhesh.in Hi, producer part of the new "build-id" XML attribute. Jan gdb/ 2014-02-26 Aleksandar Ristovski + l_ld="0x152350" build-id="9afccf7cc41e6293476223fe72480854"/> @end smallexample @@ -38233,13 +38235,14 @@ The format of an SVR4 library list is described by this DTD: @smallexample - - + + - - - - + + + + + @end smallexample @node Memory Map Format diff --git a/gdb/features/library-list-svr4.dtd b/gdb/features/library-list-svr4.dtd index e0c0ebd..ff79990 100644 --- a/gdb/features/library-list-svr4.dtd +++ b/gdb/features/library-list-svr4.dtd @@ -6,11 +6,12 @@ - - + + - - - - + + + + + diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 2d8d5f5..972b609 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -20,6 +20,7 @@ #include "linux-low.h" #include "linux-osdata.h" #include "agent.h" +#include "linux-maps.h" #include "nat/linux-nat.h" #include "nat/linux-waitpid.h" @@ -44,9 +45,11 @@ #include #include #include +#include #include "filestuff.h" #include "tracepoint.h" #include "hostio.h" +#include "rsp-low.h" #ifndef ELFMAG0 /* Don't include here. If it got included by gdb_proc_service.h then ELFMAG0 will have been defined. If it didn't get included by @@ -138,6 +141,31 @@ typedef struct } Elf64_auxv_t; #endif +typedef union ElfXX_Ehdr +{ + Elf32_Ehdr _32; + Elf64_Ehdr _64; +} ElfXX_Ehdr; + +typedef union ElfXX_Phdr +{ + Elf32_Phdr _32; + Elf64_Phdr _64; +} ElfXX_Phdr; + +typedef union ElfXX_Nhdr +{ + Elf32_Nhdr _32; + Elf64_Nhdr _64; +} ElfXX_Nhdr; + +#define ELFXX_FLD(elf64, hdr, fld) ((elf64) ? (hdr)._64.fld : (hdr)._32.fld) +#define ELFXX_SIZEOF(elf64, hdr) ((elf64) ? sizeof ((hdr)._64) \ + : sizeof ((hdr)._32)) +/* Round up to next 4 byte boundary. */ +#define ELFXX_ROUNDUP_4(elf64, what) (((what) + 3) & ~(ULONGEST) 3) +#define BUILD_ID_INVALID "?" + /* A list of all unknown processes which receive stop signals. Some other process will presumably claim each of these as forked children momentarily. */ @@ -5482,15 +5510,38 @@ get_phdr_phnum_from_proc_auxv (const int pid, const int is_elf64, return 0; } +/* Linearly traverse pheaders and look for P_TYPE pheader. */ + +static const void * +find_phdr (const int is_elf64, const void *const phdr_begin, + const void *const phdr_end, const ULONGEST p_type) +{ +#define PHDR_NEXT(hdrp) ((const void *) ((const gdb_byte *) (hdrp) + \ + ELFXX_SIZEOF (is_elf64, *hdrp))) + + const ElfXX_Phdr *phdr = phdr_begin; + + while (PHDR_NEXT (phdr) <= phdr_end) + { + if (ELFXX_FLD (is_elf64, *phdr, p_type) == p_type) + return phdr; + phdr = PHDR_NEXT (phdr); + } + + return NULL; +#undef PHDR_NEXT +} + /* Return &_DYNAMIC (via PT_DYNAMIC) in the inferior, or 0 if not present. */ static CORE_ADDR get_dynamic (const int pid, const int is_elf64) { CORE_ADDR phdr_memaddr, relocation; - int num_phdr, i; + int num_phdr; unsigned char *phdr_buf; - const int phdr_size = is_elf64 ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr); + const ElfXX_Phdr *phdr; + const int phdr_size = ELFXX_SIZEOF (is_elf64, *phdr); if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, &phdr_memaddr, &num_phdr)) return 0; @@ -5504,22 +5555,10 @@ get_dynamic (const int pid, const int is_elf64) /* Compute relocation: it is expected to be 0 for "regular" executables, non-zero for PIE ones. */ relocation = -1; - for (i = 0; relocation == -1 && i < num_phdr; i++) - if (is_elf64) - { - Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size); - - if (p->p_type == PT_PHDR) - relocation = phdr_memaddr - p->p_vaddr; - } - else - { - Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size); - - if (p->p_type == PT_PHDR) - relocation = phdr_memaddr - p->p_vaddr; - } - + phdr = find_phdr (is_elf64, phdr_buf, phdr_buf + num_phdr * phdr_size, + PT_PHDR); + if (phdr != NULL) + relocation = phdr_memaddr - ELFXX_FLD (is_elf64, *phdr, p_vaddr); if (relocation == -1) { /* PT_PHDR is optional, but necessary for PIE in general. Fortunately @@ -5535,23 +5574,11 @@ get_dynamic (const int pid, const int is_elf64) return 0; } - for (i = 0; i < num_phdr; i++) - { - if (is_elf64) - { - Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size); + phdr = find_phdr (is_elf64, phdr_buf, phdr_buf + num_phdr * phdr_size, + PT_DYNAMIC); - if (p->p_type == PT_DYNAMIC) - return p->p_vaddr + relocation; - } - else - { - Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size); - - if (p->p_type == PT_DYNAMIC) - return p->p_vaddr + relocation; - } - } + if (phdr != NULL) + return ELFXX_FLD (is_elf64, *phdr, p_vaddr) + relocation; return 0; } @@ -5691,6 +5718,278 @@ struct link_map_offsets int l_prev_offset; }; + +/* Structure for holding a mapping. Only mapping + containing l_ld can have hex_build_id set. */ + +struct mapping_entry +{ + /* Fields are populated from linux_find_memory_region parameters. */ + + ULONGEST vaddr; + ULONGEST size; + ULONGEST offset; + ULONGEST inode; + + /* Hex encoded string allocated using xmalloc, and + needs to be freed. It can be NULL. */ + + char *hex_build_id; +}; + +typedef struct mapping_entry mapping_entry_s; + +DEF_VEC_O(mapping_entry_s); + +/* Free vector of mapping_entry_s objects. */ + +static void +free_mapping_entry_vec (VEC (mapping_entry_s) *lst) +{ + int ix; + mapping_entry_s *p; + + for (ix = 0; VEC_iterate (mapping_entry_s, lst, ix, p); ++ix) + xfree (p->hex_build_id); + + VEC_free (mapping_entry_s, lst); +} + +/* Used for finding a mapping containing the given + l_ld passed in K. */ + +static int +compare_mapping_entry_range (const void *const k, const void *const b) +{ + const ULONGEST key = *(const CORE_ADDR *) k; + const mapping_entry_s *const p = b; + + if (key < p->vaddr) + return -1; + + if (key < p->vaddr + p->size) + return 0; + + return 1; +} + +struct find_memory_region_callback_data +{ + unsigned is_elf64; + + /* Return. Must be freed with free_mapping_entry_vec. */ + VEC (mapping_entry_s) *list; +}; + +/* Read build-id from PT_NOTE. + Argument LOAD_ADDR represents run time virtual address corresponding to + the beginning of the first loadable segment. L_ADDR is displacement + as supplied by the dynamic linker. */ + +static void +read_build_id (struct find_memory_region_callback_data *const p, + mapping_entry_s *const bil, const CORE_ADDR load_addr, + const CORE_ADDR l_addr) +{ + const int is_elf64 = p->is_elf64; + ElfXX_Ehdr ehdr; + + if (linux_read_memory (load_addr, (unsigned char *) &ehdr, + ELFXX_SIZEOF (is_elf64, ehdr)) == 0 + && ELFXX_FLD (is_elf64, ehdr, e_ident[EI_MAG0]) == ELFMAG0 + && ELFXX_FLD (is_elf64, ehdr, e_ident[EI_MAG1]) == ELFMAG1 + && ELFXX_FLD (is_elf64, ehdr, e_ident[EI_MAG2]) == ELFMAG2 + && ELFXX_FLD (is_elf64, ehdr, e_ident[EI_MAG3]) == ELFMAG3) + { + const ElfXX_Phdr *phdr; + void *phdr_buf; + const unsigned e_phentsize = ELFXX_FLD (is_elf64, ehdr, e_phentsize); + + if (ELFXX_FLD (is_elf64, ehdr, e_phnum) >= 100 + || e_phentsize != ELFXX_SIZEOF (is_elf64, *phdr)) + { + /* Basic sanity check failed. */ + warning (_("Could not identify program header at %s."), + paddress (load_addr)); + return; + } + + phdr_buf = alloca (ELFXX_FLD (is_elf64, ehdr, e_phnum) * e_phentsize); + + if (linux_read_memory (load_addr + ELFXX_FLD (is_elf64, ehdr, e_phoff), + phdr_buf, + ELFXX_FLD (is_elf64, ehdr, e_phnum) * e_phentsize) + != 0) + { + warning (_("Could not read program header at %s."), + paddress (load_addr)); + return; + } + + phdr = phdr_buf; + + for (;;) + { + gdb_byte *pt_note; + const gdb_byte *pt_end; + const ElfXX_Nhdr *nhdr; + CORE_ADDR note_addr; + + phdr = find_phdr (p->is_elf64, phdr, (gdb_byte *) phdr_buf + + ELFXX_FLD (is_elf64, ehdr, e_phnum) * e_phentsize, + PT_NOTE); + if (phdr == NULL) + break; + pt_note = xmalloc (ELFXX_FLD (is_elf64, *phdr, p_memsz)); + note_addr = ELFXX_FLD (is_elf64, *phdr, p_vaddr) + l_addr; + if (linux_read_memory (note_addr, pt_note, + ELFXX_FLD (is_elf64, *phdr, p_memsz)) != 0) + { + xfree (pt_note); + warning (_("Could not read note at address 0x%s"), + paddress (note_addr)); + break; + } + + pt_end = pt_note + ELFXX_FLD (is_elf64, *phdr, p_memsz); + nhdr = (void *) pt_note; + while ((const gdb_byte *) nhdr < pt_end) + { + const size_t namesz + = ELFXX_ROUNDUP_4 (is_elf64, ELFXX_FLD (is_elf64, *nhdr, + n_namesz)); + const size_t descsz + = ELFXX_ROUNDUP_4 (is_elf64, ELFXX_FLD (is_elf64, *nhdr, + n_descsz)); + const size_t note_sz = (ELFXX_SIZEOF (is_elf64, *nhdr) + namesz + + descsz); + + if (((const gdb_byte *) nhdr + note_sz) > pt_end || note_sz == 0 + || descsz == 0) + { + warning (_("Malformed PT_NOTE at address 0x%s\n"), + paddress (note_addr + (gdb_byte *) nhdr - pt_note)); + break; + } + if (ELFXX_FLD (is_elf64, *nhdr, n_type) == NT_GNU_BUILD_ID + && ELFXX_FLD (is_elf64, *nhdr, n_namesz) == 4) + { + const char gnu[4] = "GNU\0"; + const char *const pname + = (char *) nhdr + ELFXX_SIZEOF (is_elf64, *nhdr); + + if (memcmp (pname, gnu, 4) == 0) + { + const size_t n_descsz = ELFXX_FLD (is_elf64, *nhdr, + n_descsz); + + bil->hex_build_id = xmalloc (n_descsz * 2 + 1); + bin2hex ((const gdb_byte *) pname + namesz, + bil->hex_build_id, n_descsz); + xfree (pt_note); + return; + } + } + nhdr = (void *) ((gdb_byte *) nhdr + note_sz); + } + xfree (pt_note); + } + } +} + +static linux_find_memory_region_ftype find_memory_region_callback; + +/* Add mapping_entry. See linux_find_memory_ftype for the parameters + description. */ + +static int +find_memory_region_callback (ULONGEST vaddr, ULONGEST size, ULONGEST offset, + ULONGEST inode, int read, int write, int exec, + int modified, const char *filename, void *data) +{ + if (inode != 0) + { + struct find_memory_region_callback_data *const p = data; + mapping_entry_s bil; + + bil.vaddr = vaddr; + bil.size = size; + bil.offset = offset; + bil.inode = inode; + bil.hex_build_id = NULL; + + VEC_safe_push (mapping_entry_s, p->list, &bil); + } + + /* Continue the traversal. */ + return 0; +} + +/* Linear reverse find starting from RBEGIN towards REND looking for + the lowest vaddr mapping of the same inode and zero offset. */ + +static mapping_entry_s * +lrfind_mapping_entry (mapping_entry_s *const rbegin, + const mapping_entry_s *const rend) +{ + mapping_entry_s *p; + + for (p = rbegin - 1; p >= rend; --p) + if (p->offset == 0 && p->inode == rbegin->inode) + return p; + + return NULL; +} + +/* Get build-id for the given L_LD, where L_LD corresponds to + link_map.l_ld as specified by the dynamic linker. + DATA must point to already filled list of mapping_entry elements. + + If build-id had not been read, read it and cache in corresponding + list element. + + Return build_id as stored in the list element corresponding + to L_LD. + + NULL may be returned if build-id could not be fetched. + + Returned string must not be freed explicitly. */ + +static const char * +get_hex_build_id (const CORE_ADDR l_addr, const CORE_ADDR l_ld, + struct find_memory_region_callback_data *const data) +{ + mapping_entry_s *bil; + + bil = bsearch (&l_ld, VEC_address (mapping_entry_s, data->list), + VEC_length (mapping_entry_s, data->list), + sizeof (mapping_entry_s), compare_mapping_entry_range); + + if (bil == NULL) + return NULL; + + if (bil->hex_build_id == NULL) + { + mapping_entry_s *bil_min; + + bil_min = lrfind_mapping_entry (bil, VEC_address (mapping_entry_s, + data->list)); + if (bil_min != NULL) + read_build_id (data, bil, bil_min->vaddr, l_addr); + else + { + /* Do not try to find hex_build_id again. */ + bil->hex_build_id = xstrdup (BUILD_ID_INVALID); + warning (_("Could not determine load address; mapping entry with " + "offset 0 corresponding to l_ld = 0x%s could not be " + "found; build-id can not be used."), + paddress (l_ld)); + } + } + + return bil->hex_build_id; +} + /* Construct qXfer:libraries-svr4:read reply. */ static int @@ -5703,6 +6002,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf, struct process_info_private *const priv = current_process ()->private; char filename[PATH_MAX]; int pid, is_elf64; + struct find_memory_region_callback_data data; static const struct link_map_offsets lmo_32bit_offsets = { @@ -5745,6 +6045,13 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf, lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets; ptr_size = is_elf64 ? 8 : 4; + data.is_elf64 = is_elf64; + data.list = NULL; + VEC_reserve (mapping_entry_s, data.list, 16); + if (linux_find_memory_regions_full (pid, find_memory_region_callback, &data, + NULL) < 0) + warning (_("Finding memory regions failed")); + while (annex[0] != '\0') { const char *sep; @@ -5851,6 +6158,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf, /* 6x the size for xml_escape_text below. */ size_t len = 6 * strlen ((char *) libname); char *name; + const char *hex_enc_build_id = NULL; if (!header_done) { @@ -5859,7 +6167,11 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf, header_done = 1; } - while (allocated < p - document + len + 200) + hex_enc_build_id = get_hex_build_id (l_addr, l_ld, &data); + + while (allocated < (p - document + len + 200 + + (hex_enc_build_id != NULL + ? strlen (hex_enc_build_id) : 0))) { /* Expand to guarantee sufficient storage. */ uintptr_t document_len = p - document; @@ -5871,9 +6183,13 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf, name = xml_escape_text ((char *) libname); p += sprintf (p, "", + "l_addr=\"0x%lx\" l_ld=\"0x%lx\"", name, (unsigned long) lm_addr, (unsigned long) l_addr, (unsigned long) l_ld); + if (hex_enc_build_id != NULL + && strcmp (hex_enc_build_id, BUILD_ID_INVALID) != 0) + p += sprintf (p, " build-id=\"%s\"", hex_enc_build_id); + p += sprintf (p, "/>"); free (name); } } @@ -5900,6 +6216,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf, memcpy (readbuf, document + offset, len); xfree (document); + free_mapping_entry_vec (data.list); return len; }