From patchwork Mon Jan 20 21:22:01 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean Pihet X-Patchwork-Id: 3514581 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 53962C02DD for ; Mon, 20 Jan 2014 21:24:59 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 19DC42013D for ; Mon, 20 Jan 2014 21:24:58 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4B3FF20148 for ; Mon, 20 Jan 2014 21:24:56 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1W5MJn-0000QX-Lu; Mon, 20 Jan 2014 21:23:40 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1W5MJS-0004DJ-QM; Mon, 20 Jan 2014 21:23:18 +0000 Received: from mail-ee0-f53.google.com ([74.125.83.53]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1W5MIs-00048L-ED for linux-arm-kernel@lists.infradead.org; Mon, 20 Jan 2014 21:22:46 +0000 Received: by mail-ee0-f53.google.com with SMTP id t10so3659203eei.26 for ; Mon, 20 Jan 2014 13:22:20 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=ae5x7CCTnglhWyd7Rc4wONfQNZBwe5NjxsX5So8tCOc=; b=AFf3o/2bNhuLH/0/ROJcxdj02XimlD2d9G0SjW3waTSC/gvKtRo83965TJPmb3/pNn KDi4gZhITqzsCmH7NoLL9PAdYihW8TgBtBZQqtyuEBsjw5Ec2hQALVPeCvLJIa5tAjB8 fIdX2N+fIGfYCOncmE2FvERE59LYDcXFrUhpTkPJARz1bRTy/PSOSSNB80+Yaz83x/6w zv2BtHv1rQxgl10OqLmk5femOl/clzkqdYKmAgABC5WrZ8dDhGsIdsqq3Z0ysldoy/j9 xtX4ojpeM8zXEZG32tI3sZGlN3bFZN+c7RC7HvtJkLUGo7NzyqMsU7Ks5ottwJxyZvOk N8vw== X-Gm-Message-State: ALoCoQkygiOMehg49tcZTYidV4j7AQhBjXAjfTekxrmcI4171OP9kzV3qsHrpcU6dHWBwCa64U8P X-Received: by 10.14.251.68 with SMTP id a44mr6941356ees.64.1390252940673; Mon, 20 Jan 2014 13:22:20 -0800 (PST) Received: from localhost.localdomain (222.28-245-81.adsl-dyn.isp.belgacom.be. [81.245.28.222]) by mx.google.com with ESMTPSA id w4sm7306209eef.20.2014.01.20.13.22.19 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Mon, 20 Jan 2014 13:22:20 -0800 (PST) From: Jean Pihet To: libunwind-devel Subject: [PATCH 2/3] Dwarf: load and parse the debug info of different target address sizes Date: Mon, 20 Jan 2014 22:22:01 +0100 Message-Id: <1390252922-25889-3-git-send-email-jean.pihet@linaro.org> X-Mailer: git-send-email 1.7.11.7 In-Reply-To: <1390252922-25889-1-git-send-email-jean.pihet@linaro.org> References: <1390252922-25889-1-git-send-email-jean.pihet@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140120_162242_749342_3D1490FE X-CRM114-Status: GOOD ( 20.97 ) X-Spam-Score: -2.6 (--) Cc: patches@linaro.org, Will Deacon , linux-kernel@vger.kernel.org, Jean Pihet , Jiri Olsa , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.8 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP When in compat mode, load and parse the dwarf debug info accordingly. Tested dwarf local unwinding on ARMv8 (aka AARCH64) with ARMv7 and ARMv8 binaries. Signed-off-by: Jean Pihet --- src/dwarf/Gfde.c | 25 ++++-- src/dwarf/Gfind_proc_info-lsb.c | 194 +++++++++++++++++++++++++++++++--------- 2 files changed, 170 insertions(+), 49 deletions(-) diff --git a/src/dwarf/Gfde.c b/src/dwarf/Gfde.c index 8659624..81959d1 100644 --- a/src/dwarf/Gfde.c +++ b/src/dwarf/Gfde.c @@ -59,14 +59,23 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, /* Pick appropriate default for FDE-encoding. DWARF spec says start-IP (initial_location) and the code-size (address_range) are "address-unit sized constants". The `R' augmentation can be used - to override this, but by default, we pick an address-sized unit - for fde_encoding. */ - switch (dwarf_addr_size (as)) - { - case 4: fde_encoding = DW_EH_PE_udata4; break; - case 8: fde_encoding = DW_EH_PE_udata8; break; - default: fde_encoding = DW_EH_PE_omit; break; - } + to override this, but by default, we pick the target binary address + size unit for fde_encoding. */ + switch (as->target_addr_size) + { + /* If defined at binary load time (e.g. from the ELF format) */ + case TARGET_ADDR_SIZE_32: fde_encoding = DW_EH_PE_udata4; break; + case TARGET_ADDR_SIZE_64: fde_encoding = DW_EH_PE_udata8; break; + /* If not defined, use the current address size unit */ + case TARGET_ADDR_SIZE_DEFAULT: + default: + switch (dwarf_addr_size (as)) + { + case 4: fde_encoding = DW_EH_PE_udata4; break; + case 8: fde_encoding = DW_EH_PE_udata8; break; + default: fde_encoding = DW_EH_PE_omit; break; + } + } dci->lsda_encoding = DW_EH_PE_omit; dci->handler = 0; diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c index f75bda2..8c35e58 100644 --- a/src/dwarf/Gfind_proc_info-lsb.c +++ b/src/dwarf/Gfind_proc_info-lsb.c @@ -81,36 +81,89 @@ linear_search (unw_addr_space_t as, unw_word_t ip, #endif /* !UNW_REMOTE_ONLY */ #ifdef CONFIG_DEBUG_FRAME -/* Load .debug_frame section from FILE. Allocates and returns space - in *BUF, and sets *BUFSIZE to its size. IS_LOCAL is 1 if using the - local process, in which case we can search the system debug file - directory; 0 for other address spaces, in which case we do not; or - -1 for recursive calls following .gnu_debuglink. Returns 0 on - success, 1 on error. Succeeds even if the file contains no - .debug_frame. */ -/* XXX: Could use mmap; but elf_map_image keeps tons mapped in. */ - -static int -load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) +static int load_debug_frame_Elf32(const char *file, FILE *f, char *linkbuf, + size_t *linksize, char **buf, size_t *bufsize) { - FILE *f; - Elf_W (Ehdr) ehdr; - Elf_W (Half) shstrndx; - Elf_W (Shdr) *sec_hdrs = NULL; + Elf32_Ehdr ehdr; + Elf32_Shdr *sec_hdrs = NULL; + Elf32_Half shstrndx; char *stringtab = NULL; unsigned int i; - size_t linksize = 0; - char *linkbuf = NULL; + + if (fseek(f, 0L, SEEK_SET)) + goto file_error; + if (fread (&ehdr, sizeof(Elf32_Ehdr), 1, f) != 1) + goto file_error; - *buf = NULL; - *bufsize = 0; + shstrndx = ehdr.e_shstrndx; - f = fopen (file, "r"); + Debug (4, "opened file '%s'. Section header at offset %d\n", + file, (int) ehdr.e_shoff); + + fseek (f, ehdr.e_shoff, SEEK_SET); + sec_hdrs = calloc (ehdr.e_shnum, sizeof(Elf32_Shdr)); + if (fread (sec_hdrs, sizeof(Elf32_Shdr), ehdr.e_shnum, f) != ehdr.e_shnum) + goto file_error; - if (!f) - return 1; + Debug (4, "loading string table of size %zd\n", + sec_hdrs[shstrndx].sh_size); + stringtab = malloc (sec_hdrs[shstrndx].sh_size); + fseek (f, sec_hdrs[shstrndx].sh_offset, SEEK_SET); + if (fread (stringtab, 1, sec_hdrs[shstrndx].sh_size, f) != sec_hdrs[shstrndx].sh_size) + goto file_error; - if (fread (&ehdr, sizeof (Elf_W (Ehdr)), 1, f) != 1) + for (i = 1; i < ehdr.e_shnum && *buf == NULL; i++) + { + char *secname = &stringtab[sec_hdrs[i].sh_name]; + + if (strcmp (secname, ".debug_frame") == 0) + { + *bufsize = sec_hdrs[i].sh_size; + *buf = malloc (*bufsize); + + fseek (f, sec_hdrs[i].sh_offset, SEEK_SET); + if (fread (*buf, 1, *bufsize, f) != *bufsize) + goto file_error; + + Debug (4, "read %zd bytes of .debug_frame from offset %d\n", + *bufsize, sec_hdrs[i].sh_offset); + } + else if (strcmp (secname, ".gnu_debuglink") == 0) + { + *linksize = sec_hdrs[i].sh_size; + linkbuf = malloc(*linksize); + + fseek (f, sec_hdrs[i].sh_offset, SEEK_SET); + if (fread (linkbuf, 1, *linksize, f) != *linksize) + goto file_error; + + Debug (4, "read %d bytes of .gnu_debuglink from offset %d\n", + (int) *linksize, sec_hdrs[i].sh_offset); + } + } + + free(sec_hdrs); + free(stringtab); + return 0; + +file_error: + free(sec_hdrs); + free(stringtab); + return -1; +} + +static int load_debug_frame_Elf64(const char *file, FILE *f, char *linkbuf, + size_t *linksize, char **buf, size_t *bufsize) +{ + Elf64_Ehdr ehdr; + Elf64_Shdr *sec_hdrs = NULL; + Elf64_Half shstrndx; + char *stringtab = NULL; + unsigned int i; + + if (fseek(f, 0L, SEEK_SET)) + goto file_error; + if (fread (&ehdr, sizeof(Elf64_Ehdr), 1, f) != 1) goto file_error; shstrndx = ehdr.e_shstrndx; @@ -119,8 +172,8 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) file, (int) ehdr.e_shoff); fseek (f, ehdr.e_shoff, SEEK_SET); - sec_hdrs = calloc (ehdr.e_shnum, sizeof (Elf_W (Shdr))); - if (fread (sec_hdrs, sizeof (Elf_W (Shdr)), ehdr.e_shnum, f) != ehdr.e_shnum) + sec_hdrs = calloc (ehdr.e_shnum, sizeof(Elf64_Shdr)); + if (fread (sec_hdrs, sizeof(Elf64_Shdr), ehdr.e_shnum, f) != ehdr.e_shnum) goto file_error; Debug (4, "loading string table of size %zd\n", @@ -131,7 +184,7 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) goto file_error; for (i = 1; i < ehdr.e_shnum && *buf == NULL; i++) - { + { char *secname = &stringtab[sec_hdrs[i].sh_name]; if (strcmp (secname, ".debug_frame") == 0) @@ -148,20 +201,71 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) } else if (strcmp (secname, ".gnu_debuglink") == 0) { - linksize = sec_hdrs[i].sh_size; - linkbuf = malloc (linksize); + *linksize = sec_hdrs[i].sh_size; + linkbuf = malloc(*linksize); fseek (f, sec_hdrs[i].sh_offset, SEEK_SET); - if (fread (linkbuf, 1, linksize, f) != linksize) + if (fread (linkbuf, 1, *linksize, f) != *linksize) goto file_error; - Debug (4, "read %zd bytes of .gnu_debuglink from offset %zd\n", - linksize, sec_hdrs[i].sh_offset); + Debug (4, "read %d bytes of .gnu_debuglink from offset %zd\n", + (int) *linksize, sec_hdrs[i].sh_offset); } - } + } + + free(sec_hdrs); + free(stringtab); + return 0; + +file_error: + free(sec_hdrs); + free(stringtab); + return -1; +} + +/* Load .debug_frame section from FILE. Allocates and returns space + in *BUF, and sets *BUFSIZE to its size. IS_LOCAL is 1 if using the + local process, in which case we can search the system debug file + directory; 0 for other address spaces, in which case we do not; or + -1 for recursive calls following .gnu_debuglink. Returns 0 on + success, 1 on error. Succeeds even if the file contains no + .debug_frame. */ +/* XXX: Could use mmap; but elf_map_image keeps tons mapped in. */ - free (stringtab); - free (sec_hdrs); +static int +load_debug_frame (const char *file, char **buf, size_t *bufsize, + unw_addr_space_t as, int is_local) +{ + FILE *f; + unsigned char e_ident[sizeof(((Elf32_Ehdr *)0)->e_ident)]; + size_t linksize = 0; + char *linkbuf = NULL; + + *buf = NULL; + *bufsize = 0; + + f = fopen (file, "r"); + + if (!f) + return 1; + + if (fread (&e_ident, sizeof(e_ident), 1, f) != 1) + goto file_error; + + switch (e_ident[EI_CLASS]) { + case ELFCLASS32: + as->target_addr_size = TARGET_ADDR_SIZE_32; + load_debug_frame_Elf32(file, f, linkbuf, &linksize, buf, bufsize); + break; + case ELFCLASS64: + as->target_addr_size = TARGET_ADDR_SIZE_64; + load_debug_frame_Elf64(file, f, linkbuf, &linksize, buf, bufsize); + break; + case ELFCLASSNONE: + default: + Debug (15, "Wrong ELF class 0x%02x\n", e_ident[EI_CLASS]); + goto file_error; + } fclose (f); @@ -195,14 +299,14 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) strcpy (newname, basedir); strcat (newname, "/"); strcat (newname, linkbuf); - ret = load_debug_frame (newname, buf, bufsize, -1); + ret = load_debug_frame (newname, buf, bufsize, as, -1); if (ret == 1) { strcpy (newname, basedir); strcat (newname, "/.debug/"); strcat (newname, linkbuf); - ret = load_debug_frame (newname, buf, bufsize, -1); + ret = load_debug_frame (newname, buf, bufsize, as, -1); } if (ret == 1 && is_local == 1) @@ -211,20 +315,19 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) strcat (newname, basedir); strcat (newname, "/"); strcat (newname, linkbuf); - ret = load_debug_frame (newname, buf, bufsize, -1); + ret = load_debug_frame (newname, buf, bufsize, as, -1); } free (basedir); free (newname); } - free (linkbuf); + + free(linkbuf); return 0; /* An error reading image file. Release resources and return error code */ file_error: - free(stringtab); - free(sec_hdrs); free(linkbuf); fclose(f); @@ -303,7 +406,8 @@ locate_debug_info (unw_addr_space_t as, unw_word_t addr, const char *dlname, else name = (char*) dlname; - err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space); + err = load_debug_frame (name, &buf, &bufsize, as, + as == unw_local_addr_space); if (!err) { @@ -851,6 +955,14 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, #ifndef UNW_REMOTE_ONLY struct unw_debug_frame_list *fdesc = (void *) di->u.ti.table_data; + /* + * Set the target address size as found in the loaded debug binary. + * Note: in case of local unwinding the caller 'as' is set to + * unw_local_addr_space, cf. below. Let's assign the value to + * the caller 'as' before changing the value of 'as'. + */ + as->target_addr_size = unw_local_addr_space->target_addr_size; + /* UNW_INFO_FORMAT_TABLE (i.e. .debug_frame) is read from local address space. Both the index and the unwind tables live in local memory, but the address space to check for properties like the address size and