[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bug
From: |
Nurdin Premji |
Subject: |
Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch. |
Date: |
Wed, 02 May 2007 11:34:26 -0400 |
User-agent: |
Thunderbird 1.5.0.10 (X11/20070302) |
Arun Sharma wrote:
On 4/24/07, *Nurdin Premji* <address@hidden
<mailto:address@hidden>> wrote:
While attempting to unwind an i386 binary on an x86_64 machine I found
the following two bugs.
The first is that _UPTi_find_unwind_table when reading the
eh_frame_ptr
and fde_count assume the existence of unw_local_addr_space, which
doesn't exist when compiled for the non-native i386 architecture.
This was fixed by creating a dummy address space with an access_mem
callback which is the only callback called by this method.
I think your problem stems from this statement in libunwind-ptrace(3)
man page:
AVAILABILITY
Since ptrace(2) works within a single machine only, the
_UPT-facility
by definition is not available in libunwind-versions
configured for
cross-unwinding.
It looks like you're trying to use ptrace() for the non-native case.
Can you give a higher level description of what you're trying to
accomplish?
Perhaps you should write your own accessors for accessing an i386
address space from a x86_64 process and call unw_create_addr_space()?
-Arun
PS: Were you trying to use _UPT_accessors by any chance?
Ah, you are right. I had actually written another find_proc_info
accessor, but a large chunk of the code was copied directly from the
_UPT_find_proc_info accessor method's internal stuff. I had originally
used the UPT_find_proc_info accessor method directly and splitting up
the code, I found the bugs in this accessor and decided to also apply
them to the UPT_find_proc_info_accessor method thereby allowing the UPT
accessors to unwind an i386 binary on an x86_64 machine.
Attached is the file libunwind/src/mi/Gget_unwind_table.c. The function
unw_get_unwind_table is passed in a pointer to an elf image mapped into
local memory and creates a unw_proc_info_t from that.
I needed to add this file to the libunwind src in order to access the
hidden dwarf read methods. (and maybe some hidden libunwind methods as
well).
With this code added frysk is able to unwind an i386 process running on
an x86_64.
// Copyright 2007, Red Hat Inc.
/* This file is part of libunwind.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#include "libunwind_i.h"
//#include "remote.h"
#include "dwarf-eh.h"
int
unw_get_unwind_table(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
int need_unwind_info, void *image, size_t size,
unsigned long segbase, unsigned long mapoff, void *arg)
{
Debug(99, "Entering get_unwind_table\n");
Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
unw_word_t addr, eh_frame_start, fde_count, load_base;
struct dwarf_eh_frame_hdr *hdr;
Elf_W(Ehdr) *ehdr;
int i, ret;
unw_dyn_info_t di_cache;
if (size <= EI_CLASS)
return -1;
Debug(99, "Checking elf size\n");
if (!(memcmp (image, ELFMAG, SELFMAG) == 0
&& ((uint8_t *) image)[EI_CLASS] == ELF_CLASS))
return -1;
Debug(99, "Checked elf class\n");
ehdr = image;
phdr = (Elf_W(Phdr) *) ((char *) image + ehdr->e_phoff);
for (i = 0; i < ehdr->e_phnum; ++i)
{
switch (phdr[i].p_type)
{
case PT_LOAD:
if (phdr[i].p_offset == mapoff)
ptxt = phdr + i;
break;
case PT_GNU_EH_FRAME:
peh_hdr = phdr + i;
break;
case PT_DYNAMIC:
pdyn = phdr + i;
break;
default:
break;
}
}
Debug(99, "Traversed headers\n");
if (!ptxt || !peh_hdr)
return -UNW_ENOINFO;
if (pdyn)
{
Debug(99, "Got dynamic header\n");
/* For dynamicly linked executables and shared libraries,
DT_PLTGOT is the value that data-relative addresses are
relative to for that object. We call this the "gp". */
Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset
+ (char *) image);
for (; dyn->d_tag != DT_NULL; ++dyn)
if (dyn->d_tag == DT_PLTGOT)
{
/* Assume that _DYNAMIC is writable and GLIBC has
relocated it (true for x86 at least). */
di_cache.gp = dyn->d_un.d_ptr;
break;
}
}
else
/* Otherwise this is a static executable with no _DYNAMIC. Assume
that data-relative addresses are relative to 0, i.e.,
absolute. */
di_cache.gp = 0;
Debug(99, "Got eh_frame_hdr\n");
hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
+ (char *) image);
if (hdr->version != DW_EH_VERSION)
{
Debug (1, "table has unexpected version %d\n",
hdr->version);
return 0;
}
Debug(99, "EH_VERSION is correct\n");
addr = (unw_word_t) (hdr + 1);
Debug (99, "Got addr\n");
/* Fill in a dummy proc_info structure. We just need to fill in
enough to ensure that dwarf_read_encoded_pointer() can do it's
job. Since we don't have a procedure-context at this point, all
we have to do is fill in the global-pointer. */
memset (pi, 0, sizeof (*pi));
Debug(99, "cleared pi\n");
pi->gp = di_cache.gp;
Debug(99, "set pi gp\n");
//The following is a dummy local address space used by
dwarf_read_encoded_pointer.
int
local_access_mem (unw_addr_space_t as, unw_word_t addr,
unw_word_t *val, int write, void *arg)
{
Debug(99, "entering local_access_mem, reading addr: 0x%lx into: %p\n",
(long) addr, val);
if (write)
{
Debug (16, "mem[%x] <- %x\n", addr, *val);
*(unw_word_t *) addr = *val;
}
else
{
*val = *(unw_word_t *) addr;
Debug (16, "mem[%x] -> %x\n", addr, *val);
}
Debug(99, "leaving local_access_mem\n");
return 0;
}
unw_accessors_t temp_local_accessors = {NULL, NULL, NULL, local_access_mem,
NULL, NULL, NULL, NULL, NULL};
unw_addr_space_t temp_local_addr_space
= unw_create_addr_space(&temp_local_accessors, 0);
/* (Optionally) read eh_frame_ptr: */
if ((ret = dwarf_read_encoded_pointer (temp_local_addr_space,
&temp_local_accessors,
&addr, hdr->eh_frame_ptr_enc, pi,
&eh_frame_start, NULL)) < 0)
return -1;
Debug(99, "read eh_frame_start: 0x%lx\n", (long) eh_frame_start);
/* (Optionally) read fde_count: */
if ((ret = dwarf_read_encoded_pointer (temp_local_addr_space,
&temp_local_accessors,
&addr, hdr->fde_count_enc, pi,
&fde_count, NULL)) < 0)
return -1;
Debug(99, "read fde_count\n");
if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
{
abort ();
}
load_base = segbase - ptxt->p_vaddr;
di_cache.start_ip = segbase;
di_cache.end_ip = di_cache.start_ip + ptxt->p_memsz;
di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
di_cache.u.rti.name_ptr = 0;
/* two 32-bit values (ip_offset/fde_offset) per table-entry: */
di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
+ (addr - (unw_word_t) image
- peh_hdr->p_offset));
/* For the binary-search table in the eh_frame_hdr, data-relative
means relative to the start of that section... */
di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
+ ((unw_word_t) hdr - (unw_word_t) image
- peh_hdr->p_offset));
Debug(99, "Leaving get_unwind_table\n");
return tdep_search_unwind_table (as, ip, &di_cache, pi, need_unwind_info,
arg);
}
--- libunwind-0.99-alpha/src/Makefile.am 2007-04-10 22:16:56.000000000
-0400
+++ mainworkspace/frysk/frysk-imports/libunwind/src/Makefile.am 2007-04-25
10:13:19.000000000 -0400
@@ -55,7 +52,7 @@
mi/Gput_dynamic_unwind_info.c mi/Gdestroy_addr_space.c \
mi/Gget_reg.c mi/Gset_reg.c \
mi/Gget_fpreg.c mi/Gset_fpreg.c \
- mi/Gset_caching_policy.c
+ mi/Gset_caching_policy.c mi/Gget_unwind_table.c
# List of arch-independent files needed by local-only library (libunwind):
libunwind_la_SOURCES_local = \
- Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch., Arun Sharma, 2007/05/01
- Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch.,
Nurdin Premji <=
- Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch., Arun Sharma, 2007/05/02
- Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch., Andrew Cagney, 2007/05/02
- Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch., Arun Sharma, 2007/05/02
- Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch., Andrew Cagney, 2007/05/03
- Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch., David Mosberger-Tang, 2007/05/03
- Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch., Andrew Cagney, 2007/05/03
- Re: [Libunwind-devel] Multi-platform unwinding i386 binary on x86_64 bugs and patch., David Mosberger-Tang, 2007/05/03