solib-darwin: support PIE for spawned processes.

solib-darwin is now able to read the load address of the executable
before any inferior execution.
This commit is contained in:
Tristan Gingold 2015-11-23 11:45:05 +01:00
parent 3eb831e0ca
commit ad2073b0b4
3 changed files with 113 additions and 35 deletions

View File

@ -1,3 +1,15 @@
2015-11-23 Tristan Gingold <gingold@adacore.com>
* darwin-nat.c (darwin_read_dyld_info): Write address in
big-endian order.
* solib-darwin.c (darwin_validate_exec_header): New function,
extracted from...
(darwin_read_exec_load_addr_from_dyld): ...here.
(darwin_read_exec_load_addr_at_init): New function.
(darwin_solib_read_all_image_info_addr): Adjust after
darwin_read_dyld_info change.
(darwin_solib_create_inferior_hook): Support PIE.
2015-11-23 Tristan Gingold <gingold@adacore.com>
* darwin-nat.c (darwin_ptrace): Add a cast to fix warning.

View File

@ -1965,7 +1965,7 @@ darwin_read_write_inferior (task_t task, CORE_ADDR addr,
}
/* Read LENGTH bytes at offset ADDR of task_dyld_info for TASK, and copy them
to RDADDR.
to RDADDR (in big endian).
Return 0 on failure; number of bytes read / written otherwise. */
#ifdef TASK_DYLD_INFO_COUNT
@ -1979,17 +1979,17 @@ darwin_read_dyld_info (task_t task, CORE_ADDR addr, gdb_byte *rdaddr,
int sz = TASK_DYLD_INFO_COUNT * sizeof (natural_t);
kern_return_t kret;
if (addr >= sz)
if (addr != 0 || length > sizeof (mach_vm_address_t))
return TARGET_XFER_EOF;
kret = task_info (task, TASK_DYLD_INFO, (task_info_t) &task_dyld_info, &count);
kret = task_info (task, TASK_DYLD_INFO,
(task_info_t) &task_dyld_info, &count);
MACH_CHECK_ERROR (kret);
if (kret != KERN_SUCCESS)
return TARGET_XFER_E_IO;
/* Truncate. */
if (addr + length > sz)
length = sz - addr;
memcpy (rdaddr, (char *)&task_dyld_info + addr, length);
store_unsigned_integer (rdaddr, length, BFD_ENDIAN_BIG,
task_dyld_info.all_image_info_addr);
*xfered_len = (ULONGEST) length;
return TARGET_XFER_OK;
}

View File

