From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5233 invoked by alias); 24 Feb 2010 22:49:30 -0000 Received: (qmail 5212 invoked by uid 22791); 24 Feb 2010 22:49:28 -0000 X-SWARE-Spam-Status: No, hits=-2.5 required=5.0 tests=AWL,BAYES_00 X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (38.113.113.100) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 24 Feb 2010 22:49:23 +0000 Received: (qmail 8243 invoked from network); 24 Feb 2010 22:49:21 -0000 Received: from unknown (HELO caradoc.them.org) (dan@127.0.0.2) by mail.codesourcery.com with ESMTPA; 24 Feb 2010 22:49:21 -0000 Date: Wed, 24 Feb 2010 22:49:00 -0000 From: Daniel Jacobowitz To: gdb-patches@sourceware.org Cc: Jan Kratochvil Subject: RFC: Verify AT_ENTRY before using it Message-ID: <20100224224913.GA25437@caradoc.them.org> Mail-Followup-To: gdb-patches@sourceware.org, Jan Kratochvil MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.20 (2009-06-14) Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-02/txt/msg00612.txt.bz2 I have a lot of occasions to run programs with a non-default dynamic loader. Typically, I do this by taking advantage of gdbserver. In one window, "gdbserver :PORT /path/to/this/loader /path/to/binary". In the other, "gdb /path/to/binary" and "target remote :PORT". Starting with the merge of PIE support, this blows up. GDB tries to relocate BINARY by the auxv AT_ENTRY value, but AT_ENTRY describes the loader, not the binary. I chose to fix this by a program header comparison. We can fetch program headers from AT_PHDRS / AT_PHENT / AT_PHNUM, or from the BFD. If the program headers in both places are the same, the odds are very good that AT_ENTRY (which will match AT_PHDRS - the kernel handles that) belongs to the binary we think it does. If not, give up on auxv-based relocation. There's a short-circuit check so that the extra memory read can be skipped for non-PIE. I also have a local executable that acts as a proxy dynamic loader. That triggers the same scenario. I've tested this on x86_64, where it unbreaks the ld.so scenario described above. I'll test it with our proxy loader on ARM tomorrow; that machine is currently down for maintenance. Any comments, or shall I commit this? -- Daniel Jacobowitz CodeSourcery 2010-02-24 Daniel Jacobowitz * solib-svr4.c (read_program_header): Support type == -1 to read all program headers. (bfd_read_program_headers): New function. (svr4_exec_displacement): Verify that AT_ENTRY is associated with exec_bfd. Index: solib-svr4.c =================================================================== RCS file: /cvs/src/src/gdb/solib-svr4.c,v retrieving revision 1.125 diff -u -p -r1.125 solib-svr4.c --- solib-svr4.c 24 Feb 2010 00:29:02 -0000 1.125 +++ solib-svr4.c 24 Feb 2010 22:41:35 -0000 @@ -451,6 +451,9 @@ bfd_lookup_symbol (bfd *abfd, char *symn /* Read program header TYPE from inferior memory. The header is found by scanning the OS auxillary vector. + If TYPE == -1, return the program headers instead of the contents of + one program header. + Return a pointer to allocated memory holding the program header contents, or NULL on failure. If sucessful, and unless P_SECT_SIZE is NULL, the size of those contents is returned to P_SECT_SIZE. Likewise, the target @@ -483,8 +486,13 @@ read_program_header (int type, int *p_se else return 0; - /* Find .dynamic section via the PT_DYNAMIC PHDR. */ - if (arch_size == 32) + /* Find the requested segment. */ + if (type == -1) + { + sect_addr = at_phdr; + sect_size = at_phent * at_phnum; + } + else if (arch_size == 32) { Elf32_External_Phdr phdr; int i; @@ -1669,6 +1677,29 @@ svr4_static_exec_displacement (void) return 0; } +static gdb_byte * +bfd_read_program_headers (bfd *abfd, int *phdrs_size) +{ + Elf_Internal_Ehdr *ehdr; + gdb_byte *buf; + + ehdr = elf_elfheader (abfd); + + *phdrs_size = ehdr->e_phnum * ehdr->e_phentsize; + if (*phdrs_size == 0) + return NULL; + + buf = xmalloc (*phdrs_size); + if (bfd_seek (abfd, ehdr->e_phoff, SEEK_SET) != 0 + || bfd_bread (buf, *phdrs_size, abfd) != *phdrs_size) + { + xfree (buf); + return NULL; + } + + return buf; +} + /* We relocate all of the sections by the same amount. This behavior is mandated by recent editions of the System V ABI. According to the System V Application Binary Interface, @@ -1702,7 +1733,36 @@ svr4_exec_displacement (void) return 0; if (target_auxv_search (¤t_target, AT_ENTRY, &entry_point) == 1) - return entry_point - bfd_get_start_address (exec_bfd); + { + /* Verify that the auxilliary vector describes the same file as + exec_bfd, by comparing their program headers. If the program + headers in the auxilliary vector do not match the program + headers in the executable, then we are looking at a different + file than the one used by the kernel - for instance, "gdb + program" connected to "gdbserver :PORT ld.so program". */ + int phdrs_size, phdrs2_size, ok = 0; + gdb_byte *buf, *buf2; + + /* Take a shortcut for the common case. If the entry addresses + match, then it is incredibly unlikely that anything + complicated has happened. It's not impossible, if the loader + and executable are both PIE, but it would still require a + rare conjunction of load addresses. */ + if (entry_point == bfd_get_start_address (exec_bfd)) + return 0; + + buf = read_program_header (-1, &phdrs_size, NULL); + buf2 = bfd_read_program_headers (exec_bfd, &phdrs2_size); + if (buf != NULL && buf2 != NULL + && phdrs_size == phdrs2_size + && memcmp (buf, buf2, phdrs_size) == 0) + ok = 1; + xfree (buf); + xfree (buf2); + + if (ok) + return entry_point - bfd_get_start_address (exec_bfd); + } return svr4_static_exec_displacement (); }