@ -326,11 +326,41 @@ darwin_current_sos (void)
return head;
}
/* Get the load address of the executable. We assume that the dyld info are
correct. */
/* Check LOAD_ADDR points to a Mach-O executable header. Return LOAD_ADDR
in case of success, 0 in case of failure. */
static CORE_ADDR
darwin_read_exec_load_addr (struct darwin_info *info)
darwin_validate_exec_header (CORE_ADDR load_addr)
{
enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
struct mach_o_header_external hdr;
unsigned long hdr_val;
/* Read Mach-O header from memory. */
if (target_read_memory (load_addr, (gdb_byte *) &hdr, sizeof (hdr) - 4))
return 0;
/* Discard wrong magic numbers. Shouldn't happen. */
hdr_val = extract_unsigned_integer
(hdr.magic, sizeof (hdr.magic), byte_order);
if (hdr_val != BFD_MACH_O_MH_MAGIC && hdr_val != BFD_MACH_O_MH_MAGIC_64)
return 0;
/* Check executable. */
hdr_val = extract_unsigned_integer
(hdr.filetype, sizeof (hdr.filetype), byte_order);
if (hdr_val == BFD_MACH_O_MH_EXECUTE)
return load_addr;
return 0;
}
/* Get the load address of the executable using dyld list of images.
We assume that the dyld info are correct (which is wrong if the target
is stopped at the first instruction). */
static CORE_ADDR
darwin_read_exec_load_addr_from_dyld (struct darwin_info *info)
{
struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
@ -344,33 +374,47 @@ darwin_read_exec_load_addr (struct darwin_info *info)
CORE_ADDR iinfo = info->all_image.info + i * image_info_size;
gdb_byte buf[image_info_size];
CORE_ADDR load_addr;
struct mach_o_header_external hdr;
unsigned long hdr_val;
/* Read image info from inferior. */
if (target_read_memory (iinfo, buf, image_info_size))
break;
load_addr = extract_typed_address (buf, ptr_type);
/* Read Mach-O header from memory. */
if (target_read_memory (load_addr, (gdb_byte *) &hdr, sizeof (hdr) - 4))
break;
/* Discard wrong magic numbers. Shouldn't happen. */
hdr_val = extract_unsigned_integer
(hdr.magic, sizeof (hdr.magic), byte_order);
if (hdr_val != BFD_MACH_O_MH_MAGIC && hdr_val != BFD_MACH_O_MH_MAGIC_64)
continue;
/* Check executable. */
hdr_val = extract_unsigned_integer
(hdr.filetype, sizeof (hdr.filetype), byte_order);
if (hdr_val == BFD_MACH_O_MH_EXECUTE)
if (darwin_validate_exec_header (load_addr) == load_addr)
return load_addr;
}
return 0;
}
/* Get the load address of the executable when the PC is at the dyld
entry point using parameter passed by the kernel (at SP). */
static CORE_ADDR
darwin_read_exec_load_addr_at_init (struct darwin_info *info)
{
struct gdbarch *gdbarch = target_gdbarch ();
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
int addr_size = gdbarch_addr_bit (gdbarch) / 8;
ULONGEST load_ptr_addr;
ULONGEST load_addr;
gdb_byte buf[8];
/* Get SP. */
if (regcache_cooked_read_unsigned (get_current_regcache (),
gdbarch_sp_regnum (gdbarch),
&load_ptr_addr) != REG_VALID)
return 0;
/* Read value at SP (image load address). */
if (target_read_memory (load_ptr_addr, buf, addr_size))
return 0;
load_addr = extract_unsigned_integer (buf, addr_size, byte_order);
return darwin_validate_exec_header (load_addr);
}
/* Return 1 if PC lies in the dynamic symbol resolution code of the
run time loader. */
@ -438,8 +482,8 @@ darwin_solib_get_all_image_info_addr_at_init (struct darwin_info *info)
bfd *sub;
make_cleanup_bfd_unref (dyld_bfd);
sub = gdb_bfd_mach_o_fat_extract (dyld_bfd, bfd_object,
gdbarch_bfd_arch_info (target_gdbarch ()));
sub = gdb_bfd_mach_o_fat_extract
(dyld_bfd, bfd_object, gdbarch_bfd_arch_info (target_gdbarch ()));
if (sub)
{
dyld_bfd = sub;
@ -472,22 +516,29 @@ darwin_solib_get_all_image_info_addr_at_init (struct darwin_info *info)
info->all_image_addr += load_addr;
}
/* Extract dyld_all_image_addr reading it from
/* Extract dyld_all_image_addr reading it from
TARGET_OBJECT_DARWIN_DYLD_INFO. */
static void
darwin_solib_read_all_image_info_addr (struct darwin_info *info)
{
gdb_byte buf[8 + 8 + 4];
gdb_byte buf[8];
LONGEST len;
enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
len = target_read (&current_target, TARGET_OBJECT_DARWIN_DYLD_INFO, NULL,
buf, 0, sizeof (buf));
if (len != sizeof (buf))
/* Sanity check. */
if (ptr_type->length > sizeof (buf))
return;
info->all_image_addr = extract_unsigned_integer (buf, 8, byte_order);
len = target_read (&current_target, TARGET_OBJECT_DARWIN_DYLD_INFO, NULL,
buf, 0, ptr_type->length);
if (len <= 0)
return;
/* The use of BIG endian is intended, as BUF is a raw stream of bytes. This
makes the support of remote protocol easier. */
info->all_image_addr = extract_unsigned_integer (buf, len, BFD_ENDIAN_BIG);
}
/* Shared library startup support. See documentation in solib-svr4.c. */
@ -516,10 +567,25 @@ darwin_solib_create_inferior_hook (int from_tty)
return;
}
/* Add the breakpoint which is hit by dyld when the list of solib is
modified. */
create_solib_event_breakpoint (target_gdbarch (), info->all_image.notifier);
/* Possible relocate the main executable (PIE). */
load_addr = darwin_read_exec_load_addr (info);
if (info->all_image.count != 0)
{
/* Possible relocate the main executable (PIE). */
load_addr = darwin_read_exec_load_addr_from_dyld (info);
}
else
{
/* Possible issue:
Do not break on the notifier if dyld is not initialized (deduced from
count == 0). In that case, dyld hasn't relocated itself and the
notifier may point to a wrong address. */
load_addr = darwin_read_exec_load_addr_at_init (info);
}
if (load_addr != 0 && symfile_objfile != NULL)
{
CORE_ADDR vmaddr;