From 508b371c851d3efd9e0af555c548b3565f9df985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0abata?= Date: Thu, 15 Oct 2020 18:41:22 +0200 Subject: [PATCH] RHEL 9.0.0 Alpha bootstrap The content of this branch was automatically imported from Fedora ELN with the following as its source: https://src.fedoraproject.org/rpms/ltrace#7583740404d8c248d717605657d603b466b18a66 --- .gitignore | 6 + ltrace-0.7.2-arm.patch | 2811 +++++++ ltrace-0.7.2-bits.patch | 6472 +++++++++++++++++ ltrace-0.7.2-e_machine.patch | 20 + ltrace-0.7.91-XDG_CONFIG_DIRS.patch | 151 + ltrace-0.7.91-aarch64-headers.patch | 24 + ltrace-0.7.91-aarch64-params.patch | 37 + ltrace-0.7.91-aarch64.patch | 2416 ++++++ ltrace-0.7.91-account_execl.patch | 834 +++ ltrace-0.7.91-arm.patch | 610 ++ ltrace-0.7.91-breakpoint-on_install.patch | 81 + ltrace-0.7.91-cant_open.patch | 36 + ltrace-0.7.91-cet.patch | 121 + ltrace-0.7.91-man.patch | 45 + ltrace-0.7.91-multithread-no-f-1.patch | 175 + ltrace-0.7.91-multithread-no-f-2.patch | 50 + ltrace-0.7.91-null.patch | 14 + ltrace-0.7.91-parser-ws_after_id.patch | 144 + ltrace-0.7.91-ppc-bias.patch | 91 + ltrace-0.7.91-ppc64-fork.patch | 52 + ltrace-0.7.91-ppc64-unprelink.patch | 221 + ltrace-0.7.91-ppc64le-configure.patch | 44 + ltrace-0.7.91-ppc64le-fixes.patch | 421 ++ ltrace-0.7.91-ppc64le-support.patch | 786 ++ ltrace-0.7.91-rh1799619.patch | 16 + ltrace-0.7.91-s390-fetch-syscall.patch | 69 + ltrace-0.7.91-s390-irelative.patch | 67 + ltrace-0.7.91-tautology.patch | 96 + ltrace-0.7.91-testsuite-includes-2.patch | 48 + ltrace-0.7.91-testsuite-includes.patch | 216 + ...-0.7.91-testsuite-system_call_params.patch | 68 + ltrace-0.7.91-unwind-elfutils.patch | 422 ++ ltrace-0.7.91-x86-plt_map.patch | 101 + ltrace-0.7.91-x86-unused_label.patch | 32 + ltrace-0.7.91-x86_64-irelative.patch | 156 + ltrace-rh1225568.patch | 12 + ltrace-rh1307754.patch | 12 + ltrace-rh1423913.patch | 20 + ltrace.spec | 709 ++ sources | 1 + 40 files changed, 17707 insertions(+) create mode 100644 ltrace-0.7.2-arm.patch create mode 100644 ltrace-0.7.2-bits.patch create mode 100644 ltrace-0.7.2-e_machine.patch create mode 100644 ltrace-0.7.91-XDG_CONFIG_DIRS.patch create mode 100644 ltrace-0.7.91-aarch64-headers.patch create mode 100644 ltrace-0.7.91-aarch64-params.patch create mode 100644 ltrace-0.7.91-aarch64.patch create mode 100644 ltrace-0.7.91-account_execl.patch create mode 100644 ltrace-0.7.91-arm.patch create mode 100644 ltrace-0.7.91-breakpoint-on_install.patch create mode 100644 ltrace-0.7.91-cant_open.patch create mode 100644 ltrace-0.7.91-cet.patch create mode 100644 ltrace-0.7.91-man.patch create mode 100644 ltrace-0.7.91-multithread-no-f-1.patch create mode 100644 ltrace-0.7.91-multithread-no-f-2.patch create mode 100644 ltrace-0.7.91-null.patch create mode 100644 ltrace-0.7.91-parser-ws_after_id.patch create mode 100644 ltrace-0.7.91-ppc-bias.patch create mode 100644 ltrace-0.7.91-ppc64-fork.patch create mode 100644 ltrace-0.7.91-ppc64-unprelink.patch create mode 100644 ltrace-0.7.91-ppc64le-configure.patch create mode 100644 ltrace-0.7.91-ppc64le-fixes.patch create mode 100644 ltrace-0.7.91-ppc64le-support.patch create mode 100644 ltrace-0.7.91-rh1799619.patch create mode 100644 ltrace-0.7.91-s390-fetch-syscall.patch create mode 100644 ltrace-0.7.91-s390-irelative.patch create mode 100644 ltrace-0.7.91-tautology.patch create mode 100644 ltrace-0.7.91-testsuite-includes-2.patch create mode 100644 ltrace-0.7.91-testsuite-includes.patch create mode 100644 ltrace-0.7.91-testsuite-system_call_params.patch create mode 100644 ltrace-0.7.91-unwind-elfutils.patch create mode 100644 ltrace-0.7.91-x86-plt_map.patch create mode 100644 ltrace-0.7.91-x86-unused_label.patch create mode 100644 ltrace-0.7.91-x86_64-irelative.patch create mode 100644 ltrace-rh1225568.patch create mode 100644 ltrace-rh1307754.patch create mode 100644 ltrace-rh1423913.patch create mode 100644 ltrace.spec create mode 100644 sources diff --git a/.gitignore b/.gitignore index e69de29..45c391d 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,6 @@ +ltrace-0.5-svn45.tar.gz +ltrace-*/ +/ltrace-0.6.0.tar.bz2 +/ltrace-0.7.0.tar.bz2 +/ltrace-0.7.2.tar.bz2 +/ltrace-0.7.91.tar.bz2 diff --git a/ltrace-0.7.2-arm.patch b/ltrace-0.7.2-arm.patch new file mode 100644 index 0000000..72859a0 --- /dev/null +++ b/ltrace-0.7.2-arm.patch @@ -0,0 +1,2811 @@ +diff --git a/Makefile.am b/Makefile.am +index c3356de..141ff85 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,5 +1,5 @@ + # This file is part of ltrace. +-# Copyright (C) 2012 Petr Machata, Red Hat Inc. ++# Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. + # Copyright (C) 2010 Marc Kleine-Budde, Pengutronix + # Copyright (C) 2010 Zachary T Welch, CodeSourcery + # +@@ -33,6 +33,7 @@ noinst_LTLIBRARIES = \ + libltrace.la + + libltrace_la_SOURCES = \ ++ bits.c \ + breakpoints.c \ + debug.c \ + demangle.c \ +@@ -83,6 +84,7 @@ ltrace_LDADD = \ + + + noinst_HEADERS = \ ++ bits.h \ + backend.h \ + breakpoint.h \ + common.h \ +diff --git a/README b/README +index 3db5bc8..95871d1 100644 +--- a/README ++++ b/README +@@ -24,6 +24,8 @@ The following targets are currently (at least somewhat) supported. + Some of them may be more or less broken in reality, it is not feasible + to test each release comprehensively on each target. + ++ armv6l-*-linux-gnueabi ++ armv7l-*-linux-gnueabihf + i[4567]86-*-linux-gnu + ia64-*-linux-gnu + m68k-*-linux-gnu +@@ -41,11 +43,6 @@ current status is unknown: + sparc64*-*-linux-gnu + alpha*-*-linux-gnu + +-Support of the following systems is known to be broken and requires +-fixing: +- +- arm-*-linux-gnueabi +- + + Bug Reports + ----------- +@@ -83,7 +80,7 @@ quick one-liner), it is advisable to send an e-mail beforehand. + + + ------------------------------------------------------------------------------- +-Copyright (C) 2012 Petr Machata ++Copyright (C) 2012,2013 Petr Machata + Copyright (C) 1997-2009 Juan Cespedes + This file is part of ltrace. + +diff --git a/backend.h b/backend.h +index cfac65e..a9de3b4 100644 +--- a/backend.h ++++ b/backend.h +@@ -107,10 +107,6 @@ void *get_stack_pointer(struct process *proc); + * function returns. */ + void *get_return_addr(struct process *proc, void *stack_pointer); + +-/* Adjust PROC so that when the current function returns, it returns +- * to ADDR. */ +-void set_return_addr(struct process *proc, void *addr); +- + /* Enable breakpoint SBP in process PROC. */ + void enable_breakpoint(struct process *proc, struct breakpoint *sbp); + +diff --git a/bits.c b/bits.c +new file mode 100644 +index 0000000..bde2e71 +--- /dev/null ++++ b/bits.c +@@ -0,0 +1,34 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include "bits.h" ++ ++/* This is called rarely, and any overhead will be lost in ptrace ++ * noise, so the algorithm doesn't need to be terribly clever. For ++ * the same reason we don't bother defining the corresponding _32 ++ * variant. */ ++unsigned ++bitcount(uint64_t u) ++{ ++ int c = 0; ++ for (; u > 0; u &= u - 1) ++ c++; ++ return c; ++} +diff --git a/bits.h b/bits.h +new file mode 100644 +index 0000000..7dbe478 +--- /dev/null ++++ b/bits.h +@@ -0,0 +1,29 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef _BITS_H_ ++#define _BITS_H_ ++ ++#include ++ ++/* Count bits in U that are 1. */ ++unsigned bitcount(uint64_t u); ++ ++#endif /* _BITS_H_ */ +diff --git a/breakpoint.h b/breakpoint.h +index 18af7a9..963cc66 100644 +--- a/breakpoint.h ++++ b/breakpoint.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -82,11 +82,10 @@ int breakpoint_init(struct breakpoint *bp, struct process *proc, + arch_addr_t addr, struct library_symbol *libsym); + + /* Make a clone of breakpoint BP into the area of memory pointed to by +- * RETP. The original breakpoint was assigned to process OLD_PROC, +- * the cloned breakpoint will be attached to process NEW_PROC. ++ * RETP. Symbols of cloned breakpoint are looked up in NEW_PROC. + * Returns 0 on success or a negative value on failure. */ + int breakpoint_clone(struct breakpoint *retp, struct process *new_proc, +- struct breakpoint *bp, struct process *old_proc); ++ struct breakpoint *bp); + + /* Set callbacks. If CBS is non-NULL, then BP->cbs shall be NULL. */ + void breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs); +diff --git a/breakpoints.c b/breakpoints.c +index 8db4e26..7b5530a 100644 +--- a/breakpoints.c ++++ b/breakpoints.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2006,2007,2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2006,2007,2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * Copyright (C) 1998,2001,2002,2003,2007,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -117,7 +117,7 @@ arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) + #endif + + static void +-breakpoint_init_base(struct breakpoint *bp, struct process *proc, ++breakpoint_init_base(struct breakpoint *bp, + arch_addr_t addr, struct library_symbol *libsym) + { + bp->cbs = NULL; +@@ -135,7 +135,7 @@ int + breakpoint_init(struct breakpoint *bp, struct process *proc, + arch_addr_t addr, struct library_symbol *libsym) + { +- breakpoint_init_base(bp, proc, addr, libsym); ++ breakpoint_init_base(bp, addr, libsym); + return arch_breakpoint_init(proc, bp); + } + +@@ -157,7 +157,7 @@ breakpoint_destroy(struct breakpoint *bp) + + int + breakpoint_clone(struct breakpoint *retp, struct process *new_proc, +- struct breakpoint *bp, struct process *old_proc) ++ struct breakpoint *bp) + { + struct library_symbol *libsym = NULL; + if (bp->libsym != NULL) { +@@ -165,7 +165,7 @@ breakpoint_clone(struct breakpoint *retp, struct process *new_proc, + assert(rc == 0); + } + +- breakpoint_init_base(retp, new_proc, bp->addr, libsym); ++ breakpoint_init_base(retp, bp->addr, libsym); + memcpy(retp->orig_value, bp->orig_value, sizeof(bp->orig_value)); + retp->enabled = bp->enabled; + if (arch_breakpoint_clone(retp, bp) < 0) +@@ -211,6 +211,22 @@ insert_breakpoint(struct process *proc, void *addr, + + assert(addr != 0); + ++ /* We first create the breakpoint to find out what it's real ++ * address is. This makes a difference on ARM. ++ * ++ * XXX The real problem here is that to create a return ++ * breakpoint ltrace calls get_return_addr and then ++ * insert_breakpoint. So get_return_addr needs to encode all ++ * the information necessary for breakpoint_init into the ++ * address itself, so ADDR is potentially mangled. We filter ++ * the noise out by first creating the breakpoint on stack, ++ * and then looking at the address of the created breakpoint. ++ * Replacing get_return_addr with get_return_breakpoint might ++ * be a better solution. */ ++ struct breakpoint bp; ++ if (breakpoint_init(&bp, proc, addr, libsym) < 0) ++ return NULL; ++ + /* XXX what we need to do instead is have a list of + * breakpoints that are enabled at this address. The + * following works if every breakpoint is the same and there's +@@ -218,20 +234,21 @@ insert_breakpoint(struct process *proc, void *addr, + * will suffice, about the only realistic case where we need + * to have more than one breakpoint per address is return from + * a recursive library call. */ +- struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr); +- if (sbp == NULL) { ++ struct breakpoint *sbp = dict_find_entry(leader->breakpoints, bp.addr); ++ if (sbp != NULL) { ++ breakpoint_destroy(&bp); ++ } else { ++ //fprintf(stderr, "new BP at %p\n", addr); + sbp = malloc(sizeof(*sbp)); +- if (sbp == NULL +- || breakpoint_init(sbp, proc, addr, libsym) < 0) { +- free(sbp); +- return NULL; +- } +- if (proc_add_breakpoint(leader, sbp) < 0) { ++ if (sbp == NULL) { + fail: +- breakpoint_destroy(sbp); + free(sbp); ++ breakpoint_destroy(&bp); + return NULL; + } ++ memcpy(sbp, &bp, sizeof(*sbp)); ++ if (proc_add_breakpoint(leader, sbp) < 0) ++ goto fail; + } + + if (breakpoint_turn_on(sbp, proc) < 0) { +diff --git a/handle_event.c b/handle_event.c +index 9dbb696..1eaea09 100644 +--- a/handle_event.c ++++ b/handle_event.c +@@ -607,7 +607,6 @@ handle_breakpoint(Event *event) + calc_time_spent(event->proc); + } + } +- event->proc->return_addr = brk_addr; + + struct library_symbol *libsym = + event->proc->callstack[i].c_un.libfunc; +@@ -663,8 +662,6 @@ handle_breakpoint(Event *event) + if (event->proc->state != STATE_IGNORED + && sbp->libsym != NULL) { + event->proc->stack_pointer = get_stack_pointer(event->proc); +- event->proc->return_addr = +- get_return_addr(event->proc, event->proc->stack_pointer); + callstack_push_symfunc(event->proc, sbp->libsym); + output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym); + } +@@ -722,9 +719,11 @@ callstack_push_symfunc(struct process *proc, struct library_symbol *sym) + elem->is_syscall = 0; + elem->c_un.libfunc = sym; + +- elem->return_addr = proc->return_addr; +- if (elem->return_addr) +- insert_breakpoint(proc, elem->return_addr, NULL); ++ arch_addr_t return_addr = get_return_addr(proc, proc->stack_pointer); ++ struct breakpoint *rbp = NULL; ++ if (return_addr != 0) ++ rbp = insert_breakpoint(proc, return_addr, NULL); ++ elem->return_addr = rbp != NULL ? rbp->addr : 0; + + if (opt_T || options.summary) { + struct timezone tz; +diff --git a/lens_default.c b/lens_default.c +index ed3d0e1..47b8c70 100644 +--- a/lens_default.c ++++ b/lens_default.c +@@ -29,6 +29,7 @@ + #include + #include + ++#include "bits.h" + #include "proc.h" + #include "lens_default.h" + #include "value.h" +@@ -608,15 +609,6 @@ out_bits(FILE *stream, size_t low, size_t high) + return fprintf(stream, "%zd-%zd", low, high); + } + +-static unsigned +-bitcount(unsigned u) +-{ +- int c = 0; +- for (; u > 0; u &= u - 1) +- c++; +- return c; +-} +- + static int + bitvect_lens_format_cb(struct lens *lens, FILE *stream, + struct value *value, struct value_dict *arguments) +diff --git a/ltrace-elf.c b/ltrace-elf.c +index 1d0f769..af25f8f 100644 +--- a/ltrace-elf.c ++++ b/ltrace-elf.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2006,2010,2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2006,2010,2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Zachary T Welch, CodeSourcery + * Copyright (C) 2010 Joe Damato + * Copyright (C) 1997,1998,2001,2004,2007,2008,2009 Juan Cespedes +@@ -141,8 +141,9 @@ elf_get_section_if(struct ltelf *lte, Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr, + return 0; + } + } +- return -1; + ++ *tgt_sec = NULL; ++ return 0; + } + + static int +@@ -203,23 +204,23 @@ elf_get_section_named(struct ltelf *lte, const char *name, + &name_p, &data); + } + +-static int +-need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size) ++int ++elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword size) + { + assert(data != NULL); + if (data->d_size < size || offset > data->d_size - size) { + debug(1, "Not enough data to read %"PRId64"-byte value" + " at offset %"PRId64".", size, offset); +- return -1; ++ return 0; + } +- return 0; ++ return 1; + } + + #define DEF_READER(NAME, SIZE) \ + int \ + NAME(Elf_Data *data, GElf_Xword offset, uint##SIZE##_t *retp) \ + { \ +- if (!need_data(data, offset, SIZE / 8) < 0) \ ++ if (!elf_can_read_next(data, offset, SIZE / 8)) \ + return -1; \ + \ + if (data->d_buf == NULL) /* NODATA section */ { \ +@@ -236,12 +237,63 @@ need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size) + return 0; \ + } + ++DEF_READER(elf_read_u8, 8) + DEF_READER(elf_read_u16, 16) + DEF_READER(elf_read_u32, 32) + DEF_READER(elf_read_u64, 64) + + #undef DEF_READER + ++#define DEF_READER(NAME, SIZE) \ ++ int \ ++ NAME(Elf_Data *data, GElf_Xword *offset, uint##SIZE##_t *retp) \ ++ { \ ++ int rc = elf_read_u##SIZE(data, *offset, retp); \ ++ if (rc < 0) \ ++ return rc; \ ++ *offset += SIZE / 8; \ ++ return 0; \ ++ } ++ ++DEF_READER(elf_read_next_u8, 8) ++DEF_READER(elf_read_next_u16, 16) ++DEF_READER(elf_read_next_u32, 32) ++DEF_READER(elf_read_next_u64, 64) ++ ++#undef DEF_READER ++ ++int ++elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp) ++{ ++ uint64_t result = 0; ++ int shift = 0; ++ int size = 8 * sizeof result; ++ ++ while (1) { ++ uint8_t byte; ++ if (elf_read_next_u8(data, offset, &byte) < 0) ++ return -1; ++ ++ uint8_t payload = byte & 0x7f; ++ result |= (uint64_t)payload << shift; ++ shift += 7; ++ if (shift > size && byte != 0x1) ++ return -1; ++ if ((byte & 0x80) == 0) ++ break; ++ } ++ ++ if (retp != NULL) ++ *retp = result; ++ return 0; ++} ++ ++int ++elf_read_uleb128(Elf_Data *data, GElf_Xword offset, uint64_t *retp) ++{ ++ return elf_read_next_uleb128(data, &offset, retp); ++} ++ + int + open_elf(struct ltelf *lte, const char *filename) + { +diff --git a/ltrace-elf.h b/ltrace-elf.h +index b76d1eb..178258b 100644 +--- a/ltrace-elf.h ++++ b/ltrace-elf.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2006,2010,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2006,2010,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Zachary T Welch + * Copyright (C) 2001,2004,2007,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -95,6 +95,12 @@ int elf_get_sym_info(struct ltelf *lte, const char *filename, + size_t sym_index, GElf_Rela *rela, GElf_Sym *sym); + + Elf_Data *elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr); ++ ++/* The following three look for sections based on various criteria. ++ * They return 0 if there was no error, or a negative value if there ++ * was. If the section was found, it is returned in *TGT_SEC, and the ++ * header is stored te TGT_SHDR. If it wasn't found, *TGT_SEC is set ++ * to NULL. */ + int elf_get_section_covering(struct ltelf *lte, GElf_Addr addr, + Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr); + int elf_get_section_type(struct ltelf *lte, GElf_Word type, +@@ -102,13 +108,29 @@ int elf_get_section_type(struct ltelf *lte, GElf_Word type, + int elf_get_section_named(struct ltelf *lte, const char *name, + Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr); + +-/* Read, respectively, 2, 4, or 8 bytes from Elf data at given OFFSET, +- * and store it in *RETP. Returns 0 on success or a negative value if +- * there's not enough data. */ ++/* Read, respectively, 1, 2, 4, or 8 bytes from Elf data at given ++ * OFFSET, and store it in *RETP. Returns 0 on success or a negative ++ * value if there's not enough data. */ ++int elf_read_u8(Elf_Data *data, GElf_Xword offset, uint8_t *retp); + int elf_read_u16(Elf_Data *data, GElf_Xword offset, uint16_t *retp); + int elf_read_u32(Elf_Data *data, GElf_Xword offset, uint32_t *retp); + int elf_read_u64(Elf_Data *data, GElf_Xword offset, uint64_t *retp); + ++/* Read at most 64-bit quantity recorded in an ULEB128 variable-length ++ * encoding. */ ++int elf_read_uleb128(Elf_Data *data, GElf_Xword offset, uint64_t *retp); ++ ++/* These are same as above, but update *OFFSET with the width ++ * of read datum. */ ++int elf_read_next_u8(Elf_Data *data, GElf_Xword *offset, uint8_t *retp); ++int elf_read_next_u16(Elf_Data *data, GElf_Xword *offset, uint16_t *retp); ++int elf_read_next_u32(Elf_Data *data, GElf_Xword *offset, uint32_t *retp); ++int elf_read_next_u64(Elf_Data *data, GElf_Xword *offset, uint64_t *retp); ++int elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp); ++ ++/* Return whether there's AMOUNT more bytes after OFFSET in DATA. */ ++int elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword amount); ++ + #if __WORDSIZE == 32 + #define PRI_ELF_ADDR PRIx32 + #define GELF_ADDR_CAST(x) (void *)(uint32_t)(x) +diff --git a/output.c b/output.c +index fe62bb4..f046df8 100644 +--- a/output.c ++++ b/output.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Joe Damato + * Copyright (C) 1997,1998,1999,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes + * Copyright (C) 2006 Paul Gilliam, IBM Corporation +@@ -119,12 +119,15 @@ begin_of_line(struct process *proc, int is_func, int indent) + } + } + if (opt_i) { +- if (is_func) ++ if (is_func) { ++ struct callstack_element *stel ++ = &proc->callstack[proc->callstack_depth - 1]; + current_column += fprintf(options.output, "[%p] ", +- proc->return_addr); +- else ++ stel->return_addr); ++ } else { + current_column += fprintf(options.output, "[%p] ", + proc->instruction_pointer); ++ } + } + if (options.indent > 0 && indent) { + output_indent(proc); +diff --git a/proc.c b/proc.c +index db3f645..7dfde7c 100644 +--- a/proc.c ++++ b/proc.c +@@ -314,8 +314,7 @@ clone_single_bp(void *key, void *value, void *u) + + struct breakpoint *clone = malloc(sizeof(*clone)); + if (clone == NULL +- || breakpoint_clone(clone, data->new_proc, +- bp, data->old_proc) < 0) { ++ || breakpoint_clone(clone, data->new_proc, bp) < 0) { + fail: + free(clone); + data->error = -1; +@@ -1050,6 +1049,7 @@ proc_each_symbol(struct process *proc, struct library_symbol *start_after, + return 0; \ + } + ++DEF_READER(proc_read_8, 8) + DEF_READER(proc_read_16, 16) + DEF_READER(proc_read_32, 32) + DEF_READER(proc_read_64, 64) +diff --git a/proc.h b/proc.h +index 04c0ef7..03708dc 100644 +--- a/proc.h ++++ b/proc.h +@@ -65,7 +65,7 @@ struct callstack_element { + struct library_symbol * libfunc; + } c_un; + int is_syscall; +- void * return_addr; ++ arch_addr_t return_addr; + struct timeval time_spent; + struct fetch_context *fetch_context; + struct value_dict *arguments; +@@ -106,7 +106,6 @@ struct process { + /* Arch-dependent: */ + void * instruction_pointer; + void * stack_pointer; /* To get return addr, args... */ +- void * return_addr; + void * arch_ptr; + + /* XXX We would like to replace this with a pointer to ABI +@@ -116,11 +115,6 @@ struct process { + short e_machine; + char e_class; + +- /* XXX this shoudl go to ARM's arch_process_data. */ +-#ifdef __arm__ +- int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */ +-#endif +- + #if defined(HAVE_LIBUNWIND) + /* libunwind address space */ + unw_addr_space_t unwind_as; +@@ -254,10 +248,11 @@ struct library_symbol *proc_each_symbol + enum callback_status (*cb)(struct library_symbol *, void *), + void *data); + +-/* Read 16, 32 or 64-bit quantity located at ADDR in PROC. The ++/* Read 8, 16, 32 or 64-bit quantity located at ADDR in PROC. The + * resulting value is stored in *LP. 0 is returned on success or a + * negative value on failure. This uses umovebytes under the hood + * (see backend.h). */ ++int proc_read_8(struct process *proc, arch_addr_t addr, uint8_t *lp); + int proc_read_16(struct process *proc, arch_addr_t addr, uint16_t *lp); + int proc_read_32(struct process *proc, arch_addr_t addr, uint32_t *lp); + int proc_read_64(struct process *proc, arch_addr_t addr, uint64_t *lp); +diff --git a/sysdeps/linux-gnu/alpha/regs.c b/sysdeps/linux-gnu/alpha/regs.c +index c197225..9ccd8f2 100644 +--- a/sysdeps/linux-gnu/alpha/regs.c ++++ b/sysdeps/linux-gnu/alpha/regs.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2004,2008,2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -58,9 +59,3 @@ get_return_addr(struct process *proc, void *stack_pointer) + { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 26 /* RA */ , 0); + } +- +-void +-set_return_addr(struct process *proc, void *addr) +-{ +- ptrace(PTRACE_POKEUSER, proc->pid, 26 /* RA */ , addr); +-} +diff --git a/sysdeps/linux-gnu/arm/Makefile.am b/sysdeps/linux-gnu/arm/Makefile.am +index 385424c..2c180c6 100644 +--- a/sysdeps/linux-gnu/arm/Makefile.am ++++ b/sysdeps/linux-gnu/arm/Makefile.am +@@ -1,4 +1,5 @@ + # This file is part of ltrace. ++# Copyright (C) 2013 Petr Machata, Red Hat Inc. + # Copyright (C) 2010 Marc Kleine-Budde, Pengutronix + # + # This program is free software; you can redistribute it and/or +@@ -16,21 +17,11 @@ + # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + # 02110-1301 USA + +-noinst_LTLIBRARIES = \ +- ../libcpu.la ++noinst_LTLIBRARIES = ../libcpu.la + +-___libcpu_la_SOURCES = \ +- breakpoint.c \ +- plt.c \ +- regs.c \ +- trace.c ++___libcpu_la_SOURCES = breakpoint.c fetch.c plt.c regs.c trace.c + +-noinst_HEADERS = \ +- arch.h \ +- arch_syscallent.h \ +- ptrace.h \ +- signalent.h \ +- syscallent.h ++noinst_HEADERS = arch.h arch_syscallent.h ptrace.h regs.h signalent.h \ ++ syscallent.h + +-MAINTAINERCLEANFILES = \ +- Makefile.in ++MAINTAINERCLEANFILES = Makefile.in +diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h +index 291443a..58a7fdf 100644 +--- a/sysdeps/linux-gnu/arm/arch.h ++++ b/sysdeps/linux-gnu/arm/arch.h +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 1998,2004,2008 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -18,6 +19,9 @@ + * 02110-1301 USA + */ + ++#ifndef LTRACE_ARM_ARCH_H ++#define LTRACE_ARM_ARCH_H ++ + #define ARCH_HAVE_ENABLE_BREAKPOINT 1 + #define ARCH_HAVE_DISABLE_BREAKPOINT 1 + +@@ -31,7 +35,24 @@ + #define LT_ELFCLASS ELFCLASS32 + #define LT_ELF_MACHINE EM_ARM + ++#define ARCH_HAVE_SW_SINGLESTEP ++#define ARCH_HAVE_FETCH_ARG ++#define ARCH_HAVE_FETCH_PACK ++#define ARCH_HAVE_SIZEOF ++#define ARCH_HAVE_ALIGNOF + #define ARCH_HAVE_BREAKPOINT_DATA + struct arch_breakpoint_data { + int thumb_mode; + }; ++ ++#define ARCH_HAVE_LTELF_DATA ++struct arch_ltelf_data { ++ /* We have this only for the hooks. */ ++}; ++ ++#define ARCH_HAVE_LIBRARY_DATA ++struct arch_library_data { ++ unsigned int hardfp:1; ++}; ++ ++#endif /* LTRACE_ARM_ARCH_H */ +diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c +index 2fb9578..fcd43a7 100644 +--- a/sysdeps/linux-gnu/arm/breakpoint.c ++++ b/sysdeps/linux-gnu/arm/breakpoint.c +@@ -94,14 +94,11 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) + int + arch_breakpoint_init(struct process *proc, struct breakpoint *sbp) + { +- /* XXX That uintptr_t cast is there temporarily until +- * arch_addr_t becomes integral type. */ +- int thumb_mode = ((uintptr_t)sbp->addr) & 1; +- if (thumb_mode) +- sbp->addr = (void *)((uintptr_t)sbp->addr & ~1); +- sbp->arch.thumb_mode = thumb_mode | proc->thumb_mode; +- /* XXX This doesn't seem like it belongs here. */ +- proc->thumb_mode = 0; ++ /* XXX double cast */ ++ sbp->arch.thumb_mode = ((uintptr_t)sbp->addr) & 1; ++ if (sbp->arch.thumb_mode) ++ /* XXX double cast */ ++ sbp->addr = (arch_addr_t)((uintptr_t)sbp->addr & ~1); + return 0; + } + +diff --git a/sysdeps/linux-gnu/arm/fetch.c b/sysdeps/linux-gnu/arm/fetch.c +new file mode 100644 +index 0000000..0064d91 +--- /dev/null ++++ b/sysdeps/linux-gnu/arm/fetch.c +@@ -0,0 +1,529 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "backend.h" ++#include "fetch.h" ++#include "library.h" ++#include "ltrace-elf.h" ++#include "proc.h" ++#include "ptrace.h" ++#include "regs.h" ++#include "type.h" ++#include "value.h" ++ ++static int ++get_hardfp(uint64_t abi_vfp_args) ++{ ++ if (abi_vfp_args == 2) ++ fprintf(stderr, ++ "Tag_ABI_VFP_args value 2 (tool chain-specific " ++ "conventions) not supported.\n"); ++ return abi_vfp_args == 1; ++} ++ ++int ++arch_elf_init(struct ltelf *lte, struct library *lib) ++{ ++ /* Nothing in this section is strictly critical. It's not ++ * that much of a deal if we fail to guess right whether the ++ * ABI is softfp or hardfp. */ ++ unsigned hardfp = 0; ++ ++ Elf_Scn *scn; ++ Elf_Data *data; ++ GElf_Shdr shdr; ++ if (elf_get_section_type(lte, SHT_ARM_ATTRIBUTES, &scn, &shdr) < 0 ++ || (scn != NULL && (data = elf_loaddata(scn, &shdr)) == NULL)) { ++ fprintf(stderr, ++ "Error when obtaining ARM attribute section: %s\n", ++ elf_errmsg(-1)); ++ goto done; ++ ++ } else if (scn != NULL && data != NULL) { ++ GElf_Xword offset = 0; ++ uint8_t version; ++ if (elf_read_next_u8(data, &offset, &version) < 0) { ++ goto done; ++ } else if (version != 'A') { ++ fprintf(stderr, "Unsupported ARM attribute section " ++ "version %d ('%c').\n", version, version); ++ goto done; ++ } ++ ++ do { ++ const char signature[] = "aeabi"; ++ /* N.B. LEN is including the length field ++ * itself. */ ++ uint32_t sec_len; ++ if (elf_read_u32(data, offset, &sec_len) < 0 ++ || !elf_can_read_next(data, offset, sec_len)) { ++ goto done; ++ } ++ const GElf_Xword next_offset = offset + sec_len; ++ offset += 4; ++ ++ if (sec_len < 4 + sizeof signature ++ || strcmp(signature, data->d_buf + offset) != 0) ++ goto skip; ++ offset += sizeof signature; ++ ++ const GElf_Xword offset0 = offset; ++ uint64_t tag; ++ uint32_t sub_len; ++ if (elf_read_next_uleb128(data, &offset, &tag) < 0 ++ || elf_read_next_u32(data, &offset, &sub_len) < 0 ++ || !elf_can_read_next(data, offset0, sub_len)) ++ goto done; ++ ++ if (tag != 1) ++ /* IHI0045D_ABI_addenda: "section and ++ * symbol attributes are deprecated ++ * [...] consumers are permitted to ++ * ignore them." */ ++ goto skip; ++ ++ while (offset < offset0 + sub_len) { ++ if (elf_read_next_uleb128(data, ++ &offset, &tag) < 0) ++ goto done; ++ ++ switch (tag) { ++ uint64_t v; ++ case 6: /* Tag_CPU_arch */ ++ case 7: /* Tag_CPU_arch_profile */ ++ case 8: /* Tag_ARM_ISA_use */ ++ case 9: /* Tag_THUMB_ISA_use */ ++ case 10: /* Tag_FP_arch */ ++ case 11: /* Tag_WMMX_arch */ ++ case 12: /* Tag_Advanced_SIMD_arch */ ++ case 13: /* Tag_PCS_config */ ++ case 14: /* Tag_ABI_PCS_R9_use */ ++ case 15: /* Tag_ABI_PCS_RW_data */ ++ case 16: /* Tag_ABI_PCS_RO_data */ ++ case 17: /* Tag_ABI_PCS_GOT_use */ ++ case 18: /* Tag_ABI_PCS_wchar_t */ ++ case 19: /* Tag_ABI_FP_rounding */ ++ case 20: /* Tag_ABI_FP_denormal */ ++ case 21: /* Tag_ABI_FP_exceptions */ ++ case 22: /* Tag_ABI_FP_user_exceptions */ ++ case 23: /* Tag_ABI_FP_number_model */ ++ case 24: /* Tag_ABI_align_needed */ ++ case 25: /* Tag_ABI_align_preserved */ ++ case 26: /* Tag_ABI_enum_size */ ++ case 27: /* Tag_ABI_HardFP_use */ ++ case 28: /* Tag_ABI_VFP_args */ ++ case 29: /* Tag_ABI_WMMX_args */ ++ case 30: /* Tag_ABI_optimization_goals */ ++ case 31: /* Tag_ABI_FP_optimization_goals */ ++ case 32: /* Tag_compatibility */ ++ case 34: /* Tag_CPU_unaligned_access */ ++ case 36: /* Tag_FP_HP_extension */ ++ case 38: /* Tag_ABI_FP_16bit_format */ ++ case 42: /* Tag_MPextension_use */ ++ case 70: /* Tag_MPextension_use as well */ ++ case 44: /* Tag_DIV_use */ ++ case 64: /* Tag_nodefaults */ ++ case 66: /* Tag_T2EE_use */ ++ case 68: /* Tag_Virtualization_use */ ++ uleb128: ++ if (elf_read_next_uleb128 ++ (data, &offset, &v) < 0) ++ goto done; ++ if (tag == 28) ++ hardfp = get_hardfp(v); ++ if (tag != 32) ++ continue; ++ ++ /* Tag 32 has two arguments, ++ * fall through. */ ++ ++ case 4: /* Tag_CPU_raw_name */ ++ case 5: /* Tag_CPU_name */ ++ case 65: /* Tag_also_compatible_with */ ++ case 67: /* Tag_conformance */ ++ ntbs: ++ offset += strlen(data->d_buf ++ + offset) + 1; ++ continue; ++ } ++ ++ /* Handle unknown tags in a generic ++ * manner, if possible. */ ++ if (tag <= 32) { ++ fprintf(stderr, ++ "Unknown tag %lld " ++ "at offset %#llx " ++ "of ARM attribute section.", ++ tag, offset); ++ goto skip; ++ } else if (tag % 2 == 0) { ++ goto uleb128; ++ } else { ++ goto ntbs; ++ } ++ } ++ ++ skip: ++ offset = next_offset; ++ ++ } while (elf_can_read_next(data, offset, 1)); ++ ++ } ++ ++done: ++ lib->arch.hardfp = hardfp; ++ return 0; ++} ++ ++void ++arch_elf_destroy(struct ltelf *lte) ++{ ++} ++ ++void ++arch_library_init(struct library *lib) ++{ ++} ++ ++void ++arch_library_destroy(struct library *lib) ++{ ++} ++ ++void ++arch_library_clone(struct library *retp, struct library *lib) ++{ ++ retp->arch = lib->arch; ++} ++ ++enum { ++ /* How many (double) VFP registers the AAPCS uses for ++ * parameter passing. */ ++ NUM_VFP_REGS = 8, ++}; ++ ++struct fetch_context { ++ struct pt_regs regs; ++ ++ struct { ++ union { ++ double d[32]; ++ float s[64]; ++ }; ++ uint32_t fpscr; ++ } fpregs; ++ ++ /* VFP register allocation. ALLOC.S tracks whether the ++ * corresponding FPREGS.S register is taken, ALLOC.D the same ++ * for FPREGS.D. We only track 8 (16) registers, because ++ * that's what the ABI uses for parameter passing. */ ++ union { ++ int16_t d[NUM_VFP_REGS]; ++ int8_t s[NUM_VFP_REGS * 2]; ++ } alloc; ++ ++ unsigned ncrn; ++ arch_addr_t sp; ++ arch_addr_t nsaa; ++ arch_addr_t ret_struct; ++ ++ bool hardfp:1; ++ bool in_varargs:1; ++}; ++ ++static int ++fetch_register_banks(struct process *proc, struct fetch_context *context) ++{ ++ if (ptrace(PTRACE_GETREGS, proc->pid, NULL, &context->regs) == -1) ++ return -1; ++ ++ if (context->hardfp ++ && ptrace(PTRACE_GETVFPREGS, proc->pid, ++ NULL, &context->fpregs) == -1) ++ return -1; ++ ++ context->ncrn = 0; ++ context->nsaa = context->sp = get_stack_pointer(proc); ++ memset(&context->alloc, 0, sizeof(context->alloc)); ++ ++ return 0; ++} ++ ++struct fetch_context * ++arch_fetch_arg_init(enum tof type, struct process *proc, ++ struct arg_type_info *ret_info) ++{ ++ struct fetch_context *context = malloc(sizeof(*context)); ++ ++ { ++ struct process *mainp = proc; ++ while (mainp->libraries == NULL && mainp->parent != NULL) ++ mainp = mainp->parent; ++ context->hardfp = mainp->libraries->arch.hardfp; ++ } ++ ++ if (context == NULL ++ || fetch_register_banks(proc, context) < 0) { ++ free(context); ++ return NULL; ++ } ++ ++ if (ret_info->type == ARGTYPE_STRUCT ++ || ret_info->type == ARGTYPE_ARRAY) { ++ size_t sz = type_sizeof(proc, ret_info); ++ assert(sz != (size_t)-1); ++ if (sz > 4) { ++ /* XXX double cast */ ++ context->ret_struct ++ = (arch_addr_t)context->regs.uregs[0]; ++ context->ncrn++; ++ } ++ } ++ ++ return context; ++} ++ ++struct fetch_context * ++arch_fetch_arg_clone(struct process *proc, ++ struct fetch_context *context) ++{ ++ struct fetch_context *clone = malloc(sizeof(*context)); ++ if (clone == NULL) ++ return NULL; ++ *clone = *context; ++ return clone; ++} ++ ++/* 0 is success, 1 is failure, negative value is an error. */ ++static int ++pass_in_vfp(struct fetch_context *ctx, struct process *proc, ++ enum arg_type type, size_t count, struct value *valuep) ++{ ++ assert(type == ARGTYPE_FLOAT || type == ARGTYPE_DOUBLE); ++ unsigned max = type == ARGTYPE_DOUBLE ? NUM_VFP_REGS : 2 * NUM_VFP_REGS; ++ if (count > max) ++ return 1; ++ ++ size_t i; ++ size_t j; ++ for (i = 0; i < max; ++i) { ++ for (j = i; j < i + count; ++j) ++ if ((type == ARGTYPE_DOUBLE && ctx->alloc.d[j] != 0) ++ || (type == ARGTYPE_FLOAT && ctx->alloc.s[j] != 0)) ++ goto next; ++ ++ /* Found COUNT consecutive unallocated registers at I. */ ++ const size_t sz = (type == ARGTYPE_FLOAT ? 4 : 8) * count; ++ unsigned char *data = value_reserve(valuep, sz); ++ if (data == NULL) ++ return -1; ++ ++ for (j = i; j < i + count; ++j) ++ if (type == ARGTYPE_DOUBLE) ++ ctx->alloc.d[j] = -1; ++ else ++ ctx->alloc.s[j] = -1; ++ ++ if (type == ARGTYPE_DOUBLE) ++ memcpy(data, ctx->fpregs.d + i, sz); ++ else ++ memcpy(data, ctx->fpregs.s + i, sz); ++ ++ return 0; ++ ++ next: ++ continue; ++ } ++ return 1; ++} ++ ++/* 0 is success, 1 is failure, negative value is an error. */ ++static int ++consider_vfp(struct fetch_context *ctx, struct process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ struct arg_type_info *float_info = NULL; ++ size_t hfa_size = 1; ++ if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) ++ float_info = info; ++ else ++ float_info = type_get_hfa_type(info, &hfa_size); ++ ++ if (float_info != NULL && hfa_size <= 4) ++ return pass_in_vfp(ctx, proc, float_info->type, ++ hfa_size, valuep); ++ return 1; ++} ++ ++int ++arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, ++ struct process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ const size_t sz = type_sizeof(proc, info); ++ assert(sz != (size_t)-1); ++ ++ if (ctx->hardfp && !ctx->in_varargs) { ++ int rc; ++ if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1) ++ return rc; ++ } ++ ++ /* IHI0042E_aapcs: If the argument requires double-word ++ * alignment (8-byte), the NCRN is rounded up to the next even ++ * register number. */ ++ const size_t al = type_alignof(proc, info); ++ assert(al != (size_t)-1); ++ if (al == 8) ++ ctx->ncrn = ((ctx->ncrn + 1) / 2) * 2; ++ ++ /* If the size in words of the argument is not more than r4 ++ * minus NCRN, the argument is copied into core registers, ++ * starting at the NCRN. */ ++ /* If the NCRN is less than r4 and the NSAA is equal to the ++ * SP, the argument is split between core registers and the ++ * stack. */ ++ ++ const size_t words = (sz + 3) / 4; ++ if (ctx->ncrn < 4 && ctx->nsaa == ctx->sp) { ++ unsigned char *data = value_reserve(valuep, words * 4); ++ if (data == NULL) ++ return -1; ++ size_t i; ++ for (i = 0; i < words && ctx->ncrn < 4; ++i) { ++ memcpy(data, &ctx->regs.uregs[ctx->ncrn++], 4); ++ data += 4; ++ } ++ const size_t rest = (words - i) * 4; ++ if (rest > 0) { ++ umovebytes(proc, ctx->nsaa, data, rest); ++ ctx->nsaa += rest; ++ } ++ return 0; ++ } ++ ++ assert(ctx->ncrn == 4); ++ ++ /* If the argument required double-word alignment (8-byte), ++ * then the NSAA is rounded up to the next double-word ++ * address. */ ++ if (al == 8) ++ /* XXX double cast. */ ++ ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 7) / 8) * 8); ++ else ++ ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 3) / 4) * 4); ++ ++ value_in_inferior(valuep, ctx->nsaa); ++ ctx->nsaa += sz; ++ ++ return 0; ++} ++ ++int ++arch_fetch_retval(struct fetch_context *ctx, enum tof type, ++ struct process *proc, struct arg_type_info *info, ++ struct value *valuep) ++{ ++ if (fetch_register_banks(proc, ctx) < 0) ++ return -1; ++ ++ if (ctx->hardfp && !ctx->in_varargs) { ++ int rc; ++ if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1) ++ return rc; ++ } ++ ++ size_t sz = type_sizeof(proc, info); ++ assert(sz != (size_t)-1); ++ ++ switch (info->type) { ++ unsigned char *data; ++ ++ case ARGTYPE_VOID: ++ return 0; ++ ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ if (ctx->hardfp && !ctx->in_varargs) { ++ unsigned char *data = value_reserve(valuep, sz); ++ if (data == NULL) ++ return -1; ++ memmove(data, &ctx->fpregs, sz); ++ return 0; ++ } ++ goto pass_in_registers; ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ if (sz > 4) { ++ value_in_inferior(valuep, ctx->ret_struct); ++ return 0; ++ } ++ /* Fall through. */ ++ ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ pass_in_registers: ++ if ((data = value_reserve(valuep, sz)) == NULL) ++ return -1; ++ memmove(data, ctx->regs.uregs, sz); ++ return 0; ++ } ++ assert(info->type != info->type); ++ abort(); ++} ++ ++void ++arch_fetch_arg_done(struct fetch_context *context) ++{ ++ free(context); ++} ++ ++int ++arch_fetch_param_pack_start(struct fetch_context *context, ++ enum param_pack_flavor ppflavor) ++{ ++ if (ppflavor == PARAM_PACK_VARARGS) ++ context->in_varargs = true; ++ return 0; ++} ++ ++void ++arch_fetch_param_pack_end(struct fetch_context *context) ++{ ++ context->in_varargs = false; ++} +diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c +index 377df62..e9e825e 100644 +--- a/sysdeps/linux-gnu/arm/regs.c ++++ b/sysdeps/linux-gnu/arm/regs.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes + * Copyright (C) 2009 Juan Cespedes + * +@@ -24,9 +25,11 @@ + #include + #include + #include ++#include + + #include "proc.h" + #include "common.h" ++#include "regs.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) + # define PTRACE_PEEKUSER PTRACE_PEEKUSR +@@ -36,50 +39,119 @@ + # define PTRACE_POKEUSER PTRACE_POKEUSR + #endif + +-#define off_pc ((void *)60) +-#define off_lr ((void *)56) +-#define off_sp ((void *)52) ++int ++arm_get_register(struct process *proc, enum arm_register reg, uint32_t *lp) ++{ ++ errno = 0; ++ long l = ptrace(PTRACE_PEEKUSER, proc->pid, (void *)(reg * 4L), 0); ++ if (l == -1 && errno != 0) ++ return -1; ++ *lp = (uint32_t)l; ++ return 0; ++} + +-void * +-get_instruction_pointer(struct process *proc) ++int ++arm_set_register(struct process *proc, enum arm_register reg, uint32_t lp) + { +- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); ++ return ptrace(PTRACE_PEEKUSER, proc->pid, ++ (void *)(reg * 4L), (void *)lp); + } + +-void +-set_instruction_pointer(struct process *proc, void *addr) ++int ++arm_get_register_offpc(struct process *proc, enum arm_register reg, ++ uint32_t *lp) + { +- ptrace(PTRACE_POKEUSER, proc->pid, off_pc, addr); ++ if (arm_get_register(proc, reg, lp) < 0) ++ return -1; ++ if (reg == ARM_REG_PC) ++ *lp += 8; ++ return 0; + } + +-void * +-get_stack_pointer(struct process *proc) ++int ++arm_get_shifted_register(struct process *proc, uint32_t inst, int carry, ++ arch_addr_t pc_val, uint32_t *lp) + { +- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0); ++ enum arm_register rm = BITS(inst, 0, 3); ++ unsigned long shifttype = BITS(inst, 5, 6); ++ ++ uint32_t shift; ++ if (BIT(inst, 4)) { ++ if (arm_get_register_offpc(proc, BITS(inst, 8, 11), &shift) < 0) ++ return -1; ++ shift &= 0xff; ++ } else { ++ shift = BITS(inst, 7, 11); ++ } ++ ++ uint32_t res; ++ if (rm == ARM_REG_PC) ++ /* xxx double cast */ ++ res = (uintptr_t)pc_val + (BIT(inst, 4) ? 12 : 8); ++ else if (arm_get_register(proc, rm, &res) < 0) ++ return -1; ++ ++ switch (shifttype) { ++ case 0: /* LSL */ ++ res = shift >= 32 ? 0 : res << shift; ++ break; ++ ++ case 1: /* LSR */ ++ res = shift >= 32 ? 0 : res >> shift; ++ break; ++ ++ case 2: /* ASR */ ++ if (shift >= 32) ++ shift = 31; ++ res = ((res & 0x80000000L) ++ ? ~((~res) >> shift) : res >> shift); ++ break; ++ ++ case 3: /* ROR/RRX */ ++ shift &= 31; ++ if (shift == 0) ++ res = (res >> 1) | (carry ? 0x80000000L : 0); ++ else ++ res = (res >> shift) | (res << (32 - shift)); ++ break; ++ } ++ ++ *lp = res & 0xffffffff; ++ return 0; + } + +-/* really, this is given the *stack_pointer expecting +- * a CISC architecture; in our case, we don't need that */ +-void * +-get_return_addr(struct process *proc, void *stack_pointer) ++static arch_addr_t ++get_register_nocheck(struct process *proc, enum arm_register r) + { +- long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0); +- +- /* Remember & unset the thumb mode bit. XXX This is really a +- * bit of a hack, as we assume that the following +- * insert_breakpoint call will be related to this address. +- * This interface should really be get_return_breakpoint, or +- * maybe install_return_breakpoint. */ +- proc->thumb_mode = addr & 1; +- if (proc->thumb_mode) +- addr &= ~1; +- +- return (void *)addr; ++ uint32_t reg; ++ if (arm_get_register(proc, r, ®) < 0) ++ /* XXX double cast. */ ++ return (arch_addr_t)-1; ++ /* XXX double cast. */ ++ return (arch_addr_t)(uintptr_t)reg; ++} ++ ++arch_addr_t ++get_instruction_pointer(struct process *proc) ++{ ++ return get_register_nocheck(proc, ARM_REG_PC); + } + + void +-set_return_addr(struct process *proc, void *addr) ++set_instruction_pointer(struct process *proc, arch_addr_t addr) ++{ ++ /* XXX double cast. */ ++ arm_set_register(proc, ARM_REG_PC, (uint32_t)addr); ++} ++ ++void * ++get_stack_pointer(struct process *proc) ++{ ++ return get_register_nocheck(proc, ARM_REG_SP); ++} ++ ++arch_addr_t ++get_return_addr(struct process *proc, arch_addr_t stack_pointer) + { +- long iaddr = (int)addr | proc->thumb_mode; +- ptrace(PTRACE_POKEUSER, proc->pid, off_lr, (void *)iaddr); ++ return get_register_nocheck(proc, ARM_REG_LR); + } +diff --git a/sysdeps/linux-gnu/arm/regs.h b/sysdeps/linux-gnu/arm/regs.h +new file mode 100644 +index 0000000..f9a5a86 +--- /dev/null ++++ b/sysdeps/linux-gnu/arm/regs.h +@@ -0,0 +1,47 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#define SUBMASK(x) ((1L << ((x) + 1)) - 1) ++#define BIT(obj,st) (((obj) >> (st)) & 1) ++#define BITS(obj,st,fn) (((obj) >> (st)) & SUBMASK((fn) - (st))) ++#define SBITS(obj,st,fn) \ ++ ((long) (BITS(obj,st,fn) | ((long) BIT(obj,fn) * ~ SUBMASK(fn - st)))) ++ ++enum arm_register { ++ ARM_REG_R7 = 7, ++ ARM_REG_IP = 12, ++ ARM_REG_SP = 13, ++ ARM_REG_LR = 14, ++ ARM_REG_PC = 15, ++ ARM_REG_CPSR = 16, ++}; ++ ++/* Write value of register REG to *LP. Return 0 on success or a ++ * negative value on failure. */ ++int arm_get_register(struct process *proc, enum arm_register reg, uint32_t *lp); ++ ++/* Same as above, but if REG==ARM_REG_PC, it returns the value +8. */ ++int arm_get_register_offpc(struct process *proc, enum arm_register reg, ++ uint32_t *lp); ++ ++/* Same as arm_get_register, but shift is performed depending on ++ * instruction INST. */ ++int arm_get_shifted_register(struct process *proc, uint32_t inst, int carry, ++ arch_addr_t pc, uint32_t *lp); +diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c +index fbbf676..5e51e91 100644 +--- a/sysdeps/linux-gnu/arm/trace.c ++++ b/sysdeps/linux-gnu/arm/trace.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 1998,2004,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand + * +@@ -29,10 +29,13 @@ + #include + #include + +-#include "proc.h" ++#include "bits.h" + #include "common.h" ++#include "proc.h" + #include "output.h" + #include "ptrace.h" ++#include "regs.h" ++#include "type.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) + # define PTRACE_PEEKUSER PTRACE_PEEKUSR +@@ -42,11 +45,6 @@ + # define PTRACE_POKEUSER PTRACE_POKEUSR + #endif + +-#define off_r0 ((void *)0) +-#define off_r7 ((void *)28) +-#define off_ip ((void *)48) +-#define off_pc ((void *)60) +- + void + get_arch_dep(struct process *proc) + { +@@ -68,18 +66,24 @@ syscall_p(struct process *proc, int status, int *sysnum) + { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { +- /* get the user's pc (plus 8) */ +- unsigned pc = ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); ++ uint32_t pc, ip; ++ if (arm_get_register(proc, ARM_REG_PC, &pc) < 0 ++ || arm_get_register(proc, ARM_REG_IP, &ip) < 0) ++ return -1; ++ + pc = pc - 4; ++ + /* fetch the SWI instruction */ + unsigned insn = ptrace(PTRACE_PEEKTEXT, proc->pid, + (void *)pc, 0); +- int ip = ptrace(PTRACE_PEEKUSER, proc->pid, off_ip, 0); + + if (insn == 0xef000000 || insn == 0x0f000000 + || (insn & 0xffff0000) == 0xdf000000) { + /* EABI syscall */ +- *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, off_r7, 0); ++ uint32_t r7; ++ if (arm_get_register(proc, ARM_REG_R7, &r7) < 0) ++ return -1; ++ *sysnum = r7; + } else if ((insn & 0xfff00000) == 0xef900000) { + /* old ABI syscall */ + *sysnum = insn & 0xfffff; +@@ -105,47 +109,605 @@ syscall_p(struct process *proc, int status, int *sysnum) + return 0; + } + +-long +-gimme_arg(enum tof type, struct process *proc, int arg_num, +- struct arg_type_info *info) ++static arch_addr_t ++arm_branch_dest(const arch_addr_t pc, const uint32_t insn) + { +- proc_archdep *a = (proc_archdep *) proc->arch_ptr; ++ /* Bits 0-23 are signed immediate value. */ ++ return pc + ((((insn & 0xffffff) ^ 0x800000) - 0x800000) << 2) + 8; ++} + +- if (arg_num == -1) { /* return value */ +- return ptrace(PTRACE_PEEKUSER, proc->pid, off_r0, 0); +- } ++/* Addresses for calling Thumb functions have the bit 0 set. ++ Here are some macros to test, set, or clear bit 0 of addresses. */ ++/* XXX double cast */ ++#define IS_THUMB_ADDR(addr) ((uintptr_t)(addr) & 1) ++#define MAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) | 1)) ++#define UNMAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) & ~1)) + +- /* deal with the ARM calling conventions */ +- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { +- if (arg_num < 4) { +- if (a->valid && type == LT_TOF_FUNCTION) +- return a->regs.uregs[arg_num]; +- if (a->valid && type == LT_TOF_FUNCTIONR) +- return a->func_arg[arg_num]; +- return ptrace(PTRACE_PEEKUSER, proc->pid, +- (void *)(4 * arg_num), 0); +- } else { +- return ptrace(PTRACE_PEEKDATA, proc->pid, +- proc->stack_pointer + 4 * (arg_num - 4), +- 0); ++enum { ++ COND_ALWAYS = 0xe, ++ COND_NV = 0xf, ++ FLAG_C = 0x20000000, ++}; ++ ++static int ++arm_get_next_pcs(struct process *proc, ++ const arch_addr_t pc, arch_addr_t next_pcs[2]) ++{ ++ uint32_t this_instr; ++ uint32_t status; ++ if (proc_read_32(proc, pc, &this_instr) < 0 ++ || arm_get_register(proc, ARM_REG_CPSR, &status) < 0) ++ return -1; ++ ++ /* In theory, we sometimes don't even need to add any ++ * breakpoints at all. If the conditional bits of the ++ * instruction indicate that it should not be taken, then we ++ * can just skip it altogether without bothering. We could ++ * also emulate the instruction under the breakpoint. ++ * ++ * Here, we make it as simple as possible (though We Accept ++ * Patches). */ ++ int nr = 0; ++ ++ /* ARM can branch either relatively by using a branch ++ * instruction, or absolutely, by doing arbitrary arithmetic ++ * with PC as the destination. */ ++ const unsigned cond = BITS(this_instr, 28, 31); ++ const unsigned opcode = BITS(this_instr, 24, 27); ++ ++ if (cond == COND_NV) ++ switch (opcode) { ++ arch_addr_t addr; ++ case 0xa: ++ case 0xb: ++ /* Branch with Link and change to Thumb. */ ++ /* XXX double cast. */ ++ addr = (arch_addr_t) ++ ((uint32_t)arm_branch_dest(pc, this_instr) ++ | (((this_instr >> 24) & 0x1) << 1)); ++ next_pcs[nr++] = MAKE_THUMB_ADDR(addr); ++ break; + } +- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { +- if (arg_num < 5) { +- if (a->valid && type == LT_TOF_SYSCALL) +- return a->regs.uregs[arg_num]; +- if (a->valid && type == LT_TOF_SYSCALLR) +- return a->sysc_arg[arg_num]; +- return ptrace(PTRACE_PEEKUSER, proc->pid, +- (void *)(4 * arg_num), 0); +- } else { +- return ptrace(PTRACE_PEEKDATA, proc->pid, +- proc->stack_pointer + 4 * (arg_num - 5), +- 0); ++ else ++ switch (opcode) { ++ uint32_t operand1, operand2, result = 0; ++ case 0x0: ++ case 0x1: /* data processing */ ++ case 0x2: ++ case 0x3: ++ if (BITS(this_instr, 12, 15) != ARM_REG_PC) ++ break; ++ ++ if (BITS(this_instr, 22, 25) == 0 ++ && BITS(this_instr, 4, 7) == 9) { /* multiply */ ++ invalid: ++ fprintf(stderr, ++ "Invalid update to pc in instruction.\n"); ++ break; ++ } ++ ++ /* BX , BLX */ ++ if (BITS(this_instr, 4, 27) == 0x12fff1 ++ || BITS(this_instr, 4, 27) == 0x12fff3) { ++ enum arm_register reg = BITS(this_instr, 0, 3); ++ /* XXX double cast: no need to go ++ * through tmp. */ ++ uint32_t tmp; ++ if (arm_get_register_offpc(proc, reg, &tmp) < 0) ++ return -1; ++ next_pcs[nr++] = (arch_addr_t)tmp; ++ return 0; ++ } ++ ++ /* Multiply into PC. */ ++ if (arm_get_register_offpc ++ (proc, BITS(this_instr, 16, 19), &operand1) < 0) ++ return -1; ++ ++ int c = (status & FLAG_C) ? 1 : 0; ++ if (BIT(this_instr, 25)) { ++ uint32_t immval = BITS(this_instr, 0, 7); ++ uint32_t rotate = 2 * BITS(this_instr, 8, 11); ++ operand2 = (((immval >> rotate) ++ | (immval << (32 - rotate))) ++ & 0xffffffff); ++ } else { ++ /* operand 2 is a shifted register. */ ++ if (arm_get_shifted_register ++ (proc, this_instr, c, pc, &operand2) < 0) ++ return -1; ++ } ++ ++ switch (BITS(this_instr, 21, 24)) { ++ case 0x0: /*and */ ++ result = operand1 & operand2; ++ break; ++ ++ case 0x1: /*eor */ ++ result = operand1 ^ operand2; ++ break; ++ ++ case 0x2: /*sub */ ++ result = operand1 - operand2; ++ break; ++ ++ case 0x3: /*rsb */ ++ result = operand2 - operand1; ++ break; ++ ++ case 0x4: /*add */ ++ result = operand1 + operand2; ++ break; ++ ++ case 0x5: /*adc */ ++ result = operand1 + operand2 + c; ++ break; ++ ++ case 0x6: /*sbc */ ++ result = operand1 - operand2 + c; ++ break; ++ ++ case 0x7: /*rsc */ ++ result = operand2 - operand1 + c; ++ break; ++ ++ case 0x8: ++ case 0x9: ++ case 0xa: ++ case 0xb: /* tst, teq, cmp, cmn */ ++ /* Only take the default branch. */ ++ result = 0; ++ break; ++ ++ case 0xc: /*orr */ ++ result = operand1 | operand2; ++ break; ++ ++ case 0xd: /*mov */ ++ /* Always step into a function. */ ++ result = operand2; ++ break; ++ ++ case 0xe: /*bic */ ++ result = operand1 & ~operand2; ++ break; ++ ++ case 0xf: /*mvn */ ++ result = ~operand2; ++ break; ++ } ++ ++ /* XXX double cast */ ++ next_pcs[nr++] = (arch_addr_t)result; ++ break; ++ ++ case 0x4: ++ case 0x5: /* data transfer */ ++ case 0x6: ++ case 0x7: ++ /* Ignore if insn isn't load or Rn not PC. */ ++ if (!BIT(this_instr, 20) ++ || BITS(this_instr, 12, 15) != ARM_REG_PC) ++ break; ++ ++ if (BIT(this_instr, 22)) ++ goto invalid; ++ ++ /* byte write to PC */ ++ uint32_t base; ++ if (arm_get_register_offpc ++ (proc, BITS(this_instr, 16, 19), &base) < 0) ++ return -1; ++ ++ if (BIT(this_instr, 24)) { ++ /* pre-indexed */ ++ int c = (status & FLAG_C) ? 1 : 0; ++ uint32_t offset; ++ if (BIT(this_instr, 25)) { ++ if (arm_get_shifted_register ++ (proc, this_instr, c, ++ pc, &offset) < 0) ++ return -1; ++ } else { ++ offset = BITS(this_instr, 0, 11); ++ } ++ ++ if (BIT(this_instr, 23)) ++ base += offset; ++ else ++ base -= offset; ++ } ++ ++ /* XXX two double casts. */ ++ uint32_t next; ++ if (proc_read_32(proc, (arch_addr_t)base, &next) < 0) ++ return -1; ++ next_pcs[nr++] = (arch_addr_t)next; ++ break; ++ ++ case 0x8: ++ case 0x9: /* block transfer */ ++ if (!BIT(this_instr, 20)) ++ break; ++ /* LDM */ ++ if (BIT(this_instr, 15)) { ++ /* Loading pc. */ ++ int offset = 0; ++ enum arm_register rn = BITS(this_instr, 16, 19); ++ uint32_t rn_val; ++ if (arm_get_register(proc, rn, &rn_val) < 0) ++ return -1; ++ ++ int pre = BIT(this_instr, 24); ++ if (BIT(this_instr, 23)) { ++ /* Bit U = up. */ ++ unsigned reglist ++ = BITS(this_instr, 0, 14); ++ offset = bitcount(reglist) * 4; ++ if (pre) ++ offset += 4; ++ } else if (pre) { ++ offset = -4; ++ } ++ ++ /* XXX double cast. */ ++ arch_addr_t addr ++ = (arch_addr_t)(rn_val + offset); ++ uint32_t next; ++ if (proc_read_32(proc, addr, &next) < 0) ++ return -1; ++ next_pcs[nr++] = (arch_addr_t)next; ++ } ++ break; ++ ++ case 0xb: /* branch & link */ ++ case 0xa: /* branch */ ++ next_pcs[nr++] = arm_branch_dest(pc, this_instr); ++ break; ++ ++ case 0xc: ++ case 0xd: ++ case 0xe: /* coproc ops */ ++ case 0xf: /* SWI */ ++ break; ++ } ++ ++ /* Otherwise take the next instruction. */ ++ if (cond != COND_ALWAYS || nr == 0) ++ next_pcs[nr++] = pc + 4; ++ return 0; ++} ++ ++/* Return the size in bytes of the complete Thumb instruction whose ++ * first halfword is INST1. */ ++ ++static int ++thumb_insn_size (unsigned short inst1) ++{ ++ if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0) ++ return 4; ++ else ++ return 2; ++} ++ ++static int ++thumb_get_next_pcs(struct process *proc, ++ const arch_addr_t pc, arch_addr_t next_pcs[2]) ++{ ++ uint16_t inst1; ++ uint32_t status; ++ if (proc_read_16(proc, pc, &inst1) < 0 ++ || arm_get_register(proc, ARM_REG_CPSR, &status) < 0) ++ return -1; ++ ++ int nr = 0; ++ ++ /* We currently ignore Thumb-2 conditional execution support ++ * (the IT instruction). No branches are allowed in IT block, ++ * and it's not legal to jump in the middle of it, so unless ++ * we need to singlestep through large swaths of code, which ++ * we currently don't, we can ignore them. */ ++ ++ if ((inst1 & 0xff00) == 0xbd00) { /* pop {rlist, pc} */ ++ /* Fetch the saved PC from the stack. It's stored ++ * above all of the other registers. */ ++ const unsigned offset = bitcount(BITS(inst1, 0, 7)) * 4; ++ uint32_t sp; ++ uint32_t next; ++ /* XXX two double casts */ ++ if (arm_get_register(proc, ARM_REG_SP, &sp) < 0 ++ || proc_read_32(proc, (arch_addr_t)(sp + offset), ++ &next) < 0) ++ return -1; ++ next_pcs[nr++] = (arch_addr_t)next; ++ } else if ((inst1 & 0xf000) == 0xd000) { /* conditional branch */ ++ const unsigned long cond = BITS(inst1, 8, 11); ++ if (cond != 0x0f) { /* SWI */ ++ next_pcs[nr++] = pc + (SBITS(inst1, 0, 7) << 1); ++ if (cond == COND_ALWAYS) ++ return 0; ++ } ++ } else if ((inst1 & 0xf800) == 0xe000) { /* unconditional branch */ ++ next_pcs[nr++] = pc + (SBITS(inst1, 0, 10) << 1); ++ } else if (thumb_insn_size(inst1) == 4) { /* 32-bit instruction */ ++ unsigned short inst2; ++ if (proc_read_16(proc, pc + 2, &inst2) < 0) ++ return -1; ++ ++ if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000) { ++ /* Branches and miscellaneous control instructions. */ ++ ++ if ((inst2 & 0x1000) != 0 ++ || (inst2 & 0xd001) == 0xc000) { ++ /* B, BL, BLX. */ ++ ++ const int imm1 = SBITS(inst1, 0, 10); ++ const unsigned imm2 = BITS(inst2, 0, 10); ++ const unsigned j1 = BIT(inst2, 13); ++ const unsigned j2 = BIT(inst2, 11); ++ ++ int32_t offset ++ = ((imm1 << 12) + (imm2 << 1)); ++ offset ^= ((!j2) << 22) | ((!j1) << 23); ++ ++ /* XXX double cast */ ++ uint32_t next = (uint32_t)(pc + offset); ++ /* For BLX make sure to clear the low bits. */ ++ if (BIT(inst2, 12) == 0) ++ next = next & 0xfffffffc; ++ /* XXX double cast */ ++ next_pcs[nr++] = (arch_addr_t)next; ++ return 0; ++ } else if (inst1 == 0xf3de ++ && (inst2 & 0xff00) == 0x3f00) { ++ /* SUBS PC, LR, #imm8. */ ++ uint32_t next; ++ if (arm_get_register(proc, ARM_REG_LR, ++ &next) < 0) ++ return -1; ++ next -= inst2 & 0x00ff; ++ /* XXX double cast */ ++ next_pcs[nr++] = (arch_addr_t)next; ++ return 0; ++ } else if ((inst2 & 0xd000) == 0x8000 ++ && (inst1 & 0x0380) != 0x0380) { ++ /* Conditional branch. */ ++ const int sign = SBITS(inst1, 10, 10); ++ const unsigned imm1 = BITS(inst1, 0, 5); ++ const unsigned imm2 = BITS(inst2, 0, 10); ++ const unsigned j1 = BIT(inst2, 13); ++ const unsigned j2 = BIT(inst2, 11); ++ ++ int32_t offset = (sign << 20) ++ + (j2 << 19) + (j1 << 18); ++ offset += (imm1 << 12) + (imm2 << 1); ++ next_pcs[nr++] = pc + offset; ++ if (BITS(inst1, 6, 9) == COND_ALWAYS) ++ return 0; ++ } ++ } else if ((inst1 & 0xfe50) == 0xe810) { ++ int load_pc = 1; ++ int offset; ++ const enum arm_register rn = BITS(inst1, 0, 3); ++ ++ if (BIT(inst1, 7) && !BIT(inst1, 8)) { ++ /* LDMIA or POP */ ++ if (!BIT(inst2, 15)) ++ load_pc = 0; ++ offset = bitcount(inst2) * 4 - 4; ++ } else if (!BIT(inst1, 7) && BIT(inst1, 8)) { ++ /* LDMDB */ ++ if (!BIT(inst2, 15)) ++ load_pc = 0; ++ offset = -4; ++ } else if (BIT(inst1, 7) && BIT(inst1, 8)) { ++ /* RFEIA */ ++ offset = 0; ++ } else if (!BIT(inst1, 7) && !BIT(inst1, 8)) { ++ /* RFEDB */ ++ offset = -8; ++ } else { ++ load_pc = 0; ++ } ++ ++ if (load_pc) { ++ uint32_t addr; ++ if (arm_get_register(proc, rn, &addr) < 0) ++ return -1; ++ arch_addr_t a = (arch_addr_t)(addr + offset); ++ uint32_t next; ++ if (proc_read_32(proc, a, &next) < 0) ++ return -1; ++ /* XXX double cast */ ++ next_pcs[nr++] = (arch_addr_t)next; ++ } ++ } else if ((inst1 & 0xffef) == 0xea4f ++ && (inst2 & 0xfff0) == 0x0f00) { ++ /* MOV PC or MOVS PC. */ ++ const enum arm_register rn = BITS(inst2, 0, 3); ++ uint32_t next; ++ if (arm_get_register(proc, rn, &next) < 0) ++ return -1; ++ /* XXX double cast */ ++ next_pcs[nr++] = (arch_addr_t)next; ++ } else if ((inst1 & 0xff70) == 0xf850 ++ && (inst2 & 0xf000) == 0xf000) { ++ /* LDR PC. */ ++ const enum arm_register rn = BITS(inst1, 0, 3); ++ uint32_t base; ++ if (arm_get_register(proc, rn, &base) < 0) ++ return -1; ++ ++ int load_pc = 1; ++ if (rn == ARM_REG_PC) { ++ base = (base + 4) & ~(uint32_t)0x3; ++ if (BIT(inst1, 7)) ++ base += BITS(inst2, 0, 11); ++ else ++ base -= BITS(inst2, 0, 11); ++ } else if (BIT(inst1, 7)) { ++ base += BITS(inst2, 0, 11); ++ } else if (BIT(inst2, 11)) { ++ if (BIT(inst2, 10)) { ++ if (BIT(inst2, 9)) ++ base += BITS(inst2, 0, 7); ++ else ++ base -= BITS(inst2, 0, 7); ++ } ++ } else if ((inst2 & 0x0fc0) == 0x0000) { ++ const int shift = BITS(inst2, 4, 5); ++ const enum arm_register rm = BITS(inst2, 0, 3); ++ uint32_t v; ++ if (arm_get_register(proc, rm, &v) < 0) ++ return -1; ++ base += v << shift; ++ } else { ++ /* Reserved. */ ++ load_pc = 0; ++ } ++ ++ if (load_pc) { ++ /* xxx double casts */ ++ uint32_t next; ++ if (proc_read_32(proc, ++ (arch_addr_t)base, &next) < 0) ++ return -1; ++ next_pcs[nr++] = (arch_addr_t)next; ++ } ++ } else if ((inst1 & 0xfff0) == 0xe8d0 ++ && (inst2 & 0xfff0) == 0xf000) { ++ /* TBB. */ ++ const enum arm_register tbl_reg = BITS(inst1, 0, 3); ++ const enum arm_register off_reg = BITS(inst2, 0, 3); ++ ++ uint32_t table; ++ if (tbl_reg == ARM_REG_PC) ++ /* Regcache copy of PC isn't right yet. */ ++ /* XXX double cast */ ++ table = (uint32_t)pc + 4; ++ else if (arm_get_register(proc, tbl_reg, &table) < 0) ++ return -1; ++ ++ uint32_t offset; ++ if (arm_get_register(proc, off_reg, &offset) < 0) ++ return -1; ++ ++ table += offset; ++ uint8_t length; ++ /* XXX double cast */ ++ if (proc_read_8(proc, (arch_addr_t)table, &length) < 0) ++ return -1; ++ ++ next_pcs[nr++] = pc + 2 * length; ++ ++ } else if ((inst1 & 0xfff0) == 0xe8d0 ++ && (inst2 & 0xfff0) == 0xf010) { ++ /* TBH. */ ++ const enum arm_register tbl_reg = BITS(inst1, 0, 3); ++ const enum arm_register off_reg = BITS(inst2, 0, 3); ++ ++ uint32_t table; ++ if (tbl_reg == ARM_REG_PC) ++ /* Regcache copy of PC isn't right yet. */ ++ /* XXX double cast */ ++ table = (uint32_t)pc + 4; ++ else if (arm_get_register(proc, tbl_reg, &table) < 0) ++ return -1; ++ ++ uint32_t offset; ++ if (arm_get_register(proc, off_reg, &offset) < 0) ++ return -1; ++ ++ table += 2 * offset; ++ uint16_t length; ++ /* XXX double cast */ ++ if (proc_read_16(proc, (arch_addr_t)table, &length) < 0) ++ return -1; ++ ++ next_pcs[nr++] = pc + 2 * length; + } +- } else { +- fprintf(stderr, "gimme_arg called with wrong arguments\n"); +- exit(1); + } + ++ ++ /* Otherwise take the next instruction. */ ++ if (nr == 0) ++ next_pcs[nr++] = pc + thumb_insn_size(inst1); + return 0; + } ++ ++enum sw_singlestep_status ++arch_sw_singlestep(struct process *proc, struct breakpoint *sbp, ++ int (*add_cb)(arch_addr_t, struct sw_singlestep_data *), ++ struct sw_singlestep_data *add_cb_data) ++{ ++ const arch_addr_t pc = get_instruction_pointer(proc); ++ ++ uint32_t cpsr; ++ if (arm_get_register(proc, ARM_REG_CPSR, &cpsr) < 0) ++ return SWS_FAIL; ++ ++ const unsigned thumb_p = BIT(cpsr, 5); ++ arch_addr_t next_pcs[2] = {}; ++ if ((thumb_p ? &thumb_get_next_pcs ++ : &arm_get_next_pcs)(proc, pc, next_pcs) < 0) ++ return SWS_FAIL; ++ ++ int i; ++ for (i = 0; i < 2; ++i) { ++ /* XXX double cast. */ ++ arch_addr_t target ++ = (arch_addr_t)(((uintptr_t)next_pcs[i]) | thumb_p); ++ if (next_pcs[i] != 0 && add_cb(target, add_cb_data) < 0) ++ return SWS_FAIL; ++ } ++ ++ debug(1, "PTRACE_CONT"); ++ ptrace(PTRACE_CONT, proc->pid, 0, 0); ++ return SWS_OK; ++} ++ ++size_t ++arch_type_sizeof(struct process *proc, struct arg_type_info *info) ++{ ++ if (proc == NULL) ++ return (size_t)-2; ++ ++ switch (info->type) { ++ case ARGTYPE_VOID: ++ return 0; ++ ++ case ARGTYPE_CHAR: ++ return 1; ++ ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ return 2; ++ ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ return 4; ++ ++ case ARGTYPE_FLOAT: ++ return 4; ++ case ARGTYPE_DOUBLE: ++ return 8; ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ /* Use default value. */ ++ return (size_t)-2; ++ ++ default: ++ assert(info->type != info->type); ++ abort(); ++ } ++} ++ ++size_t ++arch_type_alignof(struct process *proc, struct arg_type_info *info) ++{ ++ return arch_type_sizeof(proc, info); ++} +diff --git a/sysdeps/linux-gnu/ia64/fetch.c b/sysdeps/linux-gnu/ia64/fetch.c +index e90dbed..171c7a2 100644 +--- a/sysdeps/linux-gnu/ia64/fetch.c ++++ b/sysdeps/linux-gnu/ia64/fetch.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2008,2009 Juan Cespedes + * Copyright (C) 2006 Steve Fink + * Copyright (C) 2006 Ian Wienand +@@ -249,37 +249,6 @@ allocate_float(struct fetch_context *ctx, struct process *proc, + return 0; + } + +-static enum arg_type +-get_hfa_type(struct arg_type_info *info, size_t *countp) +-{ +- size_t n = type_aggregate_size(info); +- if (n == (size_t)-1) +- return ARGTYPE_VOID; +- +- enum arg_type type = ARGTYPE_VOID; +- *countp = 0; +- +- while (n-- > 0) { +- struct arg_type_info *emt = type_element(info, n); +- +- enum arg_type emt_type = emt->type; +- size_t emt_count = 1; +- if (emt_type == ARGTYPE_STRUCT || emt_type == ARGTYPE_ARRAY) +- emt_type = get_hfa_type(emt, &emt_count); +- +- if (type == ARGTYPE_VOID) { +- if (emt_type != ARGTYPE_FLOAT +- && emt_type != ARGTYPE_DOUBLE) +- return ARGTYPE_VOID; +- type = emt_type; +- } +- if (emt_type != type) +- return ARGTYPE_VOID; +- *countp += emt_count; +- } +- return type; +-} +- + static int + allocate_hfa(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, +@@ -380,10 +349,11 @@ allocate_ret(struct fetch_context *ctx, struct process *proc, + * floating-point registers, beginning with f8. */ + if (info->type == ARGTYPE_STRUCT || info->type == ARGTYPE_ARRAY) { + size_t hfa_size; +- enum arg_type hfa_type = get_hfa_type(info, &hfa_size); +- if (hfa_type != ARGTYPE_VOID && hfa_size <= 8) ++ struct arg_type_info *hfa_info ++ = type_get_hfa_type(info, &hfa_size); ++ if (hfa_info != NULL && hfa_size <= 8) + return allocate_hfa(ctx, proc, info, valuep, +- hfa_type, hfa_size); ++ hfa_info->type, hfa_size); + } + + /* Integers and pointers are passed in r8. 128-bit integers +@@ -409,7 +379,7 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, + struct arg_type_info *info, struct value *valuep) + { + switch (info->type) { +- enum arg_type hfa_type; ++ struct arg_type_info *hfa_info; + size_t hfa_size; + + case ARGTYPE_VOID: +@@ -421,10 +391,10 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, + return allocate_float(ctx, proc, info, valuep, 1); + + case ARGTYPE_STRUCT: +- hfa_type = get_hfa_type(info, &hfa_size); +- if (hfa_type != ARGTYPE_VOID) ++ hfa_info = type_get_hfa_type(info, &hfa_size); ++ if (hfa_info != NULL) + return allocate_hfa(ctx, proc, info, valuep, +- hfa_type, hfa_size); ++ hfa_info->type, hfa_size); + /* Fall through. */ + case ARGTYPE_CHAR: + case ARGTYPE_SHORT: +diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c +index fb79e8a..67873ce 100644 +--- a/sysdeps/linux-gnu/ia64/regs.c ++++ b/sysdeps/linux-gnu/ia64/regs.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand + * +@@ -77,9 +77,3 @@ get_return_addr(struct process *proc, void *stack_pointer) + return NULL; + return (void *)l; + } +- +-void +-set_return_addr(struct process *proc, void *addr) +-{ +- ptrace(PTRACE_POKEUSER, proc->pid, PT_B0, addr); +-} +diff --git a/sysdeps/linux-gnu/m68k/regs.c b/sysdeps/linux-gnu/m68k/regs.c +index c2fafe1..e25aefb 100644 +--- a/sysdeps/linux-gnu/m68k/regs.c ++++ b/sysdeps/linux-gnu/m68k/regs.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -58,9 +59,3 @@ get_return_addr(struct process *proc, void *stack_pointer) + { + return (void *)ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0); + } +- +-void +-set_return_addr(struct process *proc, void *addr) +-{ +- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); +-} +diff --git a/sysdeps/linux-gnu/mipsel/regs.c b/sysdeps/linux-gnu/mipsel/regs.c +index 19f97cb..d6a7a50 100644 +--- a/sysdeps/linux-gnu/mipsel/regs.c ++++ b/sysdeps/linux-gnu/mipsel/regs.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2008,2009 Juan Cespedes + * Copyright (C) 2006 Eric Vaitl, Cisco Systems, Inc. + * +@@ -94,9 +95,3 @@ get_return_addr(struct process *proc, void *stack_pointer) + { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0); + } +- +-void +-set_return_addr(struct process *proc, void *addr) +-{ +- ptrace(PTRACE_POKEUSER, proc->pid, off_lr, addr); +-} +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 439b8e8..fe1602a 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -262,7 +262,8 @@ load_opd_data(struct ltelf *lte, struct library *lib) + { + Elf_Scn *sec; + GElf_Shdr shdr; +- if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0) { ++ if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0 ++ || sec == NULL) { + fail: + fprintf(stderr, "couldn't find .opd data\n"); + return -1; +@@ -290,8 +291,9 @@ get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, Elf_Data *plt_data) + Elf_Scn *ppcgot_sec = NULL; + GElf_Shdr ppcgot_shdr; + if (ppcgot != 0 +- && elf_get_section_covering(lte, ppcgot, +- &ppcgot_sec, &ppcgot_shdr) < 0) ++ && (elf_get_section_covering(lte, ppcgot, ++ &ppcgot_sec, &ppcgot_shdr) < 0 ++ || ppcgot_sec == NULL)) + fprintf(stderr, + "DT_PPC_GOT=%#"PRIx64", but no such section found\n", + ppcgot); +diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c +index ed9b398..40d7e7a 100644 +--- a/sysdeps/linux-gnu/ppc/regs.c ++++ b/sysdeps/linux-gnu/ppc/regs.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2002,2008,2009 Juan Cespedes + * Copyright (C) 2009 Juan Cespedes + * Copyright (C) 2008 Luis Machado, IBM Corporation +@@ -63,9 +64,3 @@ get_return_addr(struct process *proc, void *stack_pointer) + { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, sizeof(long)*PT_LNK, 0); + } +- +-void +-set_return_addr(struct process *proc, void *addr) +-{ +- ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_LNK, addr); +-} +diff --git a/sysdeps/linux-gnu/s390/regs.c b/sysdeps/linux-gnu/s390/regs.c +index 44e8f67..bb16c61 100644 +--- a/sysdeps/linux-gnu/s390/regs.c ++++ b/sysdeps/linux-gnu/s390/regs.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2002,2004,2008,2009 Juan Cespedes + * Copyright (C) 2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -87,13 +88,3 @@ get_return_addr(struct process *proc, void *stack_pointer) + #endif + return (void *)ret; + } +- +-void +-set_return_addr(struct process *proc, void *addr) +-{ +-#ifdef __s390x__ +- if (proc->mask_32bit) +- addr = (void *)((long)addr & PSW_MASK31); +-#endif +- ptrace(PTRACE_POKEUSER, proc->pid, PT_GPR14, addr); +-} +diff --git a/sysdeps/linux-gnu/sparc/regs.c b/sysdeps/linux-gnu/sparc/regs.c +index 8431c9b..c474c83 100644 +--- a/sysdeps/linux-gnu/sparc/regs.c ++++ b/sysdeps/linux-gnu/sparc/regs.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2004,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand + * +@@ -65,12 +66,3 @@ get_return_addr(struct process *proc, void *stack_pointer) + return (void *)a->regs.u_regs[UREG_I6] + 12; + return (void *)a->regs.u_regs[UREG_I6] + 8; + } +- +-void +-set_return_addr(struct process *proc, void *addr) +-{ +- proc_archdep *a = (proc_archdep *) (proc->arch_ptr); +- if (!a->valid) +- return; +- ptrace(PTRACE_POKETEXT, proc->pid, a->regs.u_regs[UREG_I6] + 8, addr); +-} +diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c +index e57a5ed..3aea082 100644 +--- a/sysdeps/linux-gnu/trace.c ++++ b/sysdeps/linux-gnu/trace.c +@@ -561,12 +561,12 @@ remove_sw_breakpoints(struct process *proc) + assert(self != NULL); + assert(self->super.on_event == process_stopping_on_event); + +- int ct = sizeof(self->sws_bp_addrs) / sizeof(*self->sws_bp_addrs); ++ int ct = sizeof(self->sws_bps) / sizeof(*self->sws_bps); + int i; + for (i = 0; i < ct; ++i) +- if (self->sws_bp_addrs[i] != 0) { +- delete_breakpoint(proc, self->sws_bp_addrs[i]); +- self->sws_bp_addrs[i] = 0; ++ if (self->sws_bps[i] != NULL) { ++ delete_breakpoint(proc, self->sws_bps[i]->addr); ++ self->sws_bps[i] = NULL; + } + } + +@@ -586,18 +586,17 @@ sw_singlestep_add_bp(arch_addr_t addr, struct sw_singlestep_data *data) + struct process_stopping_handler *self = data->self; + struct process *proc = self->task_enabling_breakpoint; + +- int ct = sizeof(self->sws_bp_addrs) +- / sizeof(*self->sws_bp_addrs); ++ int ct = sizeof(self->sws_bps) / sizeof(*self->sws_bps); + int i; + for (i = 0; i < ct; ++i) +- if (self->sws_bp_addrs[i] == 0) { +- self->sws_bp_addrs[i] = addr; ++ if (self->sws_bps[i] == NULL) { + static struct bp_callbacks cbs = { + .on_hit = sw_singlestep_bp_on_hit, + }; + struct breakpoint *bp + = insert_breakpoint(proc, addr, NULL); + breakpoint_set_callbacks(bp, &cbs); ++ self->sws_bps[i] = bp; + return 0; + } + +@@ -608,7 +607,9 @@ sw_singlestep_add_bp(arch_addr_t addr, struct sw_singlestep_data *data) + static int + singlestep(struct process_stopping_handler *self) + { +- struct process *proc = self->task_enabling_breakpoint; ++ size_t i; ++ for (i = 0; i < sizeof(self->sws_bps) / sizeof(*self->sws_bps); ++i) ++ self->sws_bps[i] = NULL; + + struct sw_singlestep_data data = { self }; + switch (arch_sw_singlestep(self->task_enabling_breakpoint, +@@ -617,7 +618,8 @@ singlestep(struct process_stopping_handler *self) + case SWS_HW: + /* Otherwise do the default action: singlestep. */ + debug(1, "PTRACE_SINGLESTEP"); +- if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) { ++ if (ptrace(PTRACE_SINGLESTEP, ++ self->task_enabling_breakpoint->pid, 0, 0)) { + perror("PTRACE_SINGLESTEP"); + return -1; + } +@@ -1038,7 +1040,7 @@ ltrace_exiting_install_handler(struct process *proc) + struct process_vfork_handler + { + struct event_handler super; +- void *bp_addr; ++ int vfork_bp_refd:1; + }; + + static Event * +@@ -1049,38 +1051,33 @@ process_vfork_on_event(struct event_handler *super, Event *event) + event->proc->pid, event->type); + + struct process_vfork_handler *self = (void *)super; +- struct breakpoint *sbp; ++ struct process *proc = event->proc; + assert(self != NULL); + + switch (event->type) { + case EVENT_BREAKPOINT: +- /* Remember the vfork return breakpoint. */ +- if (self->bp_addr == 0) +- self->bp_addr = event->e_un.brk_addr; ++ /* We turn on the vfork return breakpoint (which ++ * should be the one that we have tripped over just ++ * now) one extra time, so that the vfork parent hits ++ * it as well. */ ++ if (!self->vfork_bp_refd) { ++ struct breakpoint *const sbp = ++ dict_find_entry(proc->leader->breakpoints, ++ event->e_un.brk_addr); ++ assert(sbp != NULL); ++ breakpoint_turn_on(sbp, proc->leader); ++ self->vfork_bp_refd = 1; ++ } + break; + + case EVENT_EXIT: + case EVENT_EXIT_SIGNAL: + case EVENT_EXEC: +- /* Smuggle back in the vfork return breakpoint, so +- * that our parent can trip over it once again. */ +- if (self->bp_addr != 0) { +- sbp = dict_find_entry(event->proc->leader->breakpoints, +- self->bp_addr); +- if (sbp != NULL) +- assert(sbp->libsym == NULL); +- /* We don't mind failing that, it's not a big +- * deal to not display one extra vfork return. */ +- insert_breakpoint(event->proc->parent, +- self->bp_addr, NULL); +- } +- +- continue_process(event->proc->parent->pid); +- + /* Remove the leader that we artificially set up + * earlier. */ +- change_process_leader(event->proc, event->proc); +- destroy_event_handler(event->proc); ++ change_process_leader(proc, proc); ++ destroy_event_handler(proc); ++ continue_process(proc->parent->pid); + + default: + ; +diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h +index e988f70..5bb8380 100644 +--- a/sysdeps/linux-gnu/trace.h ++++ b/sysdeps/linux-gnu/trace.h +@@ -64,8 +64,8 @@ struct process_stopping_handler + /* The pointer being re-enabled. */ + struct breakpoint *breakpoint_being_enabled; + +- /* Artificial atomic skip breakpoint, if any needed. */ +- arch_addr_t sws_bp_addrs[2]; ++ /* Software singlestep breakpoints, if any needed. */ ++ struct breakpoint *sws_bps[2]; + + /* When all tasks are stopped, this callback gets called. */ + void (*on_all_stopped)(struct process_stopping_handler *); +diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c +index 3886e84..0a42c6e 100644 +--- a/sysdeps/linux-gnu/x86/regs.c ++++ b/sysdeps/linux-gnu/x86/regs.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand + * +@@ -107,11 +107,3 @@ get_return_addr(struct process *proc, void *sp) + ret = conv_32(ret); + return ret; + } +- +-void +-set_return_addr(struct process *proc, void *addr) +-{ +- if (proc->e_machine == EM_386) +- addr = (void *)((long int)addr & 0xffffffff); +- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); +-} +diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp +index e54086f..b585bc9 100644 +--- a/testsuite/ltrace.main/parameters.exp ++++ b/testsuite/ltrace.main/parameters.exp +@@ -35,9 +35,6 @@ if [regexp {ELF from incompatible architecture} $exec_output] { + return + } + +-set xfail_spec {"arm*-*" } +-set xfail_spec_arm {"arm*-*"} +- + # Verify the output + set pattern "func_intptr(17)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +@@ -63,7 +60,6 @@ set pattern "func_ushort(33, 34)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_float(3.40*, -3.40*).*= 3.40*" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-eval "setup_xfail $xfail_spec" + set pattern "func_double(3.40*, -3.40*).*= -3.40*" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_typedef(BLUE)" +@@ -86,7 +82,6 @@ set pattern "func_work(\\\"x\\\")" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_struct_2(17, { \\\"ABCDE\\\\\\\\0\\\", 0.250* }, 0.50*).*= { 0.250*, 'B', 'C' }" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-eval "setup_xfail $xfail_spec_arm" + set pattern "<... func_call resumed> \\\"x\\\", \\\"y\\\")" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + +diff --git a/testsuite/ltrace.torture/Makefile.am b/testsuite/ltrace.torture/Makefile.am +index daa772f..5a45265 100644 +--- a/testsuite/ltrace.torture/Makefile.am ++++ b/testsuite/ltrace.torture/Makefile.am +@@ -15,15 +15,9 @@ + # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + # + +-EXTRA_DIST = \ +- ia64-sigill.exp \ +- ia64-sigill.s \ +- ppc-lwarx.c \ +- ppc-lwarx.exp \ +- signals.c \ +- signals.exp \ +- vfork-thread.c \ +- vfork-thread.exp ++EXTRA_DIST = arm-singlestep.exp ia64-sigill.exp ia64-sigill.s \ ++ ppc-lwarx.c ppc-lwarx.exp signals.c signals.exp \ ++ vfork-thread.c vfork-thread.exp + + CLEANFILES = *.o *.so *.log *.sum *.ltrace setval.tmp \ + signals +diff --git a/testsuite/ltrace.torture/arm-singlestep.exp b/testsuite/ltrace.torture/arm-singlestep.exp +new file mode 100644 +index 0000000..0d633d9 +--- /dev/null ++++ b/testsuite/ltrace.torture/arm-singlestep.exp +@@ -0,0 +1,44 @@ ++# This file is part of ltrace. ++# Copyright (C) 2013 Petr Machata, Red Hat Inc. ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License as ++# published by the Free Software Foundation; either version 2 of the ++# License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++# 02110-1301 USA ++ ++if {![istarget arm*-*]} { ++ unsupported "arm-specific test" ++ return ++} ++ ++set exe [ltraceCompile {} [ltraceSource c { ++ int puc(void) { return 0; } ++ ++ int bar(void); ++ int baz(void); ++ __asm__ (" .type bar, %function\n" ++ "bar: \n" ++ " b puc \n" ++ " .type baz, %function\n" ++ "baz: \n" ++ " b puc \n"); ++ ++ int main(void) { return bar() + baz(); } ++}]] ++ ++ltraceMatch [ltraceRun -L -xbar+baz $exe] { ++ {{bar} == 1} ++ {{baz} == 1} ++} ++ ++ltraceDone +diff --git a/type.c b/type.c +index d80550b..e06a9c2 100644 +--- a/type.c ++++ b/type.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2007,2008 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -568,3 +568,39 @@ type_get_fp_equivalent(struct arg_type_info *info) + } + abort(); + } ++ ++struct arg_type_info * ++type_get_hfa_type(struct arg_type_info *info, size_t *countp) ++{ ++ assert(info != NULL); ++ if (info->type != ARGTYPE_STRUCT ++ && info->type != ARGTYPE_ARRAY) ++ return NULL; ++ ++ size_t n = type_aggregate_size(info); ++ if (n == (size_t)-1) ++ return NULL; ++ ++ struct arg_type_info *ret = NULL; ++ *countp = 0; ++ ++ while (n-- > 0) { ++ struct arg_type_info *emt = type_element(info, n); ++ ++ size_t emt_count = 1; ++ if (emt->type == ARGTYPE_STRUCT || emt->type == ARGTYPE_ARRAY) ++ emt = type_get_hfa_type(emt, &emt_count); ++ if (emt == NULL) ++ return NULL; ++ if (ret == NULL) { ++ if (emt->type != ARGTYPE_FLOAT ++ && emt->type != ARGTYPE_DOUBLE) ++ return NULL; ++ ret = emt; ++ } ++ if (emt->type != ret->type) ++ return NULL; ++ *countp += emt_count; ++ } ++ return ret; ++} +diff --git a/type.h b/type.h +index b92c1af..3210677 100644 +--- a/type.h ++++ b/type.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 1997-2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -142,4 +142,13 @@ int type_is_signed(enum arg_type type); + * type. */ + struct arg_type_info *type_get_fp_equivalent(struct arg_type_info *info); + ++/* If INFO is homogeneous floating-point aggregate, return the ++ * corresponding floating point type, and set *COUNTP to number of ++ * fields of the structure. Otherwise return NULL. INFO is a HFA if ++ * it's an aggregate whose each field is either a HFA, or a ++ * floating-point type. */ ++struct arg_type_info *type_get_hfa_type(struct arg_type_info *info, ++ size_t *countp); ++ ++ + #endif /* TYPE_H */ diff --git a/ltrace-0.7.2-bits.patch b/ltrace-0.7.2-bits.patch new file mode 100644 index 0000000..490defd --- /dev/null +++ b/ltrace-0.7.2-bits.patch @@ -0,0 +1,6472 @@ +diff --git a/Makefile.am b/Makefile.am +index 6e46949..c3356de 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -60,7 +60,8 @@ libltrace_la_SOURCES = \ + zero.c \ + lens.c \ + lens_default.c \ +- lens_enum.c ++ lens_enum.c \ ++ memstream.c + + libltrace_la_LIBADD = \ + $(libelf_LIBS) \ +@@ -112,7 +113,8 @@ noinst_HEADERS = \ + zero.h \ + lens.h \ + lens_default.h \ +- lens_enum.h ++ lens_enum.h \ ++ memstream.h + + dist_man1_MANS = ltrace.1 + dist_man5_MANS = ltrace.conf.5 +diff --git a/backend.h b/backend.h +index 89c05c3..cfac65e 100644 +--- a/backend.h ++++ b/backend.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as +@@ -27,12 +27,12 @@ + #include + + enum process_status { +- ps_invalid, /* Failure. */ +- ps_stop, /* Job-control stop. */ +- ps_tracing_stop, +- ps_sleeping, +- ps_zombie, +- ps_other, /* Necessary other states can be added as needed. */ ++ PS_INVALID, /* Failure. */ ++ PS_STOP, /* Job-control stop. */ ++ PS_TRACING_STOP, ++ PS_SLEEPING, ++ PS_ZOMBIE, ++ PS_OTHER, /* Necessary other states can be added as needed. */ + }; + + /* +@@ -70,7 +70,7 @@ int wait_for_proc(pid_t pid); + int task_kill(pid_t pid, int sig); + + /* Called after PID is attached, but before it is continued. */ +-void trace_set_options(struct Process *proc); ++void trace_set_options(struct process *proc); + + /* Called after ltrace forks. Should attach the newly created child, + * in whose context this function is called. */ +@@ -86,7 +86,7 @@ void untrace_pid(pid_t pid); + /* The back end may need to store arbitrary data to a process. This + * is a place where it can initialize PROC->arch_dep. XXX this should + * be dropped in favor of arhc_process_init on pmachata/libs. */ +-void get_arch_dep(struct Process *proc); ++void get_arch_dep(struct process *proc); + + /* Return current instruction pointer of PROC. + * +@@ -95,34 +95,34 @@ void get_arch_dep(struct Process *proc); + * that would otherwise support this. Above we have a definition of + * arch_addr_t. This should be converted to an integral type and + * used for target addresses throughout. */ +-void *get_instruction_pointer(struct Process *proc); ++void *get_instruction_pointer(struct process *proc); + + /* Set instruction pointer of PROC to ADDR. XXX see above. */ +-void set_instruction_pointer(struct Process *proc, void *addr); ++void set_instruction_pointer(struct process *proc, void *addr); + + /* Return current stack pointer of PROC. XXX see above. */ +-void *get_stack_pointer(struct Process *proc); ++void *get_stack_pointer(struct process *proc); + + /* Find and return caller address, i.e. the address where the current + * function returns. */ +-void *get_return_addr(struct Process *proc, void *stack_pointer); ++void *get_return_addr(struct process *proc, void *stack_pointer); + + /* Adjust PROC so that when the current function returns, it returns + * to ADDR. */ +-void set_return_addr(struct Process *proc, void *addr); ++void set_return_addr(struct process *proc, void *addr); + + /* Enable breakpoint SBP in process PROC. */ +-void enable_breakpoint(struct Process *proc, struct breakpoint *sbp); ++void enable_breakpoint(struct process *proc, struct breakpoint *sbp); + + /* Disable breakpoint SBP in process PROC. */ +-void disable_breakpoint(struct Process *proc, struct breakpoint *sbp); ++void disable_breakpoint(struct process *proc, struct breakpoint *sbp); + + /* Determine whether the event that we have just seen (and that is + * recorded in STATUS) was a syscall. If it was, return 1. If it was + * a return from syscall, return 2. In both cases, set *SYSNUM to the + * number of said syscall. If it wasn't a syscall, return 0. If + * there was an error, return -1. */ +-int syscall_p(struct Process *proc, int status, int *sysnum); ++int syscall_p(struct process *proc, int status, int *sysnum); + + /* Continue execution of the process with given PID. */ + void continue_process(pid_t pid); +@@ -136,17 +136,21 @@ void continue_after_signal(pid_t pid, int signum); + * is system call, otherwise it's return from a system call. The + * callback should do whatever book-keeping is necessary and continue + * the process if necessary. */ +-void continue_after_syscall(struct Process *proc, int sysnum, int ret_p); ++void continue_after_syscall(struct process *proc, int sysnum, int ret_p); + + /* Called after we hit a breakpoint SBP. Should do whatever + * book-keeping is necessary and then continue the process. */ +-void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp); ++void continue_after_breakpoint(struct process *proc, struct breakpoint *sbp); + + /* Called after we received a vfork. Should do whatever book-keeping + * is necessary and continue the process if necessary. N.B. right + * now, with Linux/GNU the only back end, this is not necessary. I + * imagine other systems may be different. */ +-void continue_after_vfork(struct Process *proc); ++void continue_after_vfork(struct process *proc); ++ ++/* Called after the process exec's. Should do whatever book-keeping ++ * is necessary and then continue the process. */ ++void continue_after_exec(struct process *proc); + + /* Called when trace_me or primary trace_pid fail. This may plug in + * any platform-specific knowledge of why it could be so. */ +@@ -171,14 +175,14 @@ void os_ltrace_exiting(void); + + /* Should copy COUNT bytes from address ADDR of process PROC to local + * buffer BUF. */ +-size_t umovebytes (struct Process *proc, void *addr, void *buf, size_t count); ++size_t umovebytes(struct process *proc, void *addr, void *buf, size_t count); + + /* Find out an address of symbol SYM in process PROC, and return. + * Returning NULL delays breakpoint insertion and enables heaps of + * arch-specific black magic that we should clean up some day. + * + * XXX the same points as for get_instruction_pointer apply. */ +-void *sym2addr(struct Process *proc, struct library_symbol *sym); ++void *sym2addr(struct process *proc, struct library_symbol *sym); + + /* Obtain address of PLT entry corresponding to relocation RELA in + * file LTE. This is NDX-th PLT entry in the file. +@@ -189,7 +193,7 @@ GElf_Addr arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela); + /* Called at some point after we have attached to PROC. This callback + * should insert an introspection breakpoint for handling dynamic + * linker library loads. */ +-int linkmap_init(struct Process *proc, arch_addr_t dyn_addr); ++int linkmap_init(struct process *proc, arch_addr_t dyn_addr); + + /* This should produce and return the next event of one of the traced + * processes. The returned pointer will not be freed by the core and +@@ -198,14 +202,14 @@ int linkmap_init(struct Process *proc, arch_addr_t dyn_addr); + struct Event *next_event(void); + + /* Called when process PROC was removed. */ +-void process_removed(struct Process *proc); ++void process_removed(struct process *proc); + + /* This should extract entry point address and interpreter (dynamic + * linker) bias if possible. Returns 0 if there were no errors, -1 + * otherwise. Sets *ENTRYP and *INTERP_BIASP to non-zero values if + * the corresponding value is known, or zero otherwise; this is not + * done for pointers that are NULL. */ +-int process_get_entry(struct Process *proc, ++int process_get_entry(struct process *proc, + arch_addr_t *entryp, + arch_addr_t *interp_biasp); + +@@ -232,7 +236,7 @@ void arch_elf_destroy(struct ltelf *lte); + * destroy, and clone SBP->arch. arch_breakpoint_init and + * arch_breakpoint_clone return 0 on success or a negative value on + * failure. */ +-int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp); ++int arch_breakpoint_init(struct process *proc, struct breakpoint *sbp); + void arch_breakpoint_destroy(struct breakpoint *sbp); + int arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp); + +@@ -259,19 +263,19 @@ int arch_library_symbol_clone(struct library_symbol *retp, + * PROC->arch in case that PROC underwent an exec. See notes at + * process_init, process_destroy, process_clone and process_exec in + * proc.h. */ +-int arch_process_init(struct Process *proc); +-void arch_process_destroy(struct Process *proc); +-int arch_process_clone(struct Process *retp, struct Process *proc); +-int arch_process_exec(struct Process *proc); ++int arch_process_init(struct process *proc); ++void arch_process_destroy(struct process *proc); ++int arch_process_clone(struct process *retp, struct process *proc); ++int arch_process_exec(struct process *proc); + + /* The following callbacks have to be implemented in OS backend if + * os.h defines OS_HAVE_PROCESS_DATA. The protocol is same as for, + * respectively, arch_process_init, arch_process_destroy, + * arch_process_clone and arch_process_exec. */ +-int os_process_init(struct Process *proc); +-void os_process_destroy(struct Process *proc); +-int os_process_clone(struct Process *retp, struct Process *proc); +-int os_process_exec(struct Process *proc); ++int os_process_init(struct process *proc); ++void os_process_destroy(struct process *proc); ++int os_process_clone(struct process *retp, struct process *proc); ++int os_process_exec(struct process *proc); + + /* The following callback has to be implemented in backend if arch.h + * defines ARCH_HAVE_GET_SYM_INFO. +@@ -289,9 +293,9 @@ int arch_get_sym_info(struct ltelf *lte, const char *filename, + size_t sym_index, GElf_Rela *rela, GElf_Sym *sym); + + enum plt_status { +- plt_fail, +- plt_ok, +- plt_default, ++ PLT_FAIL, ++ PLT_OK, ++ PLT_DEFAULT, + }; + + /* The following callback has to be implemented in backend if arch.h +@@ -302,22 +306,22 @@ enum plt_status { + * The corresponding PLT entry is for symbol called NAME, and it's + * I-th relocation in the file. + * +- * If this function returns plt_default, PLT address is obtained by +- * calling arch_plt_sym_val, and symbol is allocated. If plt_ok or +- * plt_default are returned, the chain of symbols passed back in RET ++ * If this function returns PLT_DEFAULT, PLT address is obtained by ++ * calling arch_plt_sym_val, and symbol is allocated. If PLT_OK or ++ * PLT_DEFAULT are returned, the chain of symbols passed back in RET + * is added to library under construction. */ +-enum plt_status arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++enum plt_status arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *name, GElf_Rela *rela, + size_t i, struct library_symbol **ret); + + /* This callback needs to be implemented if arch.h defines + * ARCH_HAVE_DYNLINK_DONE. It is called after the dynamic linker is + * done with the process startup. */ +-void arch_dynlink_done(struct Process *proc); ++void arch_dynlink_done(struct process *proc); + + /* This callback needs to be implemented if arch.h defines + * ARCH_HAVE_SYMBOL_RET. It is called after a traced call returns. */ +-void arch_symbol_ret(struct Process *proc, struct library_symbol *libsym); ++void arch_symbol_ret(struct process *proc, struct library_symbol *libsym); + + + /* This callback needs to be implemented if arch.h defines +@@ -327,7 +331,7 @@ void arch_symbol_ret(struct Process *proc, struct library_symbol *libsym); + * DYN_ADDR holds the address of the dynamic section. + * If the debug area is found, return 0 and fill in the address in *RET. + * If the debug area is not found, return a negative value. */ +-int arch_find_dl_debug(struct Process *proc, arch_addr_t dyn_addr, ++int arch_find_dl_debug(struct process *proc, arch_addr_t dyn_addr, + arch_addr_t *ret); + + /* If arch.h defines ARCH_HAVE_FETCH_ARG, the following callbacks have +@@ -340,4 +344,34 @@ int arch_find_dl_debug(struct Process *proc, arch_addr_t dyn_addr, + * implemented: arch_fetch_param_pack_start, + * arch_fetch_param_pack_end. See fetch.h for details. */ + ++enum sw_singlestep_status { ++ SWS_FAIL, ++ SWS_OK, ++ SWS_HW, ++}; ++struct sw_singlestep_data; ++ ++/* The following callback has to be implemented in backend if arch.h ++ * defines ARCH_HAVE_SW_SINGLESTEP. ++ * ++ * This is called before the OS backend requests hardware singlestep. ++ * arch_sw_singlestep should consider whether a singlestep needs to be ++ * done in software. If not, it returns SWS_HW. Otherwise it needs ++ * to add one or several breakpoints by calling ADD_CB. When it is ++ * done, it continues the process as appropriate, and answers either ++ * SWS_OK, or SWS_FAIL, depending on how it went. ++ * ++ * PROC is the process that should perform the singlestep, BP the ++ * breakpoint that we are singlestepping over. ADD_CB is a callback ++ * to request adding breakpoints that should trap the process after ++ * it's continued. The arguments to ADD_CB are the address where the ++ * breakpoint should be added, and DATA. ADD_CB returns 0 on success ++ * or a negative value on failure. It is expected that ++ * arch_sw_singlestep returns SWS_FAIL if ADD_CB returns error. */ ++enum sw_singlestep_status arch_sw_singlestep(struct process *proc, ++ struct breakpoint *bp, ++ int (*add_cb)(arch_addr_t addr, ++ struct sw_singlestep_data *), ++ struct sw_singlestep_data *data); ++ + #endif /* BACKEND_H */ +diff --git a/breakpoint.h b/breakpoint.h +index 7cd914e..18af7a9 100644 +--- a/breakpoint.h ++++ b/breakpoint.h +@@ -41,14 +41,12 @@ + + #include "sysdep.h" + #include "library.h" +- +-struct Process; +-struct breakpoint; ++#include "forward.h" + + struct bp_callbacks { +- void (*on_hit)(struct breakpoint *bp, struct Process *proc); +- void (*on_continue)(struct breakpoint *bp, struct Process *proc); +- void (*on_retract)(struct breakpoint *bp, struct Process *proc); ++ void (*on_hit)(struct breakpoint *bp, struct process *proc); ++ void (*on_continue)(struct breakpoint *bp, struct process *proc); ++ void (*on_retract)(struct breakpoint *bp, struct process *proc); + }; + + struct breakpoint { +@@ -61,11 +59,11 @@ struct breakpoint { + }; + + /* Call on-hit handler of BP, if any is set. */ +-void breakpoint_on_hit(struct breakpoint *bp, struct Process *proc); ++void breakpoint_on_hit(struct breakpoint *bp, struct process *proc); + + /* Call on-continue handler of BP. If none is set, call + * continue_after_breakpoint. */ +-void breakpoint_on_continue(struct breakpoint *bp, struct Process *proc); ++void breakpoint_on_continue(struct breakpoint *bp, struct process *proc); + + /* Call on-retract handler of BP, if any is set. This should be + * called before the breakpoints are destroyed. The reason for a +@@ -74,21 +72,21 @@ void breakpoint_on_continue(struct breakpoint *bp, struct Process *proc); + * be called every time we disable the breakpoint, which is too often + * (a breakpoint has to be disabled every time that we need to execute + * the instruction underneath it). */ +-void breakpoint_on_retract(struct breakpoint *bp, struct Process *proc); ++void breakpoint_on_retract(struct breakpoint *bp, struct process *proc); + + /* Initialize a breakpoint structure. That doesn't actually realize + * the breakpoint. The breakpoint is initially assumed to be + * disabled. orig_value has to be set separately. CBS may be + * NULL. */ +-int breakpoint_init(struct breakpoint *bp, struct Process *proc, ++int breakpoint_init(struct breakpoint *bp, struct process *proc, + arch_addr_t addr, struct library_symbol *libsym); + + /* Make a clone of breakpoint BP into the area of memory pointed to by + * RETP. The original breakpoint was assigned to process OLD_PROC, + * the cloned breakpoint will be attached to process NEW_PROC. + * Returns 0 on success or a negative value on failure. */ +-int breakpoint_clone(struct breakpoint *retp, struct Process *new_proc, +- struct breakpoint *bp, struct Process *old_proc); ++int breakpoint_clone(struct breakpoint *retp, struct process *new_proc, ++ struct breakpoint *bp, struct process *old_proc); + + /* Set callbacks. If CBS is non-NULL, then BP->cbs shall be NULL. */ + void breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs); +@@ -98,12 +96,12 @@ void breakpoint_destroy(struct breakpoint *bp); + + /* Call enable_breakpoint the first time it's called. Returns 0 on + * success and a negative value on failure. */ +-int breakpoint_turn_on(struct breakpoint *bp, struct Process *proc); ++int breakpoint_turn_on(struct breakpoint *bp, struct process *proc); + + /* Call disable_breakpoint when turned off the same number of times + * that it was turned on. Returns 0 on success and a negative value + * on failure. */ +-int breakpoint_turn_off(struct breakpoint *bp, struct Process *proc); ++int breakpoint_turn_off(struct breakpoint *bp, struct process *proc); + + /* Utility function that does what typically needs to be done when a + * breakpoint is to be inserted. It checks whether there is another +@@ -113,7 +111,7 @@ int breakpoint_turn_off(struct breakpoint *bp, struct Process *proc); + * added as well as preexisting breakpoints, it then calls + * BREAKPOINT_TURN_ON. If anything fails, it cleans up and returns + * NULL. Otherwise it returns the breakpoint for ADDR. */ +-struct breakpoint *insert_breakpoint(struct Process *proc, void *addr, ++struct breakpoint *insert_breakpoint(struct process *proc, void *addr, + struct library_symbol *libsym); + + /* Name of a symbol associated with BP. May be NULL. */ +@@ -127,12 +125,12 @@ struct library *breakpoint_library(const struct breakpoint *bp); + * - proc_remove_breakpoint + * - breakpoint_destroy + * XXX */ +-void delete_breakpoint(struct Process *proc, void *addr); ++void delete_breakpoint(struct process *proc, void *addr); + + /* XXX some of the following belongs to proc.h/proc.c. */ +-struct breakpoint *address2bpstruct(struct Process *proc, void *addr); +-void enable_all_breakpoints(struct Process *proc); +-void disable_all_breakpoints(struct Process *proc); +-int breakpoints_init(struct Process *proc); ++struct breakpoint *address2bpstruct(struct process *proc, void *addr); ++void enable_all_breakpoints(struct process *proc); ++void disable_all_breakpoints(struct process *proc); ++int breakpoints_init(struct process *proc); + + #endif /* BREAKPOINT_H */ +diff --git a/breakpoints.c b/breakpoints.c +index 258be93..8db4e26 100644 +--- a/breakpoints.c ++++ b/breakpoints.c +@@ -42,7 +42,7 @@ + + #ifndef ARCH_HAVE_TRANSLATE_ADDRESS + int +-arch_translate_address_dyn(struct Process *proc, ++arch_translate_address_dyn(struct process *proc, + arch_addr_t addr, arch_addr_t *ret) + { + *ret = addr; +@@ -60,7 +60,7 @@ arch_translate_address(struct ltelf *lte, + #endif + + void +-breakpoint_on_hit(struct breakpoint *bp, struct Process *proc) ++breakpoint_on_hit(struct breakpoint *bp, struct process *proc) + { + assert(bp != NULL); + if (bp->cbs != NULL && bp->cbs->on_hit != NULL) +@@ -68,7 +68,7 @@ breakpoint_on_hit(struct breakpoint *bp, struct Process *proc) + } + + void +-breakpoint_on_continue(struct breakpoint *bp, struct Process *proc) ++breakpoint_on_continue(struct breakpoint *bp, struct process *proc) + { + assert(bp != NULL); + if (bp->cbs != NULL && bp->cbs->on_continue != NULL) +@@ -78,7 +78,7 @@ breakpoint_on_continue(struct breakpoint *bp, struct Process *proc) + } + + void +-breakpoint_on_retract(struct breakpoint *bp, struct Process *proc) ++breakpoint_on_retract(struct breakpoint *bp, struct process *proc) + { + assert(bp != NULL); + if (bp->cbs != NULL && bp->cbs->on_retract != NULL) +@@ -88,7 +88,7 @@ breakpoint_on_retract(struct breakpoint *bp, struct Process *proc) + /*****************************************************************************/ + + struct breakpoint * +-address2bpstruct(Process *proc, void *addr) ++address2bpstruct(struct process *proc, void *addr) + { + assert(proc != NULL); + assert(proc->breakpoints != NULL); +@@ -99,7 +99,7 @@ address2bpstruct(Process *proc, void *addr) + + #ifndef ARCH_HAVE_BREAKPOINT_DATA + int +-arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) ++arch_breakpoint_init(struct process *proc, struct breakpoint *sbp) + { + return 0; + } +@@ -117,7 +117,7 @@ arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) + #endif + + static void +-breakpoint_init_base(struct breakpoint *bp, struct Process *proc, ++breakpoint_init_base(struct breakpoint *bp, struct process *proc, + arch_addr_t addr, struct library_symbol *libsym) + { + bp->cbs = NULL; +@@ -132,7 +132,7 @@ breakpoint_init_base(struct breakpoint *bp, struct Process *proc, + * static lookups of various sections in the ELF file. We shouldn't + * need process for anything. */ + int +-breakpoint_init(struct breakpoint *bp, struct Process *proc, ++breakpoint_init(struct breakpoint *bp, struct process *proc, + arch_addr_t addr, struct library_symbol *libsym) + { + breakpoint_init_base(bp, proc, addr, libsym); +@@ -156,8 +156,8 @@ breakpoint_destroy(struct breakpoint *bp) + } + + int +-breakpoint_clone(struct breakpoint *retp, struct Process *new_proc, +- struct breakpoint *bp, struct Process *old_proc) ++breakpoint_clone(struct breakpoint *retp, struct process *new_proc, ++ struct breakpoint *bp, struct process *old_proc) + { + struct library_symbol *libsym = NULL; + if (bp->libsym != NULL) { +@@ -175,7 +175,7 @@ breakpoint_clone(struct breakpoint *retp, struct Process *new_proc, + } + + int +-breakpoint_turn_on(struct breakpoint *bp, struct Process *proc) ++breakpoint_turn_on(struct breakpoint *bp, struct process *proc) + { + bp->enabled++; + if (bp->enabled == 1) { +@@ -186,7 +186,7 @@ breakpoint_turn_on(struct breakpoint *bp, struct Process *proc) + } + + int +-breakpoint_turn_off(struct breakpoint *bp, struct Process *proc) ++breakpoint_turn_off(struct breakpoint *bp, struct process *proc) + { + bp->enabled--; + if (bp->enabled == 0) +@@ -196,10 +196,10 @@ breakpoint_turn_off(struct breakpoint *bp, struct Process *proc) + } + + struct breakpoint * +-insert_breakpoint(struct Process *proc, void *addr, ++insert_breakpoint(struct process *proc, void *addr, + struct library_symbol *libsym) + { +- Process *leader = proc->leader; ++ struct process *leader = proc->leader; + + /* Only the group leader should be getting the breakpoints and + * thus have ->breakpoint initialized. */ +@@ -243,11 +243,11 @@ insert_breakpoint(struct Process *proc, void *addr, + } + + void +-delete_breakpoint(Process *proc, void *addr) ++delete_breakpoint(struct process *proc, void *addr) + { + debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); + +- Process * leader = proc->leader; ++ struct process *leader = proc->leader; + assert(leader != NULL); + + struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr); +@@ -285,13 +285,14 @@ breakpoint_library(const struct breakpoint *bp) + static void + enable_bp_cb(void *addr, void *sbp, void *proc) + { +- debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid); ++ debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ++ ((struct process *)proc)->pid); + if (((struct breakpoint *)sbp)->enabled) + enable_breakpoint(proc, sbp); + } + + void +-enable_all_breakpoints(Process *proc) ++enable_all_breakpoints(struct process *proc) + { + debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid); + +@@ -305,13 +306,15 @@ enable_all_breakpoints(Process *proc) + static void + disable_bp_cb(void *addr, void *sbp, void *proc) + { +- debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); ++ debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ++ ((struct process *)proc)->pid); + if (((struct breakpoint *)sbp)->enabled) + disable_breakpoint(proc, sbp); + } + + void +-disable_all_breakpoints(Process *proc) { ++disable_all_breakpoints(struct process *proc) ++{ + debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid); + assert(proc->leader == proc); + dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); +@@ -330,7 +333,7 @@ struct entry_breakpoint { + }; + + static void +-entry_breakpoint_on_hit(struct breakpoint *a, struct Process *proc) ++entry_breakpoint_on_hit(struct breakpoint *a, struct process *proc) + { + struct entry_breakpoint *bp = (void *)a; + if (proc == NULL || proc->leader == NULL) +@@ -342,7 +345,7 @@ entry_breakpoint_on_hit(struct breakpoint *a, struct Process *proc) + } + + int +-entry_breakpoint_init(struct Process *proc, ++entry_breakpoint_init(struct process *proc, + struct entry_breakpoint *bp, arch_addr_t addr, + struct library *lib) + { +@@ -360,7 +363,7 @@ entry_breakpoint_init(struct Process *proc, + } + + int +-breakpoints_init(Process *proc) ++breakpoints_init(struct process *proc) + { + debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); + +diff --git a/configure.ac b/configure.ac +index 20c84f4..47bd87e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -21,9 +21,9 @@ + # 02110-1301 USA + + # Process this file with autoconf to produce a configure script. +-AC_PREREQ(2.65) ++AC_PREREQ([2.65]) + +-AC_INIT([ltrace],[0.7.2],[ltrace-devel@lists.alioth.debian.org]) ++AC_INIT([ltrace],[0.7.90-git],[ltrace-devel@lists.alioth.debian.org]) + AC_CONFIG_HEADERS([config.h]) + AC_CONFIG_SRCDIR(libltrace.c) + AC_CONFIG_MACRO_DIR([config/m4]) +@@ -245,23 +246,48 @@ AC_CHECK_SIZEOF([long]) + + + # Checks for library functions. +-AC_FUNC_ERROR_AT_LINE + AC_FUNC_FORK + AC_CHECK_FUNCS([ \ + alarm \ + atexit \ +- getcwd \ + gettimeofday \ + memset \ +- mkdir \ +- rmdir \ + strchr \ + strdup \ + strerror \ ++ strsignal \ + strtol \ + strtoul \ + ]) + ++# ++# Define HAVE_OPEN_MEMSTREAM if open_memstream is available. glibc ++# before 2.10, eglibc and uClibc all need _GNU_SOURCE defined for ++# open_memstream to become visible, so check for that as well. If ++# unavailable, require that tmpfile be present. There's no ++# HAVE_TMPFILE, as we plain require that to be present as a fallback. ++# ++AC_CHECK_FUNCS([open_memstream], [], ++ [AC_MSG_CHECKING([for open_memstream with _GNU_SOURCE]) ++ AC_LINK_IFELSE( ++ [AC_LANG_PROGRAM([[#define _GNU_SOURCE 1 ++ #include ]], ++ [[char *buf; size_t sz; ++ return open_memstream(&buf, &sz) != 0;]])], ++ ++ [AC_MSG_RESULT([yes]) ++ AC_DEFINE([HAVE_OPEN_MEMSTREAM], [1], ++ [Define if open_memstream exists.])], ++ ++ [AC_MSG_RESULT([no]) ++ AC_CHECK_FUNC([tmpfile], [], ++ [AC_MSG_ERROR( ++ [Either open_memstream or tmpfile required.])])])]) ++ ++# ++# Define HAVE_GETOPT_LONG if that is available. ++# ++AC_CHECK_HEADER([getopt.h], [AC_CHECK_FUNCS([getopt_long])]) + + # + # Debugging +diff --git a/debug.c b/debug.c +index 1b03189..ce220fe 100644 +--- a/debug.c ++++ b/debug.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. + * Copyright (C) 2003,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand + * +@@ -23,6 +24,7 @@ + #include + + #include "common.h" ++#include "backend.h" + + void + debug_(int level, const char *file, int line, const char *fmt, ...) { +@@ -40,95 +42,26 @@ debug_(int level, const char *file, int line, const char *fmt, ...) { + fflush(options.output); + } + +-/* +- * The following section provides a way to print things, like hex dumps, +- * with out using buffered output. This was written by Steve Munroe of IBM. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-static int +-xwritehexl(long i) { +- int rc = 0; +- char text[17]; +- int j; +- unsigned long temp = (unsigned long)i; +- +- for (j = 15; j >= 0; j--) { +- char c; +- c = (char)((temp & 0x0f) + '0'); +- if (c > '9') { +- c = (char)(c + ('a' - '9' - 1)); +- } +- text[j] = c; +- temp = temp >> 4; +- } +- +- rc = write(1, text, 16); +- return rc; +-} +- +-static int +-xwritec(char c) { +- char temp = c; +- char *text = &temp; +- int rc = 0; +- rc = write(1, text, 1); +- return rc; +-} +- +-static int +-xwritecr(void) { +- return xwritec('\n'); +-} +- + static int +-xwritedump(void *ptr, long addr, int len) { +- int rc = 0; +- long *tprt = (long *)ptr; +- int i; +- +- for (i = 0; i < len; i += 8) { +- xwritehexl(addr); +- xwritec('-'); +- xwritec('>'); +- xwritehexl(*tprt++); +- xwritecr(); ++xwritedump(long *ptr, arch_addr_t addr, size_t count) ++{ ++ size_t i; ++ for (i = 0; i < count; ++i) { ++ if (fprintf(stderr, "%p->%0*lx\n", ++ addr, 2 * (int)sizeof(long), ptr[i]) < 0) ++ return -1; + addr += sizeof(long); + } + +- return rc; ++ return 0; + } + + int +-xinfdump(long pid, void *ptr, int len) { +- int rc; +- int i; +- long wrdcnt; +- long *infwords; +- long addr; +- +- wrdcnt = len / sizeof(long) + 1; +- infwords = malloc(wrdcnt * sizeof(long)); +- if (!infwords) { +- perror("ltrace: malloc"); +- exit(1); +- } +- addr = (long)ptr; +- +- addr = ((addr + sizeof(long) - 1) / sizeof(long)) * sizeof(long); +- +- for (i = 0; i < wrdcnt; ++i) { +- infwords[i] = ptrace(PTRACE_PEEKTEXT, pid, (void *)addr, NULL); +- addr += sizeof(long); +- } +- +- rc = xwritedump(infwords, (long)ptr, len); +- +- free(infwords); +- return rc; ++xinfdump(struct process *proc, arch_addr_t addr, size_t length) ++{ ++ unsigned char buf[length]; ++ size_t got = umovebytes(proc, addr, buf, length); ++ if (got == (size_t)-1) ++ return -1; ++ return xwritedump((long *)buf, addr, got / sizeof(long)); + } +diff --git a/debug.h b/debug.h +index 4775d2f..542dda5 100644 +--- a/debug.h ++++ b/debug.h +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. + * Copyright (C) 2003,2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -21,6 +22,9 @@ + #ifndef _DEBUG_H + #define _DEBUG_H + ++#include "backend.h" ++#include "forward.h" ++ + /* debug levels: + */ + enum { +@@ -32,8 +36,10 @@ enum { + void debug_(int level, const char *file, int line, + const char *fmt, ...) __attribute__((format(printf,4,5))); + +-int xinfdump(long, void *, int); ++/* Dump LENGTH bytes of memory starting on address ADDR of inferior ++ * PID. */ ++int xinfdump(struct process *proc, arch_addr_t addr, size_t length); + +-# define debug(level, expr...) debug_(level, __FILE__, __LINE__, expr) ++#define debug(level, expr...) debug_(level, __FILE__, __LINE__, expr) + + #endif +diff --git a/expr.c b/expr.c +index 32860fd..552a53c 100644 +--- a/expr.c ++++ b/expr.c +@@ -21,7 +21,6 @@ + #include + #include + #include +-#include + #include + + #include "expr.h" +@@ -327,12 +326,11 @@ expr_eval_constant(struct expr_node *node, long *valuep) + struct expr_node * + expr_self(void) + { +- static struct expr_node *node = NULL; +- if (node == NULL) { +- node = malloc(sizeof(*node)); +- if (node == NULL) +- error(1, errno, "malloc expr_self"); +- expr_init_self(node); ++ static struct expr_node *nodep = NULL; ++ if (nodep == NULL) { ++ static struct expr_node node; ++ expr_init_self(&node); ++ nodep = &node; + } +- return node; ++ return nodep; + } +diff --git a/fetch.c b/fetch.c +index 88966a5..cbceefb 100644 +--- a/fetch.c ++++ b/fetch.c +@@ -27,18 +27,18 @@ + #include "type.h" + + #ifdef ARCH_HAVE_FETCH_ARG +-struct fetch_context *arch_fetch_arg_init(enum tof type, struct Process *proc, ++struct fetch_context *arch_fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info); + +-struct fetch_context *arch_fetch_arg_clone(struct Process *proc, ++struct fetch_context *arch_fetch_arg_clone(struct process *proc, + struct fetch_context *context); + + int arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep); + + int arch_fetch_retval(struct fetch_context *ctx, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep); + + void arch_fetch_arg_done(struct fetch_context *context); +@@ -53,7 +53,7 @@ void arch_fetch_param_pack_end(struct fetch_context *context); + #else + /* Fall back to gimme_arg. */ + +-long gimme_arg(enum tof type, struct Process *proc, int arg_num, ++long gimme_arg(enum tof type, struct process *proc, int arg_num, + struct arg_type_info *info); + + struct fetch_context { +@@ -61,14 +61,14 @@ struct fetch_context { + }; + + struct fetch_context * +-arch_fetch_arg_init(enum tof type, struct Process *proc, ++arch_fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info) + { + return calloc(sizeof(struct fetch_context), 1); + } + + struct fetch_context * +-arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) ++arch_fetch_arg_clone(struct process *proc, struct fetch_context *context) + { + struct fetch_context *ret = malloc(sizeof(*ret)); + if (ret == NULL) +@@ -78,7 +78,7 @@ arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) + + int + arch_fetch_arg_next(struct fetch_context *context, enum tof type, +- struct Process *proc, ++ struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + long l = gimme_arg(type, proc, context->argnum++, info); +@@ -88,7 +88,7 @@ arch_fetch_arg_next(struct fetch_context *context, enum tof type, + + int + arch_fetch_retval(struct fetch_context *context, enum tof type, +- struct Process *proc, ++ struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + long l = gimme_arg(type, proc, -1, info); +@@ -118,21 +118,21 @@ arch_fetch_param_pack_end(struct fetch_context *context) + #endif + + struct fetch_context * +-fetch_arg_init(enum tof type, struct Process *proc, ++fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info) + { + return arch_fetch_arg_init(type, proc, ret_info); + } + + struct fetch_context * +-fetch_arg_clone(struct Process *proc, struct fetch_context *context) ++fetch_arg_clone(struct process *proc, struct fetch_context *context) + { + return arch_fetch_arg_clone(proc, context); + } + + int + fetch_arg_next(struct fetch_context *context, enum tof type, +- struct Process *proc, ++ struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + return arch_fetch_arg_next(context, type, proc, info, valuep); +@@ -140,7 +140,7 @@ fetch_arg_next(struct fetch_context *context, enum tof type, + + int + fetch_retval(struct fetch_context *context, enum tof type, +- struct Process *proc, ++ struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + return arch_fetch_retval(context, type, proc, info, valuep); +diff --git a/fetch.h b/fetch.h +index 2a13214..3a1644a 100644 +--- a/fetch.h ++++ b/fetch.h +@@ -38,24 +38,24 @@ struct fetch_context; + + /* Initialize argument fetching. Returns NULL on failure. RET_INFO + * is the return type of the function. */ +-struct fetch_context *fetch_arg_init(enum tof type, struct Process *proc, ++struct fetch_context *fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info); + + /* Make a clone of context. */ +-struct fetch_context *fetch_arg_clone(struct Process *proc, ++struct fetch_context *fetch_arg_clone(struct process *proc, + struct fetch_context *context); + + /* Load next argument. The function returns 0 on success or a + * negative value on failure. The extracted value is stored in + * *VALUEP. */ + int fetch_arg_next(struct fetch_context *context, enum tof type, +- struct Process *proc, ++ struct process *proc, + struct arg_type_info *info, struct value *valuep); + + /* Load return value. The function returns 0 on success or a negative + * value on failure. The extracted value is stored in *VALUEP. */ + int fetch_retval(struct fetch_context *context, enum tof type, +- struct Process *proc, ++ struct process *proc, + struct arg_type_info *info, struct value *valuep); + + /* Destroy fetch context. CONTEXT shall be the same memory location +@@ -74,15 +74,15 @@ void fetch_param_pack_end(struct fetch_context *context); + /* The following callbacks have to be implemented in backend if arch.h + * defines ARCH_HAVE_FETCH_ARG. These backend callbacks correspond to + * above functions. */ +-struct fetch_context *arch_fetch_arg_init(enum tof type, struct Process *proc, ++struct fetch_context *arch_fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info); +-struct fetch_context *arch_fetch_arg_clone(struct Process *proc, ++struct fetch_context *arch_fetch_arg_clone(struct process *proc, + struct fetch_context *context); + int arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep); + int arch_fetch_retval(struct fetch_context *ctx, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep); + void arch_fetch_arg_done(struct fetch_context *context); + +diff --git a/forward.h b/forward.h +index 85a0630..d334339 100644 +--- a/forward.h ++++ b/forward.h +@@ -21,7 +21,7 @@ + /* Important types defined in other header files are declared + here. */ + struct Event; +-struct Process; ++struct process; + struct arg_type_info; + struct breakpoint; + struct expr_node; +diff --git a/glob.c b/glob.c +index 9af633f..b26637f 100644 +--- a/glob.c ++++ b/glob.c +@@ -180,7 +180,7 @@ glob_to_regex(const char *glob, char **retp) + goto fail; + } + *retp = buf; +- return REG_NOERROR; ++ return 0; + } + + int +@@ -188,7 +188,7 @@ globcomp(regex_t *preg, const char *glob, int cflags) + { + char *regex = NULL; + int status = glob_to_regex(glob, ®ex); +- if (status != REG_NOERROR) ++ if (status != 0) + return status; + assert(regex != NULL); + status = regcomp(preg, regex, cflags); +diff --git a/handle_event.c b/handle_event.c +index 42a7a05..9dbb696 100644 +--- a/handle_event.c ++++ b/handle_event.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Arnaud Patard, Mandriva SA + * Copyright (C) 1998,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes + * Copyright (C) 2008 Luis Machado, IBM Corporation +@@ -54,20 +54,20 @@ static void handle_exec(Event *event); + static void handle_breakpoint(Event *event); + static void handle_new(Event *event); + +-static void callstack_push_syscall(Process *proc, int sysnum); +-static void callstack_push_symfunc(Process *proc, ++static void callstack_push_syscall(struct process *proc, int sysnum); ++static void callstack_push_symfunc(struct process *proc, + struct library_symbol *sym); + /* XXX Stack maintenance should be moved to a dedicated module, or to + * proc.c, and push/pop should be visible outside this module. For + * now, because we need this in proc.c, this is non-static. */ +-void callstack_pop(struct Process *proc); ++void callstack_pop(struct process *proc); + +-static char * shortsignal(Process *proc, int signum); +-static char * sysname(Process *proc, int sysnum); +-static char * arch_sysname(Process *proc, int sysnum); ++static char *shortsignal(struct process *proc, int signum); ++static char *sysname(struct process *proc, int sysnum); ++static char *arch_sysname(struct process *proc, int sysnum); + + static Event * +-call_handler(Process * proc, Event * event) ++call_handler(struct process *proc, Event *event) + { + assert(proc != NULL); + +@@ -256,7 +256,7 @@ handle_clone(Event *event) + { + debug(DEBUG_FUNCTION, "handle_clone(pid=%d)", event->proc->pid); + +- struct Process *proc = malloc(sizeof(*proc)); ++ struct process *proc = malloc(sizeof(*proc)); + if (proc == NULL) { + fail: + free(proc); +@@ -297,12 +297,11 @@ handle_clone(Event *event) + } + + static void +-handle_new(Event * event) { +- Process * proc; +- ++handle_new(Event *event) ++{ + debug(DEBUG_FUNCTION, "handle_new(pid=%d)", event->e_un.newpid); + +- proc = pid2proc(event->e_un.newpid); ++ struct process *proc = pid2proc(event->e_un.newpid); + if (!proc) { + pending_new_insert(event->e_un.newpid); + } else { +@@ -317,7 +316,8 @@ handle_new(Event * event) { + } + + static char * +-shortsignal(Process *proc, int signum) { ++shortsignal(struct process *proc, int signum) ++{ + static char *signalent0[] = { + #include "signalent.h" + }; +@@ -341,7 +341,8 @@ shortsignal(Process *proc, int signum) { + } + + static char * +-sysname(Process *proc, int sysnum) { ++sysname(struct process *proc, int sysnum) ++{ + static char result[128]; + static char *syscalent0[] = { + #include "syscallent.h" +@@ -369,7 +370,8 @@ sysname(Process *proc, int sysnum) { + } + + static char * +-arch_sysname(Process *proc, int sysnum) { ++arch_sysname(struct process *proc, int sysnum) ++{ + static char result[128]; + static char *arch_syscalent[] = { + #include "arch_syscallent.h" +@@ -388,6 +390,10 @@ arch_sysname(Process *proc, int sysnum) { + } + } + ++#ifndef HAVE_STRSIGNAL ++# define strsignal(SIGNUM) "???" ++#endif ++ + static void + handle_signal(Event *event) { + debug(DEBUG_FUNCTION, "handle_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); +@@ -420,8 +426,8 @@ handle_exit_signal(Event *event) { + } + + static void +-output_syscall(struct Process *proc, const char *name, enum tof tof, +- void (*output)(enum tof, struct Process *, ++output_syscall(struct process *proc, const char *name, enum tof tof, ++ void (*output)(enum tof, struct process *, + struct library_symbol *)) + { + struct library_symbol syscall; +@@ -432,13 +438,13 @@ output_syscall(struct Process *proc, const char *name, enum tof tof, + } + + static void +-output_syscall_left(struct Process *proc, const char *name) ++output_syscall_left(struct process *proc, const char *name) + { + output_syscall(proc, name, LT_TOF_SYSCALL, &output_left); + } + + static void +-output_syscall_right(struct Process *proc, const char *name) ++output_syscall_right(struct process *proc, const char *name) + { + output_syscall(proc, name, LT_TOF_SYSCALLR, &output_right); + } +@@ -457,8 +463,9 @@ handle_syscall(Event *event) { + } + + static void +-handle_exec(Event * event) { +- Process * proc = event->proc; ++handle_exec(Event *event) ++{ ++ struct process *proc = event->proc; + + /* Save the PID so that we can use it after unsuccessful + * process_exec. */ +@@ -479,18 +486,7 @@ handle_exec(Event * event) { + goto untrace; + } + +- continue_process(proc->pid); +- +- /* After the exec, we expect to hit the first executable +- * instruction. +- * +- * XXX TODO It would be nice to have this removed, but then we +- * need to do that also for initial call to wait_for_proc in +- * execute_program. In that case we could generate a +- * EVENT_FIRST event or something, or maybe this could somehow +- * be rolled into EVENT_NEW. */ +- wait_for_proc(proc->pid); +- continue_process(proc->pid); ++ continue_after_exec(proc); + } + + static void +@@ -510,7 +506,8 @@ handle_arch_syscall(Event *event) { + struct timeval current_time_spent; + + static void +-calc_time_spent(Process *proc) { ++calc_time_spent(struct process *proc) ++{ + struct timeval tv; + struct timezone tz; + struct timeval diff; +@@ -568,7 +565,7 @@ handle_arch_sysret(Event *event) { + } + + static void +-output_right_tos(struct Process *proc) ++output_right_tos(struct process *proc) + { + size_t d = proc->callstack_depth; + struct callstack_element *elem = &proc->callstack[d - 1]; +@@ -577,7 +574,7 @@ output_right_tos(struct Process *proc) + } + + #ifndef ARCH_HAVE_SYMBOL_RET +-void arch_symbol_ret(struct Process *proc, struct library_symbol *libsym) ++void arch_symbol_ret(struct process *proc, struct library_symbol *libsym) + { + } + #endif +@@ -587,7 +584,7 @@ handle_breakpoint(Event *event) + { + int i, j; + struct breakpoint *sbp; +- Process *leader = event->proc->leader; ++ struct process *leader = event->proc->leader; + void *brk_addr = event->e_un.brk_addr; + + /* The leader has terminated. */ +@@ -682,7 +679,8 @@ handle_breakpoint(Event *event) + } + + static void +-callstack_push_syscall(Process *proc, int sysnum) { ++callstack_push_syscall(struct process *proc, int sysnum) ++{ + struct callstack_element *elem; + + debug(DEBUG_FUNCTION, "callstack_push_syscall(pid=%d, sysnum=%d)", proc->pid, sysnum); +@@ -707,7 +705,8 @@ callstack_push_syscall(Process *proc, int sysnum) { + } + + static void +-callstack_push_symfunc(Process *proc, struct library_symbol *sym) { ++callstack_push_symfunc(struct process *proc, struct library_symbol *sym) ++{ + struct callstack_element *elem; + + debug(DEBUG_FUNCTION, "callstack_push_symfunc(pid=%d, symbol=%s)", proc->pid, sym->name); +@@ -734,7 +733,7 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { + } + + void +-callstack_pop(struct Process *proc) ++callstack_pop(struct process *proc) + { + struct callstack_element *elem; + assert(proc->callstack_depth > 0); +diff --git a/libltrace.c b/libltrace.c +index 559edfa..b69a0c9 100644 +--- a/libltrace.c ++++ b/libltrace.c +@@ -21,6 +21,7 @@ + + #include "config.h" + ++#include + #include + #include + #include +@@ -40,13 +41,13 @@ char *command = NULL; + int exiting = 0; /* =1 if a SIGINT or SIGTERM has been received */ + + static enum callback_status +-stop_non_p_processes(Process *proc, void *data) ++stop_non_p_processes(struct process *proc, void *data) + { + int stop = 1; + + struct opt_p_t *it; + for (it = opt_p; it != NULL; it = it->next) { +- Process * p_proc = pid2proc(it->pid); ++ struct process *p_proc = pid2proc(it->pid); + if (p_proc == NULL) { + printf("stop_non_p_processes: %d terminated?\n", it->pid); + continue; +@@ -141,7 +142,7 @@ ltrace_init(int argc, char **argv) { + do_close_elf(<e); + + pid_t pid = execute_program(command, argv); +- struct Process *proc = open_program(command, pid); ++ struct process *proc = open_program(command, pid); + if (proc == NULL) { + fprintf(stderr, "couldn't open program '%s': %s\n", + command, strerror(errno)); +diff --git a/library.c b/library.c +index 594472b..b5f6386 100644 +--- a/library.c ++++ b/library.c +@@ -412,7 +412,7 @@ library_add_symbol(struct library *lib, struct library_symbol *first) + } + + enum callback_status +-library_named_cb(struct Process *proc, struct library *lib, void *name) ++library_named_cb(struct process *proc, struct library *lib, void *name) + { + if (name == lib->soname + || strcmp(lib->soname, (char *)name) == 0) +@@ -422,7 +422,7 @@ library_named_cb(struct Process *proc, struct library *lib, void *name) + } + + enum callback_status +-library_with_key_cb(struct Process *proc, struct library *lib, void *keyp) ++library_with_key_cb(struct process *proc, struct library *lib, void *keyp) + { + return lib->key == *(arch_addr_t *)keyp ? CBS_STOP : CBS_CONT; + } +diff --git a/library.h b/library.h +index 555fa80..74e1df2 100644 +--- a/library.h ++++ b/library.h +@@ -23,12 +23,11 @@ + #define _LIBRARY_H_ + + #include ++ + #include "callback.h" ++#include "forward.h" + #include "sysdep.h" + +-struct Process; +-struct library; +- + enum toplt { + LS_TOPLT_NONE = 0, /* PLT not used for this symbol. */ + LS_TOPLT_EXEC, /* PLT for this symbol is executable. */ +@@ -195,7 +194,7 @@ void library_add_symbol(struct library *lib, struct library_symbol *sym); + + /* A function that can be used as proc_each_library callback. Looks + * for a library with the name passed in DATA. PROC is ignored. */ +-enum callback_status library_named_cb(struct Process *proc, ++enum callback_status library_named_cb(struct process *proc, + struct library *lib, void *name); + + /* A function that can be used as proc_each_library callback. Looks +@@ -203,7 +202,7 @@ enum callback_status library_named_cb(struct Process *proc, + * + * NOTE: The key is passed as a POINTER to arch_addr_t (that + * because in general, arch_addr_t doesn't fit in void*). */ +-enum callback_status library_with_key_cb(struct Process *proc, ++enum callback_status library_with_key_cb(struct process *proc, + struct library *lib, void *keyp); + + /* XXX this should really be in backend.h (as on pmachata/revamp +@@ -220,7 +219,7 @@ int arch_translate_address(struct ltelf *lte, + arch_addr_t addr, arch_addr_t *ret); + /* This is the same function as arch_translate_address, except it's + * used at the point that we don't have ELF available anymore. */ +-int arch_translate_address_dyn(struct Process *proc, ++int arch_translate_address_dyn(struct process *proc, + arch_addr_t addr, arch_addr_t *ret); + + #endif /* _LIBRARY_H_ */ +diff --git a/ltrace-elf.c b/ltrace-elf.c +index c571d2a..1d0f769 100644 +--- a/ltrace-elf.c ++++ b/ltrace-elf.c +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + #include + + #include "backend.h" +@@ -64,7 +65,7 @@ arch_elf_destroy(struct ltelf *lte) + #endif + + int +-default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++default_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, + struct library_symbol **ret) + { +@@ -101,11 +102,11 @@ default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, + + #ifndef ARCH_HAVE_ADD_PLT_ENTRY + enum plt_status +-arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, + struct library_symbol **ret) + { +- return plt_default; ++ return PLT_DEFAULT; + } + #endif + +@@ -540,7 +541,7 @@ mark_chain_latent(struct library_symbol *libsym) + } + + static int +-populate_plt(struct Process *proc, const char *filename, ++populate_plt(struct process *proc, const char *filename, + struct ltelf *lte, struct library *lib, + int latent_plts) + { +@@ -565,14 +566,14 @@ populate_plt(struct Process *proc, const char *filename, + struct library_symbol *libsym = NULL; + switch (arch_elf_add_plt_entry(proc, lte, name, + &rela, i, &libsym)) { +- case plt_default: ++ case PLT_DEFAULT: + if (default_elf_add_plt_entry(proc, lte, name, + &rela, i, &libsym) < 0) + /* fall-through */ +- case plt_fail: ++ case PLT_FAIL: + return -1; + /* fall-through */ +- case plt_ok: ++ case PLT_OK: + if (libsym != NULL) { + /* If we are adding those symbols just + * for tracing exports, mark them all +@@ -614,7 +615,7 @@ symbol_with_address(struct library_symbol *sym, void *addrptr) + } + + static int +-populate_this_symtab(struct Process *proc, const char *filename, ++populate_this_symtab(struct process *proc, const char *filename, + struct ltelf *lte, struct library *lib, + Elf_Data *symtab, const char *strtab, size_t size, + struct library_exported_name **names) +@@ -777,7 +778,7 @@ populate_this_symtab(struct Process *proc, const char *filename, + } + + static int +-populate_symtab(struct Process *proc, const char *filename, ++populate_symtab(struct process *proc, const char *filename, + struct ltelf *lte, struct library *lib, + int symtabs, int exports) + { +@@ -802,7 +803,7 @@ populate_symtab(struct Process *proc, const char *filename, + } + + static int +-read_module(struct library *lib, struct Process *proc, ++read_module(struct library *lib, struct process *proc, + const char *filename, GElf_Addr bias, int main) + { + struct ltelf lte = {}; +@@ -942,7 +943,7 @@ fail: + } + + int +-ltelf_read_library(struct library *lib, struct Process *proc, ++ltelf_read_library(struct library *lib, struct process *proc, + const char *filename, GElf_Addr bias) + { + return read_module(lib, proc, filename, bias, 0); +@@ -950,7 +951,7 @@ ltelf_read_library(struct library *lib, struct Process *proc, + + + struct library * +-ltelf_read_main_binary(struct Process *proc, const char *path) ++ltelf_read_main_binary(struct process *proc, const char *path) + { + struct library *lib = malloc(sizeof(*lib)); + if (lib == NULL) +diff --git a/ltrace-elf.h b/ltrace-elf.h +index 1a1321e..b76d1eb 100644 +--- a/ltrace-elf.h ++++ b/ltrace-elf.h +@@ -26,11 +26,9 @@ + + #include + #include +-#include "sysdep.h" + +-struct Process; +-struct library; +-struct library_symbol; ++#include "forward.h" ++#include "sysdep.h" + + /* XXX Ok, the original idea was to separate the low-level ELF data + * from the abstract "struct library" object, but we use some of the +@@ -73,21 +71,21 @@ void do_close_elf(struct ltelf *lte); + /* XXX is it possible to put breakpoints in VDSO and VSYSCALL + * pseudo-libraries? For now we assume that all libraries can be + * opened via a filesystem. BASE is ignored for ET_EXEC files. */ +-int ltelf_read_library(struct library *lib, struct Process *proc, ++int ltelf_read_library(struct library *lib, struct process *proc, + const char *filename, GElf_Addr bias); + + /* Create a library object representing the main binary. The entry + * point address is stored to *ENTRYP. */ +-struct library *ltelf_read_main_binary(struct Process *proc, const char *path); ++struct library *ltelf_read_main_binary(struct process *proc, const char *path); + + /* Create a default PLT entry. This can be used instead (or in +- * addition to) returning plt_default from arch_elf_add_plt_entry. ++ * addition to) returning PLT_DEFAULT from arch_elf_add_plt_entry. + * RET shall be initialized, the created symbol will be added to the + * beginning of the linked list at *RET. This function doesn't add + * the symbol to LTE. arch_elf_add_plt_entry has the chance to adjust +- * symbol internals to its liking, and then return either plt_default +- * or plt_ok. */ +-int default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++ * symbol internals to its liking, and then return either PLT_DEFAULT ++ * or PLT_OK. */ ++int default_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, + struct library_symbol **ret); + +diff --git a/ltrace.1 b/ltrace.1 +index 8b459ee..7f3c5bc 100644 +--- a/ltrace.1 ++++ b/ltrace.1 +@@ -1,5 +1,5 @@ + .\" -*-nroff-*- +-.\" Copyright (c) 2012 Petr Machata, Red Hat Inc. ++.\" Copyright (c) 2012, 2013 Petr Machata, Red Hat Inc. + .\" Copyright (c) 1997-2005 Juan Cespedes + .\" + .\" This program is free software; you can redistribute it and/or +@@ -17,13 +17,65 @@ + .\" Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + .\" 02110-1301 USA + .\" +-.TH LTRACE "1" "October 2012" "" "User Commands" ++.TH LTRACE "1" "January 2013" "" "User Commands" + .SH NAME + ltrace \- A library call tracer + + .SH SYNOPSIS ++.\" ++.\" --------------------------------------------------------------------------- ++.\" ++.PP + .B ltrace +-.I "[-bCfghiLrStttV] [-a column] [-A maxelts] [-D level] [-e expr] [-l library_pattern] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-w count] [-x extern] ... [--align=column] [--debug=level] [--demangle] [--help] [--indent=nr] [--library=library_pattern] [--no-signals] [--output=filename] [--version] [--where=NR] [command [arg ...]]" ++.\" ++.\" What events to trace: ++.\" ++[\-e \fIfilter\fR|\-L] [\-l|\-\-library=\fIlibrary_pattern\fR] ++[\-x \fIfilter\fR] [\-S] [\-b|\-\-no-signals] ++.\" ++.\" What to display with each event: ++.\" ++[\-i] [\-w|\-\-where=\fInr\fR] [\-r|\-t|\-tt|\-ttt] [\-T] ++.\" ++.\" Output formatting: ++.\" ++[\-F \fIfilename\fR] ++[\-A \fImaxelts\fR] [\-s \fIstrsize\fR] [\-C|\-\-demangle] ++[\-a|\-\-align \fIcolumn\fR] [\-n|\-\-indent \fInr\fR] ++[\-o|\-\-output \fIfilename\fR] ++.\" ++.\" Various: ++.\" ++[\-D|\-\-debug \fImask\fR] [\-u \fIusername\fR] ++.\" ++.\" What processes to trace: ++.\" ++[\-f] [\-p \fIpid\fR] [[\-\-] \fIcommand [arg ...]\fR] ++.\" ++.\" --------------------------------------------------------------------------- ++.\" ++.PP ++.BR ltrace " -c" ++.\" ++.\" What events to trace: ++.\" ++[\-e \fIfilter\fR|\-L] [\-l|\-\-library=\fIlibrary_pattern\fR] ++[\-x \fIfilter\fR] [\-S] ++.\" ++.\" Output formatting: ++.\" ++[\-o|\-\-output \fIfilename\fR] ++.\" ++.\" What processes to trace: ++.\" ++[\-f] [\-p \fIpid\fR] [[\-\-] \fIcommand [arg ...]\fR] ++.\" ++.\" --------------------------------------------------------------------------- ++.\" ++.PP ++.BR ltrace " \-V|\-\-version" ++.PP ++.BR ltrace " \-h|\-\-help" + + .SH DESCRIPTION + .B ltrace +@@ -38,77 +90,51 @@ Its use is very similar to + .BR strace(1) . + + .SH OPTIONS +-.TP +-.I \-a, \-\-align column ++.PP ++.IP "\-a, \-\-align \fIcolumn" + Align return values in a specific + .IR column + (default column is 5/8 of screen width). +-.TP +-.I \-A maxelts ++.IP "\-A \fImaxelts" + Maximum number of array elements to print before suppressing the rest + with an ellipsis ("..."). This also limits number of recursive + structure expansions. +-.TP +-.I \-b, \-\-no-signals ++.IP "\-b, \-\-no-signals" + Disable printing of signals recieved by the traced process. +-.TP +-.I \-c +-Count time and calls for each library call and report a summary on program exit. +-.TP +-.I \-C, \-\-demangle ++.IP \-c ++Count time and calls for each library call and report a summary on ++program exit. ++.IP "\-C, \-\-demangle" + Decode (demangle) low-level symbol names into user-level names. + Besides removing any initial underscore prefix used by the system, + this makes C++ function names readable. +-.TP +-.I \-D, \-\-debug level +-Show debugging output of +-.B ltrace +-itself. +-.I level +-must be a sum of some of the following numbers: +-.RS +-.TP +-.B 01 +-DEBUG_GENERAL. Shows helpful progress information +-.TP +-.B 010 +-DEBUG_EVENT. Shows every event received by a traced program +-.TP +-.B 020 +-DEBUG_PROCESS. Shows every action +-.B ltrace +-carries upon a traced process +-.TP +-.B 040 +-DEBUG_FUNCTION. Shows every entry to internal functions +-.RE +-.TP +-.I \-e filter ++.IP "\-D, \-\-debug \fRmask\fI" ++Show debugging output of \fBltrace\fR itself. \fImask\fR is a number ++with internal meaning that's not really well defined at all. ++\fImask\fR of 77 shows all debug messages, which is what you usually ++need. ++.IP "\-e \fIfilter" + A qualifying expression which modifies which library calls to trace. + The format of the filter expression is described in the section + \fBFILTER EXPRESSIONS\fR. If more than one \-e option appears on the + command line, the library calls that match any of them are traced. If + no \-e is given, \fB@MAIN\fR is assumed as a default. +-.TP +-.I \-f ++.IP \-f + Trace child processes as they are created by + currently traced processes as a result of the fork(2) + or clone(2) system calls. + The new process is attached immediately. +-.TP +-.I \-F ++.IP "\-F \fIfilename" + Load an alternate config file. Normally, /etc/ltrace.conf and +-~/.ltrace.conf will be read (the latter only if it exists). +-Use this option to load the given file or files instead of +-those two default files. +-.TP +-.I \-h, \-\-help ++~/.ltrace.conf will be read (the latter only if it exists). Use this ++option to load the given file or files instead of those two default ++files. See ltrace.conf(5) for details on the syntax of ltrace ++configuration files. ++.IP "\-h, \-\-help" + Show a summary of the options to ltrace and exit. +-.TP +-.I \-i ++.IP \-i + Print the instruction pointer at the time of the library call. +-.TP +-.I \-l, \-\-library library_pattern ++.IP "\-l, \-\-library \fIlibrary_pattern" + Display only calls to functions implemented by libraries that match + .I library_pattern. + Multiple library patters can be specified with several instances of +@@ -120,71 +146,55 @@ the selected libraries, there's no actual guarantee that the call + won't be directed elsewhere due to e.g. LD_PRELOAD or simply + dependency ordering. If you want to make sure that symbols in given + library are actually called, use \fB-x @\fIlibrary_pattern\fR instead. +-.TP +-.I \-L ++.IP \-L + When no -e option is given, don't assume the default action of + \fB@MAIN\fR. +-.TP +-.I \-n, \-\-indent nr +-Indent trace output by +-.I nr +-number of spaces for each new nested call. Using this option makes +-the program flow visualization easy to follow. +-.TP +-.I \-o, \-\-output filename +-Write the trace output to the file +-.I filename +-rather than to stderr. +-.TP +-.I \-p pid +-Attach to the process with the process ID +-.I pid +-and begin tracing. +-.TP +-.I \-r +-Print a relative timestamp with each line of the trace. +-This records the time difference between the beginning of +-successive lines. +-.TP +-.I \-s strsize ++.IP "\-n, \-\-indent \fInr" ++Indent trace output by \fInr\fR spaces for each level of call ++nesting. Using this option makes the program flow visualization easy ++to follow. This indents uselessly also functions that never return, ++such as service functions for throwing exceptions in the C++ runtime. ++.IP "\-o, \-\-output \fIfilename" ++Write the trace output to the file \fIfilename\fR rather than to ++stderr. ++.IP "\-p \fIpid" ++Attach to the process with the process ID \fIpid\fR and begin tracing. ++This option can be used together with passing a command to execute. ++It is possible to attach to several processes by passing more than one ++option \-p. ++.IP \-r ++Print a relative timestamp with each line of the trace. This records ++the time difference between the beginning of successive lines. ++.IP "\-s \fIstrsize" + Specify the maximum string size to print (the default is 32). +-.TP +-.I \-S ++.IP \-S + Display system calls as well as library calls +-.TP +-.I \-t ++.IP \-t + Prefix each line of the trace with the time of day. +-.TP +-.I \-tt ++.IP \-tt + If given twice, the time printed will include the microseconds. +-.TP +-.I \-ttt ++.IP \-ttt + If given thrice, the time printed will include the microseconds and + the leading portion will be printed as the number of seconds since the + epoch. +-.TP +-.I \-T ++.IP \-T + Show the time spent inside each call. This records the time difference + between the beginning and the end of each call. +-.TP +-.I \-u username ++.IP "\-u \fIusername" + Run command with the userid, groupid and supplementary groups of + .IR username . + This option is only useful when running as root and enables the + correct execution of setuid and/or setgid binaries. +-.TP +-.I \-w, --where NR +-Show backtrace of NR stack frames for each traced function. This option enabled +-only if libunwind support was enabled at compile time. +-.TP +-.I \-x filter ++.IP "\-w, --where \fInr" ++Show backtrace of \fInr\fR stack frames for each traced function. This ++option enabled only if libunwind support was enabled at compile time. ++.IP "\-x \fIfilter" + A qualifying expression which modifies which symbol table entry points + to trace. The format of the filter expression is described in the + section \fBFILTER EXPRESSIONS\fR. If more than one \-x option appears + on the command line, the symbols that match any of them are traced. + No entry points are traced if no \-x is given. +-.TP +-.I \-V, \-\-version ++.IP "\-V, \-\-version" + Show the version number of ltrace and exit. + + .SH FILTER EXPRESSIONS +@@ -235,7 +245,8 @@ path name. + + The first rule may lack a sign, in which case \fB+\fR is assumed. If, + on the other hand, the first rule has a \fB-\fR sign, it is as if +-there was another rule \fB@*\fR in front of it. ++there was another rule \fB@\fR in front of it, which has the effect of ++tracing complement of given rule. + + The above rules are used to construct the set of traced symbols. Each + candidate symbol is passed through the chain of above rules. +diff --git a/ltrace.h b/ltrace.h +index ee7e27b..3e714a8 100644 +--- a/ltrace.h ++++ b/ltrace.h +@@ -44,7 +44,7 @@ enum Event_type { + typedef struct Event Event; + struct Event { + struct Event * next; +- struct Process * proc; ++ struct process *proc; + Event_type type; + union { + int ret_val; /* EVENT_EXIT */ +diff --git a/ltrace.spec b/ltrace.spec +deleted file mode 100644 +index 3740190..0000000 +--- a/ltrace.spec ++++ /dev/null +@@ -1,164 +0,0 @@ +-Summary: Tracks runtime library calls from dynamically linked executables. +-Name: ltrace +-Version: 0.3.36 +-Release: 4.2 +-Source: ftp://ftp.debian.org/debian/pool/main/l/ltrace/ltrace_%{version}.orig.tar.gz +-Patch1: ftp://ftp.debian.org/debian/pool/main/l/ltrace/ltrace_0.3.36-2.diff.gz +-Patch2: ltrace-ppc64.patch +-Patch3: ltrace-ppc64-2.patch +-Patch4: ltrace-s390x.patch +-Patch5: ltrace-syscallent-update.patch +-Patch6: ltrace-fixes.patch +-Patch7: ltrace-ia64.patch +-License: GPL +-Group: Development/Debuggers +-ExclusiveArch: i386 x86_64 ia64 ppc ppc64 s390 s390x alpha sparc +-Prefix: %{_prefix} +-BuildRoot: /var/tmp/%{name}-root +-BuildRequires: elfutils-libelf-devel +- +-%description +-Ltrace is a debugging program which runs a specified command until the +-command exits. While the command is executing, ltrace intercepts and +-records both the dynamic library calls called by the executed process +-and the signals received by the executed process. Ltrace can also +-intercept and print system calls executed by the process. +- +-You should install ltrace if you need a sysadmin tool for tracking the +-execution of processes. +- +-%prep +-%setup -q +-%patch1 -p1 +-%patch2 -p1 +-%patch3 -p1 +-%patch4 -p1 +-%patch5 -p1 +-%patch6 -p1 +-%patch7 -p1 +-sed -i -e 's/-o root -g root//' Makefile.in +- +-%build +-export CC="gcc`echo $RPM_OPT_FLAGS | sed -n 's/^.*\(-m[36][124]\).*$/ \1/p'`" +-%configure CC="$CC" +-make +- +-%install +-make DESTDIR=$RPM_BUILD_ROOT mandir=%{_mandir} install +-rm -f ChangeLog; mv -f debian/changelog ChangeLog +-rm -rf $RPM_BUILD_ROOT/%{_prefix}/doc +- +-%clean +-rm -rf $RPM_BUILD_ROOT +- +-%files +-%defattr(-,root,root) +-%doc COPYING README TODO BUGS ChangeLog +-%{_prefix}/bin/ltrace +-%{_mandir}/man1/ltrace.1* +-%config /etc/ltrace.conf +- +-%changelog +-* Fri Feb 10 2006 Jesse Keating - 0.3.36-4.2 +-- bump again for double-long bug on ppc(64) +- +-* Tue Feb 07 2006 Jesse Keating - 0.3.36-4.1 +-- rebuilt for new gcc4.1 snapshot and glibc changes +- +-* Mon Jan 9 2006 Jakub Jelinek 0.3.36-4 +-- added ppc64 and s390x support (IBM) +-- added ia64 support (Ian Wienand) +- +-* Sat Mar 5 2005 Jakub Jelinek 0.3.36-3 +-- rebuilt with GCC 4 +- +-* Tue Dec 14 2004 Jakub Jelinek 0.3.36-2 +-- make x86_64 ltrace trace both 32-bit and 64-bit binaries (#141955, +- IT#55600) +-- fix tracing across execve +-- fix printf-style format handling on 64-bit arches +- +-* Thu Nov 18 2004 Jakub Jelinek 0.3.36-1 +-- update to 0.3.36 +- +-* Mon Oct 11 2004 Jakub Jelinek 0.3.35-1 +-- update to 0.3.35 +-- update syscall tables from latest kernel source +- +-* Tue Jun 15 2004 Elliot Lee +-- rebuilt +- +-* Tue Jun 8 2004 Jakub Jelinek 0.3.32-3 +-- buildreq elfutils-libelf-devel (#124921) +- +-* Thu Apr 22 2004 Jakub Jelinek 0.3.32-2 +-- fix demangling +- +-* Thu Apr 22 2004 Jakub Jelinek 0.3.32-1 +-- update to 0.3.32 +- - fix dict.c assertion (#114359) +- - x86_64 support +-- rewrite elf.[ch] using libelf +-- don't rely on st_value of SHN_UNDEF symbols in binaries, +- instead walk .rel{,a}.plt and compute the addresses (#115299) +-- fix x86-64 support +-- some ltrace.conf additions +-- some format string printing fixes +- +-* Fri Feb 13 2004 Elliot Lee +-- rebuilt +- +-* Mon Feb 3 2003 Jakub Jelinek 0.3.29-1 +-- update to 0.3.29 +- +-* Wed Jan 22 2003 Tim Powers +-- rebuilt +- +-* Sun Sep 1 2002 Jakub Jelinek 0.3.10-12 +-- add a bunch of missing functions to ltrace.conf +- (like strlen, ugh) +- +-* Fri Jun 21 2002 Tim Powers +-- automated rebuild +- +-* Tue May 28 2002 Phil Knirsch +-- Added the 'official' s390 patch. +- +-* Thu May 23 2002 Tim Powers +-- automated rebuild +- +-* Wed Jan 09 2002 Tim Powers +-- automated rebuild +- +-* Fri Jul 20 2001 Jakub Jelinek +-- fix stale symlink in documentation directory (#47749) +- +-* Sun Jun 24 2001 Elliot Lee +-- Bump release + rebuild. +- +-* Thu Aug 2 2000 Tim Waugh +-- fix off-by-one problem in checking syscall number +- +-* Wed Jul 12 2000 Prospector +-- automatic rebuild +- +-* Mon Jun 19 2000 Matt Wilson +-- rebuilt for next release +-- patched Makefile.in to take a hint on mandir (patch2) +-- use %%{_mandir} and %%makeinstall +- +-* Wed Feb 02 2000 Cristian Gafton +-- fix description +- +-* Fri Jan 7 2000 Jeff Johnson +-- update to 0.3.10. +-- include (but don't apply) sparc patch from Jakub Jellinek. +- +-* Sun Mar 21 1999 Cristian Gafton +-- auto rebuild in the new build environment (release 2) +- +-* Fri Mar 12 1999 Jeff Johnson +-- update to 0.3.6. +- +-* Mon Sep 21 1998 Preston Brown +-- upgraded to 0.3.4 +diff --git a/memstream.c b/memstream.c +new file mode 100644 +index 0000000..ac3f31a +--- /dev/null ++++ b/memstream.c +@@ -0,0 +1,73 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++/* _GNU_SOURCE may be necessary for open_memstream visibility (see ++ * configure.ac), and there's no harm defining it just in case. */ ++#define _GNU_SOURCE ++ ++#include ++#include ++ ++#include "config.h" ++#include "memstream.h" ++ ++int ++memstream_init(struct memstream *memstream) ++{ ++#if HAVE_OPEN_MEMSTREAM ++ memstream->stream = open_memstream(&memstream->buf, ++ &memstream->size); ++#else ++ memstream->stream = tmpfile(); ++#endif ++ memstream->buf = NULL; ++ return memstream->stream != NULL ? 0 : -1; ++} ++ ++int ++memstream_close(struct memstream *memstream) ++{ ++#if !defined(HAVE_OPEN_MEMSTREAM) || !HAVE_OPEN_MEMSTREAM ++ if (fseek(memstream->stream, 0, SEEK_END) < 0) { ++ fail: ++ fclose(memstream->stream); ++ return -1; ++ } ++ memstream->size = ftell(memstream->stream); ++ if (memstream->size == (size_t)-1) ++ goto fail; ++ memstream->buf = malloc(memstream->size); ++ if (memstream->buf == NULL) ++ goto fail; ++ ++ rewind(memstream->stream); ++ if (fread(memstream->buf, 1, memstream->size, memstream->stream) ++ < memstream->size) ++ goto fail; ++#endif ++ ++ return fclose(memstream->stream) == 0 ? 0 : -1; ++} ++ ++void ++memstream_destroy(struct memstream *memstream) ++{ ++ free(memstream->buf); ++} +diff --git a/memstream.h b/memstream.h +new file mode 100644 +index 0000000..eb06541 +--- /dev/null ++++ b/memstream.h +@@ -0,0 +1,35 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++ ++/* Portability wrapper for platforms that don't have ++ * open_memstream. */ ++ ++struct memstream ++{ ++ FILE *stream; ++ char *buf; ++ size_t size; ++}; ++ ++int memstream_init(struct memstream *memstream); ++int memstream_close(struct memstream *memstream); ++void memstream_destroy(struct memstream *memstream); +diff --git a/options.c b/options.c +index e8fd2a2..eaf32fa 100644 +--- a/options.c ++++ b/options.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2009,2010 Joe Damato + * Copyright (C) 1998,1999,2002,2003,2004,2007,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -86,27 +86,27 @@ usage(void) { + fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n" + "Trace library calls of a given program.\n\n" + " -a, --align=COLUMN align return values in a secific column.\n" +- " -A ARRAYLEN maximum number of array elements to print.\n" ++ " -A MAXELTS maximum number of array elements to print.\n" + " -b, --no-signals don't print signals.\n" + " -c count time and calls, and report a summary on exit.\n" + # ifdef USE_DEMANGLE + " -C, --demangle decode low-level symbol names into user-level names.\n" + # endif +- " -D, --debug=LEVEL enable debugging (see -Dh or --debug=help).\n" ++ " -D, --debug=MASK enable debugging (see -Dh or --debug=help).\n" + " -Dh, --debug=help show help on debugging.\n" +- " -e expr modify which events to trace.\n" ++ " -e FILTER modify which library calls to trace.\n" + " -f trace children (fork() and clone()).\n" + " -F, --config=FILE load alternate configuration file (may be repeated).\n" + " -h, --help display this help and exit.\n" + " -i print instruction pointer at time of library call.\n" +- " -l, --library=FILE only trace symbols implemented by this library.\n" ++ " -l, --library=LIBRARY_PATTERN only trace symbols implemented by this library.\n" + " -L do NOT display library calls.\n" + " -n, --indent=NR indent output by NR spaces for each call level nesting.\n" +- " -o, --output=FILE write the trace output to that file.\n" ++ " -o, --output=FILENAME write the trace output to file with given name.\n" + " -p PID attach to the process with the process ID pid.\n" + " -r print relative timestamps.\n" +- " -s STRLEN specify the maximum string size to print.\n" +- " -S display system calls.\n" ++ " -s STRSIZE specify the maximum string size to print.\n" ++ " -S trace system calls as well as library calls.\n" + " -t, -tt, -ttt print absolute timestamps.\n" + " -T show the time spent inside each call.\n" + " -u USERNAME run command with the userid, groupid of username.\n" +@@ -114,7 +114,7 @@ usage(void) { + #if defined(HAVE_LIBUNWIND) + " -w, --where=NR print backtrace showing NR stack frames at most.\n" + #endif /* defined(HAVE_LIBUNWIND) */ +- " -x NAME treat the global NAME like a library subroutine.\n" ++ " -x FILTER modify which static functions to trace.\n" + "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n", + progname); + } +@@ -204,7 +204,7 @@ compile_libname(const char *expr, const char *a_lib, int lib_re_p, + + regex_t lib_re; + int status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0); +- if (status != REG_NOERROR) { ++ if (status != 0) { + char buf[100]; + regerror(status, &lib_re, buf, sizeof buf); + fprintf(stderr, "Rule near '%s' will be ignored: %s.\n", +@@ -459,6 +459,7 @@ process_options(int argc, char **argv) + while (1) { + int c; + char *p; ++#ifdef HAVE_GETOPT_LONG + int option_index = 0; + static struct option long_options[] = { + {"align", 1, 0, 'a'}, +@@ -466,28 +467,34 @@ process_options(int argc, char **argv) + {"debug", 1, 0, 'D'}, + # ifdef USE_DEMANGLE + {"demangle", 0, 0, 'C'}, +-#endif ++# endif + {"indent", 1, 0, 'n'}, + {"help", 0, 0, 'h'}, + {"library", 1, 0, 'l'}, + {"output", 1, 0, 'o'}, + {"version", 0, 0, 'V'}, + {"no-signals", 0, 0, 'b'}, +-#if defined(HAVE_LIBUNWIND) ++# if defined(HAVE_LIBUNWIND) + {"where", 1, 0, 'w'}, +-#endif /* defined(HAVE_LIBUNWIND) */ ++# endif /* defined(HAVE_LIBUNWIND) */ + {0, 0, 0, 0} + }; +- c = getopt_long(argc, argv, "+cfhiLrStTVb" +-# ifdef USE_DEMANGLE +- "C" +-# endif ++#endif ++ ++ const char *opts = "+" ++#ifdef USE_DEMANGLE ++ "C" ++#endif + #if defined(HAVE_LIBUNWIND) +- "a:A:D:e:F:l:n:o:p:s:u:x:X:w:", long_options, +-#else /* !defined(HAVE_LIBUNWIND) */ +- "a:A:D:e:F:l:n:o:p:s:u:x:X:", long_options, ++ "w:" ++#endif ++ "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:X:"; ++ ++#ifdef HAVE_GETOPT_LONG ++ c = getopt_long(argc, argv, opts, long_options, &option_index); ++#else ++ c = getopt(argc, argv, opts); + #endif +- &option_index); + if (c == -1) { + break; + } +@@ -610,10 +617,12 @@ process_options(int argc, char **argv) + options.user = optarg; + break; + case 'V': +- printf("ltrace version " PACKAGE_VERSION ".\n" +- "Copyright (C) 1997-2009 Juan Cespedes .\n" +- "This is free software; see the GNU General Public Licence\n" +- "version 2 or later for copying conditions. There is NO warranty.\n"); ++ printf("ltrace " PACKAGE_VERSION "\n" ++ "Copyright (C) 2010-2012 Petr Machata, Red Hat Inc.\n" ++ "Copyright (C) 1997-2009 Juan Cespedes .\n" ++ "License GPLv2+: GNU GPL version 2 or later \n" ++ "This is free software: you are free to change and redistribute it.\n" ++ "There is NO WARRANTY, to the extent permitted by law.\n"); + exit(0); + break; + #if defined(HAVE_LIBUNWIND) +diff --git a/output.c b/output.c +index 1d01d31..fe62bb4 100644 +--- a/output.c ++++ b/output.c +@@ -22,10 +22,6 @@ + * 02110-1301 USA + */ + +-/* glibc before 2.10, eglibc and uClibc all need _GNU_SOURCE defined +- * for open_memstream to become visible. */ +-#define _GNU_SOURCE +- + #include "config.h" + + #include +@@ -47,25 +43,26 @@ + #include "param.h" + #include "fetch.h" + #include "lens_default.h" ++#include "memstream.h" + + /* TODO FIXME XXX: include in common.h: */ + extern struct timeval current_time_spent; + + Dict *dict_opt_c = NULL; + +-static Process *current_proc = 0; ++static struct process *current_proc = 0; + static size_t current_depth = 0; + static int current_column = 0; + + static void +-output_indent(struct Process *proc) ++output_indent(struct process *proc) + { + int d = options.indent * (proc->callstack_depth - 1); + current_column += fprintf(options.output, "%*s", d, ""); + } + + static void +-begin_of_line(Process *proc, int is_func, int indent) ++begin_of_line(struct process *proc, int is_func, int indent) + { + current_column = 0; + if (!proc) { +@@ -208,7 +205,7 @@ name2func(char const *name) { + } + + void +-output_line(struct Process *proc, const char *fmt, ...) ++output_line(struct process *proc, const char *fmt, ...) + { + if (options.summary) + return; +@@ -248,7 +245,8 @@ output_error(FILE *stream) + } + + static int +-fetch_simple_param(enum tof type, Process *proc, struct fetch_context *context, ++fetch_simple_param(enum tof type, struct process *proc, ++ struct fetch_context *context, + struct value_dict *arguments, + struct arg_type_info *info, int own, + struct value *valuep) +@@ -290,7 +288,8 @@ fetch_param_stop(struct value_dict *arguments, ssize_t *params_leftp) + } + + static int +-fetch_param_pack(enum tof type, Process *proc, struct fetch_context *context, ++fetch_param_pack(enum tof type, struct process *proc, ++ struct fetch_context *context, + struct value_dict *arguments, struct param *param, + ssize_t *params_leftp) + { +@@ -343,7 +342,8 @@ fetch_param_pack(enum tof type, Process *proc, struct fetch_context *context, + } + + static int +-fetch_one_param(enum tof type, Process *proc, struct fetch_context *context, ++fetch_one_param(enum tof type, struct process *proc, ++ struct fetch_context *context, + struct value_dict *arguments, struct param *param, + ssize_t *params_leftp) + { +@@ -372,7 +372,8 @@ fetch_one_param(enum tof type, Process *proc, struct fetch_context *context, + } + + static int +-fetch_params(enum tof type, Process *proc, struct fetch_context *context, ++fetch_params(enum tof type, struct process *proc, ++ struct fetch_context *context, + struct value_dict *arguments, Function *func, ssize_t *params_leftp) + { + size_t i; +@@ -424,7 +425,7 @@ output_params(struct value_dict *arguments, size_t start, size_t end, + } + + void +-output_left(enum tof type, struct Process *proc, ++output_left(enum tof type, struct process *proc, + struct library_symbol *libsym) + { + const char *function_name = libsym->name; +@@ -499,7 +500,7 @@ output_left(enum tof type, struct Process *proc, + } + + void +-output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) ++output_right(enum tof type, struct process *proc, struct library_symbol *libsym) + { + const char *function_name = libsym->name; + Function *func = name2func(function_name); +@@ -640,18 +641,18 @@ delim_output(FILE *stream, int *need_delimp, + o = writer(stream, data); + + } else { +- char *buf; +- size_t bufsz; +- FILE *tmp = open_memstream(&buf, &bufsz); +- o = writer(tmp, data); +- fclose(tmp); +- ++ struct memstream ms; ++ if (memstream_init(&ms) < 0) ++ return -1; ++ o = writer(ms.stream, data); ++ if (memstream_close(&ms) < 0) ++ o = -1; + if (o > 0 && ((*need_delimp + && account_output(&o, fprintf(stream, ", ")) < 0) +- || fwrite(buf, 1, bufsz, stream) != bufsz)) ++ || fwrite(ms.buf, 1, ms.size, stream) != ms.size)) + o = -1; + +- free(buf); ++ memstream_destroy(&ms); + } + + if (o < 0) +diff --git a/output.h b/output.h +index 0d16657..b9f0518 100644 +--- a/output.h ++++ b/output.h +@@ -24,10 +24,10 @@ + #include "fetch.h" + #include "forward.h" + +-void output_line(struct Process *proc, const char *fmt, ...); +-void output_left(enum tof type, struct Process *proc, ++void output_line(struct process *proc, const char *fmt, ...); ++void output_left(enum tof type, struct process *proc, + struct library_symbol *libsym); +-void output_right(enum tof type, struct Process *proc, ++void output_right(enum tof type, struct process *proc, + struct library_symbol *libsym); + + /* This function is for emitting lists of comma-separated strings. +diff --git a/proc.c b/proc.c +index 3b4bfdb..db3f645 100644 +--- a/proc.c ++++ b/proc.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Joe Damato + * Copyright (C) 1998,2009 Juan Cespedes + * +@@ -43,24 +43,24 @@ + + #ifndef ARCH_HAVE_PROCESS_DATA + int +-arch_process_init(struct Process *proc) ++arch_process_init(struct process *proc) + { + return 0; + } + + void +-arch_process_destroy(struct Process *proc) ++arch_process_destroy(struct process *proc) + { + } + + int +-arch_process_clone(struct Process *retp, struct Process *proc) ++arch_process_clone(struct process *retp, struct process *proc) + { + return 0; + } + + int +-arch_process_exec(struct Process *proc) ++arch_process_exec(struct process *proc) + { + return 0; + } +@@ -68,24 +68,24 @@ arch_process_exec(struct Process *proc) + + #ifndef OS_HAVE_PROCESS_DATA + int +-os_process_init(struct Process *proc) ++os_process_init(struct process *proc) + { + return 0; + } + + void +-os_process_destroy(struct Process *proc) ++os_process_destroy(struct process *proc) + { + } + + int +-os_process_clone(struct Process *retp, struct Process *proc) ++os_process_clone(struct process *retp, struct process *proc) + { + return 0; + } + + int +-os_process_exec(struct Process *proc) ++os_process_exec(struct process *proc) + { + return 0; + } +@@ -93,16 +93,16 @@ os_process_exec(struct Process *proc) + + #ifndef ARCH_HAVE_DYNLINK_DONE + void +-arch_dynlink_done(struct Process *proc) ++arch_dynlink_done(struct process *proc) + { + } + #endif + +-static void add_process(struct Process *proc, int was_exec); +-static void unlist_process(struct Process *proc); ++static void add_process(struct process *proc, int was_exec); ++static void unlist_process(struct process *proc); + + static void +-destroy_unwind(struct Process *proc) ++destroy_unwind(struct process *proc) + { + #if defined(HAVE_LIBUNWIND) + _UPT_destroy(proc->unwind_priv); +@@ -111,7 +111,7 @@ destroy_unwind(struct Process *proc) + } + + static int +-process_bare_init(struct Process *proc, const char *filename, ++process_bare_init(struct process *proc, const char *filename, + pid_t pid, int was_exec) + { + if (!was_exec) { +@@ -151,7 +151,7 @@ process_bare_init(struct Process *proc, const char *filename, + } + + static void +-process_bare_destroy(struct Process *proc, int was_exec) ++process_bare_destroy(struct process *proc, int was_exec) + { + dict_clear(proc->breakpoints); + if (!was_exec) { +@@ -162,7 +162,7 @@ process_bare_destroy(struct Process *proc, int was_exec) + } + + static int +-process_init_main(struct Process *proc) ++process_init_main(struct process *proc) + { + if (breakpoints_init(proc) < 0) { + fprintf(stderr, "failed to init breakpoints %d\n", +@@ -174,7 +174,7 @@ process_init_main(struct Process *proc) + } + + int +-process_init(struct Process *proc, const char *filename, pid_t pid) ++process_init(struct process *proc, const char *filename, pid_t pid) + { + if (process_bare_init(proc, filename, pid, 0) < 0) { + fail: +@@ -204,7 +204,7 @@ process_init(struct Process *proc, const char *filename, pid_t pid) + } + + static enum callback_status +-destroy_breakpoint_cb(struct Process *proc, struct breakpoint *bp, void *data) ++destroy_breakpoint_cb(struct process *proc, struct breakpoint *bp, void *data) + { + breakpoint_destroy(bp); + free(bp); +@@ -212,10 +212,10 @@ destroy_breakpoint_cb(struct Process *proc, struct breakpoint *bp, void *data) + } + + // XXX see comment in handle_event.c +-void callstack_pop(struct Process *proc); ++void callstack_pop(struct process *proc); + + static void +-private_process_destroy(struct Process *proc, int was_exec) ++private_process_destroy(struct process *proc, int was_exec) + { + /* Pop remaining stack elements. */ + while (proc->callstack_depth > 0) { +@@ -257,7 +257,7 @@ private_process_destroy(struct Process *proc, int was_exec) + } + + void +-process_destroy(struct Process *proc) ++process_destroy(struct process *proc) + { + arch_process_destroy(proc); + os_process_destroy(proc); +@@ -265,7 +265,7 @@ process_destroy(struct Process *proc) + } + + int +-process_exec(struct Process *proc) ++process_exec(struct process *proc) + { + /* Call exec handlers first, before we destroy the main + * state. */ +@@ -284,11 +284,11 @@ process_exec(struct Process *proc) + return 0; + } + +-struct Process * ++struct process * + open_program(const char *filename, pid_t pid) + { + assert(pid != 0); +- struct Process *proc = malloc(sizeof(*proc)); ++ struct process *proc = malloc(sizeof(*proc)); + if (proc == NULL || process_init(proc, filename, pid) < 0) { + free(proc); + return NULL; +@@ -297,8 +297,8 @@ open_program(const char *filename, pid_t pid) + } + + struct clone_single_bp_data { +- struct Process *old_proc; +- struct Process *new_proc; ++ struct process *old_proc; ++ struct process *new_proc; + int error; + }; + +@@ -327,7 +327,7 @@ clone_single_bp(void *key, void *value, void *u) + } + + int +-process_clone(struct Process *retp, struct Process *proc, pid_t pid) ++process_clone(struct process *retp, struct process *proc, pid_t pid) + { + if (process_bare_init(retp, proc->filename, pid, 0) < 0) { + fail1: +@@ -456,20 +456,18 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid) + static int + open_one_pid(pid_t pid) + { +- Process *proc; +- char *filename; + debug(DEBUG_PROCESS, "open_one_pid(pid=%d)", pid); + + /* Get the filename first. Should the trace_pid fail, we can + * easily free it, untracing is more work. */ +- if ((filename = pid2name(pid)) == NULL +- || trace_pid(pid) < 0) { ++ char *filename = pid2name(pid); ++ if (filename == NULL || trace_pid(pid) < 0) { + fail: + free(filename); + return -1; + } + +- proc = open_program(filename, pid); ++ struct process *proc = open_program(filename, pid); + if (proc == NULL) + goto fail; + free(filename); +@@ -479,7 +477,7 @@ open_one_pid(pid_t pid) + } + + static enum callback_status +-start_one_pid(Process * proc, void * data) ++start_one_pid(struct process *proc, void *data) + { + continue_process(proc->pid); + return CBS_CONT; +@@ -537,7 +535,7 @@ open_pid(pid_t pid) + old_ntasks = ntasks; + } + +- struct Process *leader = pid2proc(pid)->leader; ++ struct process *leader = pid2proc(pid)->leader; + + /* XXX Is there a way to figure out whether _start has + * actually already been hit? */ +@@ -548,29 +546,29 @@ open_pid(pid_t pid) + } + + static enum callback_status +-find_proc(Process * proc, void * data) ++find_proc(struct process *proc, void *data) + { + pid_t pid = (pid_t)(uintptr_t)data; + return proc->pid == pid ? CBS_STOP : CBS_CONT; + } + +-Process * +-pid2proc(pid_t pid) { ++struct process * ++pid2proc(pid_t pid) ++{ + return each_process(NULL, &find_proc, (void *)(uintptr_t)pid); + } + +-static Process * list_of_processes = NULL; ++static struct process *list_of_processes = NULL; + + static void +-unlist_process(Process * proc) ++unlist_process(struct process *proc) + { +- Process *tmp; +- + if (list_of_processes == proc) { + list_of_processes = list_of_processes->next; + return; + } + ++ struct process *tmp; + for (tmp = list_of_processes; ; tmp = tmp->next) { + /* If the following assert fails, the process wasn't + * in the list. */ +@@ -583,17 +581,17 @@ unlist_process(Process * proc) + } + } + +-struct Process * +-each_process(struct Process *start_after, +- enum callback_status(*cb)(struct Process *proc, void *data), ++struct process * ++each_process(struct process *start_after, ++ enum callback_status(*cb)(struct process *proc, void *data), + void *data) + { +- struct Process *it = start_after == NULL ? list_of_processes ++ struct process *it = start_after == NULL ? list_of_processes + : start_after->next; + + while (it != NULL) { + /* Callback might call remove_process. */ +- struct Process *next = it->next; ++ struct process *next = it->next; + switch ((*cb)(it, data)) { + case CBS_FAIL: + /* XXX handle me */ +@@ -607,20 +605,20 @@ each_process(struct Process *start_after, + return NULL; + } + +-Process * +-each_task(struct Process *proc, struct Process *start_after, +- enum callback_status(*cb)(struct Process *proc, void *data), ++struct process * ++each_task(struct process *proc, struct process *start_after, ++ enum callback_status(*cb)(struct process *proc, void *data), + void *data) + { + assert(proc != NULL); +- struct Process *it = start_after == NULL ? proc->leader ++ struct process *it = start_after == NULL ? proc->leader + : start_after->next; + + if (it != NULL) { +- struct Process *leader = it->leader; ++ struct process *leader = it->leader; + while (it != NULL && it->leader == leader) { + /* Callback might call remove_process. */ +- struct Process *next = it->next; ++ struct process *next = it->next; + switch ((*cb)(it, data)) { + case CBS_FAIL: + /* XXX handle me */ +@@ -636,19 +634,19 @@ each_task(struct Process *proc, struct Process *start_after, + } + + static void +-add_process(struct Process *proc, int was_exec) ++add_process(struct process *proc, int was_exec) + { +- Process ** leaderp = &list_of_processes; ++ struct process **leaderp = &list_of_processes; + if (proc->pid) { + pid_t tgid = process_leader(proc->pid); + if (tgid == 0) + /* Must have been terminated before we managed + * to fully attach. */ + return; +- if (tgid == proc->pid) ++ if (tgid == proc->pid) { + proc->leader = proc; +- else { +- Process * leader = pid2proc(tgid); ++ } else { ++ struct process *leader = pid2proc(tgid); + proc->leader = leader; + if (leader != NULL) + leaderp = &leader->next; +@@ -662,9 +660,9 @@ add_process(struct Process *proc, int was_exec) + } + + void +-change_process_leader(Process * proc, Process * leader) ++change_process_leader(struct process *proc, struct process *leader) + { +- Process ** leaderp = &list_of_processes; ++ struct process **leaderp = &list_of_processes; + if (proc->leader == leader) + return; + +@@ -679,7 +677,7 @@ change_process_leader(Process * proc, Process * leader) + } + + static enum callback_status +-clear_leader(struct Process *proc, void *data) ++clear_leader(struct process *proc, void *data) + { + debug(DEBUG_FUNCTION, "detach_task %d from leader %d", + proc->pid, proc->leader->pid); +@@ -688,7 +686,7 @@ clear_leader(struct Process *proc, void *data) + } + + void +-remove_process(Process *proc) ++remove_process(struct process *proc) + { + debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); + +@@ -702,7 +700,7 @@ remove_process(Process *proc) + } + + void +-install_event_handler(Process *proc, struct event_handler *handler) ++install_event_handler(struct process *proc, struct event_handler *handler) + { + debug(DEBUG_FUNCTION, "install_event_handler(pid=%d, %p)", proc->pid, handler); + assert(proc->event_handler == NULL); +@@ -710,7 +708,7 @@ install_event_handler(Process *proc, struct event_handler *handler) + } + + void +-destroy_event_handler(Process * proc) ++destroy_event_handler(struct process *proc) + { + struct event_handler *handler = proc->event_handler; + debug(DEBUG_FUNCTION, "destroy_event_handler(pid=%d, %p)", proc->pid, handler); +@@ -722,7 +720,7 @@ destroy_event_handler(Process * proc) + } + + static int +-breakpoint_for_symbol(struct library_symbol *libsym, struct Process *proc) ++breakpoint_for_symbol(struct library_symbol *libsym, struct process *proc) + { + arch_addr_t bp_addr; + assert(proc->leader == proc); +@@ -799,7 +797,7 @@ cb_breakpoint_for_symbol(struct library_symbol *libsym, void *data) + } + + static int +-proc_activate_latent_symbol(struct Process *proc, ++proc_activate_latent_symbol(struct process *proc, + struct library_symbol *libsym) + { + assert(libsym->latent); +@@ -809,7 +807,7 @@ proc_activate_latent_symbol(struct Process *proc, + } + + int +-proc_activate_delayed_symbol(struct Process *proc, ++proc_activate_delayed_symbol(struct process *proc, + struct library_symbol *libsym) + { + assert(libsym->delayed); +@@ -819,7 +817,7 @@ proc_activate_delayed_symbol(struct Process *proc, + } + + static enum callback_status +-activate_latent_in(struct Process *proc, struct library *lib, void *data) ++activate_latent_in(struct process *proc, struct library *lib, void *data) + { + struct library_exported_name *exported; + for (exported = data; exported != NULL; exported = exported->next) { +@@ -836,7 +834,7 @@ activate_latent_in(struct Process *proc, struct library *lib, void *data) + } + + void +-proc_add_library(struct Process *proc, struct library *lib) ++proc_add_library(struct process *proc, struct library *lib) + { + assert(lib->next == NULL); + lib->next = proc->libraries; +@@ -864,7 +862,7 @@ proc_add_library(struct Process *proc, struct library *lib) + } + + int +-proc_remove_library(struct Process *proc, struct library *lib) ++proc_remove_library(struct process *proc, struct library *lib) + { + struct library **libp; + for (libp = &proc->libraries; *libp != NULL; libp = &(*libp)->next) +@@ -876,8 +874,8 @@ proc_remove_library(struct Process *proc, struct library *lib) + } + + struct library * +-proc_each_library(struct Process *proc, struct library *it, +- enum callback_status (*cb)(struct Process *proc, ++proc_each_library(struct process *proc, struct library *it, ++ enum callback_status (*cb)(struct process *proc, + struct library *lib, void *data), + void *data) + { +@@ -903,7 +901,7 @@ proc_each_library(struct Process *proc, struct library *it, + } + + static void +-check_leader(struct Process *proc) ++check_leader(struct process *proc) + { + /* Only the group leader should be getting the breakpoints and + * thus have ->breakpoint initialized. */ +@@ -913,7 +911,7 @@ check_leader(struct Process *proc) + } + + int +-proc_add_breakpoint(struct Process *proc, struct breakpoint *bp) ++proc_add_breakpoint(struct process *proc, struct breakpoint *bp) + { + debug(DEBUG_FUNCTION, "proc_add_breakpoint(pid=%d, %s@%p)", + proc->pid, breakpoint_name(bp), bp->addr); +@@ -935,7 +933,7 @@ proc_add_breakpoint(struct Process *proc, struct breakpoint *bp) + } + + void +-proc_remove_breakpoint(struct Process *proc, struct breakpoint *bp) ++proc_remove_breakpoint(struct process *proc, struct breakpoint *bp) + { + debug(DEBUG_FUNCTION, "proc_remove_breakpoint(pid=%d, %s@%p)", + proc->pid, breakpoint_name(bp), bp->addr); +@@ -950,8 +948,8 @@ struct each_breakpoint_data + { + void *start; + void *end; +- struct Process *proc; +- enum callback_status (*cb)(struct Process *proc, ++ struct process *proc; ++ enum callback_status (*cb)(struct process *proc, + struct breakpoint *bp, + void *data); + void *cb_data; +@@ -979,8 +977,8 @@ each_breakpoint_cb(void *key, void *value, void *d) + } + + void * +-proc_each_breakpoint(struct Process *proc, void *start, +- enum callback_status (*cb)(struct Process *proc, ++proc_each_breakpoint(struct process *proc, void *start, ++ enum callback_status (*cb)(struct process *proc, + struct breakpoint *bp, + void *data), void *data) + { +@@ -995,7 +993,7 @@ proc_each_breakpoint(struct Process *proc, void *start, + } + + int +-proc_find_symbol(struct Process *proc, struct library_symbol *sym, ++proc_find_symbol(struct process *proc, struct library_symbol *sym, + struct library **retlib, struct library_symbol **retsym) + { + struct library *lib = sym->lib; +@@ -1021,7 +1019,7 @@ proc_find_symbol(struct Process *proc, struct library_symbol *sym, + } + + struct library_symbol * +-proc_each_symbol(struct Process *proc, struct library_symbol *start_after, ++proc_each_symbol(struct process *proc, struct library_symbol *start_after, + enum callback_status (*cb)(struct library_symbol *, void *), + void *data) + { +@@ -1035,3 +1033,25 @@ proc_each_symbol(struct Process *proc, struct library_symbol *start_after, + + return NULL; + } ++ ++#define DEF_READER(NAME, SIZE) \ ++ int \ ++ NAME(struct process *proc, arch_addr_t addr, \ ++ uint##SIZE##_t *lp) \ ++ { \ ++ union { \ ++ uint##SIZE##_t dst; \ ++ char buf[0]; \ ++ } u; \ ++ if (umovebytes(proc, addr, &u.buf, sizeof(u.dst)) \ ++ != sizeof(u.dst)) \ ++ return -1; \ ++ *lp = u.dst; \ ++ return 0; \ ++ } ++ ++DEF_READER(proc_read_16, 16) ++DEF_READER(proc_read_32, 32) ++DEF_READER(proc_read_64, 64) ++ ++#undef DEF_READER +diff --git a/proc.h b/proc.h +index a97796c..04c0ef7 100644 +--- a/proc.h ++++ b/proc.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2010,2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2010,2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Joe Damato + * Copyright (C) 1998,2001,2008,2009 Juan Cespedes + * +@@ -26,6 +26,7 @@ + #include "config.h" + + #include ++#include + + #if defined(HAVE_LIBUNWIND) + # include +@@ -75,13 +76,12 @@ struct callstack_element { + #define MAX_CALLDEPTH 64 + + /* XXX We would rather have this all organized a little differently, +- * have Process for the whole group and Task for what's there for +- * per-thread stuff. But for now this is the less invasive way of +- * structuring it. */ +-typedef struct Process Process; +-struct Process { ++ * have struct process for the whole group and struct task (or struct ++ * lwp, struct thread) for what's there for per-thread stuff. But for ++ * now this is the less invasive way of structuring it. */ ++struct process { + enum process_state state; +- Process * parent; /* needed by STATE_BEING_CREATED */ ++ struct process *parent; /* needed by STATE_BEING_CREATED */ + char * filename; + pid_t pid; + +@@ -133,15 +133,15 @@ struct Process { + /** + * Process chaining. + **/ +- Process * next; ++ struct process *next; + + /* LEADER points to the leader thread of the POSIX.1 process. + If X->LEADER == X, then X is the leader thread and the +- Process structures chained by NEXT represent other threads, ++ process structures chained by NEXT represent other threads, + up until, but not including, the next leader thread. + LEADER may be NULL after the leader has already exited. In + that case this process is waiting to be collected. */ +- Process * leader; ++ struct process *leader; + + struct os_process_data os; + struct arch_process_data arch; +@@ -149,12 +149,12 @@ struct Process { + + /* Initialize a process given a path to binary FILENAME, with a PID, + * and add the process to an internal chain of traced processes. */ +-int process_init(struct Process *proc, const char *filename, pid_t pid); ++int process_init(struct process *proc, const char *filename, pid_t pid); + + /* PROC underwent an exec. This is a bit like process_destroy + * followed by process_init, except that some state is kept and the + * process doesn't lose it's place in the list of processes. */ +-int process_exec(struct Process *proc); ++int process_exec(struct process *proc); + + /* Release any memory allocated for PROC (but not PROC itself). Does + * NOT remove PROC from internal chain. +@@ -162,74 +162,74 @@ int process_exec(struct Process *proc); + * XXX clearly this init/destroy pair is different than others and + * should be fixed. process_init should presumably be separate from + * process_add. */ +-void process_destroy(struct Process *proc); ++void process_destroy(struct process *proc); + +-struct Process *open_program(const char *filename, pid_t pid); ++struct process *open_program(const char *filename, pid_t pid); + void open_pid(pid_t pid); +-Process * pid2proc(pid_t pid); ++struct process *pid2proc(pid_t pid); + + /* Clone the contents of PROC into the memory referenced by RETP. + * Returns 0 on success or a negative value on failure. */ +-int process_clone(struct Process *retp, struct Process *proc, pid_t pid); ++int process_clone(struct process *retp, struct process *proc, pid_t pid); + + /* Iterate through the processes that ltrace currently traces. Tasks + * are considered to be processes for the purpose of this iterator. + * See callback.h for notes on iteration interfaces. */ +-Process *each_process(Process *start_after, +- enum callback_status (*cb)(struct Process *proc, +- void *data), +- void *data); ++struct process *each_process(struct process *start_after, ++ enum callback_status (*cb)(struct process *proc, ++ void *data), ++ void *data); + + /* Iterate through list of tasks of given process PROC. See + * callback.h for notes on iteration interfaces. */ +-Process *each_task(struct Process *proc, struct Process *start_after, +- enum callback_status (*cb)(struct Process *proc, +- void *data), +- void *data); ++struct process *each_task(struct process *proc, struct process *start_after, ++ enum callback_status (*cb)(struct process *proc, ++ void *data), ++ void *data); + +-void change_process_leader(Process *proc, Process *leader); ++void change_process_leader(struct process *proc, struct process *leader); + + /* Remove process from the list of traced processes, drop any events + * in the event queue, destroy it and free memory. */ +-void remove_process(struct Process *proc); ++void remove_process(struct process *proc); + +-void install_event_handler(Process *proc, struct event_handler *handler); +-void destroy_event_handler(Process *proc); ++void install_event_handler(struct process *proc, struct event_handler *handler); ++void destroy_event_handler(struct process *proc); + + /* Add a library LIB to the list of PROC's libraries. */ +-void proc_add_library(struct Process *proc, struct library *lib); ++void proc_add_library(struct process *proc, struct library *lib); + + /* Remove LIB from list of PROC's libraries. Returns 0 if the library + * was found and unlinked, otherwise returns a negative value. */ +-int proc_remove_library(struct Process *proc, struct library *lib); ++int proc_remove_library(struct process *proc, struct library *lib); + + /* Clear a delayed flag. If a symbol is neither latent, nor delayed, + * a breakpoint is inserted for it. Returns 0 if the activation was + * successful or a negative value if it failed. Note that if a symbol + * is both latent and delayed, this will not enable the corresponding + * breakpoint. */ +-int proc_activate_delayed_symbol(struct Process *proc, ++int proc_activate_delayed_symbol(struct process *proc, + struct library_symbol *libsym); + + /* Iterate through the libraries of PROC. See callback.h for notes on + * iteration interfaces. */ +-struct library *proc_each_library(struct Process *proc, struct library *start, +- enum callback_status (*cb)(struct Process *p, ++struct library *proc_each_library(struct process *proc, struct library *start, ++ enum callback_status (*cb)(struct process *p, + struct library *l, + void *data), + void *data); + + /* Insert BP into PROC. */ +-int proc_add_breakpoint(struct Process *proc, struct breakpoint *bp); ++int proc_add_breakpoint(struct process *proc, struct breakpoint *bp); + + /* Remove BP from PROC. This has no reason to fail in runtime. If it + * does not find BP in PROC, it's hard error guarded by assertion. */ +-void proc_remove_breakpoint(struct Process *proc, struct breakpoint *bp); ++void proc_remove_breakpoint(struct process *proc, struct breakpoint *bp); + + /* Iterate through the breakpoints of PROC. See callback.h for notes + * on iteration interfaces. */ +-void *proc_each_breakpoint(struct Process *proc, void *start, +- enum callback_status (*cb)(struct Process *proc, ++void *proc_each_breakpoint(struct process *proc, void *start, ++ enum callback_status (*cb)(struct process *proc, + struct breakpoint *bp, + void *data), + void *data); +@@ -237,21 +237,29 @@ void *proc_each_breakpoint(struct Process *proc, void *start, + /* Iterate through the dynamic section at src_addr looking for D_TAG. + * If tag is found, fill it's value in RET and return 0. + * If tag is not found, return a negative value. */ +-int proc_find_dynamic_entry_addr(struct Process *proc, arch_addr_t src_addr, ++int proc_find_dynamic_entry_addr(struct process *proc, arch_addr_t src_addr, + int d_tag, arch_addr_t *ret); + + /* Finds a symbol corresponding to LIBSYM in a process PROC. Returns + * 0 and sets *RETLIB and *RETSYM if the corresponding pointer is + * non-NULL. Returns a negative value when the symbols couldn't be + * found. */ +-int proc_find_symbol(struct Process *proc, struct library_symbol *sym, ++int proc_find_symbol(struct process *proc, struct library_symbol *sym, + struct library **retlib, struct library_symbol **retsym); + + /* Iterate through all symbols in all libraries of PROC. See + * callback.h for notes on this interface. */ + struct library_symbol *proc_each_symbol +- (struct Process *proc, struct library_symbol *start_after, ++ (struct process *proc, struct library_symbol *start_after, + enum callback_status (*cb)(struct library_symbol *, void *), + void *data); + ++/* Read 16, 32 or 64-bit quantity located at ADDR in PROC. The ++ * resulting value is stored in *LP. 0 is returned on success or a ++ * negative value on failure. This uses umovebytes under the hood ++ * (see backend.h). */ ++int proc_read_16(struct process *proc, arch_addr_t addr, uint16_t *lp); ++int proc_read_32(struct process *proc, arch_addr_t addr, uint32_t *lp); ++int proc_read_64(struct process *proc, arch_addr_t addr, uint64_t *lp); ++ + #endif /* _PROC_H_ */ +diff --git a/read_config_file.c b/read_config_file.c +index e247436..015be50 100644 +--- a/read_config_file.c ++++ b/read_config_file.c +@@ -27,7 +27,6 @@ + #include + #include + #include +-#include + #include + + #include "common.h" +@@ -41,6 +40,19 @@ + #include "lens_default.h" + #include "lens_enum.h" + ++/* Lifted from GCC: The ctype functions are often implemented as ++ * macros which do lookups in arrays using the parameter as the ++ * offset. If the ctype function parameter is a char, then gcc will ++ * (appropriately) warn that a "subscript has type char". Using a ++ * (signed) char as a subscript is bad because you may get negative ++ * offsets and thus it is not 8-bit safe. The CTYPE_CONV macro ++ * ensures that the parameter is cast to an unsigned char when a char ++ * is passed in. When an int is passed in, the parameter is left ++ * alone so we don't lose EOF. */ ++ ++#define CTYPE_CONV(CH) \ ++ (sizeof(CH) == sizeof(unsigned char) ? (int)(unsigned char)(CH) : (int)(CH)) ++ + static int line_no; + static char *filename; + struct typedef_node_t; +@@ -97,7 +109,7 @@ parse_arg_type(char **name, enum arg_type *ret) + #undef KEYWORD + + ok: +- if (isalnum(*rest)) ++ if (isalnum(CTYPE_CONV(*rest))) + return -1; + + *name = rest; +@@ -128,12 +140,12 @@ static char * + parse_ident(char **str) { + char *ident = *str; + +- if (!isalpha(**str) && **str != '_') { ++ if (!isalpha(CTYPE_CONV(**str)) && **str != '_') { + report_error(filename, line_no, "bad identifier"); + return NULL; + } + +- while (**str && (isalnum(**str) || **str == '_')) { ++ while (**str && (isalnum(CTYPE_CONV(**str)) || **str == '_')) { + ++(*str); + } + +@@ -280,7 +292,7 @@ parse_argnum(char **str, int *ownp, int zero) + if (expr == NULL) + return NULL; + +- if (isdigit(**str)) { ++ if (isdigit(CTYPE_CONV(**str))) { + long l; + if (parse_int(str, &l) < 0 + || check_nonnegative(l) < 0 +@@ -381,7 +393,7 @@ static struct arg_type_info * + parse_typedef_name(char **str) + { + char *end = *str; +- while (*end && (isalnum(*end) || *end == '_')) ++ while (*end && (isalnum(CTYPE_CONV(*end)) || *end == '_')) + ++end; + if (end == *str) + return NULL; +@@ -568,7 +580,7 @@ parse_string(char **str, struct arg_type_info **retp, int *ownp) + int own_length; + int with_arg = 0; + +- if (isdigit(**str)) { ++ if (isdigit(CTYPE_CONV(**str))) { + /* string0 is string[retval], length is zero(retval) + * stringN is string[argN], length is zero(argN) */ + long l; +@@ -683,7 +695,7 @@ try_parse_kwd(char **str, const char *kwd) + { + size_t len = strlen(kwd); + if (strncmp(*str, kwd, len) == 0 +- && !isalnum((*str)[len])) { ++ && !isalnum(CTYPE_CONV((*str)[len]))) { + (*str) += len; + return 0; + } +@@ -1258,8 +1270,11 @@ void + init_global_config(void) + { + struct arg_type_info *info = malloc(2 * sizeof(*info)); +- if (info == NULL) +- error(1, errno, "malloc in init_global_config"); ++ if (info == NULL) { ++ fprintf(stderr, "Couldn't init global config: %s\n", ++ strerror(errno)); ++ exit(1); ++ } + + memset(info, 0, 2 * sizeof(*info)); + info[0].type = ARGTYPE_POINTER; +diff --git a/sysdeps/linux-gnu/alpha/plt.c b/sysdeps/linux-gnu/alpha/plt.c +index c7ce9fe..1185363 100644 +--- a/sysdeps/linux-gnu/alpha/plt.c ++++ b/sysdeps/linux-gnu/alpha/plt.c +@@ -23,11 +23,13 @@ + #include "common.h" + + GElf_Addr +-arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { ++arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) ++{ + return lte->plt_addr + ndx * 12 + 32; + } + + void * +-sym2addr(Process *proc, struct library_symbol *sym) { ++sym2addr(struct process *proc, struct library_symbol *sym) ++{ + return sym->enter_addr; + } +diff --git a/sysdeps/linux-gnu/alpha/regs.c b/sysdeps/linux-gnu/alpha/regs.c +index 0b46afd..c197225 100644 +--- a/sysdeps/linux-gnu/alpha/regs.c ++++ b/sysdeps/linux-gnu/alpha/regs.c +@@ -36,26 +36,31 @@ + #endif + + void * +-get_instruction_pointer(Process *proc) { ++get_instruction_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 64 /* REG_PC */ , 0); + } + + void +-set_instruction_pointer(Process *proc, void *addr) { ++set_instruction_pointer(struct process *proc, void *addr) ++{ + ptrace(PTRACE_POKEUSER, proc->pid, 64 /* REG_PC */ , addr); + } + + void * +-get_stack_pointer(Process *proc) { ++get_stack_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 30 /* REG_FP */ , 0); + } + + void * +-get_return_addr(Process *proc, void *stack_pointer) { ++get_return_addr(struct process *proc, void *stack_pointer) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 26 /* RA */ , 0); + } + + void +-set_return_addr(Process *proc, void *addr) { ++set_return_addr(struct process *proc, void *addr) ++{ + ptrace(PTRACE_POKEUSER, proc->pid, 26 /* RA */ , addr); + } +diff --git a/sysdeps/linux-gnu/alpha/trace.c b/sysdeps/linux-gnu/alpha/trace.c +index a7c5e59..c6f7494 100644 +--- a/sysdeps/linux-gnu/alpha/trace.c ++++ b/sysdeps/linux-gnu/alpha/trace.c +@@ -40,13 +40,15 @@ + #endif + + void +-get_arch_dep(Process *proc) { ++get_arch_dep(struct process *proc) ++{ + } + + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct process *proc, int status, int *sysnum) ++{ + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + char *ip = get_instruction_pointer(proc) - 4; +@@ -69,7 +71,8 @@ syscall_p(Process *proc, int status, int *sysnum) { + } + + long +-gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++gimme_arg(enum tof type, struct process *proc, int arg_num, ++ struct arg_type_info *info) + { + if (arg_num == -1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 0 /* REG_R0 */ , 0); +diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c +index 5748401..2fb9578 100644 +--- a/sysdeps/linux-gnu/arm/breakpoint.c ++++ b/sysdeps/linux-gnu/arm/breakpoint.c +@@ -92,7 +92,7 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) + } + + int +-arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) ++arch_breakpoint_init(struct process *proc, struct breakpoint *sbp) + { + /* XXX That uintptr_t cast is there temporarily until + * arch_addr_t becomes integral type. */ +diff --git a/sysdeps/linux-gnu/arm/plt.c b/sysdeps/linux-gnu/arm/plt.c +index 1b97705..d1bf7ca 100644 +--- a/sysdeps/linux-gnu/arm/plt.c ++++ b/sysdeps/linux-gnu/arm/plt.c +@@ -43,6 +43,7 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + } + + void * +-sym2addr(Process *proc, struct library_symbol *sym) { ++sym2addr(struct process *proc, struct library_symbol *sym) ++{ + return sym->enter_addr; + } +diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c +index 484de7f..377df62 100644 +--- a/sysdeps/linux-gnu/arm/regs.c ++++ b/sysdeps/linux-gnu/arm/regs.c +@@ -41,24 +41,28 @@ + #define off_sp ((void *)52) + + void * +-get_instruction_pointer(Process *proc) { ++get_instruction_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); + } + + void +-set_instruction_pointer(Process *proc, void *addr) { ++set_instruction_pointer(struct process *proc, void *addr) ++{ + ptrace(PTRACE_POKEUSER, proc->pid, off_pc, addr); + } + + void * +-get_stack_pointer(Process *proc) { ++get_stack_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0); + } + + /* really, this is given the *stack_pointer expecting + * a CISC architecture; in our case, we don't need that */ + void * +-get_return_addr(Process *proc, void *stack_pointer) { ++get_return_addr(struct process *proc, void *stack_pointer) ++{ + long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0); + + /* Remember & unset the thumb mode bit. XXX This is really a +@@ -74,7 +78,8 @@ get_return_addr(Process *proc, void *stack_pointer) { + } + + void +-set_return_addr(Process *proc, void *addr) { ++set_return_addr(struct process *proc, void *addr) ++{ + long iaddr = (int)addr | proc->thumb_mode; + ptrace(PTRACE_POKEUSER, proc->pid, off_lr, (void *)iaddr); + } +diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c +index c528cfb..fbbf676 100644 +--- a/sysdeps/linux-gnu/arm/trace.c ++++ b/sysdeps/linux-gnu/arm/trace.c +@@ -48,7 +48,8 @@ + #define off_pc ((void *)60) + + void +-get_arch_dep(Process *proc) { ++get_arch_dep(struct process *proc) ++{ + proc_archdep *a; + + if (!proc->arch_ptr) +@@ -63,7 +64,8 @@ get_arch_dep(Process *proc) { + * -1 on error. + */ + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct process *proc, int status, int *sysnum) ++{ + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + /* get the user's pc (plus 8) */ +@@ -104,7 +106,8 @@ syscall_p(Process *proc, int status, int *sysnum) { + } + + long +-gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++gimme_arg(enum tof type, struct process *proc, int arg_num, ++ struct arg_type_info *info) + { + proc_archdep *a = (proc_archdep *) proc->arch_ptr; + +diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c +index fe336b1..c28d745 100644 +--- a/sysdeps/linux-gnu/breakpoint.c ++++ b/sysdeps/linux-gnu/breakpoint.c +@@ -79,7 +79,7 @@ arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) + #endif /* ARCH_HAVE_ENABLE_BREAKPOINT */ + + void +-enable_breakpoint(Process *proc, struct breakpoint *sbp) ++enable_breakpoint(struct process *proc, struct breakpoint *sbp) + { + debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", + proc->pid, sbp->addr, breakpoint_name(sbp)); +@@ -127,7 +127,7 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) + #endif /* ARCH_HAVE_DISABLE_BREAKPOINT */ + + void +-disable_breakpoint(Process *proc, struct breakpoint *sbp) ++disable_breakpoint(struct process *proc, struct breakpoint *sbp) + { + debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", + proc->pid, sbp->addr, breakpoint_name(sbp)); +diff --git a/sysdeps/linux-gnu/cris/plt.c b/sysdeps/linux-gnu/cris/plt.c +index 427ae93..fcc18d2 100644 +--- a/sysdeps/linux-gnu/cris/plt.c ++++ b/sysdeps/linux-gnu/cris/plt.c +@@ -28,7 +28,7 @@ GElf_Addr arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) + return lte->plt_addr + 0x20 + (ndx * 26); + } + +-void *sym2addr(Process *proc, struct library_symbol *sym) ++void *sym2addr(struct process *proc, struct library_symbol *sym) + { + return sym->enter_addr; + } +diff --git a/sysdeps/linux-gnu/cris/regs.c b/sysdeps/linux-gnu/cris/regs.c +index 7028b9e..4cf1fbf 100644 +--- a/sysdeps/linux-gnu/cris/regs.c ++++ b/sysdeps/linux-gnu/cris/regs.c +@@ -37,22 +37,22 @@ + # define PTRACE_POKEUSER PTRACE_POKEUSR + #endif + +-void *get_instruction_pointer(Process *proc) ++void *get_instruction_pointer(struct process *proc) + { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_PPC, 0); + } + +-void set_instruction_pointer(Process *proc, void *addr) ++void set_instruction_pointer(struct process *proc, void *addr) + { + ptrace(PTRACE_POKEUSER, proc->pid, 4 * PT_PPC, addr); + } + +-void *get_stack_pointer(Process *proc) ++void *get_stack_pointer(struct process *proc) + { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_USP, 0); + } + +-void *get_return_addr(Process *proc, void *stack_pointer) ++void *get_return_addr(struct process *proc, void *stack_pointer) + { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_SRP, 0); + } +diff --git a/sysdeps/linux-gnu/cris/trace.c b/sysdeps/linux-gnu/cris/trace.c +index 98cb7d8..7d8cca6 100644 +--- a/sysdeps/linux-gnu/cris/trace.c ++++ b/sysdeps/linux-gnu/cris/trace.c +@@ -40,14 +40,14 @@ + # define PTRACE_POKEUSER PTRACE_POKEUSR + #endif + +-void get_arch_dep(Process *proc) ++void get_arch_dep(struct process *proc) + { + } + + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ + #define SYSCALL_INSN 0xe93d +-int syscall_p(Process *proc, int status, int *sysnum) ++int syscall_p(struct process *proc, int status, int *sysnum) + { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { +@@ -71,7 +71,7 @@ int syscall_p(Process *proc, int status, int *sysnum) + return 0; + } + +-long gimme_arg(enum tof type, Process *proc, int arg_num, ++long gimme_arg(enum tof type, struct process *proc, int arg_num, + struct arg_type_info *info) + { + int pid = proc->pid; +diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c +index 077a656..6cda97e 100644 +--- a/sysdeps/linux-gnu/events.c ++++ b/sysdeps/linux-gnu/events.c +@@ -48,7 +48,7 @@ static Event * delayed_events = NULL; + static Event * end_delayed_events = NULL; + + static enum callback_status +-first (Process * proc, void * data) ++first(struct process *proc, void *data) + { + return CBS_STOP; + } +@@ -83,12 +83,12 @@ each_qd_event(enum ecb_status (*pred)(Event *, void *), void * data) + Event * event; + for (event = prev; event != NULL; ) { + switch ((*pred)(event, data)) { +- case ecb_cont: ++ case ECB_CONT: + prev = event; + event = event->next; + continue; + +- case ecb_deque: ++ case ECB_DEQUE: + debug(DEBUG_FUNCTION, "dequeuing event %d for %d", + event->type, + event->proc != NULL ? event->proc->pid : -1); +@@ -106,7 +106,7 @@ each_qd_event(enum ecb_status (*pred)(Event *, void *), void * data) + end_delayed_events = NULL; + /* fall-through */ + +- case ecb_yield: ++ case ECB_YIELD: + return event; + } + } +@@ -120,9 +120,9 @@ event_process_not_reenabling(Event * event, void * data) + if (event->proc == NULL + || event->proc->leader == NULL + || event->proc->leader->event_handler == NULL) +- return ecb_deque; ++ return ECB_DEQUE; + else +- return ecb_cont; ++ return ECB_CONT; + } + + static Event * +@@ -188,7 +188,7 @@ next_event(void) + * now) the pain of figuring this out all over again. + * Petr Machata 2011-11-22. */ + int i = 0; +- for (; i < 100 && process_status(pid) != ps_tracing_stop; ++i) { ++ for (; i < 100 && process_status(pid) != PS_TRACING_STOP; ++i) { + debug(2, "waiting for %d to stop", pid); + usleep(10000); + } +@@ -199,7 +199,7 @@ next_event(void) + } + get_arch_dep(event.proc); + debug(3, "event from pid %u", pid); +- Process *leader = event.proc->leader; ++ struct process *leader = event.proc->leader; + + /* The process should be stopped after the waitpid call. But + * when the whole thread group is terminated, we see +@@ -341,13 +341,13 @@ static enum ecb_status + event_for_proc(struct Event *event, void *data) + { + if (event->proc == data) +- return ecb_deque; ++ return ECB_DEQUE; + else +- return ecb_cont; ++ return ECB_CONT; + } + + void +-delete_events_for(struct Process *proc) ++delete_events_for(struct process *proc) + { + struct Event *event; + while ((event = each_qd_event(&event_for_proc, proc)) != NULL) +diff --git a/sysdeps/linux-gnu/events.h b/sysdeps/linux-gnu/events.h +index 3802aff..51ef309 100644 +--- a/sysdeps/linux-gnu/events.h ++++ b/sysdeps/linux-gnu/events.h +@@ -26,16 +26,16 @@ + /* Declarations for event que functions. */ + + enum ecb_status { +- ecb_cont, /* The iteration should continue. */ +- ecb_yield, /* The iteration should stop, yielding this ++ ECB_CONT, /* The iteration should continue. */ ++ ECB_YIELD, /* The iteration should stop, yielding this + * event. */ +- ecb_deque, /* Like ecb_stop, but the event should be removed ++ ECB_DEQUE, /* Like ECB_STOP, but the event should be removed + * from the queue. */ + }; + + struct Event *each_qd_event(enum ecb_status (*cb)(struct Event *event, + void *data), void *data); +-void delete_events_for(struct Process * proc); ++void delete_events_for(struct process *proc); + void enque_event(struct Event *event); + + #endif /* SYSDEPS_LINUX_GNU_EVENTS_H */ +diff --git a/sysdeps/linux-gnu/ia64/fetch.c b/sysdeps/linux-gnu/ia64/fetch.c +index 54dc5b8..e90dbed 100644 +--- a/sysdeps/linux-gnu/ia64/fetch.c ++++ b/sysdeps/linux-gnu/ia64/fetch.c +@@ -63,7 +63,7 @@ union cfm_t { + }; + + static int +-fetch_context_init(struct Process *proc, struct fetch_context *context) ++fetch_context_init(struct process *proc, struct fetch_context *context) + { + context->slot_n = 0; + context->flt = 8; +@@ -76,7 +76,7 @@ fetch_context_init(struct Process *proc, struct fetch_context *context) + } + + struct fetch_context * +-arch_fetch_arg_init(enum tof type, struct Process *proc, ++arch_fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info) + { + struct fetch_context *context = malloc(sizeof(*context)); +@@ -91,7 +91,7 @@ arch_fetch_arg_init(enum tof type, struct Process *proc, + } + + struct fetch_context * +-arch_fetch_arg_clone(struct Process *proc, ++arch_fetch_arg_clone(struct process *proc, + struct fetch_context *context) + { + struct fetch_context *clone = malloc(sizeof(*context)); +@@ -102,7 +102,7 @@ arch_fetch_arg_clone(struct Process *proc, + } + + int +-allocate_stack_slot(struct fetch_context *ctx, struct Process *proc, ++allocate_stack_slot(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + size_t al = type_alignof(proc, info); +@@ -121,7 +121,7 @@ allocate_stack_slot(struct fetch_context *ctx, struct Process *proc, + } + + static int +-allocate_reg(struct fetch_context *ctx, struct Process *proc, ++allocate_reg(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + if (ctx->slot_n >= 8) +@@ -152,7 +152,7 @@ allocate_reg(struct fetch_context *ctx, struct Process *proc, + } + + static int +-copy_aggregate_part(struct fetch_context *ctx, struct Process *proc, ++copy_aggregate_part(struct fetch_context *ctx, struct process *proc, + unsigned char *buf, size_t size) + { + size_t slots = (size + 7) / 8; +@@ -176,7 +176,7 @@ copy_aggregate_part(struct fetch_context *ctx, struct Process *proc, + } + + static int +-allocate_arg(struct fetch_context *ctx, struct Process *proc, ++allocate_arg(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + size_t sz = type_sizeof(proc, info); +@@ -213,7 +213,7 @@ fpreg_to_double (struct ia64_fpreg *fp) { + } + + static int +-allocate_float(struct fetch_context *ctx, struct Process *proc, ++allocate_float(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, + int take_slot) + { +@@ -281,7 +281,7 @@ get_hfa_type(struct arg_type_info *info, size_t *countp) + } + + static int +-allocate_hfa(struct fetch_context *ctx, struct Process *proc, ++allocate_hfa(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, + enum arg_type hfa_type, size_t hfa_count) + { +@@ -366,7 +366,7 @@ allocate_hfa(struct fetch_context *ctx, struct Process *proc, + } + + static int +-allocate_ret(struct fetch_context *ctx, struct Process *proc, ++allocate_ret(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + size_t sz = type_sizeof(proc, info); +@@ -405,7 +405,7 @@ allocate_ret(struct fetch_context *ctx, struct Process *proc, + + int + arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, +- struct Process *proc, ++ struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + switch (info->type) { +@@ -446,7 +446,7 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, + + int + arch_fetch_retval(struct fetch_context *ctx, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + if (fetch_context_init(proc, ctx) < 0) +diff --git a/sysdeps/linux-gnu/ia64/plt.c b/sysdeps/linux-gnu/ia64/plt.c +index a29488f..f6bc939 100644 +--- a/sysdeps/linux-gnu/ia64/plt.c ++++ b/sysdeps/linux-gnu/ia64/plt.c +@@ -68,12 +68,13 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) + } + + void * +-sym2addr(Process *proc, struct library_symbol *sym) { ++sym2addr(struct process *proc, struct library_symbol *sym) ++{ + return sym->enter_addr; + } + + int +-arch_translate_address_dyn(struct Process *proc, ++arch_translate_address_dyn(struct process *proc, + arch_addr_t addr, arch_addr_t *ret) + { + errno = 0; +diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c +index cbc2744..fb79e8a 100644 +--- a/sysdeps/linux-gnu/ia64/regs.c ++++ b/sysdeps/linux-gnu/ia64/regs.c +@@ -34,7 +34,8 @@ + #include "common.h" + + void * +-get_instruction_pointer(Process *proc) { ++get_instruction_pointer(struct process *proc) ++{ + unsigned long ip = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IIP, 0); + unsigned long slot = + (ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IPSR, 0) >> 41) & 3; +@@ -43,7 +44,8 @@ get_instruction_pointer(Process *proc) { + } + + void +-set_instruction_pointer(Process *proc, void *addr) { ++set_instruction_pointer(struct process *proc, void *addr) ++{ + + unsigned long newip = (unsigned long)addr; + unsigned long slot = (unsigned long)addr & 0xf; +@@ -59,7 +61,8 @@ set_instruction_pointer(Process *proc, void *addr) { + } + + void * +-get_stack_pointer(Process *proc) { ++get_stack_pointer(struct process *proc) ++{ + long l = ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0); + if (l == -1 && errno) + return NULL; +@@ -67,7 +70,8 @@ get_stack_pointer(Process *proc) { + } + + void * +-get_return_addr(Process *proc, void *stack_pointer) { ++get_return_addr(struct process *proc, void *stack_pointer) ++{ + long l = ptrace(PTRACE_PEEKUSER, proc->pid, PT_B0, 0); + if (l == -1 && errno) + return NULL; +@@ -75,6 +79,7 @@ get_return_addr(Process *proc, void *stack_pointer) { + } + + void +-set_return_addr(Process *proc, void *addr) { ++set_return_addr(struct process *proc, void *addr) ++{ + ptrace(PTRACE_POKEUSER, proc->pid, PT_B0, addr); + } +diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c +index e608275..9e554ef 100644 +--- a/sysdeps/linux-gnu/ia64/trace.c ++++ b/sysdeps/linux-gnu/ia64/trace.c +@@ -70,7 +70,8 @@ union cfm_t { + }; + + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct process *proc, int status, int *sysnum) ++{ + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + long l = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IPSR, 0); +@@ -141,5 +142,6 @@ syscall_p(Process *proc, int status, int *sysnum) { + } + + void +-get_arch_dep(Process *proc) { ++get_arch_dep(struct process *proc) ++{ + } +diff --git a/sysdeps/linux-gnu/m68k/fetch.c b/sysdeps/linux-gnu/m68k/fetch.c +index f6d8a0b..24bd8f0 100644 +--- a/sysdeps/linux-gnu/m68k/fetch.c ++++ b/sysdeps/linux-gnu/m68k/fetch.c +@@ -43,7 +43,7 @@ struct fetch_context + }; + + static int +-fetch_register_banks(struct Process *proc, struct fetch_context *context, ++fetch_register_banks(struct process *proc, struct fetch_context *context, + int floating) + { + if (ptrace(PTRACE_GETREGS, proc->pid, 0, &context->regs) < 0) +@@ -57,7 +57,7 @@ fetch_register_banks(struct Process *proc, struct fetch_context *context, + } + + struct fetch_context * +-arch_fetch_arg_init(enum tof type, struct Process *proc, ++arch_fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info) + { + struct fetch_context *context = malloc(sizeof(*context)); +@@ -92,7 +92,7 @@ arch_fetch_arg_init(enum tof type, struct Process *proc, + } + + struct fetch_context * +-arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) ++arch_fetch_arg_clone(struct process *proc, struct fetch_context *context) + { + struct fetch_context *ret = malloc(sizeof(*ret)); + if (ret == NULL) +@@ -103,7 +103,7 @@ arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) + + int + arch_fetch_arg_next(struct fetch_context *context, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + size_t sz = type_sizeof(proc, info); +@@ -143,7 +143,7 @@ arch_fetch_arg_next(struct fetch_context *context, enum tof type, + + int + arch_fetch_retval(struct fetch_context *context, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTIONR) < 0) +diff --git a/sysdeps/linux-gnu/m68k/plt.c b/sysdeps/linux-gnu/m68k/plt.c +index c1b37dd..dcd6878 100644 +--- a/sysdeps/linux-gnu/m68k/plt.c ++++ b/sysdeps/linux-gnu/m68k/plt.c +@@ -30,6 +30,7 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + } + + void * +-sym2addr(Process *proc, struct library_symbol *sym) { ++sym2addr(struct process *proc, struct library_symbol *sym) ++{ + return sym->enter_addr; + } +diff --git a/sysdeps/linux-gnu/m68k/regs.c b/sysdeps/linux-gnu/m68k/regs.c +index 4afdfbb..c2fafe1 100644 +--- a/sysdeps/linux-gnu/m68k/regs.c ++++ b/sysdeps/linux-gnu/m68k/regs.c +@@ -36,26 +36,31 @@ + #endif + + void * +-get_instruction_pointer(Process *proc) { ++get_instruction_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_PC, 0); + } + + void +-set_instruction_pointer(Process *proc, void *addr) { ++set_instruction_pointer(struct process *proc, void *addr) ++{ + ptrace(PTRACE_POKEUSER, proc->pid, 4 * PT_PC, addr); + } + + void * +-get_stack_pointer(Process *proc) { ++get_stack_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_USP, 0); + } + + void * +-get_return_addr(Process *proc, void *stack_pointer) { ++get_return_addr(struct process *proc, void *stack_pointer) ++{ + return (void *)ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0); + } + + void +-set_return_addr(Process *proc, void *addr) { ++set_return_addr(struct process *proc, void *addr) ++{ + ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); + } +diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c +index 311ffd5..01b5253 100644 +--- a/sysdeps/linux-gnu/m68k/trace.c ++++ b/sysdeps/linux-gnu/m68k/trace.c +@@ -40,13 +40,15 @@ + #endif + + void +-get_arch_dep(Process *proc) { ++get_arch_dep(struct process *proc) ++{ + } + + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct process *proc, int status, int *sysnum) ++{ + int depth; + + if (WIFSTOPPED(status) +diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h +index 684b546..ba1220d 100644 +--- a/sysdeps/linux-gnu/mipsel/arch.h ++++ b/sysdeps/linux-gnu/mipsel/arch.h +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata + * Copyright (C) 2006 Eric Vaitl + * + * This program is free software; you can redistribute it and/or +@@ -43,7 +44,7 @@ struct arch_ltelf_data { + #define ARCH_HAVE_GET_SYMINFO + #define ARCH_HAVE_DYNLINK_DONE + #define ARCH_HAVE_ADD_PLT_ENTRY +-#define ARCH_HAVE_ATOMIC_SINGLESTEP ++#define ARCH_HAVE_SW_SINGLESTEP + #define ARCH_HAVE_SYMBOL_RET + + #define ARCH_HAVE_LIBRARY_SYMBOL_DATA +diff --git a/sysdeps/linux-gnu/mipsel/plt.c b/sysdeps/linux-gnu/mipsel/plt.c +index b277fbc..ce33406 100644 +--- a/sysdeps/linux-gnu/mipsel/plt.c ++++ b/sysdeps/linux-gnu/mipsel/plt.c +@@ -104,7 +104,8 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) + breakpoint changes I just add a new breakpoint for the new address. + */ + void * +-sym2addr(Process *proc, struct library_symbol *sym) { ++sym2addr(struct process *proc, struct library_symbol *sym) ++{ + long ret; + + if (sym->arch.pltalways +@@ -125,7 +126,7 @@ sym2addr(Process *proc, struct library_symbol *sym) { + /* Address of run time loader map, used for debugging. */ + #define DT_MIPS_RLD_MAP 0x70000016 + int +-arch_find_dl_debug(struct Process *proc, arch_addr_t dyn_addr, ++arch_find_dl_debug(struct process *proc, arch_addr_t dyn_addr, + arch_addr_t *ret) + { + arch_addr_t rld_addr; +@@ -251,11 +252,11 @@ arch_elf_destroy(struct ltelf *lte) + + /* When functions return we check if the symbol needs an updated + breakpoint with the resolved address. */ +-void arch_symbol_ret(struct Process *proc, struct library_symbol *libsym) ++void arch_symbol_ret(struct process *proc, struct library_symbol *libsym) + { + struct breakpoint *bp; + arch_addr_t resolved_addr; +- struct Process *leader = proc->leader; ++ struct process *leader = proc->leader; + + /* Only deal with unresolved symbols. */ + if (libsym->arch.type != MIPS_PLT_UNRESOLVED) +@@ -302,7 +303,7 @@ err: + static enum callback_status + cb_enable_breakpoint_sym(struct library_symbol *libsym, void *data) + { +- struct Process *proc = data; ++ struct process *proc = data; + arch_addr_t bp_addr; + + if (!libsym->arch.gotonly) +@@ -329,19 +330,19 @@ cb_enable_breakpoint_sym(struct library_symbol *libsym, void *data) + } + + static enum callback_status +-cb_enable_breakpoint_lib(struct Process *proc, struct library *lib, void *data) ++cb_enable_breakpoint_lib(struct process *proc, struct library *lib, void *data) + { + library_each_symbol(lib, NULL, cb_enable_breakpoint_sym, proc); + return CBS_CONT; + } + +-void arch_dynlink_done(struct Process *proc) ++void arch_dynlink_done(struct process *proc) + { + proc_each_library(proc->leader, NULL, cb_enable_breakpoint_lib, NULL); + } + + enum plt_status +-arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, + struct library_symbol **ret) + { +@@ -350,7 +351,7 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, + + struct library_symbol *libsym = malloc(sizeof(*libsym)); + if (libsym == NULL) +- return plt_fail; ++ return PLT_FAIL; + + GElf_Addr addr = arch_plt_sym_val(lte, sym_index, 0); + +@@ -389,12 +390,12 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, + } + + *ret = libsym; +- return plt_ok; ++ return PLT_OK; + + fail: + free(name); + free(libsym); +- return plt_fail; ++ return PLT_FAIL; + } + + int +diff --git a/sysdeps/linux-gnu/mipsel/regs.c b/sysdeps/linux-gnu/mipsel/regs.c +index 8731ac5..19f97cb 100644 +--- a/sysdeps/linux-gnu/mipsel/regs.c ++++ b/sysdeps/linux-gnu/mipsel/regs.c +@@ -49,7 +49,8 @@ + \return The current instruction pointer. + */ + void * +-get_instruction_pointer(Process *proc) { ++get_instruction_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); + } + +@@ -63,7 +64,8 @@ get_instruction_pointer(Process *proc) { + we \c continue_process() after a breakpoint. Check if this is OK. + */ + void +-set_instruction_pointer(Process *proc, void *addr) { ++set_instruction_pointer(struct process *proc, void *addr) ++{ + ptrace(PTRACE_POKEUSER, proc->pid, off_pc, addr); + } + +@@ -72,7 +74,8 @@ set_instruction_pointer(Process *proc, void *addr) { + \return The current stack pointer. + */ + void * +-get_stack_pointer(Process *proc) { ++get_stack_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0); + } + +@@ -87,11 +90,13 @@ get_stack_pointer(Process *proc) { + unused. + */ + void * +-get_return_addr(Process *proc, void *stack_pointer) { ++get_return_addr(struct process *proc, void *stack_pointer) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0); + } + + void +-set_return_addr(Process *proc, void *addr) { ++set_return_addr(struct process *proc, void *addr) ++{ + ptrace(PTRACE_POKEUSER, proc->pid, off_lr, addr); + } +diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c +index db9ec21..f553166 100644 +--- a/sysdeps/linux-gnu/mipsel/trace.c ++++ b/sysdeps/linux-gnu/mipsel/trace.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2012 Edgar E. Iglesias, Axis Communications + * Copyright (C) 2010 Arnaud Patard, Mandriva SA + * Copyright (C) 2008,2009 Juan Cespedes +@@ -64,7 +65,8 @@ + private data area. + */ + void +-get_arch_dep(Process *proc) { ++get_arch_dep(struct process *proc) ++{ + } + + /** +@@ -85,7 +87,8 @@ get_arch_dep(Process *proc) { + for the system calls is 4000. + */ + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct process *proc, int status, int *sysnum) ++{ + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + /* get the user's pc (plus 8) */ +@@ -141,7 +144,7 @@ mips32_relative_offset (uint32_t inst) + return ((itype_immediate(inst) ^ 0x8000) - 0x8000) << 2; + } + +-int mips_next_pcs(struct Process *proc, uint32_t pc, uint32_t *newpc) ++int mips_next_pcs(struct process *proc, uint32_t pc, uint32_t *newpc) + { + uint32_t inst, rx; + int op; +@@ -261,10 +264,10 @@ fail: + return 0; + } + +-int +-arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, +- int (*add_cb)(void *addr, void *data), +- void *add_cb_data) ++enum sw_singlestep_status ++arch_sw_singlestep(struct process *proc, struct breakpoint *bp, ++ int (*add_cb)(arch_addr_t, struct sw_singlestep_data *), ++ struct sw_singlestep_data *add_cb_data) + { + uint32_t pc = (uint32_t) get_instruction_pointer(proc); + uint32_t newpcs[2]; +@@ -281,11 +284,11 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, + } + + if (add_cb(baddr, add_cb_data) < 0) +- return -1; ++ return SWS_FAIL; + } + + ptrace(PTRACE_SYSCALL, proc->pid, 0, 0); +- return 0; ++ return SWS_OK; + } + + /** +@@ -317,7 +320,8 @@ I'm not doing any floating point support here. + + */ + long +-gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++gimme_arg(enum tof type, struct process *proc, int arg_num, ++ struct arg_type_info *info) + { + long ret; + long addr; +diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h +index fb8768a..3b903ee 100644 +--- a/sysdeps/linux-gnu/ppc/arch.h ++++ b/sysdeps/linux-gnu/ppc/arch.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata ++ * Copyright (C) 2012,2013 Petr Machata + * Copyright (C) 2006 Paul Gilliam + * Copyright (C) 2002,2004 Juan Cespedes + * +@@ -37,7 +37,7 @@ + #define ARCH_SUPPORTS_OPD + #endif + +-#define ARCH_HAVE_ATOMIC_SINGLESTEP ++#define ARCH_HAVE_SW_SINGLESTEP + #define ARCH_HAVE_ADD_PLT_ENTRY + #define ARCH_HAVE_TRANSLATE_ADDRESS + #define ARCH_HAVE_DYNLINK_DONE +diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c +index 9963a1e..ed38336 100644 +--- a/sysdeps/linux-gnu/ppc/fetch.c ++++ b/sysdeps/linux-gnu/ppc/fetch.c +@@ -31,7 +31,7 @@ + #include "proc.h" + #include "value.h" + +-static int allocate_gpr(struct fetch_context *ctx, struct Process *proc, ++static int allocate_gpr(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep); + + /* Floating point registers have the same width on 32-bit as well as +@@ -66,7 +66,7 @@ struct fetch_context { + }; + + static int +-fetch_context_init(struct Process *proc, struct fetch_context *context) ++fetch_context_init(struct process *proc, struct fetch_context *context) + { + context->greg = 3; + context->freg = 1; +@@ -108,7 +108,7 @@ fetch_context_init(struct Process *proc, struct fetch_context *context) + } + + struct fetch_context * +-arch_fetch_arg_init(enum tof type, struct Process *proc, ++arch_fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info) + { + struct fetch_context *context = malloc(sizeof(*context)); +@@ -132,7 +132,7 @@ arch_fetch_arg_init(enum tof type, struct Process *proc, + } + + struct fetch_context * +-arch_fetch_arg_clone(struct Process *proc, ++arch_fetch_arg_clone(struct process *proc, + struct fetch_context *context) + { + struct fetch_context *clone = malloc(sizeof(*context)); +@@ -143,7 +143,7 @@ arch_fetch_arg_clone(struct Process *proc, + } + + static int +-allocate_stack_slot(struct fetch_context *ctx, struct Process *proc, ++allocate_stack_slot(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + size_t sz = type_sizeof(proc, info); +@@ -170,7 +170,7 @@ allocate_stack_slot(struct fetch_context *ctx, struct Process *proc, + } + + static uint64_t +-read_gpr(struct fetch_context *ctx, struct Process *proc, int reg_num) ++read_gpr(struct fetch_context *ctx, struct process *proc, int reg_num) + { + if (proc->e_machine == EM_PPC) + return ctx->regs.r32[reg_num]; +@@ -215,7 +215,7 @@ align_small_int(unsigned char *buf, size_t w, size_t sz) + } + + static int +-allocate_gpr(struct fetch_context *ctx, struct Process *proc, ++allocate_gpr(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + if (ctx->greg > 10) +@@ -245,7 +245,7 @@ allocate_gpr(struct fetch_context *ctx, struct Process *proc, + } + + static int +-allocate_float(struct fetch_context *ctx, struct Process *proc, ++allocate_float(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + int pool = proc->e_machine == EM_PPC64 ? 13 : 8; +@@ -276,7 +276,7 @@ allocate_float(struct fetch_context *ctx, struct Process *proc, + } + + static int +-allocate_argument(struct fetch_context *ctx, struct Process *proc, ++allocate_argument(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + /* Floating point types and void are handled specially. */ +@@ -400,7 +400,7 @@ allocate_argument(struct fetch_context *ctx, struct Process *proc, + + int + arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, +- struct Process *proc, ++ struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + return allocate_argument(ctx, proc, info, valuep); +@@ -408,7 +408,7 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, + + int + arch_fetch_retval(struct fetch_context *ctx, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + if (ctx->ret_struct) { +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index f83f087..439b8e8 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2004,2008,2009 Juan Cespedes + * Copyright (C) 2006 Paul Gilliam + * +@@ -125,51 +125,6 @@ host_powerpc64() + #endif + } + +-int +-read_target_4(struct Process *proc, arch_addr_t addr, uint32_t *lp) +-{ +- unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); +- if (l == -1UL && errno) +- return -1; +-#ifdef __powerpc64__ +- l >>= 32; +-#endif +- *lp = l; +- return 0; +-} +- +-static int +-read_target_8(struct Process *proc, arch_addr_t addr, uint64_t *lp) +-{ +- unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); +- if (l == -1UL && errno) +- return -1; +- if (host_powerpc64()) { +- *lp = l; +- } else { +- unsigned long l2 = ptrace(PTRACE_PEEKTEXT, proc->pid, +- addr + 4, 0); +- if (l2 == -1UL && errno) +- return -1; +- *lp = ((uint64_t)l << 32) | l2; +- } +- return 0; +-} +- +-int +-read_target_long(struct Process *proc, arch_addr_t addr, uint64_t *lp) +-{ +- if (proc->e_machine == EM_PPC) { +- uint32_t w; +- int ret = read_target_4(proc, addr, &w); +- if (ret >= 0) +- *lp = (uint64_t)w; +- return ret; +- } else { +- return read_target_8(proc, addr, lp); +- } +-} +- + static void + mark_as_resolved(struct library_symbol *libsym, GElf_Addr value) + { +@@ -178,14 +133,14 @@ mark_as_resolved(struct library_symbol *libsym, GElf_Addr value) + } + + void +-arch_dynlink_done(struct Process *proc) ++arch_dynlink_done(struct process *proc) + { + /* On PPC32 with BSS PLT, we need to enable delayed symbols. */ + struct library_symbol *libsym = NULL; + while ((libsym = proc_each_symbol(proc, libsym, + library_symbol_delayed_cb, NULL))) { +- if (read_target_8(proc, libsym->enter_addr, +- &libsym->arch.resolved_value) < 0) { ++ if (proc_read_64(proc, libsym->enter_addr, ++ &libsym->arch.resolved_value) < 0) { + fprintf(stderr, + "couldn't read PLT value for %s(%p): %s\n", + libsym->name, libsym->enter_addr, +@@ -258,12 +213,12 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) + * ourselves with bias, as the values in OPD have been resolved + * already. */ + int +-arch_translate_address_dyn(struct Process *proc, ++arch_translate_address_dyn(struct process *proc, + arch_addr_t addr, arch_addr_t *ret) + { + if (proc->e_machine == EM_PPC64) { + uint64_t value; +- if (read_target_8(proc, addr, &value) < 0) { ++ if (proc_read_64(proc, addr, &value) < 0) { + fprintf(stderr, + "dynamic .opd translation of %p: %s\n", + addr, strerror(errno)); +@@ -324,7 +279,7 @@ load_opd_data(struct ltelf *lte, struct library *lib) + } + + void * +-sym2addr(struct Process *proc, struct library_symbol *sym) ++sym2addr(struct process *proc, struct library_symbol *sym) + { + return sym->enter_addr; + } +@@ -560,7 +515,7 @@ arch_elf_init(struct ltelf *lte, struct library *lib) + } + + static int +-read_plt_slot_value(struct Process *proc, GElf_Addr addr, GElf_Addr *valp) ++read_plt_slot_value(struct process *proc, GElf_Addr addr, GElf_Addr *valp) + { + /* On PPC64, we read from .plt, which contains 8 byte + * addresses. On PPC32 we read from .plt, which contains 4 +@@ -568,7 +523,7 @@ read_plt_slot_value(struct Process *proc, GElf_Addr addr, GElf_Addr *valp) + * either can change. */ + uint64_t l; + /* XXX double cast. */ +- if (read_target_8(proc, (arch_addr_t)(uintptr_t)addr, &l) < 0) { ++ if (proc_read_64(proc, (arch_addr_t)(uintptr_t)addr, &l) < 0) { + fprintf(stderr, "ptrace .plt slot value @%#" PRIx64": %s\n", + addr, strerror(errno)); + return -1; +@@ -579,7 +534,7 @@ read_plt_slot_value(struct Process *proc, GElf_Addr addr, GElf_Addr *valp) + } + + static int +-unresolve_plt_slot(struct Process *proc, GElf_Addr addr, GElf_Addr value) ++unresolve_plt_slot(struct process *proc, GElf_Addr addr, GElf_Addr value) + { + /* We only modify plt_entry[0], which holds the resolved + * address of the routine. We keep the TOC and environment +@@ -594,18 +549,18 @@ unresolve_plt_slot(struct Process *proc, GElf_Addr addr, GElf_Addr value) + } + + enum plt_status +-arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, + struct library_symbol **ret) + { + if (lte->ehdr.e_machine == EM_PPC) { + if (lte->arch.secure_plt) +- return plt_default; ++ return PLT_DEFAULT; + + struct library_symbol *libsym = NULL; + if (default_elf_add_plt_entry(proc, lte, a_name, rela, ndx, + &libsym) < 0) +- return plt_fail; ++ return PLT_FAIL; + + /* On PPC32 with BSS PLT, delay the symbol until + * dynamic linker is done. */ +@@ -613,7 +568,7 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, + libsym->delayed = 1; + + *ret = libsym; +- return plt_ok; ++ return PLT_OK; + } + + /* PPC64. If we have stubs, we return a chain of breakpoint +@@ -636,7 +591,7 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, + + if (chain != NULL) { + *ret = chain; +- return plt_ok; ++ return PLT_OK; + } + + /* We don't have stub symbols. Find corresponding .plt slot, +@@ -653,7 +608,7 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, + + GElf_Addr plt_slot_value; + if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) +- return plt_fail; ++ return PLT_FAIL; + + char *name = strdup(a_name); + struct library_symbol *libsym = malloc(sizeof(*libsym)); +@@ -663,7 +618,7 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, + fail: + free(name); + free(libsym); +- return plt_fail; ++ return PLT_FAIL; + } + + /* XXX The double cast should be removed when +@@ -696,7 +651,7 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, + } + + *ret = libsym; +- return plt_ok; ++ return PLT_OK; + } + + void +@@ -712,7 +667,7 @@ arch_elf_destroy(struct ltelf *lte) + } + + static void +-dl_plt_update_bp_on_hit(struct breakpoint *bp, struct Process *proc) ++dl_plt_update_bp_on_hit(struct breakpoint *bp, struct process *proc) + { + debug(DEBUG_PROCESS, "pid=%d dl_plt_update_bp_on_hit %s(%p)", + proc->pid, breakpoint_name(bp), bp->addr); +@@ -752,7 +707,7 @@ cb_on_all_stopped(struct process_stopping_handler *self) + static enum callback_status + cb_keep_stepping_p(struct process_stopping_handler *self) + { +- struct Process *proc = self->task_enabling_breakpoint; ++ struct process *proc = self->task_enabling_breakpoint; + struct library_symbol *libsym = self->breakpoint_being_enabled->libsym; + + GElf_Addr value; +@@ -799,7 +754,7 @@ cb_keep_stepping_p(struct process_stopping_handler *self) + /* Install breakpoint to the address where the change takes + * place. If we fail, then that just means that we'll have to + * singlestep the next time around as well. */ +- struct Process *leader = proc->leader; ++ struct process *leader = proc->leader; + if (leader == NULL || leader->arch.dl_plt_update_bp != NULL) + goto done; + +@@ -827,7 +782,7 @@ done: + } + + static void +-jump_to_entry_point(struct Process *proc, struct breakpoint *bp) ++jump_to_entry_point(struct process *proc, struct breakpoint *bp) + { + /* XXX The double cast should be removed when + * arch_addr_t becomes integral type. */ +@@ -837,10 +792,10 @@ jump_to_entry_point(struct Process *proc, struct breakpoint *bp) + } + + static void +-ppc_plt_bp_continue(struct breakpoint *bp, struct Process *proc) ++ppc_plt_bp_continue(struct breakpoint *bp, struct process *proc) + { + switch (bp->libsym->arch.type) { +- struct Process *leader; ++ struct process *leader; + void (*on_all_stopped)(struct process_stopping_handler *); + enum callback_status (*keep_stepping_p) + (struct process_stopping_handler *); +@@ -899,7 +854,7 @@ ppc_plt_bp_continue(struct breakpoint *bp, struct Process *proc) + * detect both cases and do any fix-ups necessary to mend this + * situation. */ + static enum callback_status +-detach_task_cb(struct Process *task, void *data) ++detach_task_cb(struct process *task, void *data) + { + struct breakpoint *bp = data; + +@@ -919,7 +874,7 @@ detach_task_cb(struct Process *task, void *data) + } + + static void +-ppc_plt_bp_retract(struct breakpoint *bp, struct Process *proc) ++ppc_plt_bp_retract(struct breakpoint *bp, struct process *proc) + { + /* On PPC64, we rewrite .plt with PLT entry addresses. This + * needs to be undone. Unfortunately, the program may have +@@ -975,7 +930,7 @@ arch_library_symbol_clone(struct library_symbol *retp, + * don't need PROC here, we can store the data in BP if it is of + * interest to us. */ + int +-arch_breakpoint_init(struct Process *proc, struct breakpoint *bp) ++arch_breakpoint_init(struct process *proc, struct breakpoint *bp) + { + /* Artificial and entry-point breakpoints are plain. */ + if (bp->libsym == NULL || bp->libsym->plt_type != LS_TOPLT_EXEC) +@@ -1012,7 +967,7 @@ arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) + } + + int +-arch_process_init(struct Process *proc) ++arch_process_init(struct process *proc) + { + proc->arch.dl_plt_update_bp = NULL; + proc->arch.handler = NULL; +@@ -1020,19 +975,19 @@ arch_process_init(struct Process *proc) + } + + void +-arch_process_destroy(struct Process *proc) ++arch_process_destroy(struct process *proc) + { + } + + int +-arch_process_clone(struct Process *retp, struct Process *proc) ++arch_process_clone(struct process *retp, struct process *proc) + { + retp->arch = proc->arch; + return 0; + } + + int +-arch_process_exec(struct Process *proc) ++arch_process_exec(struct process *proc) + { + return arch_process_init(proc); + } +diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c +index 37f89a4..ed9b398 100644 +--- a/sysdeps/linux-gnu/ppc/regs.c ++++ b/sysdeps/linux-gnu/ppc/regs.c +@@ -40,35 +40,32 @@ + #endif + + void * +-get_instruction_pointer(Process *proc) { ++get_instruction_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, sizeof(long)*PT_NIP, 0); + } + + void +-set_instruction_pointer(Process *proc, void *addr) ++set_instruction_pointer(struct process *proc, void *addr) + { + if (ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_NIP, addr) != 0) + error(0, errno, "set_instruction_pointer"); + } + + void * +-get_stack_pointer(Process *proc) { ++get_stack_pointer(struct process *proc) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, sizeof(long)*PT_R1, 0); + } + + void * +-get_return_addr(Process *proc, void *stack_pointer) { ++get_return_addr(struct process *proc, void *stack_pointer) ++{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, sizeof(long)*PT_LNK, 0); + } + + void +-set_return_addr(Process *proc, void *addr) { ++set_return_addr(struct process *proc, void *addr) ++{ + ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_LNK, addr); + } +- +-/* Grab the value of CTR registers. */ +-void * +-get_count_register (Process *proc) { +- return (void *) ptrace (PTRACE_PEEKUSER, proc->pid, +- sizeof (long) * PT_CTR, 0); +-} +diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c +index 4357a1e..ee9a6b5 100644 +--- a/sysdeps/linux-gnu/ppc/trace.c ++++ b/sysdeps/linux-gnu/ppc/trace.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2010,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2010,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2011 Andreas Schwab + * Copyright (C) 2002,2004,2008,2009 Juan Cespedes + * Copyright (C) 2008 Luis Machado, IBM Corporation +@@ -49,7 +49,8 @@ + #endif + + void +-get_arch_dep(Process *proc) { ++get_arch_dep(struct process *proc) ++{ + #ifdef __powerpc64__ + proc->mask_32bit = (proc->e_machine == EM_PPC); + #endif +@@ -59,7 +60,8 @@ get_arch_dep(Process *proc) { + + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. */ + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct process *proc, int status, int *sysnum) ++{ + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + long pc = (long)get_instruction_pointer(proc); +@@ -84,18 +86,15 @@ syscall_p(Process *proc, int status, int *sysnum) { + + /* The atomic skip code is mostly taken from GDB. */ + +-/* In plt.h. XXX make this official interface. */ +-int read_target_4(struct Process *proc, arch_addr_t addr, uint32_t *lp); +- +-int +-arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, +- int (*add_cb)(void *addr, void *data), +- void *add_cb_data) ++enum sw_singlestep_status ++arch_sw_singlestep(struct process *proc, struct breakpoint *sbp, ++ int (*add_cb)(arch_addr_t, struct sw_singlestep_data *), ++ struct sw_singlestep_data *add_cb_data) + { + arch_addr_t ip = get_instruction_pointer(proc); + struct breakpoint *other = address2bpstruct(proc->leader, ip); + +- debug(1, "arch_atomic_singlestep pid=%d addr=%p %s(%p)", ++ debug(1, "arch_sw_singlestep pid=%d addr=%p %s(%p)", + proc->pid, ip, breakpoint_name(sbp), sbp->addr); + + /* If the original instruction was lwarx/ldarx, we can't +@@ -107,15 +106,15 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, + } u; + if (other != NULL) { + memcpy(u.buf, sbp->orig_value, BREAKPOINT_LENGTH); +- } else if (read_target_4(proc, ip, &u.insn) < 0) { ++ } else if (proc_read_32(proc, ip, &u.insn) < 0) { + fprintf(stderr, "couldn't read instruction at IP %p\n", ip); + /* Do the normal singlestep. */ +- return 1; ++ return SWS_HW; + } + + if ((u.insn & LWARX_MASK) != LWARX_INSTRUCTION + && (u.insn & LWARX_MASK) != LDARX_INSTRUCTION) +- return 1; ++ return SWS_HW; + + debug(1, "singlestep over atomic block at %p", ip); + +@@ -125,7 +124,7 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, + addr += 4; + unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); + if (l == (unsigned long)-1 && errno) +- return -1; ++ return SWS_FAIL; + uint32_t insn; + #ifdef __powerpc64__ + insn = l >> 32; +@@ -140,7 +139,7 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, + debug(1, "pid=%d, branch in atomic block from %p to %p", + proc->pid, addr, branch_addr); + if (add_cb(branch_addr, add_cb_data) < 0) +- return -1; ++ return SWS_FAIL; + } + + /* Assume that the atomic sequence ends with a +@@ -157,22 +156,22 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, + if (insn_count > 16) { + fprintf(stderr, "[%d] couldn't find end of atomic block" + " at %p\n", proc->pid, ip); +- return -1; ++ return SWS_FAIL; + } + } + + /* Put the breakpoint to the next instruction. */ + addr += 4; + if (add_cb(addr, add_cb_data) < 0) +- return -1; ++ return SWS_FAIL; + + debug(1, "PTRACE_CONT"); + ptrace(PTRACE_CONT, proc->pid, 0, 0); +- return 0; ++ return SWS_OK; + } + + size_t +-arch_type_sizeof(struct Process *proc, struct arg_type_info *info) ++arch_type_sizeof(struct process *proc, struct arg_type_info *info) + { + if (proc == NULL) + return (size_t)-2; +@@ -215,7 +214,7 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info *info) + } + + size_t +-arch_type_alignof(struct Process *proc, struct arg_type_info *info) ++arch_type_alignof(struct process *proc, struct arg_type_info *info) + { + if (proc == NULL) + return (size_t)-2; +diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c +index 5adfb16..d69c985 100644 +--- a/sysdeps/linux-gnu/proc.c ++++ b/sysdeps/linux-gnu/proc.c +@@ -177,44 +177,45 @@ process_status_cb(const char *line, const char *prefix, void *data) + } while (0) + + switch (c) { +- case 'Z': RETURN(ps_zombie); +- case 't': RETURN(ps_tracing_stop); ++ case 'Z': RETURN(PS_ZOMBIE); ++ case 't': RETURN(PS_TRACING_STOP); + case 'T': + /* This can be either "T (stopped)" or, for older + * kernels, "T (tracing stop)". */ + if (!strcmp(status, "T (stopped)\n")) +- RETURN(ps_stop); ++ RETURN(PS_STOP); + else if (!strcmp(status, "T (tracing stop)\n")) +- RETURN(ps_tracing_stop); ++ RETURN(PS_TRACING_STOP); + else { + fprintf(stderr, "Unknown process status: %s", + status); +- RETURN(ps_stop); /* Some sort of stop ++ RETURN(PS_STOP); /* Some sort of stop + * anyway. */ + } + case 'D': +- case 'S': RETURN(ps_sleeping); ++ case 'S': RETURN(PS_SLEEPING); + } + +- RETURN(ps_other); ++ RETURN(PS_OTHER); + #undef RETURN + } + + enum process_status + process_status(pid_t pid) + { +- enum process_status ret = ps_invalid; ++ enum process_status ret = PS_INVALID; + FILE * file = open_status_file(pid); + if (file != NULL) { + each_line_starting(file, "State:\t", &process_status_cb, &ret); + fclose(file); +- if (ret == ps_invalid) ++ if (ret == PS_INVALID) + fprintf(stderr, "process_status %d: %s", pid, + strerror(errno)); +- } else ++ } else { + /* If the file is not present, the process presumably + * exited already. */ +- ret = ps_zombie; ++ ret = PS_ZOMBIE; ++ } + + return ret; + } +@@ -279,7 +280,7 @@ process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n) + * ABI object, as theorized about somewhere on pmachata/revamp + * branch. */ + static void * +-select_32_64(struct Process *proc, void *p32, void *p64) ++select_32_64(struct process *proc, void *p32, void *p64) + { + if (sizeof(long) == 4 || proc->mask_32bit) + return p32; +@@ -288,7 +289,7 @@ select_32_64(struct Process *proc, void *p32, void *p64) + } + + static int +-fetch_dyn64(struct Process *proc, arch_addr_t *addr, Elf64_Dyn *ret) ++fetch_dyn64(struct process *proc, arch_addr_t *addr, Elf64_Dyn *ret) + { + if (umovebytes(proc, *addr, ret, sizeof(*ret)) != sizeof(*ret)) + return -1; +@@ -297,7 +298,7 @@ fetch_dyn64(struct Process *proc, arch_addr_t *addr, Elf64_Dyn *ret) + } + + static int +-fetch_dyn32(struct Process *proc, arch_addr_t *addr, Elf64_Dyn *ret) ++fetch_dyn32(struct process *proc, arch_addr_t *addr, Elf64_Dyn *ret) + { + Elf32_Dyn dyn; + if (umovebytes(proc, *addr, &dyn, sizeof(dyn)) != sizeof(dyn)) +@@ -311,14 +312,14 @@ fetch_dyn32(struct Process *proc, arch_addr_t *addr, Elf64_Dyn *ret) + } + + static int (* +-dyn_fetcher(struct Process *proc))(struct Process *, ++dyn_fetcher(struct process *proc))(struct process *, + arch_addr_t *, Elf64_Dyn *) + { + return select_32_64(proc, fetch_dyn32, fetch_dyn64); + } + + int +-proc_find_dynamic_entry_addr(struct Process *proc, arch_addr_t src_addr, ++proc_find_dynamic_entry_addr(struct process *proc, arch_addr_t src_addr, + int d_tag, arch_addr_t *ret) + { + debug(DEBUG_FUNCTION, "find_dynamic_entry()"); +@@ -364,7 +365,7 @@ struct lt_link_map_32 LT_LINK_MAP(32); + struct lt_link_map_64 LT_LINK_MAP(64); + + static int +-fetch_lm64(struct Process *proc, arch_addr_t addr, ++fetch_lm64(struct process *proc, arch_addr_t addr, + struct lt_link_map_64 *ret) + { + if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret)) +@@ -373,7 +374,7 @@ fetch_lm64(struct Process *proc, arch_addr_t addr, + } + + static int +-fetch_lm32(struct Process *proc, arch_addr_t addr, ++fetch_lm32(struct process *proc, arch_addr_t addr, + struct lt_link_map_64 *ret) + { + struct lt_link_map_32 lm; +@@ -390,7 +391,7 @@ fetch_lm32(struct Process *proc, arch_addr_t addr, + } + + static int (* +-lm_fetcher(struct Process *proc))(struct Process *, ++lm_fetcher(struct process *proc))(struct process *, + arch_addr_t, struct lt_link_map_64 *) + { + return select_32_64(proc, fetch_lm32, fetch_lm64); +@@ -410,7 +411,7 @@ struct lt_r_debug_32 LT_R_DEBUG(32); + struct lt_r_debug_64 LT_R_DEBUG(64); + + static int +-fetch_rd64(struct Process *proc, arch_addr_t addr, ++fetch_rd64(struct process *proc, arch_addr_t addr, + struct lt_r_debug_64 *ret) + { + if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret)) +@@ -419,7 +420,7 @@ fetch_rd64(struct Process *proc, arch_addr_t addr, + } + + static int +-fetch_rd32(struct Process *proc, arch_addr_t addr, ++fetch_rd32(struct process *proc, arch_addr_t addr, + struct lt_r_debug_64 *ret) + { + struct lt_r_debug_32 rd; +@@ -436,7 +437,7 @@ fetch_rd32(struct Process *proc, arch_addr_t addr, + } + + static int (* +-rdebug_fetcher(struct Process *proc))(struct Process *, ++rdebug_fetcher(struct process *proc))(struct process *, + arch_addr_t, struct lt_r_debug_64 *) + { + return select_32_64(proc, fetch_rd32, fetch_rd64); +@@ -463,13 +464,13 @@ fetch_auxv32_entry(int fd, Elf64_auxv_t *ret) + } + + static int (* +-auxv_fetcher(struct Process *proc))(int, Elf64_auxv_t *) ++auxv_fetcher(struct process *proc))(int, Elf64_auxv_t *) + { + return select_32_64(proc, fetch_auxv32_entry, fetch_auxv64_entry); + } + + static void +-crawl_linkmap(struct Process *proc, struct lt_r_debug_64 *dbg) ++crawl_linkmap(struct process *proc, struct lt_r_debug_64 *dbg) + { + debug (DEBUG_FUNCTION, "crawl_linkmap()"); + +@@ -551,7 +552,7 @@ crawl_linkmap(struct Process *proc, struct lt_r_debug_64 *dbg) + } + + static int +-load_debug_struct(struct Process *proc, struct lt_r_debug_64 *ret) ++load_debug_struct(struct process *proc, struct lt_r_debug_64 *ret) + { + debug(DEBUG_FUNCTION, "load_debug_struct"); + +@@ -564,7 +565,7 @@ load_debug_struct(struct Process *proc, struct lt_r_debug_64 *ret) + } + + static void +-rdebug_bp_on_hit(struct breakpoint *bp, struct Process *proc) ++rdebug_bp_on_hit(struct breakpoint *bp, struct process *proc) + { + debug(DEBUG_FUNCTION, "arch_check_dbg"); + +@@ -595,7 +596,7 @@ rdebug_bp_on_hit(struct breakpoint *bp, struct Process *proc) + + #ifndef ARCH_HAVE_FIND_DL_DEBUG + int +-arch_find_dl_debug(struct Process *proc, arch_addr_t dyn_addr, ++arch_find_dl_debug(struct process *proc, arch_addr_t dyn_addr, + arch_addr_t *ret) + { + return proc_find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, ret); +@@ -603,7 +604,7 @@ arch_find_dl_debug(struct Process *proc, arch_addr_t dyn_addr, + #endif + + int +-linkmap_init(struct Process *proc, arch_addr_t dyn_addr) ++linkmap_init(struct process *proc, arch_addr_t dyn_addr) + { + debug(DEBUG_FUNCTION, "linkmap_init(%d, dyn_addr=%p)", proc->pid, dyn_addr); + +@@ -648,13 +649,13 @@ task_kill (pid_t pid, int sig) + } + + void +-process_removed(struct Process *proc) ++process_removed(struct process *proc) + { + delete_events_for(proc); + } + + int +-process_get_entry(struct Process *proc, ++process_get_entry(struct process *proc, + arch_addr_t *entryp, + arch_addr_t *interp_biasp) + { +@@ -706,7 +707,7 @@ process_get_entry(struct Process *proc, + } + + int +-os_process_init(struct Process *proc) ++os_process_init(struct process *proc) + { + proc->os.debug_addr = 0; + proc->os.debug_state = 0; +@@ -714,19 +715,19 @@ os_process_init(struct Process *proc) + } + + void +-os_process_destroy(struct Process *proc) ++os_process_destroy(struct process *proc) + { + } + + int +-os_process_clone(struct Process *retp, struct Process *proc) ++os_process_clone(struct process *retp, struct process *proc) + { + retp->os = proc->os; + return 0; + } + + int +-os_process_exec(struct Process *proc) ++os_process_exec(struct process *proc) + { + return 0; + } +diff --git a/sysdeps/linux-gnu/s390/fetch.c b/sysdeps/linux-gnu/s390/fetch.c +index fa8f42d..0b68dbc 100644 +--- a/sysdeps/linux-gnu/s390/fetch.c ++++ b/sysdeps/linux-gnu/s390/fetch.c +@@ -61,7 +61,7 @@ s390x(struct fetch_context *ctx) + } + + static int +-fetch_register_banks(struct Process *proc, struct fetch_context *ctx) ++fetch_register_banks(struct process *proc, struct fetch_context *ctx) + { + ptrace_area parea; + parea.len = sizeof(ctx->regs); +@@ -76,7 +76,7 @@ fetch_register_banks(struct Process *proc, struct fetch_context *ctx) + } + + static int +-fetch_context_init(struct Process *proc, struct fetch_context *context) ++fetch_context_init(struct process *proc, struct fetch_context *context) + { + context->greg = 2; + context->freg = 0; +@@ -84,7 +84,7 @@ fetch_context_init(struct Process *proc, struct fetch_context *context) + } + + struct fetch_context * +-arch_fetch_arg_init(enum tof type, struct Process *proc, ++arch_fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info) + { + struct fetch_context *context = malloc(sizeof(*context)); +@@ -105,7 +105,7 @@ arch_fetch_arg_init(enum tof type, struct Process *proc, + } + + struct fetch_context * +-arch_fetch_arg_clone(struct Process *proc, ++arch_fetch_arg_clone(struct process *proc, + struct fetch_context *context) + { + struct fetch_context *clone = malloc(sizeof(*context)); +@@ -116,7 +116,7 @@ arch_fetch_arg_clone(struct Process *proc, + } + + static int +-allocate_stack_slot(struct fetch_context *ctx, struct Process *proc, ++allocate_stack_slot(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, + size_t sz) + { +@@ -148,7 +148,7 @@ copy_gpr(struct fetch_context *ctx, struct value *valuep, int regno) + } + + static int +-allocate_gpr(struct fetch_context *ctx, struct Process *proc, ++allocate_gpr(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, + size_t sz) + { +@@ -160,7 +160,7 @@ allocate_gpr(struct fetch_context *ctx, struct Process *proc, + } + + static int +-allocate_gpr_pair(struct fetch_context *ctx, struct Process *proc, ++allocate_gpr_pair(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, + size_t sz) + { +@@ -191,7 +191,7 @@ allocate_gpr_pair(struct fetch_context *ctx, struct Process *proc, + } + + static int +-allocate_fpr(struct fetch_context *ctx, struct Process *proc, ++allocate_fpr(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, + size_t sz) + { +@@ -212,7 +212,7 @@ allocate_fpr(struct fetch_context *ctx, struct Process *proc, + + int + arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, +- struct Process *proc, ++ struct process *proc, + struct arg_type_info *info, struct value *valuep) + { + size_t sz = type_sizeof(proc, info); +@@ -267,7 +267,7 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, + + int + arch_fetch_retval(struct fetch_context *ctx, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + if (info->type == ARGTYPE_STRUCT) { +diff --git a/sysdeps/linux-gnu/s390/plt.c b/sysdeps/linux-gnu/s390/plt.c +index 5f612e5..8893d45 100644 +--- a/sysdeps/linux-gnu/s390/plt.c ++++ b/sysdeps/linux-gnu/s390/plt.c +@@ -29,6 +29,7 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + } + + void * +-sym2addr(Process *proc, struct library_symbol *sym) { ++sym2addr(struct process *proc, struct library_symbol *sym) ++{ + return sym->enter_addr; + } +diff --git a/sysdeps/linux-gnu/s390/regs.c b/sysdeps/linux-gnu/s390/regs.c +index 0592ccd..44e8f67 100644 +--- a/sysdeps/linux-gnu/s390/regs.c ++++ b/sysdeps/linux-gnu/s390/regs.c +@@ -46,7 +46,8 @@ + #endif + + void * +-get_instruction_pointer(Process *proc) { ++get_instruction_pointer(struct process *proc) ++{ + long ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_PSWADDR, 0) & PSW_MASK; + #ifdef __s390x__ + if (proc->mask_32bit) +@@ -56,7 +57,8 @@ get_instruction_pointer(Process *proc) { + } + + void +-set_instruction_pointer(Process *proc, void *addr) { ++set_instruction_pointer(struct process *proc, void *addr) ++{ + #ifdef __s390x__ + if (proc->mask_32bit) + addr = (void *)((long)addr & PSW_MASK31); +@@ -65,7 +67,8 @@ set_instruction_pointer(Process *proc, void *addr) { + } + + void * +-get_stack_pointer(Process *proc) { ++get_stack_pointer(struct process *proc) ++{ + long ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR15, 0) & PSW_MASK; + #ifdef __s390x__ + if (proc->mask_32bit) +@@ -75,7 +78,8 @@ get_stack_pointer(Process *proc) { + } + + void * +-get_return_addr(Process *proc, void *stack_pointer) { ++get_return_addr(struct process *proc, void *stack_pointer) ++{ + long ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR14, 0) & PSW_MASK; + #ifdef __s390x__ + if (proc->mask_32bit) +@@ -85,7 +89,8 @@ get_return_addr(Process *proc, void *stack_pointer) { + } + + void +-set_return_addr(Process *proc, void *addr) { ++set_return_addr(struct process *proc, void *addr) ++{ + #ifdef __s390x__ + if (proc->mask_32bit) + addr = (void *)((long)addr & PSW_MASK31); +diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c +index b9e05ff..78b04c3 100644 +--- a/sysdeps/linux-gnu/s390/trace.c ++++ b/sysdeps/linux-gnu/s390/trace.c +@@ -43,7 +43,8 @@ + #endif + + void +-get_arch_dep(Process *proc) { ++get_arch_dep(struct process *proc) ++{ + #ifdef __s390x__ + unsigned long psw; + +@@ -64,7 +65,8 @@ get_arch_dep(Process *proc) { + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct process *proc, int status, int *sysnum) ++{ + long pc, opcode, offset_reg, scno, tmp; + void *svc_addr; + int gpr_offset[16] = { PT_GPR0, PT_GPR1, PT_ORIGGPR2, PT_GPR3, +@@ -175,7 +177,7 @@ syscall_p(Process *proc, int status, int *sysnum) { + } + + size_t +-arch_type_sizeof(struct Process *proc, struct arg_type_info *info) ++arch_type_sizeof(struct process *proc, struct arg_type_info *info) + { + if (proc == NULL) + return (size_t)-2; +@@ -217,7 +219,7 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info *info) + } + + size_t +-arch_type_alignof(struct Process *proc, struct arg_type_info *info) ++arch_type_alignof(struct process *proc, struct arg_type_info *info) + { + if (proc == NULL) + return (size_t)-2; +diff --git a/sysdeps/linux-gnu/sparc/plt.c b/sysdeps/linux-gnu/sparc/plt.c +index 40bbabc..3d2e589 100644 +--- a/sysdeps/linux-gnu/sparc/plt.c ++++ b/sysdeps/linux-gnu/sparc/plt.c +@@ -28,6 +28,7 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + } + + void * +-sym2addr(Process *proc, struct library_symbol *sym) { ++sym2addr(struct process *proc, struct library_symbol *sym) ++{ + return sym->enter_addr; + } +diff --git a/sysdeps/linux-gnu/sparc/regs.c b/sysdeps/linux-gnu/sparc/regs.c +index 5e5ad20..8431c9b 100644 +--- a/sysdeps/linux-gnu/sparc/regs.c ++++ b/sysdeps/linux-gnu/sparc/regs.c +@@ -27,7 +27,8 @@ + #include "common.h" + + void * +-get_instruction_pointer(Process *proc) { ++get_instruction_pointer(struct process *proc) ++{ + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + if (a->valid) + return (void *)a->regs.pc; +@@ -35,14 +36,16 @@ get_instruction_pointer(Process *proc) { + } + + void +-set_instruction_pointer(Process *proc, void *addr) { ++set_instruction_pointer(struct process *proc, void *addr) ++{ + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + if (a->valid) + a->regs.pc = (long)addr; + } + + void * +-get_stack_pointer(Process *proc) { ++get_stack_pointer(struct process *proc) ++{ + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + if (a->valid) + return (void *)a->regs.u_regs[UREG_I5]; +@@ -50,7 +53,8 @@ get_stack_pointer(Process *proc) { + } + + void * +-get_return_addr(Process *proc, void *stack_pointer) { ++get_return_addr(struct process *proc, void *stack_pointer) ++{ + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + unsigned int t; + if (!a->valid) +@@ -63,7 +67,8 @@ get_return_addr(Process *proc, void *stack_pointer) { + } + + void +-set_return_addr(Process *proc, void *addr) { ++set_return_addr(struct process *proc, void *addr) ++{ + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + if (!a->valid) + return; +diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c +index e1725ff..078d406 100644 +--- a/sysdeps/linux-gnu/sparc/trace.c ++++ b/sysdeps/linux-gnu/sparc/trace.c +@@ -31,7 +31,8 @@ + #include "common.h" + + void +-get_arch_dep(Process *proc) { ++get_arch_dep(struct process *proc) ++{ + proc_archdep *a; + if (!proc->arch_ptr) + proc->arch_ptr = (void *)malloc(sizeof(proc_archdep)); +@@ -43,7 +44,8 @@ get_arch_dep(Process *proc) { + * Returns -1 otherwise + */ + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct process *proc, int status, int *sysnum) ++{ + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + void *ip = get_instruction_pointer(proc); +@@ -66,7 +68,8 @@ syscall_p(Process *proc, int status, int *sysnum) { + } + + long +-gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++gimme_arg(enum tof type, struct process *proc, int arg_num, ++ struct arg_type_info *info) + { + proc_archdep *a = (proc_archdep *) proc->arch_ptr; + if (!a->valid) { +diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c +index e13b761..e57a5ed 100644 +--- a/sysdeps/linux-gnu/trace.c ++++ b/sysdeps/linux-gnu/trace.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2007,2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2007,2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Joe Damato + * Copyright (C) 1998,2002,2003,2004,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -111,7 +111,7 @@ trace_pid(pid_t pid) + } + + void +-trace_set_options(struct Process *proc) ++trace_set_options(struct process *proc) + { + if (proc->tracesysgood & 0x80) + return; +@@ -148,8 +148,8 @@ static enum ecb_status + event_for_pid(Event *event, void *data) + { + if (event->proc != NULL && event->proc->pid == (pid_t)(uintptr_t)data) +- return ecb_yield; +- return ecb_cont; ++ return ECB_YIELD; ++ return ECB_CONT; + } + + static int +@@ -206,7 +206,7 @@ add_task_info(struct pid_set *pids, pid_t pid) + } + + static enum callback_status +-task_stopped(struct Process *task, void *data) ++task_stopped(struct process *task, void *data) + { + enum process_status st = process_status(task->pid); + if (data != NULL) +@@ -217,13 +217,13 @@ task_stopped(struct Process *task, void *data) + * the meantime. This can happen when the whole thread group + * is terminating. */ + switch (st) { +- case ps_invalid: +- case ps_tracing_stop: +- case ps_zombie: ++ case PS_INVALID: ++ case PS_TRACING_STOP: ++ case PS_ZOMBIE: + return CBS_CONT; +- case ps_sleeping: +- case ps_stop: +- case ps_other: ++ case PS_SLEEPING: ++ case PS_STOP: ++ case PS_OTHER: + return CBS_STOP; + } + +@@ -232,7 +232,7 @@ task_stopped(struct Process *task, void *data) + + /* Task is blocked if it's stopped, or if it's a vfork parent. */ + static enum callback_status +-task_blocked(struct Process *task, void *data) ++task_blocked(struct process *task, void *data) + { + struct pid_set *pids = data; + struct pid_task *task_info = get_task_info(pids, task->pid); +@@ -246,7 +246,7 @@ task_blocked(struct Process *task, void *data) + static Event *process_vfork_on_event(struct event_handler *super, Event *event); + + static enum callback_status +-task_vforked(struct Process *task, void *data) ++task_vforked(struct process *task, void *data) + { + if (task->event_handler != NULL + && task->event_handler->on_event == &process_vfork_on_event) +@@ -255,15 +255,15 @@ task_vforked(struct Process *task, void *data) + } + + static int +-is_vfork_parent(struct Process *task) ++is_vfork_parent(struct process *task) + { + return each_task(task->leader, NULL, &task_vforked, NULL) != NULL; + } + + static enum callback_status +-send_sigstop(struct Process *task, void *data) ++send_sigstop(struct process *task, void *data) + { +- struct Process *leader = task->leader; ++ struct process *leader = task->leader; + struct pid_set *pids = data; + + /* Look for pre-existing task record, or add new. */ +@@ -299,8 +299,8 @@ send_sigstop(struct Process *task, void *data) + * vforked process. We set up event handler specially to hint + * us. In that case parent is in D state, which we use to + * weed out unnecessary looping. */ +- if (st == ps_sleeping +- && is_vfork_parent (task)) { ++ if (st == PS_SLEEPING ++ && is_vfork_parent(task)) { + task_info->vforked = 1; + return CBS_CONT; + } +@@ -321,7 +321,7 @@ send_sigstop(struct Process *task, void *data) + breakpoint where IP points and let the process continue. After + this the breakpoint can be retracted and the process detached. */ + static void +-ugly_workaround(struct Process *proc) ++ugly_workaround(struct process *proc) + { + void *ip = get_instruction_pointer(proc); + struct breakpoint *sbp = dict_find_entry(proc->leader->breakpoints, ip); +@@ -334,7 +334,7 @@ ugly_workaround(struct Process *proc) + + static void + process_stopping_done(struct process_stopping_handler *self, +- struct Process *leader) ++ struct process *leader) + { + debug(DEBUG_PROCESS, "process stopping done %d", + self->task_enabling_breakpoint->pid); +@@ -351,7 +351,7 @@ process_stopping_done(struct process_stopping_handler *self, + + if (self->exiting) { + ugly_workaround: +- self->state = psh_ugly_workaround; ++ self->state = PSH_UGLY_WORKAROUND; + ugly_workaround(self->task_enabling_breakpoint); + } else { + switch ((self->ugly_workaround_p)(self)) { +@@ -377,11 +377,11 @@ undo_breakpoint(Event *event, void *data) + && event->proc->leader == data + && event->type == EVENT_BREAKPOINT) + set_instruction_pointer(event->proc, event->e_un.brk_addr); +- return ecb_cont; ++ return ECB_CONT; + } + + static enum callback_status +-untrace_task(struct Process *task, void *data) ++untrace_task(struct process *task, void *data) + { + if (task != data) + untrace_pid(task->pid); +@@ -389,7 +389,7 @@ untrace_task(struct Process *task, void *data) + } + + static enum callback_status +-remove_task(struct Process *task, void *data) ++remove_task(struct process *task, void *data) + { + /* Don't untrace leader just yet. */ + if (task != data) +@@ -398,14 +398,14 @@ remove_task(struct Process *task, void *data) + } + + static enum callback_status +-retract_breakpoint_cb(struct Process *proc, struct breakpoint *bp, void *data) ++retract_breakpoint_cb(struct process *proc, struct breakpoint *bp, void *data) + { + breakpoint_on_retract(bp, proc); + return CBS_CONT; + } + + static void +-detach_process(struct Process *leader) ++detach_process(struct process *leader) + { + each_qd_event(&undo_breakpoint, leader); + disable_all_breakpoints(leader); +@@ -414,7 +414,7 @@ detach_process(struct Process *leader) + /* Now untrace the process, if it was attached to by -p. */ + struct opt_p_t *it; + for (it = opt_p; it != NULL; it = it->next) { +- struct Process *proc = pid2proc(it->pid); ++ struct process *proc = pid2proc(it->pid); + if (proc == NULL) + continue; + if (proc->leader == leader) { +@@ -540,19 +540,13 @@ all_stops_accountable(struct pid_set *pids) + return 1; + } + +-/* The protocol is: 0 for success, negative for failure, positive if +- * default singlestep is to be used. */ +-int arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, +- int (*add_cb)(void *addr, void *data), +- void *add_cb_data); +- +-#ifndef ARCH_HAVE_ATOMIC_SINGLESTEP +-int +-arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, +- int (*add_cb)(void *addr, void *data), +- void *add_cb_data) ++#ifndef ARCH_HAVE_SW_SINGLESTEP ++enum sw_singlestep_status ++arch_sw_singlestep(struct process *proc, struct breakpoint *bp, ++ int (*add_cb)(arch_addr_t, struct sw_singlestep_data *), ++ struct sw_singlestep_data *data) + { +- return 1; ++ return SWS_HW; + } + #endif + +@@ -560,43 +554,46 @@ static Event *process_stopping_on_event(struct event_handler *super, + Event *event); + + static void +-remove_atomic_breakpoints(struct Process *proc) ++remove_sw_breakpoints(struct process *proc) + { + struct process_stopping_handler *self + = (void *)proc->leader->event_handler; + assert(self != NULL); + assert(self->super.on_event == process_stopping_on_event); + +- int ct = sizeof(self->atomic_skip_bp_addrs) +- / sizeof(*self->atomic_skip_bp_addrs); ++ int ct = sizeof(self->sws_bp_addrs) / sizeof(*self->sws_bp_addrs); + int i; + for (i = 0; i < ct; ++i) +- if (self->atomic_skip_bp_addrs[i] != 0) { +- delete_breakpoint(proc, self->atomic_skip_bp_addrs[i]); +- self->atomic_skip_bp_addrs[i] = 0; ++ if (self->sws_bp_addrs[i] != 0) { ++ delete_breakpoint(proc, self->sws_bp_addrs[i]); ++ self->sws_bp_addrs[i] = 0; + } + } + + static void +-atomic_singlestep_bp_on_hit(struct breakpoint *bp, struct Process *proc) ++sw_singlestep_bp_on_hit(struct breakpoint *bp, struct process *proc) + { +- remove_atomic_breakpoints(proc); ++ remove_sw_breakpoints(proc); + } + ++struct sw_singlestep_data { ++ struct process_stopping_handler *self; ++}; ++ + static int +-atomic_singlestep_add_bp(void *addr, void *data) ++sw_singlestep_add_bp(arch_addr_t addr, struct sw_singlestep_data *data) + { +- struct process_stopping_handler *self = data; +- struct Process *proc = self->task_enabling_breakpoint; ++ struct process_stopping_handler *self = data->self; ++ struct process *proc = self->task_enabling_breakpoint; + +- int ct = sizeof(self->atomic_skip_bp_addrs) +- / sizeof(*self->atomic_skip_bp_addrs); ++ int ct = sizeof(self->sws_bp_addrs) ++ / sizeof(*self->sws_bp_addrs); + int i; + for (i = 0; i < ct; ++i) +- if (self->atomic_skip_bp_addrs[i] == 0) { +- self->atomic_skip_bp_addrs[i] = addr; ++ if (self->sws_bp_addrs[i] == 0) { ++ self->sws_bp_addrs[i] = addr; + static struct bp_callbacks cbs = { +- .on_hit = atomic_singlestep_bp_on_hit, ++ .on_hit = sw_singlestep_bp_on_hit, + }; + struct breakpoint *bp + = insert_breakpoint(proc, addr, NULL); +@@ -604,30 +601,35 @@ atomic_singlestep_add_bp(void *addr, void *data) + return 0; + } + +- assert(!"Too many atomic singlestep breakpoints!"); ++ assert(!"Too many sw singlestep breakpoints!"); + abort(); + } + + static int + singlestep(struct process_stopping_handler *self) + { +- struct Process *proc = self->task_enabling_breakpoint; +- +- int status = arch_atomic_singlestep(self->task_enabling_breakpoint, +- self->breakpoint_being_enabled, +- &atomic_singlestep_add_bp, self); ++ struct process *proc = self->task_enabling_breakpoint; ++ ++ struct sw_singlestep_data data = { self }; ++ switch (arch_sw_singlestep(self->task_enabling_breakpoint, ++ self->breakpoint_being_enabled, ++ &sw_singlestep_add_bp, &data)) { ++ case SWS_HW: ++ /* Otherwise do the default action: singlestep. */ ++ debug(1, "PTRACE_SINGLESTEP"); ++ if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) { ++ perror("PTRACE_SINGLESTEP"); ++ return -1; ++ } ++ return 0; + +- /* Propagate failure and success. */ +- if (status <= 0) +- return status; ++ case SWS_OK: ++ return 0; + +- /* Otherwise do the default action: singlestep. */ +- debug(1, "PTRACE_SINGLESTEP"); +- if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) { +- perror("PTRACE_SINGLESTEP"); ++ case SWS_FAIL: + return -1; + } +- return 0; ++ abort(); + } + + static void +@@ -639,16 +641,16 @@ post_singlestep(struct process_stopping_handler *self, + if (*eventp != NULL && (*eventp)->type == EVENT_BREAKPOINT) + *eventp = NULL; // handled + +- struct Process *proc = self->task_enabling_breakpoint; ++ struct process *proc = self->task_enabling_breakpoint; + +- remove_atomic_breakpoints(proc); ++ remove_sw_breakpoints(proc); + self->breakpoint_being_enabled = NULL; + } + + static void + singlestep_error(struct process_stopping_handler *self) + { +- struct Process *teb = self->task_enabling_breakpoint; ++ struct process *teb = self->task_enabling_breakpoint; + struct breakpoint *sbp = self->breakpoint_being_enabled; + fprintf(stderr, "%d couldn't continue when handling %s (%p) at %p\n", + teb->pid, breakpoint_name(sbp), sbp->addr, +@@ -659,7 +661,7 @@ singlestep_error(struct process_stopping_handler *self) + static void + pt_continue(struct process_stopping_handler *self) + { +- struct Process *teb = self->task_enabling_breakpoint; ++ struct process *teb = self->task_enabling_breakpoint; + debug(1, "PTRACE_CONT"); + ptrace(PTRACE_CONT, teb->pid, 0, 0); + } +@@ -675,12 +677,12 @@ static void + disable_and(struct process_stopping_handler *self, + void (*do_this)(struct process_stopping_handler *self)) + { +- struct Process *teb = self->task_enabling_breakpoint; ++ struct process *teb = self->task_enabling_breakpoint; + debug(DEBUG_PROCESS, "all stopped, now singlestep/cont %d", teb->pid); + if (self->breakpoint_being_enabled->enabled) + disable_breakpoint(teb, self->breakpoint_being_enabled); + (do_this)(self); +- self->state = psh_singlestep; ++ self->state = PSH_SINGLESTEP; + } + + void +@@ -705,9 +707,9 @@ static Event * + process_stopping_on_event(struct event_handler *super, Event *event) + { + struct process_stopping_handler *self = (void *)super; +- struct Process *task = event->proc; +- struct Process *leader = task->leader; +- struct Process *teb = self->task_enabling_breakpoint; ++ struct process *task = event->proc; ++ struct process *leader = task->leader; ++ struct process *teb = self->task_enabling_breakpoint; + + debug(DEBUG_PROCESS, + "process_stopping_on_event: pid %d; event type %d; state %d", +@@ -737,7 +739,7 @@ process_stopping_on_event(struct event_handler *super, Event *event) + } + + switch (state) { +- case psh_stopping: ++ case PSH_STOPPING: + /* If everyone is stopped, singlestep. */ + if (each_task(leader, NULL, &task_blocked, + &self->pids) == NULL) { +@@ -746,7 +748,7 @@ process_stopping_on_event(struct event_handler *super, Event *event) + } + break; + +- case psh_singlestep: ++ case PSH_SINGLESTEP: + /* In singlestep state, breakpoint signifies that we + * have now stepped, and can re-enable the breakpoint. */ + if (event != NULL && task == teb) { +@@ -801,13 +803,14 @@ process_stopping_on_event(struct event_handler *super, Event *event) + break; + + psh_sinking: +- state = self->state = psh_sinking; +- case psh_sinking: ++ state = self->state = PSH_SINKING; ++ /* Fall through. */ ++ case PSH_SINKING: + if (await_sigstop_delivery(&self->pids, task_info, event)) + process_stopping_done(self, leader); + break; + +- case psh_ugly_workaround: ++ case PSH_UGLY_WORKAROUND: + if (event == NULL) + break; + if (event->type == EVENT_BREAKPOINT) { +@@ -845,7 +848,7 @@ no(struct process_stopping_handler *self) + } + + int +-process_install_stopping_handler(struct Process *proc, struct breakpoint *sbp, ++process_install_stopping_handler(struct process *proc, struct breakpoint *sbp, + void (*as)(struct process_stopping_handler *), + enum callback_status (*ks) + (struct process_stopping_handler *), +@@ -894,7 +897,7 @@ process_install_stopping_handler(struct Process *proc, struct breakpoint *sbp, + } + + void +-continue_after_breakpoint(Process *proc, struct breakpoint *sbp) ++continue_after_breakpoint(struct process *proc, struct breakpoint *sbp) + { + debug(DEBUG_PROCESS, + "continue_after_breakpoint: pid=%d, addr=%p", +@@ -937,8 +940,8 @@ static Event * + ltrace_exiting_on_event(struct event_handler *super, Event *event) + { + struct ltrace_exiting_handler *self = (void *)super; +- struct Process *task = event->proc; +- struct Process *leader = task->leader; ++ struct process *task = event->proc; ++ struct process *leader = task->leader; + + debug(DEBUG_PROCESS, + "ltrace_exiting_on_event: pid %d; event type %d", +@@ -970,7 +973,7 @@ ltrace_exiting_destroy(struct event_handler *super) + } + + static int +-ltrace_exiting_install_handler(struct Process *proc) ++ltrace_exiting_install_handler(struct process *proc) + { + /* Only install to leader. */ + if (proc->leader != proc) +@@ -1087,7 +1090,7 @@ process_vfork_on_event(struct event_handler *super, Event *event) + } + + void +-continue_after_vfork(struct Process *proc) ++continue_after_vfork(struct process *proc) + { + debug(DEBUG_PROCESS, "continue_after_vfork: pid=%d", proc->pid); + struct process_vfork_handler *handler = calloc(sizeof(*handler), 1); +@@ -1116,7 +1119,7 @@ continue_after_vfork(struct Process *proc) + } + + static int +-is_mid_stopping(Process *proc) ++is_mid_stopping(struct process *proc) + { + return proc != NULL + && proc->event_handler != NULL +@@ -1124,7 +1127,7 @@ is_mid_stopping(Process *proc) + } + + void +-continue_after_syscall(struct Process *proc, int sysnum, int ret_p) ++continue_after_syscall(struct process *proc, int sysnum, int ret_p) + { + /* Don't continue if we are mid-stopping. */ + if (ret_p && (is_mid_stopping(proc) || is_mid_stopping(proc->leader))) { +@@ -1136,6 +1139,23 @@ continue_after_syscall(struct Process *proc, int sysnum, int ret_p) + continue_process(proc->pid); + } + ++void ++continue_after_exec(struct process *proc) ++{ ++ continue_process(proc->pid); ++ ++ /* After the exec, we expect to hit the first executable ++ * instruction. ++ * ++ * XXX TODO It would be nice to have this removed, but then we ++ * need to do that also for initial call to wait_for_proc in ++ * execute_program. In that case we could generate a ++ * EVENT_FIRST event or something, or maybe this could somehow ++ * be rolled into EVENT_NEW. */ ++ wait_for_proc(proc->pid); ++ continue_process(proc->pid); ++} ++ + /* If ltrace gets SIGINT, the processes directly or indirectly run by + * ltrace get it too. We just have to wait long enough for the signal + * to be delivered and the process terminated, which we notice and +@@ -1152,7 +1172,7 @@ os_ltrace_exiting(void) + { + struct opt_p_t *it; + for (it = opt_p; it != NULL; it = it->next) { +- struct Process *proc = pid2proc(it->pid); ++ struct process *proc = pid2proc(it->pid); + if (proc == NULL || proc->leader == NULL) + continue; + if (ltrace_exiting_install_handler(proc->leader) < 0) +@@ -1174,7 +1194,8 @@ os_ltrace_exiting_sighandler(void) + } + + size_t +-umovebytes(Process *proc, void *addr, void *laddr, size_t len) { ++umovebytes(struct process *proc, void *addr, void *laddr, size_t len) ++{ + + union { + long a; +diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h +index 88ac33d..e988f70 100644 +--- a/sysdeps/linux-gnu/trace.h ++++ b/sysdeps/linux-gnu/trace.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as +@@ -59,13 +59,13 @@ struct process_stopping_handler + struct event_handler super; + + /* The task that is doing the re-enablement. */ +- struct Process *task_enabling_breakpoint; ++ struct process *task_enabling_breakpoint; + + /* The pointer being re-enabled. */ + struct breakpoint *breakpoint_being_enabled; + + /* Artificial atomic skip breakpoint, if any needed. */ +- void *atomic_skip_bp_addrs[2]; ++ arch_addr_t sws_bp_addrs[2]; + + /* When all tasks are stopped, this callback gets called. */ + void (*on_all_stopped)(struct process_stopping_handler *); +@@ -84,17 +84,17 @@ struct process_stopping_handler + + enum { + /* We are waiting for everyone to land in t/T. */ +- psh_stopping = 0, ++ PSH_STOPPING = 0, + + /* We are doing the PTRACE_SINGLESTEP. */ +- psh_singlestep, ++ PSH_SINGLESTEP, + + /* We are waiting for all the SIGSTOPs to arrive so + * that we can sink them. */ +- psh_sinking, ++ PSH_SINKING, + + /* This is for tracking the ugly workaround. */ +- psh_ugly_workaround, ++ PSH_UGLY_WORKAROUND, + } state; + + int exiting; +@@ -108,7 +108,7 @@ struct process_stopping_handler + * ON_ALL_STOPPED is LINUX_PTRACE_DISABLE_AND_SINGLESTEP, the default + * for KEEP_STEPPING_P and UGLY_WORKAROUND_P is "no". */ + int process_install_stopping_handler +- (struct Process *proc, struct breakpoint *sbp, ++ (struct process *proc, struct breakpoint *sbp, + void (*on_all_stopped)(struct process_stopping_handler *), + enum callback_status (*keep_stepping_p) + (struct process_stopping_handler *), +diff --git a/sysdeps/linux-gnu/x86/fetch.c b/sysdeps/linux-gnu/x86/fetch.c +index 4dab4cc..aa02a0a 100644 +--- a/sysdeps/linux-gnu/x86/fetch.c ++++ b/sysdeps/linux-gnu/x86/fetch.c +@@ -323,13 +323,13 @@ allocate_class(enum arg_class cls, struct fetch_context *context, + } + + static ssize_t +-classify(struct Process *proc, struct fetch_context *context, ++classify(struct process *proc, struct fetch_context *context, + struct arg_type_info *info, struct value *valuep, enum arg_class classes[], + size_t sz, size_t eightbytes); + + /* This classifies one eightbyte part of an array or struct. */ + static ssize_t +-classify_eightbyte(struct Process *proc, struct fetch_context *context, ++classify_eightbyte(struct process *proc, struct fetch_context *context, + struct arg_type_info *info, struct value *valuep, + enum arg_class *classp, size_t start, size_t end, + struct arg_type_info *(*getter)(struct arg_type_info *, +@@ -364,7 +364,7 @@ classify_eightbyte(struct Process *proc, struct fetch_context *context, + + /* This classifies small arrays and structs. */ + static ssize_t +-classify_eightbytes(struct Process *proc, struct fetch_context *context, ++classify_eightbytes(struct process *proc, struct fetch_context *context, + struct arg_type_info *info, struct value *valuep, + enum arg_class classes[], size_t elements, + size_t eightbytes, +@@ -432,7 +432,7 @@ flatten_structure(struct arg_type_info *flattened, struct arg_type_info *info) + } + + static ssize_t +-classify(struct Process *proc, struct fetch_context *context, ++classify(struct process *proc, struct fetch_context *context, + struct arg_type_info *info, struct value *valuep, enum arg_class classes[], + size_t sz, size_t eightbytes) + { +@@ -517,7 +517,7 @@ pass_by_reference(struct value *valuep, enum arg_class classes[]) + } + + static ssize_t +-classify_argument(struct Process *proc, struct fetch_context *context, ++classify_argument(struct process *proc, struct fetch_context *context, + struct arg_type_info *info, struct value *valuep, + enum arg_class classes[], size_t *sizep) + { +@@ -545,7 +545,7 @@ classify_argument(struct Process *proc, struct fetch_context *context, + } + + static int +-fetch_register_banks(struct Process *proc, struct fetch_context *context, ++fetch_register_banks(struct process *proc, struct fetch_context *context, + int floating) + { + if (ptrace(PTRACE_GETREGS, proc->pid, 0, &context->iregs) < 0) +@@ -566,7 +566,7 @@ fetch_register_banks(struct Process *proc, struct fetch_context *context, + + static int + arch_fetch_arg_next_32(struct fetch_context *context, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + size_t sz = type_sizeof(proc, info); +@@ -580,7 +580,7 @@ arch_fetch_arg_next_32(struct fetch_context *context, enum tof type, + + static int + arch_fetch_retval_32(struct fetch_context *context, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTIONR) < 0) +@@ -646,7 +646,7 @@ fetch_stack_pointer(struct fetch_context *context) + + struct fetch_context * + arch_fetch_arg_init_32(struct fetch_context *context, +- enum tof type, struct Process *proc, ++ enum tof type, struct process *proc, + struct arg_type_info *ret_info) + { + context->stack_pointer = fetch_stack_pointer(context) + 4; +@@ -673,7 +673,7 @@ arch_fetch_arg_init_32(struct fetch_context *context, + + struct fetch_context * + arch_fetch_arg_init_64(struct fetch_context *ctx, enum tof type, +- struct Process *proc, struct arg_type_info *ret_info) ++ struct process *proc, struct arg_type_info *ret_info) + { + /* The first stack slot holds a return address. */ + ctx->stack_pointer = fetch_stack_pointer(ctx) + 8; +@@ -698,7 +698,7 @@ arch_fetch_arg_init_64(struct fetch_context *ctx, enum tof type, + } + + struct fetch_context * +-arch_fetch_arg_init(enum tof type, struct Process *proc, ++arch_fetch_arg_init(enum tof type, struct process *proc, + struct arg_type_info *ret_info) + { + struct fetch_context *ctx = malloc(sizeof(*ctx)); +@@ -724,7 +724,7 @@ arch_fetch_arg_init(enum tof type, struct Process *proc, + } + + struct fetch_context * +-arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) ++arch_fetch_arg_clone(struct process *proc, struct fetch_context *context) + { + struct fetch_context *ret = malloc(sizeof(*ret)); + if (ret == NULL) +@@ -734,7 +734,7 @@ arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) + + static int + arch_fetch_pool_arg_next(struct fetch_context *context, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep, enum reg_pool pool) + { + enum arg_class classes[2]; +@@ -776,7 +776,7 @@ arch_fetch_pool_arg_next(struct fetch_context *context, enum tof type, + + int + arch_fetch_fun_retval(struct fetch_context *context, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + assert(type != LT_TOF_FUNCTION +@@ -808,7 +808,7 @@ arch_fetch_fun_retval(struct fetch_context *context, enum tof type, + + int + arch_fetch_arg_next(struct fetch_context *context, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + if (proc->e_machine == EM_386) +@@ -832,7 +832,7 @@ arch_fetch_arg_next(struct fetch_context *context, enum tof type, + + int + arch_fetch_retval(struct fetch_context *context, enum tof type, +- struct Process *proc, struct arg_type_info *info, ++ struct process *proc, struct arg_type_info *info, + struct value *valuep) + { + if (proc->e_machine == EM_386) +diff --git a/sysdeps/linux-gnu/x86/plt.c b/sysdeps/linux-gnu/x86/plt.c +index dc6f183..c2a4151 100644 +--- a/sysdeps/linux-gnu/x86/plt.c ++++ b/sysdeps/linux-gnu/x86/plt.c +@@ -24,11 +24,13 @@ + #include "library.h" + + GElf_Addr +-arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { ++arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) ++{ + return lte->plt_addr + (ndx + 1) * 16; + } + + void * +-sym2addr(Process *proc, struct library_symbol *sym) { ++sym2addr(struct process *proc, struct library_symbol *sym) ++{ + return sym->enter_addr; + } +diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c +index ca6470b..3886e84 100644 +--- a/sysdeps/linux-gnu/x86/regs.c ++++ b/sysdeps/linux-gnu/x86/regs.c +@@ -56,7 +56,7 @@ conv_32(arch_addr_t val) + } + + void * +-get_instruction_pointer(struct Process *proc) ++get_instruction_pointer(struct process *proc) + { + long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, XIP, 0); + if (proc->e_machine == EM_386) +@@ -65,7 +65,7 @@ get_instruction_pointer(struct Process *proc) + } + + void +-set_instruction_pointer(struct Process *proc, arch_addr_t addr) ++set_instruction_pointer(struct process *proc, arch_addr_t addr) + { + if (proc->e_machine == EM_386) + addr = conv_32(addr); +@@ -73,7 +73,7 @@ set_instruction_pointer(struct Process *proc, arch_addr_t addr) + } + + void * +-get_stack_pointer(struct Process *proc) ++get_stack_pointer(struct process *proc) + { + long sp = ptrace(PTRACE_PEEKUSER, proc->pid, XSP, 0); + if (sp == -1 && errno) { +@@ -91,7 +91,7 @@ get_stack_pointer(struct Process *proc) + } + + void * +-get_return_addr(struct Process *proc, void *sp) ++get_return_addr(struct process *proc, void *sp) + { + long a = ptrace(PTRACE_PEEKTEXT, proc->pid, sp, 0); + if (a == -1 && errno) { +@@ -109,7 +109,8 @@ get_return_addr(struct Process *proc, void *sp) + } + + void +-set_return_addr(Process *proc, void *addr) { ++set_return_addr(struct process *proc, void *addr) ++{ + if (proc->e_machine == EM_386) + addr = (void *)((long int)addr & 0xffffffff); + ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); +diff --git a/sysdeps/linux-gnu/x86/trace.c b/sysdeps/linux-gnu/x86/trace.c +index ed8bdb4..6a1a6a5 100644 +--- a/sysdeps/linux-gnu/x86/trace.c ++++ b/sysdeps/linux-gnu/x86/trace.c +@@ -55,7 +55,7 @@ static const int x86_64 = 0; + #endif + + void +-get_arch_dep(struct Process *proc) ++get_arch_dep(struct process *proc) + { + /* Unfortunately there are still remnants of mask_32bit uses + * around. */ +@@ -75,7 +75,7 @@ get_arch_dep(struct Process *proc) + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ + int +-syscall_p(struct Process *proc, int status, int *sysnum) ++syscall_p(struct process *proc, int status, int *sysnum) + { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { +@@ -109,7 +109,7 @@ syscall_p(struct Process *proc, int status, int *sysnum) + } + + size_t +-arch_type_sizeof(struct Process *proc, struct arg_type_info *info) ++arch_type_sizeof(struct process *proc, struct arg_type_info *info) + { + if (proc == NULL) + return (size_t)-2; +@@ -151,7 +151,7 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info *info) + } + + size_t +-arch_type_alignof(struct Process *proc, struct arg_type_info *info) ++arch_type_alignof(struct process *proc, struct arg_type_info *info) + { + if (proc == NULL) + return (size_t)-2; +diff --git a/type.c b/type.c +index 3ce8563..d80550b 100644 +--- a/type.c ++++ b/type.c +@@ -123,7 +123,7 @@ type_struct_destroy(struct arg_type_info *info) + } + + static int +-layout_struct(struct Process *proc, struct arg_type_info *info, ++layout_struct(struct process *proc, struct arg_type_info *info, + size_t *sizep, size_t *alignmentp, size_t *offsetofp) + { + size_t sz = 0; +@@ -254,10 +254,10 @@ type_destroy(struct arg_type_info *info) + } + + #ifdef ARCH_HAVE_SIZEOF +-size_t arch_type_sizeof(struct Process *proc, struct arg_type_info * arg); ++size_t arch_type_sizeof(struct process *proc, struct arg_type_info *arg); + #else + size_t +-arch_type_sizeof(struct Process *proc, struct arg_type_info * arg) ++arch_type_sizeof(struct process *proc, struct arg_type_info *arg) + { + /* Use default value. */ + return (size_t)-2; +@@ -265,10 +265,10 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info * arg) + #endif + + #ifdef ARCH_HAVE_ALIGNOF +-size_t arch_type_alignof(struct Process *proc, struct arg_type_info * arg); ++size_t arch_type_alignof(struct process *proc, struct arg_type_info *arg); + #else + size_t +-arch_type_alignof(struct Process *proc, struct arg_type_info * arg) ++arch_type_alignof(struct process *proc, struct arg_type_info *arg) + { + /* Use default value. */ + return (size_t)-2; +@@ -289,7 +289,7 @@ align(size_t sz, size_t alignment) + } + + size_t +-type_sizeof(struct Process *proc, struct arg_type_info *type) ++type_sizeof(struct process *proc, struct arg_type_info *type) + { + size_t arch_size = arch_type_sizeof(proc, type); + if (arch_size != (size_t)-2) +@@ -359,7 +359,7 @@ type_sizeof(struct Process *proc, struct arg_type_info *type) + #define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st)) + + size_t +-type_alignof(struct Process *proc, struct arg_type_info *type) ++type_alignof(struct process *proc, struct arg_type_info *type) + { + size_t arch_alignment = arch_type_alignof(proc, type); + if (arch_alignment != (size_t)-2) +@@ -412,7 +412,7 @@ type_alignof(struct Process *proc, struct arg_type_info *type) + } + + size_t +-type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt) ++type_offsetof(struct process *proc, struct arg_type_info *type, size_t emt) + { + assert(type->type == ARGTYPE_STRUCT + || type->type == ARGTYPE_ARRAY); +diff --git a/type.h b/type.h +index e8dec71..b92c1af 100644 +--- a/type.h ++++ b/type.h +@@ -111,11 +111,11 @@ void type_init_pointer(struct arg_type_info *info, + void type_destroy(struct arg_type_info *info); + + /* Compute a size of given type. Return (size_t)-1 for error. */ +-size_t type_sizeof(struct Process *proc, struct arg_type_info *type); ++size_t type_sizeof(struct process *proc, struct arg_type_info *type); + + /* Compute an alignment necessary for elements of this type. Return + * (size_t)-1 for error. */ +-size_t type_alignof(struct Process *proc, struct arg_type_info *type); ++size_t type_alignof(struct process *proc, struct arg_type_info *type); + + /* Align value SZ to ALIGNMENT and return the result. */ + size_t align(size_t sz, size_t alignment); +@@ -126,7 +126,7 @@ struct arg_type_info *type_element(struct arg_type_info *type, size_t elt); + + /* Compute an offset of EMT-th element of type TYPE. This works for + * arrays and structures. Return (size_t)-1 for error. */ +-size_t type_offsetof(struct Process *proc, ++size_t type_offsetof(struct process *proc, + struct arg_type_info *type, size_t elt); + + /* Whether TYPE is an integral type as defined by the C standard. */ +diff --git a/value.c b/value.c +index f7950da..d18db17 100644 +--- a/value.c ++++ b/value.c +@@ -29,7 +29,7 @@ + #include "backend.h" + + static void +-value_common_init(struct value *valp, struct Process *inferior, ++value_common_init(struct value *valp, struct process *inferior, + struct value *parent, struct arg_type_info *type, + int own_type) + { +@@ -43,7 +43,7 @@ value_common_init(struct value *valp, struct Process *inferior, + } + + void +-value_init(struct value *valp, struct Process *inferior, struct value *parent, ++value_init(struct value *valp, struct process *inferior, struct value *parent, + struct arg_type_info *type, int own_type) + { + assert(inferior != NULL); +diff --git a/value.h b/value.h +index 795573c..f501254 100644 +--- a/value.h ++++ b/value.h +@@ -46,7 +46,7 @@ enum value_location_t { + + struct value { + struct arg_type_info *type; +- struct Process *inferior; ++ struct process *inferior; + struct value *parent; + size_t size; + union { +@@ -63,7 +63,7 @@ struct value { + * value, in case of compound types. It may be NULL. TYPE is a type + * of the value. It may be NULL if the type is not yet known. If + * OWN_TYPE, the passed-in type is owned and released by value. */ +-void value_init(struct value *value, struct Process *inferior, ++void value_init(struct value *value, struct process *inferior, + struct value *parent, struct arg_type_info *type, + int own_type); + +diff --git a/zero.c b/zero.c +index bc119ee..5757943 100644 +--- a/zero.c ++++ b/zero.c +@@ -18,7 +18,6 @@ + * 02110-1301 USA + */ + +-#include + #include + + #include "zero.h" +@@ -93,13 +92,12 @@ build_zero_w_arg(struct expr_node *expr, int own) + struct expr_node * + expr_node_zero(void) + { +- static struct expr_node *node = NULL; +- if (node == NULL) { +- node = malloc(sizeof(*node)); +- if (node == NULL) +- error(1, errno, "malloc expr_node_zero"); +- expr_init_cb1(node, &zero1_callback, ++ static struct expr_node *nodep = NULL; ++ if (nodep == NULL) { ++ static struct expr_node node; ++ expr_init_cb1(&node, &zero1_callback, + expr_self(), 0, (void *)-1); ++ nodep = &node; + } +- return node; ++ return nodep; + } diff --git a/ltrace-0.7.2-e_machine.patch b/ltrace-0.7.2-e_machine.patch new file mode 100644 index 0000000..2cfc15a --- /dev/null +++ b/ltrace-0.7.2-e_machine.patch @@ -0,0 +1,20 @@ +diff -up ltrace-0.7.2/proc.c\~ ltrace-0.7.2/proc.c +--- ltrace-0.7.2/proc.c~ 2014-02-13 12:16:33.000000000 +0100 ++++ ltrace-0.7.2/proc.c 2014-02-13 15:44:25.000000000 +0100 +@@ -194,9 +197,11 @@ process_init(struct process *proc, const + goto fail; + } + +- if (proc->leader != proc) +- return 0; +- if (process_init_main(proc) < 0) { ++ if (proc->leader != proc) { ++ proc->e_machine = proc->leader->e_machine; ++ proc->e_class = proc->leader->e_class; ++ get_arch_dep(proc); ++ } else if (process_init_main(proc) < 0) { + process_bare_destroy(proc, 0); + goto fail; + } + +Diff finished. Thu Feb 13 15:50:21 2014 diff --git a/ltrace-0.7.91-XDG_CONFIG_DIRS.patch b/ltrace-0.7.91-XDG_CONFIG_DIRS.patch new file mode 100644 index 0000000..27638e6 --- /dev/null +++ b/ltrace-0.7.91-XDG_CONFIG_DIRS.patch @@ -0,0 +1,151 @@ +diff -rupN a/options.c b/options.c +--- a/options.c 2019-06-28 17:15:31.515626363 -0400 ++++ b/options.c 2019-06-28 17:18:59.490632337 -0400 +@@ -440,7 +440,8 @@ parse_int(const char *optarg, char opt, + } + + int +-parse_colon_separated_list(const char *paths, struct vect *vec) ++parse_colon_separated_list(const char *paths, struct vect *vec, ++ enum opt_F_origin origin) + { + /* PATHS contains a colon-separated list of directories and + * files to load. It's modeled after shell PATH variable, +@@ -467,6 +468,7 @@ parse_colon_separated_list(const char *p + struct opt_F_t arg = { + .pathname = tok, + .own_pathname = tok == clone, ++ .origin = origin, + }; + if (VECT_PUSHBACK(vec, &arg) < 0) + /* Presumably this is not a deal-breaker. */ +@@ -494,16 +496,18 @@ opt_F_get_kind(struct opt_F_t *entry) + if (entry->kind == OPT_F_UNKNOWN) { + struct stat st; + if (lstat(entry->pathname, &st) < 0) { +- fprintf(stderr, "Couldn't stat %s: %s\n", +- entry->pathname, strerror(errno)); ++ if (entry->origin == OPT_F_CMDLINE) ++ fprintf(stderr, "Couldn't stat %s: %s\n", ++ entry->pathname, strerror(errno)); + entry->kind = OPT_F_BROKEN; + } else if (S_ISDIR(st.st_mode)) { + entry->kind = OPT_F_DIR; + } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + entry->kind = OPT_F_FILE; + } else { +- fprintf(stderr, "%s is neither a regular file, " +- "nor a directory.\n", entry->pathname); ++ if (entry->origin == OPT_F_CMDLINE) ++ fprintf(stderr, "%s is neither a regular file, " ++ "nor a directory.\n", entry->pathname); + entry->kind = OPT_F_BROKEN; + } + } +@@ -607,7 +611,8 @@ process_options(int argc, char **argv) + options.follow = 1; + break; + case 'F': +- parse_colon_separated_list(optarg, &opt_F); ++ parse_colon_separated_list(optarg, &opt_F, ++ OPT_F_CMDLINE); + break; + case 'h': + usage(); +diff -rupN a/options.h b/options.h +--- a/options.h 2019-06-28 17:15:31.515626363 -0400 ++++ b/options.h 2019-06-28 17:18:55.984632238 -0400 +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012, 2013, 2015 Petr Machata, Red Hat Inc. + * Copyright (C) 2009,2010 Joe Damato + * Copyright (C) 1998,2002,2008 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -77,10 +77,16 @@ enum opt_F_kind { + OPT_F_DIR, + }; + ++enum opt_F_origin { ++ OPT_F_CMDLINE = 0, ++ OPT_F_ENVIRON, ++}; ++ + struct opt_F_t { + char *pathname; + int own_pathname : 1; + enum opt_F_kind kind : 2; ++ enum opt_F_origin origin : 1; + }; + + /* If entry->kind is OPT_F_UNKNOWN, figure out whether it should be +@@ -98,7 +104,8 @@ void opt_F_destroy(struct opt_F_t *entry + * The list is split and added to VEC, which shall be a vector + * initialized like VECT_INIT(VEC, struct opt_F_t); Returns 0 on + * success or a negative value on failure. */ +-int parse_colon_separated_list(const char *paths, struct vect *vec); ++int parse_colon_separated_list(const char *paths, struct vect *vec, ++ enum opt_F_origin origin); + + /* Vector of struct opt_F_t. */ + extern struct vect opt_F; +diff -rupN a/sysdeps/linux-gnu/hooks.c b/sysdeps/linux-gnu/hooks.c +--- a/sysdeps/linux-gnu/hooks.c 2013-11-04 20:08:03.000000000 -0500 ++++ b/sysdeps/linux-gnu/hooks.c 2019-06-28 17:18:55.989632238 -0400 +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012, 2013 Petr Machata ++ * Copyright (C) 2012, 2013, 2015 Petr Machata + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as +@@ -153,7 +153,7 @@ again: + if (xdg_sys != NULL) { + struct vect v; + VECT_INIT(&v, struct opt_F_t); +- if (parse_colon_separated_list(xdg_sys, &v) < 0 ++ if (parse_colon_separated_list(xdg_sys, &v, OPT_F_ENVIRON) < 0 + || VECT_EACH(&v, struct opt_F_t, NULL, + add_dir_component_cb, &dirs) != NULL) + fprintf(stderr, +diff -rupN a/testsuite/ltrace.main/XDG_CONFIG_DIRS.exp b/testsuite/ltrace.main/XDG_CONFIG_DIRS.exp +--- a/testsuite/ltrace.main/XDG_CONFIG_DIRS.exp 1969-12-31 19:00:00.000000000 -0500 ++++ b/testsuite/ltrace.main/XDG_CONFIG_DIRS.exp 2019-06-28 17:18:55.989632238 -0400 +@@ -0,0 +1,35 @@ ++# This file is part of ltrace. ++# Copyright (C) 2015 Petr Machata, Red Hat Inc. ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License as ++# published by the Free Software Foundation; either version 2 of the ++# License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++# 02110-1301 USA ++ ++set bin [ltraceCompile {} [ltraceSource c { ++ int main() { return 0; } ++}]] ++ ++setenv XDG_CONFIG_DIRS "blah" ++ltraceRun -L -- $bin ++unsetenv XDG_CONFIG_DIRS ++ ++if {[catch "exec $LTRACE -L -F blah -- $bin" output]} { ++ ltraceMatch [ltraceSource ltrace "$output"] { ++ {blah == 1} ++ } ++} else { ++ fail "expected error message regarding `blah`" ++} ++ ++ltraceDone diff --git a/ltrace-0.7.91-aarch64-headers.patch b/ltrace-0.7.91-aarch64-headers.patch new file mode 100644 index 0000000..597fa0a --- /dev/null +++ b/ltrace-0.7.91-aarch64-headers.patch @@ -0,0 +1,24 @@ +Index: ltrace-0.7.91/sysdeps/linux-gnu/aarch64/regs.c +=================================================================== +--- ltrace-0.7.91.orig/sysdeps/linux-gnu/aarch64/regs.c ++++ ltrace-0.7.91/sysdeps/linux-gnu/aarch64/regs.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #include "backend.h" + #include "proc.h" +Index: ltrace-0.7.91/sysdeps/linux-gnu/aarch64/trace.c +=================================================================== +--- ltrace-0.7.91.orig/sysdeps/linux-gnu/aarch64/trace.c ++++ ltrace-0.7.91/sysdeps/linux-gnu/aarch64/trace.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + #include "backend.h" + #include "proc.h" diff --git a/ltrace-0.7.91-aarch64-params.patch b/ltrace-0.7.91-aarch64-params.patch new file mode 100644 index 0000000..29767c5 --- /dev/null +++ b/ltrace-0.7.91-aarch64-params.patch @@ -0,0 +1,37 @@ +diff -rup a/sysdeps/linux-gnu/aarch64/fetch.c b/sysdeps/linux-gnu/aarch64/fetch.c +--- a/sysdeps/linux-gnu/aarch64/fetch.c 2018-07-05 16:06:10.066626252 -0400 ++++ b/sysdeps/linux-gnu/aarch64/fetch.c 2018-07-05 16:17:17.659748481 -0400 +@@ -308,12 +308,9 @@ arch_fetch_arg_init(enum tof type, struc + struct fetch_script how = pass_arg(context, proc, ret_info); + if (how.c == CVT_ERR) + goto fail; +- if (how.c == CVT_NOP && how.f == FETCH_STACK) { ++ if (how.c == CVT_BYREF && how.f == FETCH_GPR) { + /* XXX double cast. */ + context->x8 = (arch_addr_t) (uintptr_t) context->gregs.regs[8]; +- /* See the comment above about the assert. */ +- assert(! "Unexpected: first argument passed on stack."); +- abort(); + } + + return context; +diff -rup a/testsuite/ltrace.main/system_call_params.exp b/testsuite/ltrace.main/system_call_params.exp +--- a/testsuite/ltrace.main/system_call_params.exp 2018-07-05 16:06:10.516624926 -0400 ++++ b/testsuite/ltrace.main/system_call_params.exp 2018-07-05 16:58:01.549830643 -0400 +@@ -61,13 +61,13 @@ set conf [ltraceNamedSource "$dir/syscal + # doesn't list readdir, that would be taken from somelib.conf with a + # wrong prototype. + +-ltraceMatch1 [ltraceRun -L -S -F $conf -- $bin] {^open@SYS\("/some/path"} == 0 ++ltraceMatch1 [ltraceRun -L -S -F $conf -- $bin] {^open@SYS\("/some/path", 0\)} == 0 + + # On the other hand, if -F somedir/ is given, we want to accept + # syscalls.conf found there. + + ltraceMatch [ltraceRun -L -S -F $dir -- $bin] { +- {{^open@SYS\("/some/path"} == 1} ++ {{^open@SYS\("/some/path", 0\)} == 1} + {{^write@SYS\(1, "something", 10\)} == 1} + {{^mount@SYS\("source", "target", "filesystemtype"} == 1} + } +Only in b/testsuite/ltrace.main: system_call_params.exp~ diff --git a/ltrace-0.7.91-aarch64.patch b/ltrace-0.7.91-aarch64.patch new file mode 100644 index 0000000..123cfe1 --- /dev/null +++ b/ltrace-0.7.91-aarch64.patch @@ -0,0 +1,2416 @@ +diff --git a/README b/README +index 414bdfb..a04b767 100644 +--- a/README ++++ b/README +@@ -24,6 +24,7 @@ The following targets are currently (at least somewhat) supported. + Some of them may be more or less broken in reality, it is not feasible + to test each release comprehensively on each target. + ++ aarch64-*-linux-gnu + armv6l-*-linux-gnueabi + armv7l-*-linux-gnueabihf + i[4567]86-*-linux-gnu +@@ -81,7 +82,7 @@ quick one-liner), it is advisable to send an e-mail beforehand. + + + ------------------------------------------------------------------------------- +-Copyright (C) 2012,2013 Petr Machata ++Copyright (C) 2012-2014 Petr Machata + Copyright (C) 1997-2009 Juan Cespedes + This file is part of ltrace. + +diff --git a/configure.ac b/configure.ac +index c6e6bf0..0e9a124 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1,6 +1,6 @@ + # -*- Autoconf -*- + # This file is part of ltrace. +-# Copyright (C) 2010,2012,2013 Petr Machata, Red Hat Inc. ++# Copyright (C) 2010,2012,2013,2014 Petr Machata, Red Hat Inc. + # Copyright (C) 2010,2011 Joe Damato + # Copyright (C) 2010 Marc Kleine-Budde + # Copyright (C) 2010 Zachary T Welch +@@ -399,6 +399,7 @@ AC_CONFIG_FILES([ + Makefile + sysdeps/Makefile + sysdeps/linux-gnu/Makefile ++ sysdeps/linux-gnu/aarch64/Makefile + sysdeps/linux-gnu/alpha/Makefile + sysdeps/linux-gnu/arm/Makefile + sysdeps/linux-gnu/cris/Makefile +diff --git a/etc/libc.so.conf b/etc/libc.so.conf +index 669c50a..48b3b01 100644 +--- a/etc/libc.so.conf ++++ b/etc/libc.so.conf +@@ -1,5 +1,12 @@ + # See ltrace.conf(5) for description of syntax of this file. + ++# XXX ltrace misses long double and long long support ++typedef ldouble = double; ++typedef llong = long; ++typedef ullong = ulong; ++ ++void __libc_start_main(hide(void*), hide(int), array(string, arg2)); ++ + # arpa/inet.h + typedef in_addr = struct(hex(uint)); + int inet_aton(string, +in_addr*); +@@ -233,9 +240,22 @@ void setbuffer(file,addr,ulong); + void setlinebuf(file); + int rename(string,string); + ++# xlocale.h ++typedef locale_t = void*; ++locale_t newlocale(hex(int), string, locale_t); ++ + # stdlib.h +-long __strtol_internal(string,addr,int); +-ulong __strtoul_internal(string,addr,int); ++long __strtol_internal(string, +string*, int); ++ulong __strtoul_internal(string, +string*, int); ++ ++double strtod(string, +string*); ++float strtof(string, +string*); ++ldouble strtold(string, +string*); ++ ++double strtod_l(string, +string*, locale_t); ++float strtof_l(string, +string*, locale_t); ++ldouble strtold_l(string, +string*, locale_t); ++ + int atexit(addr); + addr bsearch(string, addr, ulong, ulong, addr); + addr calloc(ulong, ulong); +@@ -343,6 +363,10 @@ addr gmtime(addr); + addr localtime(addr); + ulong strftime(+string2,ulong,string,addr); + long time(addr); ++# XXX in fact (time_t, long), which may be (llong, long) on 32-bit ++# arches. We don't have llong as of this writing. ++typedef timespec = struct(long, long); ++int nanosleep(timespec*, timespec*); + + # unistd.h + void _exit(int); +diff --git a/etc/syscalls.conf b/etc/syscalls.conf +index 562f4e7..09d347d 100644 +--- a/etc/syscalls.conf ++++ b/etc/syscalls.conf +@@ -1,6 +1,10 @@ + # syscall.conf -- system call prototypes + # See ltrace.conf(5) for description of syntax of this file. + ++# Special value used to indicate the *at functions should use the ++# current working directory. ++typedef at_dirfd_t = enum[int](AT_FDCWD=-100); ++ + addr brk(addr); + int close(int); + int execve(string,addr,addr); +@@ -14,7 +18,7 @@ int getpid(); + addr mmap(addr,ulong,int,int,int,long); + + int munmap(addr,ulong); +-int open(string,int,octal); ++int open(string, hex(uint), oct(uint)); + int personality(uint); + long read(int,+string0,ulong); + int stat(string,addr); +@@ -113,3 +117,30 @@ ulong writev(int,addr,int); + int mprotect(addr,int,int); + int access(string,octal); + int getdents(uint, void *, uint); ++ ++int openat(at_dirfd_t, string, hex(uint), oct(uint)); ++int mknodat(at_dirfd_t, string, oct(uint), ushort) ++int mkdirat(at_dirfd_t, string, oct(uint)); ++int unlinkat(at_dirfd_t, string, hex(uint)); ++int symlinkat(string, at_dirfd_t, string); ++int linkat(at_dirfd_t, string, at_dirfd_t, string, hex(uint)); ++int renameat(at_dirfd_t, string, at_dirfd_t, string); ++int faccessat(at_dirfd_t, string, oct(uint), hex(uint)); ++int fchmodat(at_dirfd_t, string, oct(uint), hex(uint)); ++int fchownat(at_dirfd_t, string, int, int, hex(uint)); ++int readlinkat(at_dirfd_t, string, +string[arg4], ulong); ++int fstatat(at_dirfd_t, string, addr, hex(uint)); ++int utimensat(at_dirfd_t, string, addr, hex(uint)); ++int futimens(int, addr); ++int futimesat(at_dirfd_t, string, addr); ++addr shmat(int, addr, hex(uint)); ++int shmdt(addr); ++ ++typedef fid_type = enum(FILEID_ROOT=0, FILEID_INO32_GEN=1, FILEID_INO32_GEN_PARENT=2, FILEID_BTRFS_WITHOUT_PARENT=0x4d, FILEID_BTRFS_WITH_PARENT=0x4e, FILEID_BTRFS_WITH_PARENT_ROOT=0x4f, FILEID_UDF_WITHOUT_PARENT=0x51, FILEID_UDF_WITH_PARENT=0x52, FILEID_NILFS_WITHOUT_PARENT=0x61, FILEID_NILFS_WITH_PARENT=0x62); ++typedef file_handle = struct(uint, fid_type, array(hex(char), elt1)*); ++int name_to_handle_at(at_dirfd_t, string, file_handle, int*, hex(uint)); ++int open_by_handle_at(at_dirfd_t, file_handle, hex(uint)); ++ ++int newfstatat(at_dirfd_t, string, addr, hex(uint)); ++int creat(string, oct(int)); ++int ustat(ushort, addr); +diff --git a/sysdeps/linux-gnu/Makefile.am b/sysdeps/linux-gnu/Makefile.am +index ecee577..ec26162 100644 +--- a/sysdeps/linux-gnu/Makefile.am ++++ b/sysdeps/linux-gnu/Makefile.am +@@ -1,4 +1,5 @@ + # This file is part of ltrace. ++# Copyright (C) 2014 Petr Machata, Red Hat, Inc. + # Copyright (C) 2010,2012 Marc Kleine-Budde, Pengutronix + # + # This program is free software; you can redistribute it and/or +@@ -16,7 +17,8 @@ + # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + # 02110-1301 USA + +-DIST_SUBDIRS = alpha arm cris ia64 m68k metag mips ppc s390 sparc x86 ++DIST_SUBDIRS = aarch64 alpha arm cris ia64 m68k metag mips ppc s390 \ ++ sparc x86 + + SUBDIRS = \ + $(HOST_CPU) +diff --git a/sysdeps/linux-gnu/aarch64/Makefile.am b/sysdeps/linux-gnu/aarch64/Makefile.am +new file mode 100644 +index 0000000..0af4e6e +--- /dev/null ++++ b/sysdeps/linux-gnu/aarch64/Makefile.am +@@ -0,0 +1,25 @@ ++# This file is part of ltrace. ++# Copyright (C) 2014 Petr Machata, Red Hat, Inc. ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License as ++# published by the Free Software Foundation; either version 2 of the ++# License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++# 02110-1301 USA ++ ++noinst_LTLIBRARIES = ../libcpu.la ++ ++___libcpu_la_SOURCES = fetch.c plt.c regs.c trace.c ++ ++noinst_HEADERS = arch.h ptrace.h signalent.h syscallent.h ++ ++MAINTAINERCLEANFILES = Makefile.in +diff --git a/sysdeps/linux-gnu/aarch64/arch.h b/sysdeps/linux-gnu/aarch64/arch.h +new file mode 100644 +index 0000000..4137613 +--- /dev/null ++++ b/sysdeps/linux-gnu/aarch64/arch.h +@@ -0,0 +1,37 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2014 Petr Machata, Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++#ifndef LTRACE_AARCH64_ARCH_H ++#define LTRACE_AARCH64_ARCH_H ++ ++/* | 31 21 | 20 5 | 4 0 | * ++ * | 1 1 0 1 0 1 0 0 0 0 1 | imm16 | 0 0 0 0 0 | */ ++#define BREAKPOINT_VALUE { 0xd4, 0x20, 0, 0 } ++#define BREAKPOINT_LENGTH 4 ++#define DECR_PC_AFTER_BREAK 0 ++ ++#define LT_ELFCLASS ELFCLASS64 ++#define LT_ELF_MACHINE EM_AARCH64 ++ ++#define ARCH_HAVE_FETCH_ARG ++#define ARCH_ENDIAN_BIG ++#define ARCH_HAVE_SIZEOF ++#define ARCH_HAVE_ALIGNOF ++ ++#endif /* LTRACE_AARCH64_ARCH_H */ +diff --git a/sysdeps/linux-gnu/aarch64/fetch.c b/sysdeps/linux-gnu/aarch64/fetch.c +new file mode 100644 +index 0000000..8779f03 +--- /dev/null ++++ b/sysdeps/linux-gnu/aarch64/fetch.c +@@ -0,0 +1,365 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2014 Petr Machata, Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fetch.h" ++#include "proc.h" ++#include "type.h" ++#include "value.h" ++ ++int aarch64_read_gregs(struct process *proc, struct user_pt_regs *regs); ++int aarch64_read_fregs(struct process *proc, struct user_fpsimd_state *regs); ++ ++ ++struct fetch_context ++{ ++ struct user_pt_regs gregs; ++ struct user_fpsimd_state fpregs; ++ arch_addr_t nsaa; ++ unsigned ngrn; ++ unsigned nsrn; ++ arch_addr_t x8; ++}; ++ ++static int ++context_init(struct fetch_context *context, struct process *proc) ++{ ++ if (aarch64_read_gregs(proc, &context->gregs) < 0 ++ || aarch64_read_fregs(proc, &context->fpregs) < 0) ++ return -1; ++ ++ context->ngrn = 0; ++ context->nsrn = 0; ++ /* XXX double cast */ ++ context->nsaa = (arch_addr_t) (uintptr_t) context->gregs.sp; ++ context->x8 = 0; ++ ++ return 0; ++} ++ ++struct fetch_context * ++arch_fetch_arg_clone(struct process *proc, struct fetch_context *context) ++{ ++ struct fetch_context *ret = malloc(sizeof(*ret)); ++ if (ret == NULL) ++ return NULL; ++ return memcpy(ret, context, sizeof(*ret)); ++} ++ ++static void ++fetch_next_gpr(struct fetch_context *context, unsigned char *buf) ++{ ++ uint64_t u = context->gregs.regs[context->ngrn++]; ++ memcpy(buf, &u, 8); ++} ++ ++static int ++fetch_gpr(struct fetch_context *context, struct value *value, size_t sz) ++{ ++ if (sz < 8) ++ sz = 8; ++ ++ unsigned char *buf = value_reserve(value, sz); ++ if (buf == NULL) ++ return -1; ++ ++ size_t i; ++ for (i = 0; i < sz; i += 8) ++ fetch_next_gpr(context, buf + i); ++ ++ return 0; ++} ++ ++static void ++fetch_next_sse(struct fetch_context *context, unsigned char *buf, size_t sz) ++{ ++ __int128 u = context->fpregs.vregs[context->nsrn++]; ++ memcpy(buf, &u, sz); ++} ++ ++static int ++fetch_sse(struct fetch_context *context, struct value *value, size_t sz) ++{ ++ unsigned char *buf = value_reserve(value, sz); ++ if (buf == NULL) ++ return -1; ++ ++ fetch_next_sse(context, buf, sz); ++ return 0; ++} ++ ++static int ++fetch_hfa(struct fetch_context *context, ++ struct value *value, struct arg_type_info *hfa_t, size_t count) ++{ ++ size_t sz = type_sizeof(value->inferior, hfa_t); ++ unsigned char *buf = value_reserve(value, sz * count); ++ if (buf == NULL) ++ return -1; ++ ++ size_t i; ++ for (i = 0; i < count; ++i) { ++ fetch_next_sse(context, buf, sz); ++ buf += sz; ++ } ++ return 0; ++} ++ ++static int ++fetch_stack(struct fetch_context *context, struct value *value, ++ size_t align, size_t sz) ++{ ++ if (align < 8) ++ align = 8; ++ size_t amount = ((sz + align - 1) / align) * align; ++ ++ /* XXX double casts */ ++ uintptr_t sp = (uintptr_t) context->nsaa; ++ sp = ((sp + align - 1) / align) * align; ++ ++ value_in_inferior(value, (arch_addr_t) sp); ++ ++ sp += amount; ++ context->nsaa = (arch_addr_t) sp; ++ ++ return 0; ++} ++ ++enum convert_method { ++ CVT_ERR = -1, ++ CVT_NOP = 0, ++ CVT_BYREF, ++}; ++ ++enum fetch_method { ++ FETCH_NOP, ++ FETCH_STACK, ++ FETCH_GPR, ++ FETCH_SSE, ++ FETCH_HFA, ++}; ++ ++struct fetch_script { ++ enum convert_method c; ++ enum fetch_method f; ++ size_t sz; ++ struct arg_type_info *hfa_t; ++ size_t count; ++}; ++ ++static struct fetch_script ++pass_arg(struct fetch_context const *context, ++ struct process *proc, struct arg_type_info *info) ++{ ++ enum fetch_method cvt = CVT_NOP; ++ ++ size_t sz = type_sizeof(proc, info); ++ if (sz == (size_t) -1) ++ return (struct fetch_script) { CVT_ERR, FETCH_NOP, sz }; ++ ++ switch (info->type) { ++ case ARGTYPE_VOID: ++ return (struct fetch_script) { cvt, FETCH_NOP, sz }; ++ ++ case ARGTYPE_STRUCT: ++ case ARGTYPE_ARRAY:; ++ size_t count; ++ struct arg_type_info *hfa_t = type_get_hfa_type(info, &count); ++ if (hfa_t != NULL && count <= 4) { ++ if (context->nsrn + count <= 8) ++ return (struct fetch_script) ++ { cvt, FETCH_HFA, sz, hfa_t, count }; ++ return (struct fetch_script) ++ { cvt, FETCH_STACK, sz, hfa_t, count }; ++ } ++ ++ if (sz <= 16) { ++ size_t count = sz / 8; ++ if (context->ngrn + count <= 8) ++ return (struct fetch_script) ++ { cvt, FETCH_GPR, sz }; ++ } ++ ++ cvt = CVT_BYREF; ++ sz = 8; ++ /* Fall through. */ ++ ++ case ARGTYPE_POINTER: ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ if (context->ngrn < 8 && sz <= 8) ++ return (struct fetch_script) { cvt, FETCH_GPR, sz }; ++ /* We don't support types wider than 8 bytes as of ++ * now. */ ++ assert(sz <= 8); ++ ++ return (struct fetch_script) { cvt, FETCH_STACK, sz }; ++ ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ if (context->nsrn < 8) { ++ /* ltrace doesn't support float128. */ ++ assert(sz <= 8); ++ return (struct fetch_script) { cvt, FETCH_SSE, sz }; ++ } ++ ++ return (struct fetch_script) { cvt, FETCH_STACK, sz }; ++ } ++ ++ assert(! "Failed to allocate argument."); ++ abort(); ++} ++ ++static int ++convert_arg(struct value *value, struct fetch_script how) ++{ ++ switch (how.c) { ++ case CVT_NOP: ++ return 0; ++ case CVT_BYREF: ++ return value_pass_by_reference(value); ++ case CVT_ERR: ++ return -1; ++ } ++ ++ assert(! "Don't know how to convert argument."); ++ abort(); ++} ++ ++static int ++fetch_arg(struct fetch_context *context, ++ struct process *proc, struct arg_type_info *info, ++ struct value *value, struct fetch_script how) ++{ ++ if (convert_arg(value, how) < 0) ++ return -1; ++ ++ switch (how.f) { ++ case FETCH_NOP: ++ return 0; ++ ++ case FETCH_STACK: ++ if (how.hfa_t != NULL && how.count != 0 && how.count <= 8) ++ context->nsrn = 8; ++ return fetch_stack(context, value, ++ type_alignof(proc, info), how.sz); ++ ++ case FETCH_GPR: ++ return fetch_gpr(context, value, how.sz); ++ ++ case FETCH_SSE: ++ return fetch_sse(context, value, how.sz); ++ ++ case FETCH_HFA: ++ return fetch_hfa(context, value, how.hfa_t, how.count); ++ } ++ ++ assert(! "Don't know how to fetch argument."); ++ abort(); ++} ++ ++struct fetch_context * ++arch_fetch_arg_init(enum tof type, struct process *proc, ++ struct arg_type_info *ret_info) ++{ ++ struct fetch_context *context = malloc(sizeof *context); ++ if (context == NULL || context_init(context, proc) < 0) { ++ fail: ++ free(context); ++ return NULL; ++ } ++ ++ /* There's a provision in ARMv8 parameter passing convention ++ * for returning types that, if passed as first argument to a ++ * function, would be passed on stack. For those types, x8 ++ * contains an address where the return argument should be ++ * placed. The callee doesn't need to preserve the value of ++ * x8, so we need to fetch it now. ++ * ++ * To my knowledge, there are currently no types where this ++ * holds, but the code is here, utterly untested. */ ++ ++ struct fetch_script how = pass_arg(context, proc, ret_info); ++ if (how.c == CVT_ERR) ++ goto fail; ++ if (how.c == CVT_NOP && how.f == FETCH_STACK) { ++ /* XXX double cast. */ ++ context->x8 = (arch_addr_t) (uintptr_t) context->gregs.regs[8]; ++ /* See the comment above about the assert. */ ++ assert(! "Unexpected: first argument passed on stack."); ++ abort(); ++ } ++ ++ return context; ++} ++ ++int ++arch_fetch_arg_next(struct fetch_context *context, enum tof type, ++ struct process *proc, struct arg_type_info *info, ++ struct value *value) ++{ ++ return fetch_arg(context, proc, info, value, ++ pass_arg(context, proc, info)); ++} ++ ++int ++arch_fetch_retval(struct fetch_context *context, enum tof type, ++ struct process *proc, struct arg_type_info *info, ++ struct value *value) ++{ ++ if (context->x8 != 0) { ++ value_in_inferior(value, context->x8); ++ return 0; ++ } ++ ++ if (context_init(context, proc) < 0) ++ return -1; ++ ++ return fetch_arg(context, proc, info, value, ++ pass_arg(context, proc, info)); ++} ++ ++void ++arch_fetch_arg_done(struct fetch_context *context) ++{ ++ if (context != NULL) ++ free(context); ++} ++ ++size_t ++arch_type_sizeof(struct process *proc, struct arg_type_info *arg) ++{ ++ return (size_t) -2; ++} ++ ++size_t ++arch_type_alignof(struct process *proc, struct arg_type_info *arg) ++{ ++ return (size_t) -2; ++} +diff --git a/sysdeps/linux-gnu/aarch64/plt.c b/sysdeps/linux-gnu/aarch64/plt.c +new file mode 100644 +index 0000000..29dc4c9 +--- /dev/null ++++ b/sysdeps/linux-gnu/aarch64/plt.c +@@ -0,0 +1,38 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2014 Petr Machata, Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++ ++#include "backend.h" ++#include "proc.h" ++#include "library.h" ++#include "ltrace-elf.h" ++ ++arch_addr_t ++sym2addr(struct process *proc, struct library_symbol *sym) ++{ ++ return sym->enter_addr; ++} ++ ++GElf_Addr ++arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) ++{ ++ return lte->plt_addr + 32 + ndx * 16; ++} +diff --git a/sysdeps/linux-gnu/aarch64/ptrace.h b/sysdeps/linux-gnu/aarch64/ptrace.h +new file mode 100644 +index 0000000..283c314 +--- /dev/null ++++ b/sysdeps/linux-gnu/aarch64/ptrace.h +@@ -0,0 +1,22 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2014 Petr Machata, Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include +diff --git a/sysdeps/linux-gnu/aarch64/regs.c b/sysdeps/linux-gnu/aarch64/regs.c +new file mode 100644 +index 0000000..06eb72b +--- /dev/null ++++ b/sysdeps/linux-gnu/aarch64/regs.c +@@ -0,0 +1,130 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2014 Petr Machata, Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "backend.h" ++#include "proc.h" ++ ++#define PC_OFF (32 * 4) ++ ++int ++aarch64_read_gregs(struct process *proc, struct user_pt_regs *regs) ++{ ++ *regs = (struct user_pt_regs) {}; ++ struct iovec iovec; ++ iovec.iov_base = regs; ++ iovec.iov_len = sizeof *regs; ++ return ptrace(PTRACE_GETREGSET, proc->pid, NT_PRSTATUS, &iovec) < 0 ++ ? -1 : 0; ++} ++ ++int ++aarch64_write_gregs(struct process *proc, struct user_pt_regs *regs) ++{ ++ struct iovec iovec; ++ iovec.iov_base = regs; ++ iovec.iov_len = sizeof *regs; ++ return ptrace(PTRACE_SETREGSET, proc->pid, NT_PRSTATUS, &iovec) < 0 ++ ? -1 : 0; ++} ++ ++int ++aarch64_read_fregs(struct process *proc, struct user_fpsimd_state *regs) ++{ ++ *regs = (struct user_fpsimd_state) {}; ++ struct iovec iovec; ++ iovec.iov_base = regs; ++ iovec.iov_len = sizeof *regs; ++ return ptrace(PTRACE_GETREGSET, proc->pid, NT_FPREGSET, &iovec) < 0 ++ ? -1 : 0; ++} ++ ++arch_addr_t ++get_instruction_pointer(struct process *proc) ++{ ++ struct user_pt_regs regs; ++ if (aarch64_read_gregs(proc, ®s) < 0) { ++ fprintf(stderr, "get_instruction_pointer: " ++ "Couldn't read registers of %d.\n", proc->pid); ++ return 0; ++ } ++ ++ /* ++ char buf[128]; ++ sprintf(buf, "cat /proc/%d/maps", proc->pid); ++ system(buf); ++ */ ++ ++ /* XXX double cast */ ++ return (arch_addr_t) (uintptr_t) regs.pc; ++} ++ ++void ++set_instruction_pointer(struct process *proc, arch_addr_t addr) ++{ ++ struct user_pt_regs regs; ++ if (aarch64_read_gregs(proc, ®s) < 0) { ++ fprintf(stderr, "get_instruction_pointer: " ++ "Couldn't read registers of %d.\n", proc->pid); ++ return; ++ } ++ ++ /* XXX double cast */ ++ regs.pc = (uint64_t) (uintptr_t) addr; ++ ++ if (aarch64_write_gregs(proc, ®s) < 0) { ++ fprintf(stderr, "get_instruction_pointer: " ++ "Couldn't write registers of %d.\n", proc->pid); ++ return; ++ } ++} ++ ++arch_addr_t ++get_stack_pointer(struct process *proc) ++{ ++ struct user_pt_regs regs; ++ if (aarch64_read_gregs(proc, ®s) < 0) { ++ fprintf(stderr, "get_stack_pointer: " ++ "Couldn't read registers of %d.\n", proc->pid); ++ return 0; ++ } ++ ++ /* XXX double cast */ ++ return (arch_addr_t) (uintptr_t) regs.sp; ++} ++ ++arch_addr_t ++get_return_addr(struct process *proc, arch_addr_t stack_pointer) ++{ ++ struct user_pt_regs regs; ++ if (aarch64_read_gregs(proc, ®s) < 0) { ++ fprintf(stderr, "get_return_addr: " ++ "Couldn't read registers of %d.\n", proc->pid); ++ return 0; ++ } ++ ++ /* XXX double cast */ ++ return (arch_addr_t) (uintptr_t) regs.regs[30]; ++} +diff --git a/sysdeps/linux-gnu/aarch64/signalent.h b/sysdeps/linux-gnu/aarch64/signalent.h +new file mode 100644 +index 0000000..bf56ebc +--- /dev/null ++++ b/sysdeps/linux-gnu/aarch64/signalent.h +@@ -0,0 +1,52 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++ "SIG_0", /* 0 */ ++ "SIGHUP", /* 1 */ ++ "SIGINT", /* 2 */ ++ "SIGQUIT", /* 3 */ ++ "SIGILL", /* 4 */ ++ "SIGTRAP", /* 5 */ ++ "SIGABRT", /* 6 */ ++ "SIGBUS", /* 7 */ ++ "SIGFPE", /* 8 */ ++ "SIGKILL", /* 9 */ ++ "SIGUSR1", /* 10 */ ++ "SIGSEGV", /* 11 */ ++ "SIGUSR2", /* 12 */ ++ "SIGPIPE", /* 13 */ ++ "SIGALRM", /* 14 */ ++ "SIGTERM", /* 15 */ ++ "SIGSTKFLT", /* 16 */ ++ "SIGCHLD", /* 17 */ ++ "SIGCONT", /* 18 */ ++ "SIGSTOP", /* 19 */ ++ "SIGTSTP", /* 20 */ ++ "SIGTTIN", /* 21 */ ++ "SIGTTOU", /* 22 */ ++ "SIGURG", /* 23 */ ++ "SIGXCPU", /* 24 */ ++ "SIGXFSZ", /* 25 */ ++ "SIGVTALRM", /* 26 */ ++ "SIGPROF", /* 27 */ ++ "SIGWINCH", /* 28 */ ++ "SIGIO", /* 29 */ ++ "SIGPWR", /* 30 */ ++ "SIGSYS", /* 31 */ +diff --git a/sysdeps/linux-gnu/aarch64/syscallent.h b/sysdeps/linux-gnu/aarch64/syscallent.h +new file mode 100644 +index 0000000..aca8191 +--- /dev/null ++++ b/sysdeps/linux-gnu/aarch64/syscallent.h +@@ -0,0 +1,1100 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2014 Petr Machata, Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++ "io_setup", /* 0 */ ++ "io_destroy", /* 1 */ ++ "io_submit", /* 2 */ ++ "io_cancel", /* 3 */ ++ "io_getevents", /* 4 */ ++ "setxattr", /* 5 */ ++ "lsetxattr", /* 6 */ ++ "fsetxattr", /* 7 */ ++ "getxattr", /* 8 */ ++ "lgetxattr", /* 9 */ ++ "fgetxattr", /* 10 */ ++ "listxattr", /* 11 */ ++ "llistxattr", /* 12 */ ++ "flistxattr", /* 13 */ ++ "removexattr", /* 14 */ ++ "lremovexattr", /* 15 */ ++ "fremovexattr", /* 16 */ ++ "getcwd", /* 17 */ ++ "lookup_dcookie", /* 18 */ ++ "eventfd2", /* 19 */ ++ "epoll_create1", /* 20 */ ++ "epoll_ctl", /* 21 */ ++ "epoll_pwait", /* 22 */ ++ "dup", /* 23 */ ++ "dup3", /* 24 */ ++ "fcntl", /* 25 */ ++ "inotify_init1", /* 26 */ ++ "inotify_add_watch", /* 27 */ ++ "inotify_rm_watch", /* 28 */ ++ "ioctl", /* 29 */ ++ "ioprio_set", /* 30 */ ++ "ioprio_get", /* 31 */ ++ "flock", /* 32 */ ++ "mknodat", /* 33 */ ++ "mkdirat", /* 34 */ ++ "unlinkat", /* 35 */ ++ "symlinkat", /* 36 */ ++ "linkat", /* 37 */ ++ "renameat", /* 38 */ ++ "umount2", /* 39 */ ++ "mount", /* 40 */ ++ "pivot_root", /* 41 */ ++ "nfsservctl", /* 42 */ ++ "statfs", /* 43 */ ++ "fstatfs", /* 44 */ ++ "truncate", /* 45 */ ++ "ftruncate", /* 46 */ ++ "fallocate", /* 47 */ ++ "faccessat", /* 48 */ ++ "chdir", /* 49 */ ++ "fchdir", /* 50 */ ++ "chroot", /* 51 */ ++ "fchmod", /* 52 */ ++ "fchmodat", /* 53 */ ++ "fchownat", /* 54 */ ++ "fchown", /* 55 */ ++ "openat", /* 56 */ ++ "close", /* 57 */ ++ "vhangup", /* 58 */ ++ "pipe2", /* 59 */ ++ "quotactl", /* 60 */ ++ "getdents64", /* 61 */ ++ "lseek", /* 62 */ ++ "read", /* 63 */ ++ "write", /* 64 */ ++ "readv", /* 65 */ ++ "writev", /* 66 */ ++ "pread64", /* 67 */ ++ "pwrite64", /* 68 */ ++ "preadv", /* 69 */ ++ "pwritev", /* 70 */ ++ "sendfile", /* 71 */ ++ "pselect6", /* 72 */ ++ "ppoll", /* 73 */ ++ "signalfd4", /* 74 */ ++ "vmsplice", /* 75 */ ++ "splice", /* 76 */ ++ "tee", /* 77 */ ++ "readlinkat", /* 78 */ ++ "fstatat", /* 79 */ ++ "fstat", /* 80 */ ++ "sync", /* 81 */ ++ "fsync", /* 82 */ ++ "fdatasync", /* 83 */ ++ "sync_file_range", /* 84 */ ++ "timerfd_create", /* 85 */ ++ "timerfd_settime", /* 86 */ ++ "timerfd_gettime", /* 87 */ ++ "utimensat", /* 88 */ ++ "acct", /* 89 */ ++ "capget", /* 90 */ ++ "capset", /* 91 */ ++ "personality", /* 92 */ ++ "exit", /* 93 */ ++ "exit_group", /* 94 */ ++ "waitid", /* 95 */ ++ "set_tid_address", /* 96 */ ++ "unshare", /* 97 */ ++ "futex", /* 98 */ ++ "set_robust_list", /* 99 */ ++ "get_robust_list", /* 100 */ ++ "nanosleep", /* 101 */ ++ "getitimer", /* 102 */ ++ "setitimer", /* 103 */ ++ "kexec_load", /* 104 */ ++ "init_module", /* 105 */ ++ "delete_module", /* 106 */ ++ "timer_create", /* 107 */ ++ "timer_gettime", /* 108 */ ++ "timer_getoverrun", /* 109 */ ++ "timer_settime", /* 110 */ ++ "timer_delete", /* 111 */ ++ "clock_settime", /* 112 */ ++ "clock_gettime", /* 113 */ ++ "clock_getres", /* 114 */ ++ "clock_nanosleep", /* 115 */ ++ "syslog", /* 116 */ ++ "ptrace", /* 117 */ ++ "sched_setparam", /* 118 */ ++ "sched_setscheduler", /* 119 */ ++ "sched_getscheduler", /* 120 */ ++ "sched_getparam", /* 121 */ ++ "sched_setaffinity", /* 122 */ ++ "sched_getaffinity", /* 123 */ ++ "sched_yield", /* 124 */ ++ "sched_get_priority_max", /* 125 */ ++ "sched_get_priority_min", /* 126 */ ++ "sched_rr_get_interval", /* 127 */ ++ "restart_syscall", /* 128 */ ++ "kill", /* 129 */ ++ "tkill", /* 130 */ ++ "tgkill", /* 131 */ ++ "sigaltstack", /* 132 */ ++ "rt_sigsuspend", /* 133 */ ++ "rt_sigaction", /* 134 */ ++ "rt_sigprocmask", /* 135 */ ++ "rt_sigpending", /* 136 */ ++ "rt_sigtimedwait", /* 137 */ ++ "rt_sigqueueinfo", /* 138 */ ++ "rt_sigreturn", /* 139 */ ++ "setpriority", /* 140 */ ++ "getpriority", /* 141 */ ++ "reboot", /* 142 */ ++ "setregid", /* 143 */ ++ "setgid", /* 144 */ ++ "setreuid", /* 145 */ ++ "setuid", /* 146 */ ++ "setresuid", /* 147 */ ++ "getresuid", /* 148 */ ++ "setresgid", /* 149 */ ++ "getresgid", /* 150 */ ++ "setfsuid", /* 151 */ ++ "setfsgid", /* 152 */ ++ "times", /* 153 */ ++ "setpgid", /* 154 */ ++ "getpgid", /* 155 */ ++ "getsid", /* 156 */ ++ "setsid", /* 157 */ ++ "getgroups", /* 158 */ ++ "setgroups", /* 159 */ ++ "uname", /* 160 */ ++ "sethostname", /* 161 */ ++ "setdomainname", /* 162 */ ++ "getrlimit", /* 163 */ ++ "setrlimit", /* 164 */ ++ "getrusage", /* 165 */ ++ "umask", /* 166 */ ++ "prctl", /* 167 */ ++ "getcpu", /* 168 */ ++ "gettimeofday", /* 169 */ ++ "settimeofday", /* 170 */ ++ "adjtimex", /* 171 */ ++ "getpid", /* 172 */ ++ "getppid", /* 173 */ ++ "getuid", /* 174 */ ++ "geteuid", /* 175 */ ++ "getgid", /* 176 */ ++ "getegid", /* 177 */ ++ "gettid", /* 178 */ ++ "sysinfo", /* 179 */ ++ "mq_open", /* 180 */ ++ "mq_unlink", /* 181 */ ++ "mq_timedsend", /* 182 */ ++ "mq_timedreceive", /* 183 */ ++ "mq_notify", /* 184 */ ++ "mq_getsetattr", /* 185 */ ++ "msgget", /* 186 */ ++ "msgctl", /* 187 */ ++ "msgrcv", /* 188 */ ++ "msgsnd", /* 189 */ ++ "semget", /* 190 */ ++ "semctl", /* 191 */ ++ "semtimedop", /* 192 */ ++ "semop", /* 193 */ ++ "shmget", /* 194 */ ++ "shmctl", /* 195 */ ++ "shmat", /* 196 */ ++ "shmdt", /* 197 */ ++ "socket", /* 198 */ ++ "socketpair", /* 199 */ ++ "bind", /* 200 */ ++ "listen", /* 201 */ ++ "accept", /* 202 */ ++ "connect", /* 203 */ ++ "getsockname", /* 204 */ ++ "getpeername", /* 205 */ ++ "sendto", /* 206 */ ++ "recvfrom", /* 207 */ ++ "setsockopt", /* 208 */ ++ "getsockopt", /* 209 */ ++ "shutdown", /* 210 */ ++ "sendmsg", /* 211 */ ++ "recvmsg", /* 212 */ ++ "readahead", /* 213 */ ++ "brk", /* 214 */ ++ "munmap", /* 215 */ ++ "mremap", /* 216 */ ++ "add_key", /* 217 */ ++ "request_key", /* 218 */ ++ "keyctl", /* 219 */ ++ "clone", /* 220 */ ++ "execve", /* 221 */ ++ "mmap", /* 222 */ ++ "fadvise64", /* 223 */ ++ "swapon", /* 224 */ ++ "swapoff", /* 225 */ ++ "mprotect", /* 226 */ ++ "msync", /* 227 */ ++ "mlock", /* 228 */ ++ "munlock", /* 229 */ ++ "mlockall", /* 230 */ ++ "munlockall", /* 231 */ ++ "mincore", /* 232 */ ++ "madvise", /* 233 */ ++ "remap_file_pages", /* 234 */ ++ "mbind", /* 235 */ ++ "get_mempolicy", /* 236 */ ++ "set_mempolicy", /* 237 */ ++ "migrate_pages", /* 238 */ ++ "move_pages", /* 239 */ ++ "rt_tgsigqueueinfo", /* 240 */ ++ "perf_event_open", /* 241 */ ++ "accept4", /* 242 */ ++ "recvmmsg", /* 243 */ ++ "arch_specific_syscall", /* 244 */ ++ "245", /* 245 */ ++ "246", /* 246 */ ++ "247", /* 247 */ ++ "248", /* 248 */ ++ "249", /* 249 */ ++ "250", /* 250 */ ++ "251", /* 251 */ ++ "252", /* 252 */ ++ "253", /* 253 */ ++ "254", /* 254 */ ++ "255", /* 255 */ ++ "256", /* 256 */ ++ "257", /* 257 */ ++ "258", /* 258 */ ++ "259", /* 259 */ ++ "wait4", /* 260 */ ++ "prlimit64", /* 261 */ ++ "fanotify_init", /* 262 */ ++ "fanotify_mark", /* 263 */ ++ "name_to_handle_at", /* 264 */ ++ "open_by_handle_at", /* 265 */ ++ "clock_adjtime", /* 266 */ ++ "syncfs", /* 267 */ ++ "setns", /* 268 */ ++ "sendmmsg", /* 269 */ ++ "process_vm_readv", /* 270 */ ++ "process_vm_writev", /* 271 */ ++ "kcmp", /* 272 */ ++ "finit_module", /* 273 */ ++ "syscalls", /* 274 */ ++ "275", /* 275 */ ++ "276", /* 276 */ ++ "277", /* 277 */ ++ "278", /* 278 */ ++ "279", /* 279 */ ++ "280", /* 280 */ ++ "281", /* 281 */ ++ "282", /* 282 */ ++ "283", /* 283 */ ++ "284", /* 284 */ ++ "285", /* 285 */ ++ "286", /* 286 */ ++ "287", /* 287 */ ++ "288", /* 288 */ ++ "289", /* 289 */ ++ "290", /* 290 */ ++ "291", /* 291 */ ++ "292", /* 292 */ ++ "293", /* 293 */ ++ "294", /* 294 */ ++ "295", /* 295 */ ++ "296", /* 296 */ ++ "297", /* 297 */ ++ "298", /* 298 */ ++ "299", /* 299 */ ++ "300", /* 300 */ ++ "301", /* 301 */ ++ "302", /* 302 */ ++ "303", /* 303 */ ++ "304", /* 304 */ ++ "305", /* 305 */ ++ "306", /* 306 */ ++ "307", /* 307 */ ++ "308", /* 308 */ ++ "309", /* 309 */ ++ "310", /* 310 */ ++ "311", /* 311 */ ++ "312", /* 312 */ ++ "313", /* 313 */ ++ "314", /* 314 */ ++ "315", /* 315 */ ++ "316", /* 316 */ ++ "317", /* 317 */ ++ "318", /* 318 */ ++ "319", /* 319 */ ++ "320", /* 320 */ ++ "321", /* 321 */ ++ "322", /* 322 */ ++ "323", /* 323 */ ++ "324", /* 324 */ ++ "325", /* 325 */ ++ "326", /* 326 */ ++ "327", /* 327 */ ++ "328", /* 328 */ ++ "329", /* 329 */ ++ "330", /* 330 */ ++ "331", /* 331 */ ++ "332", /* 332 */ ++ "333", /* 333 */ ++ "334", /* 334 */ ++ "335", /* 335 */ ++ "336", /* 336 */ ++ "337", /* 337 */ ++ "338", /* 338 */ ++ "339", /* 339 */ ++ "340", /* 340 */ ++ "341", /* 341 */ ++ "342", /* 342 */ ++ "343", /* 343 */ ++ "344", /* 344 */ ++ "345", /* 345 */ ++ "346", /* 346 */ ++ "347", /* 347 */ ++ "348", /* 348 */ ++ "349", /* 349 */ ++ "350", /* 350 */ ++ "351", /* 351 */ ++ "352", /* 352 */ ++ "353", /* 353 */ ++ "354", /* 354 */ ++ "355", /* 355 */ ++ "356", /* 356 */ ++ "357", /* 357 */ ++ "358", /* 358 */ ++ "359", /* 359 */ ++ "360", /* 360 */ ++ "361", /* 361 */ ++ "362", /* 362 */ ++ "363", /* 363 */ ++ "364", /* 364 */ ++ "365", /* 365 */ ++ "366", /* 366 */ ++ "367", /* 367 */ ++ "368", /* 368 */ ++ "369", /* 369 */ ++ "370", /* 370 */ ++ "371", /* 371 */ ++ "372", /* 372 */ ++ "373", /* 373 */ ++ "374", /* 374 */ ++ "375", /* 375 */ ++ "376", /* 376 */ ++ "377", /* 377 */ ++ "378", /* 378 */ ++ "379", /* 379 */ ++ "380", /* 380 */ ++ "381", /* 381 */ ++ "382", /* 382 */ ++ "383", /* 383 */ ++ "384", /* 384 */ ++ "385", /* 385 */ ++ "386", /* 386 */ ++ "387", /* 387 */ ++ "388", /* 388 */ ++ "389", /* 389 */ ++ "390", /* 390 */ ++ "391", /* 391 */ ++ "392", /* 392 */ ++ "393", /* 393 */ ++ "394", /* 394 */ ++ "395", /* 395 */ ++ "396", /* 396 */ ++ "397", /* 397 */ ++ "398", /* 398 */ ++ "399", /* 399 */ ++ "400", /* 400 */ ++ "401", /* 401 */ ++ "402", /* 402 */ ++ "403", /* 403 */ ++ "404", /* 404 */ ++ "405", /* 405 */ ++ "406", /* 406 */ ++ "407", /* 407 */ ++ "408", /* 408 */ ++ "409", /* 409 */ ++ "410", /* 410 */ ++ "411", /* 411 */ ++ "412", /* 412 */ ++ "413", /* 413 */ ++ "414", /* 414 */ ++ "415", /* 415 */ ++ "416", /* 416 */ ++ "417", /* 417 */ ++ "418", /* 418 */ ++ "419", /* 419 */ ++ "420", /* 420 */ ++ "421", /* 421 */ ++ "422", /* 422 */ ++ "423", /* 423 */ ++ "424", /* 424 */ ++ "425", /* 425 */ ++ "426", /* 426 */ ++ "427", /* 427 */ ++ "428", /* 428 */ ++ "429", /* 429 */ ++ "430", /* 430 */ ++ "431", /* 431 */ ++ "432", /* 432 */ ++ "433", /* 433 */ ++ "434", /* 434 */ ++ "435", /* 435 */ ++ "436", /* 436 */ ++ "437", /* 437 */ ++ "438", /* 438 */ ++ "439", /* 439 */ ++ "440", /* 440 */ ++ "441", /* 441 */ ++ "442", /* 442 */ ++ "443", /* 443 */ ++ "444", /* 444 */ ++ "445", /* 445 */ ++ "446", /* 446 */ ++ "447", /* 447 */ ++ "448", /* 448 */ ++ "449", /* 449 */ ++ "450", /* 450 */ ++ "451", /* 451 */ ++ "452", /* 452 */ ++ "453", /* 453 */ ++ "454", /* 454 */ ++ "455", /* 455 */ ++ "456", /* 456 */ ++ "457", /* 457 */ ++ "458", /* 458 */ ++ "459", /* 459 */ ++ "460", /* 460 */ ++ "461", /* 461 */ ++ "462", /* 462 */ ++ "463", /* 463 */ ++ "464", /* 464 */ ++ "465", /* 465 */ ++ "466", /* 466 */ ++ "467", /* 467 */ ++ "468", /* 468 */ ++ "469", /* 469 */ ++ "470", /* 470 */ ++ "471", /* 471 */ ++ "472", /* 472 */ ++ "473", /* 473 */ ++ "474", /* 474 */ ++ "475", /* 475 */ ++ "476", /* 476 */ ++ "477", /* 477 */ ++ "478", /* 478 */ ++ "479", /* 479 */ ++ "480", /* 480 */ ++ "481", /* 481 */ ++ "482", /* 482 */ ++ "483", /* 483 */ ++ "484", /* 484 */ ++ "485", /* 485 */ ++ "486", /* 486 */ ++ "487", /* 487 */ ++ "488", /* 488 */ ++ "489", /* 489 */ ++ "490", /* 490 */ ++ "491", /* 491 */ ++ "492", /* 492 */ ++ "493", /* 493 */ ++ "494", /* 494 */ ++ "495", /* 495 */ ++ "496", /* 496 */ ++ "497", /* 497 */ ++ "498", /* 498 */ ++ "499", /* 499 */ ++ "500", /* 500 */ ++ "501", /* 501 */ ++ "502", /* 502 */ ++ "503", /* 503 */ ++ "504", /* 504 */ ++ "505", /* 505 */ ++ "506", /* 506 */ ++ "507", /* 507 */ ++ "508", /* 508 */ ++ "509", /* 509 */ ++ "510", /* 510 */ ++ "511", /* 511 */ ++ "512", /* 512 */ ++ "513", /* 513 */ ++ "514", /* 514 */ ++ "515", /* 515 */ ++ "516", /* 516 */ ++ "517", /* 517 */ ++ "518", /* 518 */ ++ "519", /* 519 */ ++ "520", /* 520 */ ++ "521", /* 521 */ ++ "522", /* 522 */ ++ "523", /* 523 */ ++ "524", /* 524 */ ++ "525", /* 525 */ ++ "526", /* 526 */ ++ "527", /* 527 */ ++ "528", /* 528 */ ++ "529", /* 529 */ ++ "530", /* 530 */ ++ "531", /* 531 */ ++ "532", /* 532 */ ++ "533", /* 533 */ ++ "534", /* 534 */ ++ "535", /* 535 */ ++ "536", /* 536 */ ++ "537", /* 537 */ ++ "538", /* 538 */ ++ "539", /* 539 */ ++ "540", /* 540 */ ++ "541", /* 541 */ ++ "542", /* 542 */ ++ "543", /* 543 */ ++ "544", /* 544 */ ++ "545", /* 545 */ ++ "546", /* 546 */ ++ "547", /* 547 */ ++ "548", /* 548 */ ++ "549", /* 549 */ ++ "550", /* 550 */ ++ "551", /* 551 */ ++ "552", /* 552 */ ++ "553", /* 553 */ ++ "554", /* 554 */ ++ "555", /* 555 */ ++ "556", /* 556 */ ++ "557", /* 557 */ ++ "558", /* 558 */ ++ "559", /* 559 */ ++ "560", /* 560 */ ++ "561", /* 561 */ ++ "562", /* 562 */ ++ "563", /* 563 */ ++ "564", /* 564 */ ++ "565", /* 565 */ ++ "566", /* 566 */ ++ "567", /* 567 */ ++ "568", /* 568 */ ++ "569", /* 569 */ ++ "570", /* 570 */ ++ "571", /* 571 */ ++ "572", /* 572 */ ++ "573", /* 573 */ ++ "574", /* 574 */ ++ "575", /* 575 */ ++ "576", /* 576 */ ++ "577", /* 577 */ ++ "578", /* 578 */ ++ "579", /* 579 */ ++ "580", /* 580 */ ++ "581", /* 581 */ ++ "582", /* 582 */ ++ "583", /* 583 */ ++ "584", /* 584 */ ++ "585", /* 585 */ ++ "586", /* 586 */ ++ "587", /* 587 */ ++ "588", /* 588 */ ++ "589", /* 589 */ ++ "590", /* 590 */ ++ "591", /* 591 */ ++ "592", /* 592 */ ++ "593", /* 593 */ ++ "594", /* 594 */ ++ "595", /* 595 */ ++ "596", /* 596 */ ++ "597", /* 597 */ ++ "598", /* 598 */ ++ "599", /* 599 */ ++ "600", /* 600 */ ++ "601", /* 601 */ ++ "602", /* 602 */ ++ "603", /* 603 */ ++ "604", /* 604 */ ++ "605", /* 605 */ ++ "606", /* 606 */ ++ "607", /* 607 */ ++ "608", /* 608 */ ++ "609", /* 609 */ ++ "610", /* 610 */ ++ "611", /* 611 */ ++ "612", /* 612 */ ++ "613", /* 613 */ ++ "614", /* 614 */ ++ "615", /* 615 */ ++ "616", /* 616 */ ++ "617", /* 617 */ ++ "618", /* 618 */ ++ "619", /* 619 */ ++ "620", /* 620 */ ++ "621", /* 621 */ ++ "622", /* 622 */ ++ "623", /* 623 */ ++ "624", /* 624 */ ++ "625", /* 625 */ ++ "626", /* 626 */ ++ "627", /* 627 */ ++ "628", /* 628 */ ++ "629", /* 629 */ ++ "630", /* 630 */ ++ "631", /* 631 */ ++ "632", /* 632 */ ++ "633", /* 633 */ ++ "634", /* 634 */ ++ "635", /* 635 */ ++ "636", /* 636 */ ++ "637", /* 637 */ ++ "638", /* 638 */ ++ "639", /* 639 */ ++ "640", /* 640 */ ++ "641", /* 641 */ ++ "642", /* 642 */ ++ "643", /* 643 */ ++ "644", /* 644 */ ++ "645", /* 645 */ ++ "646", /* 646 */ ++ "647", /* 647 */ ++ "648", /* 648 */ ++ "649", /* 649 */ ++ "650", /* 650 */ ++ "651", /* 651 */ ++ "652", /* 652 */ ++ "653", /* 653 */ ++ "654", /* 654 */ ++ "655", /* 655 */ ++ "656", /* 656 */ ++ "657", /* 657 */ ++ "658", /* 658 */ ++ "659", /* 659 */ ++ "660", /* 660 */ ++ "661", /* 661 */ ++ "662", /* 662 */ ++ "663", /* 663 */ ++ "664", /* 664 */ ++ "665", /* 665 */ ++ "666", /* 666 */ ++ "667", /* 667 */ ++ "668", /* 668 */ ++ "669", /* 669 */ ++ "670", /* 670 */ ++ "671", /* 671 */ ++ "672", /* 672 */ ++ "673", /* 673 */ ++ "674", /* 674 */ ++ "675", /* 675 */ ++ "676", /* 676 */ ++ "677", /* 677 */ ++ "678", /* 678 */ ++ "679", /* 679 */ ++ "680", /* 680 */ ++ "681", /* 681 */ ++ "682", /* 682 */ ++ "683", /* 683 */ ++ "684", /* 684 */ ++ "685", /* 685 */ ++ "686", /* 686 */ ++ "687", /* 687 */ ++ "688", /* 688 */ ++ "689", /* 689 */ ++ "690", /* 690 */ ++ "691", /* 691 */ ++ "692", /* 692 */ ++ "693", /* 693 */ ++ "694", /* 694 */ ++ "695", /* 695 */ ++ "696", /* 696 */ ++ "697", /* 697 */ ++ "698", /* 698 */ ++ "699", /* 699 */ ++ "700", /* 700 */ ++ "701", /* 701 */ ++ "702", /* 702 */ ++ "703", /* 703 */ ++ "704", /* 704 */ ++ "705", /* 705 */ ++ "706", /* 706 */ ++ "707", /* 707 */ ++ "708", /* 708 */ ++ "709", /* 709 */ ++ "710", /* 710 */ ++ "711", /* 711 */ ++ "712", /* 712 */ ++ "713", /* 713 */ ++ "714", /* 714 */ ++ "715", /* 715 */ ++ "716", /* 716 */ ++ "717", /* 717 */ ++ "718", /* 718 */ ++ "719", /* 719 */ ++ "720", /* 720 */ ++ "721", /* 721 */ ++ "722", /* 722 */ ++ "723", /* 723 */ ++ "724", /* 724 */ ++ "725", /* 725 */ ++ "726", /* 726 */ ++ "727", /* 727 */ ++ "728", /* 728 */ ++ "729", /* 729 */ ++ "730", /* 730 */ ++ "731", /* 731 */ ++ "732", /* 732 */ ++ "733", /* 733 */ ++ "734", /* 734 */ ++ "735", /* 735 */ ++ "736", /* 736 */ ++ "737", /* 737 */ ++ "738", /* 738 */ ++ "739", /* 739 */ ++ "740", /* 740 */ ++ "741", /* 741 */ ++ "742", /* 742 */ ++ "743", /* 743 */ ++ "744", /* 744 */ ++ "745", /* 745 */ ++ "746", /* 746 */ ++ "747", /* 747 */ ++ "748", /* 748 */ ++ "749", /* 749 */ ++ "750", /* 750 */ ++ "751", /* 751 */ ++ "752", /* 752 */ ++ "753", /* 753 */ ++ "754", /* 754 */ ++ "755", /* 755 */ ++ "756", /* 756 */ ++ "757", /* 757 */ ++ "758", /* 758 */ ++ "759", /* 759 */ ++ "760", /* 760 */ ++ "761", /* 761 */ ++ "762", /* 762 */ ++ "763", /* 763 */ ++ "764", /* 764 */ ++ "765", /* 765 */ ++ "766", /* 766 */ ++ "767", /* 767 */ ++ "768", /* 768 */ ++ "769", /* 769 */ ++ "770", /* 770 */ ++ "771", /* 771 */ ++ "772", /* 772 */ ++ "773", /* 773 */ ++ "774", /* 774 */ ++ "775", /* 775 */ ++ "776", /* 776 */ ++ "777", /* 777 */ ++ "778", /* 778 */ ++ "779", /* 779 */ ++ "780", /* 780 */ ++ "781", /* 781 */ ++ "782", /* 782 */ ++ "783", /* 783 */ ++ "784", /* 784 */ ++ "785", /* 785 */ ++ "786", /* 786 */ ++ "787", /* 787 */ ++ "788", /* 788 */ ++ "789", /* 789 */ ++ "790", /* 790 */ ++ "791", /* 791 */ ++ "792", /* 792 */ ++ "793", /* 793 */ ++ "794", /* 794 */ ++ "795", /* 795 */ ++ "796", /* 796 */ ++ "797", /* 797 */ ++ "798", /* 798 */ ++ "799", /* 799 */ ++ "800", /* 800 */ ++ "801", /* 801 */ ++ "802", /* 802 */ ++ "803", /* 803 */ ++ "804", /* 804 */ ++ "805", /* 805 */ ++ "806", /* 806 */ ++ "807", /* 807 */ ++ "808", /* 808 */ ++ "809", /* 809 */ ++ "810", /* 810 */ ++ "811", /* 811 */ ++ "812", /* 812 */ ++ "813", /* 813 */ ++ "814", /* 814 */ ++ "815", /* 815 */ ++ "816", /* 816 */ ++ "817", /* 817 */ ++ "818", /* 818 */ ++ "819", /* 819 */ ++ "820", /* 820 */ ++ "821", /* 821 */ ++ "822", /* 822 */ ++ "823", /* 823 */ ++ "824", /* 824 */ ++ "825", /* 825 */ ++ "826", /* 826 */ ++ "827", /* 827 */ ++ "828", /* 828 */ ++ "829", /* 829 */ ++ "830", /* 830 */ ++ "831", /* 831 */ ++ "832", /* 832 */ ++ "833", /* 833 */ ++ "834", /* 834 */ ++ "835", /* 835 */ ++ "836", /* 836 */ ++ "837", /* 837 */ ++ "838", /* 838 */ ++ "839", /* 839 */ ++ "840", /* 840 */ ++ "841", /* 841 */ ++ "842", /* 842 */ ++ "843", /* 843 */ ++ "844", /* 844 */ ++ "845", /* 845 */ ++ "846", /* 846 */ ++ "847", /* 847 */ ++ "848", /* 848 */ ++ "849", /* 849 */ ++ "850", /* 850 */ ++ "851", /* 851 */ ++ "852", /* 852 */ ++ "853", /* 853 */ ++ "854", /* 854 */ ++ "855", /* 855 */ ++ "856", /* 856 */ ++ "857", /* 857 */ ++ "858", /* 858 */ ++ "859", /* 859 */ ++ "860", /* 860 */ ++ "861", /* 861 */ ++ "862", /* 862 */ ++ "863", /* 863 */ ++ "864", /* 864 */ ++ "865", /* 865 */ ++ "866", /* 866 */ ++ "867", /* 867 */ ++ "868", /* 868 */ ++ "869", /* 869 */ ++ "870", /* 870 */ ++ "871", /* 871 */ ++ "872", /* 872 */ ++ "873", /* 873 */ ++ "874", /* 874 */ ++ "875", /* 875 */ ++ "876", /* 876 */ ++ "877", /* 877 */ ++ "878", /* 878 */ ++ "879", /* 879 */ ++ "880", /* 880 */ ++ "881", /* 881 */ ++ "882", /* 882 */ ++ "883", /* 883 */ ++ "884", /* 884 */ ++ "885", /* 885 */ ++ "886", /* 886 */ ++ "887", /* 887 */ ++ "888", /* 888 */ ++ "889", /* 889 */ ++ "890", /* 890 */ ++ "891", /* 891 */ ++ "892", /* 892 */ ++ "893", /* 893 */ ++ "894", /* 894 */ ++ "895", /* 895 */ ++ "896", /* 896 */ ++ "897", /* 897 */ ++ "898", /* 898 */ ++ "899", /* 899 */ ++ "900", /* 900 */ ++ "901", /* 901 */ ++ "902", /* 902 */ ++ "903", /* 903 */ ++ "904", /* 904 */ ++ "905", /* 905 */ ++ "906", /* 906 */ ++ "907", /* 907 */ ++ "908", /* 908 */ ++ "909", /* 909 */ ++ "910", /* 910 */ ++ "911", /* 911 */ ++ "912", /* 912 */ ++ "913", /* 913 */ ++ "914", /* 914 */ ++ "915", /* 915 */ ++ "916", /* 916 */ ++ "917", /* 917 */ ++ "918", /* 918 */ ++ "919", /* 919 */ ++ "920", /* 920 */ ++ "921", /* 921 */ ++ "922", /* 922 */ ++ "923", /* 923 */ ++ "924", /* 924 */ ++ "925", /* 925 */ ++ "926", /* 926 */ ++ "927", /* 927 */ ++ "928", /* 928 */ ++ "929", /* 929 */ ++ "930", /* 930 */ ++ "931", /* 931 */ ++ "932", /* 932 */ ++ "933", /* 933 */ ++ "934", /* 934 */ ++ "935", /* 935 */ ++ "936", /* 936 */ ++ "937", /* 937 */ ++ "938", /* 938 */ ++ "939", /* 939 */ ++ "940", /* 940 */ ++ "941", /* 941 */ ++ "942", /* 942 */ ++ "943", /* 943 */ ++ "944", /* 944 */ ++ "945", /* 945 */ ++ "946", /* 946 */ ++ "947", /* 947 */ ++ "948", /* 948 */ ++ "949", /* 949 */ ++ "950", /* 950 */ ++ "951", /* 951 */ ++ "952", /* 952 */ ++ "953", /* 953 */ ++ "954", /* 954 */ ++ "955", /* 955 */ ++ "956", /* 956 */ ++ "957", /* 957 */ ++ "958", /* 958 */ ++ "959", /* 959 */ ++ "960", /* 960 */ ++ "961", /* 961 */ ++ "962", /* 962 */ ++ "963", /* 963 */ ++ "964", /* 964 */ ++ "965", /* 965 */ ++ "966", /* 966 */ ++ "967", /* 967 */ ++ "968", /* 968 */ ++ "969", /* 969 */ ++ "970", /* 970 */ ++ "971", /* 971 */ ++ "972", /* 972 */ ++ "973", /* 973 */ ++ "974", /* 974 */ ++ "975", /* 975 */ ++ "976", /* 976 */ ++ "977", /* 977 */ ++ "978", /* 978 */ ++ "979", /* 979 */ ++ "980", /* 980 */ ++ "981", /* 981 */ ++ "982", /* 982 */ ++ "983", /* 983 */ ++ "984", /* 984 */ ++ "985", /* 985 */ ++ "986", /* 986 */ ++ "987", /* 987 */ ++ "988", /* 988 */ ++ "989", /* 989 */ ++ "990", /* 990 */ ++ "991", /* 991 */ ++ "992", /* 992 */ ++ "993", /* 993 */ ++ "994", /* 994 */ ++ "995", /* 995 */ ++ "996", /* 996 */ ++ "997", /* 997 */ ++ "998", /* 998 */ ++ "999", /* 999 */ ++ "1000", /* 1000 */ ++ "1001", /* 1001 */ ++ "1002", /* 1002 */ ++ "1003", /* 1003 */ ++ "1004", /* 1004 */ ++ "1005", /* 1005 */ ++ "1006", /* 1006 */ ++ "1007", /* 1007 */ ++ "1008", /* 1008 */ ++ "1009", /* 1009 */ ++ "1010", /* 1010 */ ++ "1011", /* 1011 */ ++ "1012", /* 1012 */ ++ "1013", /* 1013 */ ++ "1014", /* 1014 */ ++ "1015", /* 1015 */ ++ "1016", /* 1016 */ ++ "1017", /* 1017 */ ++ "1018", /* 1018 */ ++ "1019", /* 1019 */ ++ "1020", /* 1020 */ ++ "1021", /* 1021 */ ++ "1022", /* 1022 */ ++ "1023", /* 1023 */ ++ "open", /* 1024 */ ++ "link", /* 1025 */ ++ "unlink", /* 1026 */ ++ "mknod", /* 1027 */ ++ "chmod", /* 1028 */ ++ "chown", /* 1029 */ ++ "mkdir", /* 1030 */ ++ "rmdir", /* 1031 */ ++ "lchown", /* 1032 */ ++ "access", /* 1033 */ ++ "rename", /* 1034 */ ++ "readlink", /* 1035 */ ++ "symlink", /* 1036 */ ++ "utimes", /* 1037 */ ++ "stat", /* 1038 */ ++ "lstat", /* 1039 */ ++ "pipe", /* 1040 */ ++ "dup2", /* 1041 */ ++ "epoll_create", /* 1042 */ ++ "inotify_init", /* 1043 */ ++ "eventfd", /* 1044 */ ++ "signalfd", /* 1045 */ ++ "sendfile", /* 1046 */ ++ "ftruncate", /* 1047 */ ++ "truncate", /* 1048 */ ++ "stat", /* 1049 */ ++ "lstat", /* 1050 */ ++ "fstat", /* 1051 */ ++ "fcntl", /* 1052 */ ++ "fadvise64", /* 1053 */ ++ "newfstatat", /* 1054 */ ++ "fstatfs", /* 1055 */ ++ "statfs", /* 1056 */ ++ "lseek", /* 1057 */ ++ "mmap", /* 1058 */ ++ "alarm", /* 1059 */ ++ "getpgrp", /* 1060 */ ++ "pause", /* 1061 */ ++ "time", /* 1062 */ ++ "utime", /* 1063 */ ++ "creat", /* 1064 */ ++ "getdents", /* 1065 */ ++ "futimesat", /* 1066 */ ++ "select", /* 1067 */ ++ "poll", /* 1068 */ ++ "epoll_wait", /* 1069 */ ++ "ustat", /* 1070 */ ++ "vfork", /* 1071 */ ++ "oldwait4", /* 1072 */ ++ "recv", /* 1073 */ ++ "send", /* 1074 */ ++ "bdflush", /* 1075 */ ++ "umount", /* 1076 */ ++ "uselib", /* 1077 */ ++ "_sysctl", /* 1078 */ ++ "fork", /* 1079 */ +diff --git a/sysdeps/linux-gnu/aarch64/trace.c b/sysdeps/linux-gnu/aarch64/trace.c +new file mode 100644 +index 0000000..5544b51 +--- /dev/null ++++ b/sysdeps/linux-gnu/aarch64/trace.c +@@ -0,0 +1,83 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2014 Petr Machata, Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "backend.h" ++#include "proc.h" ++ ++void ++get_arch_dep(struct process *proc) ++{ ++} ++ ++int aarch64_read_gregs(struct process *proc, struct user_pt_regs *regs); ++ ++/* The syscall instruction is: ++ * | 31 21 | 20 5 | 4 0 | ++ * | 1 1 0 1 0 1 0 0 | 0 0 0 | imm16 | 0 0 0 0 1 | */ ++#define SVC_MASK 0xffe0001f ++#define SVC_VALUE 0xd4000001 ++ ++int ++syscall_p(struct process *proc, int status, int *sysnum) ++{ ++ if (WIFSTOPPED(status) ++ && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { ++ ++ struct user_pt_regs regs; ++ if (aarch64_read_gregs(proc, ®s) < 0) { ++ fprintf(stderr, "syscall_p: " ++ "Couldn't read registers of %d.\n", proc->pid); ++ return -1; ++ } ++ ++ errno = 0; ++ unsigned long insn = (unsigned long) ptrace(PTRACE_PEEKTEXT, ++ proc->pid, ++ regs.pc - 4, 0); ++ if (insn == -1UL && errno != 0) { ++ fprintf(stderr, "syscall_p: " ++ "Couldn't peek into %d: %s\n", proc->pid, ++ strerror(errno)); ++ return -1; ++ } ++ ++ insn &= 0xffffffffUL; ++ if ((insn & SVC_MASK) == SVC_VALUE) { ++ *sysnum = regs.regs[8]; ++ ++ size_t d1 = proc->callstack_depth - 1; ++ if (proc->callstack_depth > 0 ++ && proc->callstack[d1].is_syscall ++ && proc->callstack[d1].c_un.syscall == *sysnum) ++ return 2; ++ ++ return 1; ++ } ++ } ++ ++ return 0; ++} +diff --git a/testsuite/ltrace.main/system_call_params.exp b/testsuite/ltrace.main/system_call_params.exp +index 787e342..2ccf840 100644 +--- a/testsuite/ltrace.main/system_call_params.exp ++++ b/testsuite/ltrace.main/system_call_params.exp +@@ -1,5 +1,5 @@ + # This file is part of ltrace. +-# Copyright (C) 2013 Petr Machata, Red Hat Inc. ++# Copyright (C) 2013, 2014 Petr Machata, Red Hat Inc. + # + # This program is free software; you can redistribute it and/or + # modify it under the terms of the GNU General Public License as +@@ -17,11 +17,25 @@ + # 02110-1301 USA + + set bin [ltraceCompile {} [ltraceSource c { ++ #define _GNU_SOURCE + #include + #include + #include ++ #include ++ #include /* For SYS_xxx definitions */ ++ ++ #ifndef SYS_open ++ # if defined(__aarch64__) ++ # /* Linux doesn't actually implement SYS_open on AArch64, but for merely ++ # * recording the syscall, it's fine. */ ++ # define SYS_open 1024 ++ # else ++ # error SYS_open not available. ++ # endif ++ #endif ++ + int main(void) { +- open("/some/path", O_RDONLY); ++ syscall(SYS_open, "/some/path", O_RDONLY); + write(1, "something", 10); + mount("source", "target", "filesystemtype", 0, 0); + } +diff --git a/testsuite/ltrace.main/system_calls.c b/testsuite/ltrace.main/system_calls.c +deleted file mode 100644 +index 7be3d04..0000000 +--- a/testsuite/ltrace.main/system_calls.c ++++ /dev/null +@@ -1,68 +0,0 @@ +-/* Ltrace Test : system_calls.c. +- Objectives : Verify that Ltrace can trace all the system calls in +- execution. +- +- You can add new system calls in it and add its verification in +- system_calls correspondingly. +- +- This file was written by Yao Qi . */ +- +-#include +-#include +-#include +-#include +-#include +- +-void exit (int); +- +-#define BUF_SIZE 100 +- +-int +-main () +-{ +- FILE* fp; +- char s[]="system_calls"; +- char buffer[BUF_SIZE]; +- struct stat state; +- +- /* SYS_open. */ +- fp = fopen ("system_calls.tmp", "w"); +- if (fp == NULL) +- { +- printf("Can not create system_calls.tmp\n"); +- exit (0); +- } +- /* SYS_write. */ +- fwrite(s, sizeof(s), 1, fp); +- /* SYS_lseek. */ +- fseek (fp, 0, SEEK_CUR); +- /* SYS_read. */ +- fread(buffer, sizeof(s), 1, fp); +- /* SYS_close. */ +- fclose(fp); +- +- /* SYS_getcwd. */ +- getcwd (buffer, BUF_SIZE); +- /* SYS_chdir. */ +- chdir ("."); +- /* SYS_symlink. */ +- symlink ("system_calls.tmp", "system_calls.link"); +- /* SYS_unlink. */ +- remove("system_calls.link"); +- /* SYS_rename. */ +- rename ("system_calls.tmp", "system_calls.tmp1"); +- /* SYS_stat. */ +- stat ("system_calls.tmp", &state); +- /* SYS_access. */ +- access ("system_calls.tmp", R_OK); +- remove("system_calls.tmp1"); +- +- /* SYS_mkdir. */ +- mkdir ("system_call_mkdir", 0777); +- /* SYS_rmdir. */ +- rmdir ("system_call_mkdir"); +- +- return 0; +-} +- +- +diff --git a/testsuite/ltrace.main/system_calls.exp b/testsuite/ltrace.main/system_calls.exp +index a74fa04..f60e319 100644 +--- a/testsuite/ltrace.main/system_calls.exp ++++ b/testsuite/ltrace.main/system_calls.exp +@@ -1,67 +1,146 @@ +-# This file was written by Yao Qi . ++# This file is part of ltrace. ++# Copyright (C) 2014 Petr Machata, Red Hat Inc. ++# Copyright (C) 2006 Yao Qi , IBM Corporation ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License as ++# published by the Free Software Foundation; either version 2 of the ++# License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++# 02110-1301 USA + +-set testfile "system_calls" +-set srcfile ${testfile}.c +-set binfile ${testfile} ++# Objectives: Verify that Ltrace can trace all the system calls in ++# execution. Note that this test is necessarily noisy. Dynamic ++# linker adds a bunch of system calls of its own. + ++set empty [ltraceCompile {} [ltraceSource c { ++ int main (void) { return 0; } ++}]] + +-verbose "compiling source file now....." +-# Build the shared libraries this test case needs. +-if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${objdir}/${subdir}/${binfile}" executable {debug} ] != "" } { +- send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" ++set bin [ltraceCompile {} [ltraceSource c { ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ ++ int ++ main () ++ { ++ FILE* fp; ++ char s[]="system_calls"; ++ char buffer[1024]; ++ struct stat state; ++ ++ fp = fopen ("system_calls.tmp", "w"); ++ if (fp == NULL) ++ { ++ printf("Can not create system_calls.tmp\n"); ++ exit (0); ++ } ++ fwrite(s, sizeof(s), 1, fp); ++ fseek (fp, 0, SEEK_CUR); ++ fread(buffer, sizeof(s), 1, fp); ++ fclose(fp); ++ ++ getcwd (buffer, sizeof buffer); ++ chdir ("."); ++ symlink ("system_calls.tmp", "system_calls.link"); ++ remove("system_calls.link"); ++ rename ("system_calls.tmp", "system_calls.tmp1"); ++ stat ("system_calls.tmp", &state); ++ access ("system_calls.tmp", R_OK); ++ remove("system_calls.tmp1"); ++ ++ mkdir ("system_call_mkdir", 0777); ++ rmdir ("system_call_mkdir"); ++ ++ return 0; ++ } ++}]] ++ ++proc Calls {logfile} { ++ set fp [open $logfile] ++ set ret {} ++ ++ while {[gets $fp line] >= 0} { ++ if [regexp -- {^[a-zA-Z0-9]*@SYS} $line] { ++ set call [lindex [split $line @] 0] ++ dict incr ret $call ++ } ++ } ++ ++ close $fp ++ return $ret ++} ++ ++proc GetDefault {d key def} { ++ if {[dict exists $d $key]} { ++ return [dict get $d $key] ++ } else { ++ return $def ++ } + } + +-# set options for ltrace. +-ltrace_options "-S" ++proc Diff {d1 d2} { ++ set keys [lsort -unique [concat [dict keys $d1] [dict keys $d2]]] ++ set ret {} ++ foreach key $keys { ++ set n1 [GetDefault $d1 $key 0] ++ set n2 [GetDefault $d2 $key 0] ++ set sum [expr $n1 - $n2] ++ if {[expr $sum != 0]} { ++ dict set ret $key $sum ++ } ++ } ++ return $ret ++} ++ ++proc Match {d patterns} { ++ foreach line $patterns { ++ set pattern [lindex $line 0] ++ set op [lindex $line 1] ++ set expect [lindex $line 2] + +-#Run PUT for ltarce. +-set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] ++ set count 0 ++ foreach key [dict keys $d] { ++ if [regexp -- $pattern $key] { ++ incr count [dict get $d $key] ++ } ++ } + +-#check the output of this program. +-verbose "ltrace runtest output: $exec_output\n" ++ set msgMain "$pattern was recorded $count times" + +-if [regexp {ELF from incompatible architecture} $exec_output] { +- fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" +- return +-} elseif [ regexp {Couldn't get .hash data} $exec_output ] { +- fail "Couldn't get .hash data!" +- return ++ if {[eval expr $count $op $expect]} { ++ pass $msgMain ++ } else { ++ fail "$msgMain, expected $op $expect" ++ } ++ } + } + ++Match [Diff [Calls [ltraceRun -L -S -- $bin]] \ ++ [Calls [ltraceRun -L -S -- $empty]]] { ++ { {^write$} == 1 } ++ { {^unlink(at)?$} >= 2 } ++ { {^open(at)?$} == 1 } ++ { {^(new|f)?stat(64)?$} == 1 } ++ { {^close$} == 1 } ++ { {^getcwd$} == 1 } ++ { {^chdir$} == 1 } ++ { {^symlink(at)?$} == 1 } ++ { {^f?access(at)?$} == 1 } ++ { {^rename(at)?$} == 1 } ++ { {^mkdir(at)?$} == 1 } ++} + +-set pattern "^munmap@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 2 +-set pattern "^write@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^unlink@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +- +-set pattern "^brk@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^open@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^(new)?fstat(64)?@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 egrep +-set pattern "^(old_)?mmap2?@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 egrep +-set pattern "^close@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +- +-set pattern "^getcwd@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^chdir@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^symlink@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^unlink@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^(new)?stat(64)?@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 egrep +-set pattern "^access@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^rename@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^mkdir@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "^rmdir@SYS" +-ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ltraceDone +diff --git a/testsuite/ltrace.minor/trace-clone.c b/testsuite/ltrace.minor/trace-clone.c +index db1936d..ded930c 100644 +--- a/testsuite/ltrace.minor/trace-clone.c ++++ b/testsuite/ltrace.minor/trace-clone.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + int child () + { +@@ -22,7 +23,8 @@ typedef int (* myfunc)(); + int main () + { + pid_t pid; +- static char stack[STACK_SIZE]; ++ static __attribute__ ((aligned (16))) char stack[STACK_SIZE]; ++ + #ifdef __ia64__ + pid = __clone2((myfunc)&child, stack, STACK_SIZE, CLONE_FS, NULL); + #else diff --git a/ltrace-0.7.91-account_execl.patch b/ltrace-0.7.91-account_execl.patch new file mode 100644 index 0000000..2aa4ba0 --- /dev/null +++ b/ltrace-0.7.91-account_execl.patch @@ -0,0 +1,834 @@ +From 0cf4ab66e9927e101a51dd9fa9adc6c8dc56b5e7 Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Thu, 21 Nov 2013 20:25:53 +0100 +Subject: [PATCH] Consider exec and exit events an end of outstanding calls + +- This cleans up a lot of stuff. The actual substance is addition of + account_current_callstack in handle_event.c (which however uses + those cleaned-up interfaces). + +- trace-exec.exp was extended to check that the exec syscall can be + seen in -c output. That's one of the symptoms of what this fixes. +--- + Makefile.am | 8 +- + common.h | 2 - + forward.h | 1 + + handle_event.c | 225 ++++++++++++++++++++------------- + libltrace.c | 5 +- + options.h | 8 +- + output.c | 86 +++---------- + output.h | 5 +- + proc.h | 2 +- + summary.c | 89 +++++++++++++- + summary.h | 35 +++++ + testsuite/ltrace.minor/trace-exec.exp | 16 ++- + 12 files changed, 299 insertions(+), 183 deletions(-) + create mode 100644 summary.h + +diff --git a/Makefile.am b/Makefile.am +index d711aec..efcf18a 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -54,10 +54,10 @@ ltrace_LDADD = \ + + noinst_HEADERS = bits.h backend.h breakpoint.h common.h debug.h \ + defs.h demangle.h dict.h forward.h ltrace-elf.h ltrace.h \ +- options.h output.h proc.h read_config_file.h library.h \ +- filter.h glob.h vect.h type.h value.h value_dict.h callback.h \ +- expr.h fetch.h vect.h param.h printf.h zero.h lens.h \ +- lens_default.h lens_enum.h memstream.h prototype.h ++ options.h output.h proc.h read_config_file.h summary.h \ ++ library.h filter.h glob.h vect.h type.h value.h value_dict.h \ ++ callback.h expr.h fetch.h vect.h param.h printf.h zero.h \ ++ lens.h lens_default.h lens_enum.h memstream.h prototype.h + + dist_man1_MANS = ltrace.1 + dist_man5_MANS = ltrace.conf.5 +diff --git a/common.h b/common.h +index a53c5db..7259ba4 100644 +--- a/common.h ++++ b/common.h +@@ -54,8 +54,6 @@ extern void handle_event(Event * event); + + extern pid_t execute_program(const char * command, char ** argv); + +-extern void show_summary(void); +- + struct breakpoint; + struct library_symbol; + +diff --git a/forward.h b/forward.h +index 8641213..58d8f05 100644 +--- a/forward.h ++++ b/forward.h +@@ -34,6 +34,7 @@ struct param_enum; + struct process; + struct protolib; + struct prototype; ++struct timedelta; + struct value; + struct value_dict; + struct vect; +diff --git a/handle_event.c b/handle_event.c +index 9ed62a2..6fa7e98 100644 +--- a/handle_event.c ++++ b/handle_event.c +@@ -32,7 +32,6 @@ + #include + #include + #include +-#include + #include + + #include "backend.h" +@@ -41,8 +40,9 @@ + #include "fetch.h" + #include "library.h" + #include "proc.h" +-#include "value_dict.h" + #include "prototype.h" ++#include "summary.h" ++#include "value_dict.h" + + static void handle_signal(Event *event); + static void handle_exit(Event *event); +@@ -419,32 +419,11 @@ handle_signal(Event *event) { + continue_after_signal(event->proc->pid, event->e_un.signum); + } + +-static void +-handle_exit(Event *event) { +- debug(DEBUG_FUNCTION, "handle_exit(pid=%d, status=%d)", event->proc->pid, event->e_un.ret_val); +- if (event->proc->state != STATE_IGNORED) { +- output_line(event->proc, "+++ exited (status %d) +++", +- event->e_un.ret_val); +- } +- remove_process(event->proc); +-} +- +-static void +-handle_exit_signal(Event *event) { +- debug(DEBUG_FUNCTION, "handle_exit_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); +- if (event->proc->state != STATE_IGNORED) { +- output_line(event->proc, "+++ killed by %s +++", +- shortsignal(event->proc, event->e_un.signum)); +- } +- remove_process(event->proc); +-} +- +-static void +-output_syscall(struct process *proc, const char *name, enum tof tof, +- void (*output)(enum tof, struct process *, +- struct library_symbol *)) ++static int ++init_syscall_symbol(struct library_symbol *libsym, const char *name) + { + static struct library syscall_lib; ++ + if (syscall_lib.protolib == NULL) { + struct protolib *protolib + = protolib_cache_load(&g_protocache, "syscalls", 0, 1); +@@ -475,10 +454,91 @@ output_syscall(struct process *proc, const char *name, enum tof tof, + syscall_lib.protolib = protolib; + } + ++ if (library_symbol_init(libsym, 0, name, 0, LS_TOPLT_NONE) < 0) ++ return -1; ++ ++ libsym->lib = &syscall_lib; ++ return 0; ++} ++ ++/* Account the unfinished functions on the call stack. */ ++static void ++account_current_callstack(struct process *proc) ++{ ++ if (! options.summary) ++ return; ++ ++ struct timedelta spent[proc->callstack_depth]; ++ ++ size_t i; ++ for (i = 0; i < proc->callstack_depth; ++i) { ++ struct callstack_element *elem = &proc->callstack[i]; ++ spent[i] = calc_time_spent(elem->enter_time); ++ } ++ ++ for (i = 0; i < proc->callstack_depth; ++i) { ++ struct callstack_element *elem = &proc->callstack[i]; ++ struct library_symbol syscall, *libsym = NULL; ++ if (elem->is_syscall) { ++ const char *name = sysname(proc, elem->c_un.syscall); ++ if (init_syscall_symbol(&syscall, name) >= 0) ++ libsym = &syscall; ++ ++ } else { ++ libsym = elem->c_un.libfunc; ++ } ++ ++ if (libsym != NULL) { ++ summary_account_call(libsym, spent[i]); ++ ++ if (elem->is_syscall) ++ library_symbol_destroy(&syscall); ++ } ++ } ++} ++ ++static void ++handle_exit(Event *event) { ++ debug(DEBUG_FUNCTION, "handle_exit(pid=%d, status=%d)", event->proc->pid, event->e_un.ret_val); ++ if (event->proc->state != STATE_IGNORED) { ++ output_line(event->proc, "+++ exited (status %d) +++", ++ event->e_un.ret_val); ++ } ++ ++ account_current_callstack(event->proc); ++ remove_process(event->proc); ++} ++ ++static void ++handle_exit_signal(Event *event) { ++ debug(DEBUG_FUNCTION, "handle_exit_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); ++ if (event->proc->state != STATE_IGNORED) { ++ output_line(event->proc, "+++ killed by %s +++", ++ shortsignal(event->proc, event->e_un.signum)); ++ } ++ ++ account_current_callstack(event->proc); ++ remove_process(event->proc); ++} ++ ++static void ++output_syscall(struct process *proc, const char *name, enum tof tof, ++ bool left, struct timedelta *spent) ++{ ++ if (left) ++ assert(spent == NULL); ++ + struct library_symbol syscall; +- if (library_symbol_init(&syscall, 0, name, 0, LS_TOPLT_NONE) >= 0) { +- syscall.lib = &syscall_lib; +- (*output)(tof, proc, &syscall); ++ if (init_syscall_symbol(&syscall, name) >= 0) { ++ if (left) { ++ if (! options.summary) ++ output_left(tof, proc, &syscall); ++ } else if (options.summary) { ++ summary_account_call(&syscall, *spent); ++ } else { ++ output_right(tof, proc, &syscall, spent); ++ } ++ + library_symbol_destroy(&syscall); + } + } +@@ -486,17 +546,19 @@ output_syscall(struct process *proc, const char *name, enum tof tof, + static void + output_syscall_left(struct process *proc, const char *name) + { +- output_syscall(proc, name, LT_TOF_SYSCALL, &output_left); ++ output_syscall(proc, name, LT_TOF_SYSCALL, true, NULL); + } + + static void +-output_syscall_right(struct process *proc, const char *name) ++output_syscall_right(struct process *proc, const char *name, ++ struct timedelta *spent) + { +- output_syscall(proc, name, LT_TOF_SYSCALLR, &output_right); ++ output_syscall(proc, name, LT_TOF_SYSCALLR, false, spent); + } + + static void +-handle_syscall(Event *event) { ++handle_syscall(Event *event) ++{ + debug(DEBUG_FUNCTION, "handle_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + callstack_push_syscall(event->proc, event->e_un.sysnum); +@@ -526,6 +588,8 @@ handle_exec(Event *event) + } + output_line(proc, "--- Called exec() ---"); + ++ account_current_callstack(proc); ++ + if (process_exec(proc) < 0) { + fprintf(stderr, + "couldn't reinitialize process %d after exec\n", pid); +@@ -549,74 +613,58 @@ handle_arch_syscall(Event *event) { + continue_process(event->proc->pid); + } + +-struct timeval current_time_spent; +- + static void +-calc_time_spent(struct process *proc) ++handle_x_sysret(Event *event, char *(*name_cb)(struct process *, int)) + { +- struct timeval tv; +- struct timezone tz; +- struct timeval diff; +- struct callstack_element *elem; +- +- debug(DEBUG_FUNCTION, "calc_time_spent(pid=%d)", proc->pid); +- elem = &proc->callstack[proc->callstack_depth - 1]; +- +- gettimeofday(&tv, &tz); ++ debug(DEBUG_FUNCTION, "handle_x_sysret(pid=%d, sysnum=%d)", ++ event->proc->pid, event->e_un.sysnum); + +- diff.tv_sec = tv.tv_sec - elem->time_spent.tv_sec; +- if (tv.tv_usec >= elem->time_spent.tv_usec) { +- diff.tv_usec = tv.tv_usec - elem->time_spent.tv_usec; +- } else { +- diff.tv_sec--; +- diff.tv_usec = 1000000 + tv.tv_usec - elem->time_spent.tv_usec; +- } +- current_time_spent = diff; +-} ++ unsigned d = event->proc->callstack_depth; ++ assert(d > 0); ++ struct callstack_element *elem = &event->proc->callstack[d - 1]; ++ assert(elem->is_syscall); + +-static void +-handle_sysret(Event *event) { +- debug(DEBUG_FUNCTION, "handle_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { +- if (opt_T || options.summary) { +- calc_time_spent(event->proc); +- } ++ struct timedelta spent = calc_time_spent(elem->enter_time); + if (options.syscalls) + output_syscall_right(event->proc, +- sysname(event->proc, +- event->e_un.sysnum)); ++ name_cb(event->proc, ++ event->e_un.sysnum), ++ &spent); + +- assert(event->proc->callstack_depth > 0); +- unsigned d = event->proc->callstack_depth - 1; +- assert(event->proc->callstack[d].is_syscall); + callstack_pop(event->proc); + } + continue_after_syscall(event->proc, event->e_un.sysnum, 1); + } + + static void +-handle_arch_sysret(Event *event) { +- debug(DEBUG_FUNCTION, "handle_arch_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); +- if (event->proc->state != STATE_IGNORED) { +- if (opt_T || options.summary) { +- calc_time_spent(event->proc); +- } +- if (options.syscalls) +- output_syscall_right(event->proc, +- arch_sysname(event->proc, +- event->e_un.sysnum)); +- callstack_pop(event->proc); +- } +- continue_process(event->proc->pid); ++handle_sysret(Event *event) ++{ ++ handle_x_sysret(event, &sysname); ++} ++ ++static void ++handle_arch_sysret(Event *event) ++{ ++ handle_x_sysret(event, &arch_sysname); + } + + static void + output_right_tos(struct process *proc) + { + size_t d = proc->callstack_depth; ++ assert(d > 0); + struct callstack_element *elem = &proc->callstack[d - 1]; +- if (proc->state != STATE_IGNORED) +- output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc); ++ assert(! elem->is_syscall); ++ ++ if (proc->state != STATE_IGNORED) { ++ struct timedelta spent = calc_time_spent(elem->enter_time); ++ if (options.summary) ++ summary_account_call(elem->c_un.libfunc, spent); ++ else ++ output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc, ++ &spent); ++ } + } + + #ifndef ARCH_HAVE_SYMBOL_RET +@@ -645,14 +693,8 @@ handle_breakpoint(Event *event) + + for (i = event->proc->callstack_depth - 1; i >= 0; i--) { + if (brk_addr == event->proc->callstack[i].return_addr) { +- for (j = event->proc->callstack_depth - 1; j > i; j--) { ++ for (j = event->proc->callstack_depth - 1; j > i; j--) + callstack_pop(event->proc); +- } +- if (event->proc->state != STATE_IGNORED) { +- if (opt_T || options.summary) { +- calc_time_spent(event->proc); +- } +- } + + struct library_symbol *libsym = + event->proc->callstack[i].c_un.libfunc; +@@ -705,11 +747,14 @@ handle_breakpoint(Event *event) + /* breakpoint_on_hit may delete its own breakpoint, so we have + * to look it up again. */ + if ((sbp = address2bpstruct(leader, brk_addr)) != NULL) { ++ + if (event->proc->state != STATE_IGNORED + && sbp->libsym != NULL) { + event->proc->stack_pointer = get_stack_pointer(event->proc); + callstack_push_symfunc(event->proc, sbp); +- output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym); ++ if (! options.summary) ++ output_left(LT_TOF_FUNCTION, event->proc, ++ sbp->libsym); + } + + breakpoint_on_continue(sbp, event->proc); +@@ -743,7 +788,7 @@ callstack_push_syscall(struct process *proc, int sysnum) + proc->callstack_depth++; + if (opt_T || options.summary) { + struct timezone tz; +- gettimeofday(&elem->time_spent, &tz); ++ gettimeofday(&elem->enter_time, &tz); + } + } + +@@ -781,7 +826,7 @@ callstack_push_symfunc(struct process *proc, struct breakpoint *bp) + + if (opt_T || options.summary) { + struct timezone tz; +- gettimeofday(&elem->time_spent, &tz); ++ gettimeofday(&elem->enter_time, &tz); + } + } + +diff --git a/libltrace.c b/libltrace.c +index 2d910a1..0112c9f 100644 +--- a/libltrace.c ++++ b/libltrace.c +@@ -32,11 +32,12 @@ + #include + #include + ++#include "backend.h" + #include "common.h" + #include "proc.h" +-#include "read_config_file.h" +-#include "backend.h" + #include "prototype.h" ++#include "read_config_file.h" ++#include "summary.h" + + char *command = NULL; + +diff --git a/options.h b/options.h +index 6c28ed9..d0df3a7 100644 +--- a/options.h ++++ b/options.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2009,2010 Joe Damato + * Copyright (C) 1998,2002,2008 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -103,12 +103,6 @@ int parse_colon_separated_list(const char *paths, struct vect *vec); + /* Vector of struct opt_F_t. */ + extern struct vect opt_F; + +-struct opt_c_struct { +- int count; +- struct timeval tv; +-}; +-extern struct dict *dict_opt_c; +- + extern char **process_options(int argc, char **argv); + + #endif /* _OPTIONS_H_ */ +diff --git a/output.c b/output.c +index edf4522..82b6a5e 100644 +--- a/output.c ++++ b/output.c +@@ -44,16 +44,12 @@ + #include "param.h" + #include "proc.h" + #include "prototype.h" ++#include "summary.h" + #include "type.h" + #include "value.h" + #include "value_dict.h" + +-/* TODO FIXME XXX: include in common.h: */ +-extern struct timeval current_time_spent; +- +-struct dict *dict_opt_c = NULL; +- +-static struct process *current_proc = 0; ++static struct process *current_proc = NULL; + static size_t current_depth = 0; + static int current_column = 0; + +@@ -498,9 +494,8 @@ void + output_left(enum tof type, struct process *proc, + struct library_symbol *libsym) + { +- if (options.summary) { +- return; +- } ++ assert(! options.summary); ++ + if (current_proc) { + fprintf(options.output, " \n"); + current_column = 0; +@@ -572,70 +567,21 @@ output_left(enum tof type, struct process *proc, + stel->out.need_delim = need_delim; + } + +-static void +-free_stringp_cb(const char **stringp, void *data) +-{ +- free((char *)*stringp); +-} +- + void +-output_right(enum tof type, struct process *proc, struct library_symbol *libsym) ++output_right(enum tof type, struct process *proc, struct library_symbol *libsym, ++ struct timedelta *spent) + { ++ assert(! options.summary); ++ + struct prototype *func = lookup_symbol_prototype(proc, libsym); + if (func == NULL) + return; + +-again: +- if (options.summary) { +- if (dict_opt_c == NULL) { +- dict_opt_c = malloc(sizeof(*dict_opt_c)); +- if (dict_opt_c == NULL) { +- oom: +- fprintf(stderr, +- "Can't allocate memory for " +- "keeping track of -c.\n"); +- free(dict_opt_c); +- options.summary = 0; +- goto again; +- } +- DICT_INIT(dict_opt_c, char *, struct opt_c_struct, +- dict_hash_string, dict_eq_string, NULL); +- } +- +- struct opt_c_struct *st +- = DICT_FIND_REF(dict_opt_c, &libsym->name, +- struct opt_c_struct); +- if (st == NULL) { +- const char *na = strdup(libsym->name); +- struct opt_c_struct new_st = {.count = 0, .tv = {0, 0}}; +- if (na == NULL +- || DICT_INSERT(dict_opt_c, &na, &new_st) < 0) { +- free((char *)na); +- DICT_DESTROY(dict_opt_c, const char *, +- struct opt_c_struct, +- free_stringp_cb, NULL, NULL); +- goto oom; +- } +- st = DICT_FIND_REF(dict_opt_c, &libsym->name, +- struct opt_c_struct); +- assert(st != NULL); +- } +- +- if (st->tv.tv_usec + current_time_spent.tv_usec > 1000000) { +- st->tv.tv_usec += current_time_spent.tv_usec - 1000000; +- st->tv.tv_sec++; +- } else { +- st->tv.tv_usec += current_time_spent.tv_usec; +- } +- st->count++; +- st->tv.tv_sec += current_time_spent.tv_sec; +- return; +- } +- +- if (current_proc && (current_proc != proc || +- current_depth != proc->callstack_depth)) { ++ if (current_proc != NULL ++ && (current_proc != proc ++ || current_depth != proc->callstack_depth)) { + fprintf(options.output, " \n"); +- current_proc = 0; ++ current_proc = NULL; + } + if (current_proc != proc) { + begin_of_line(proc, type == LT_TOF_FUNCTIONR, 1); +@@ -689,10 +635,12 @@ again: + value_destroy(&retval); + + if (opt_T) { ++ assert(spent != NULL); + fprintf(options.output, " <%lu.%06d>", +- (unsigned long)current_time_spent.tv_sec, +- (int)current_time_spent.tv_usec); ++ (unsigned long) spent->tm.tv_sec, ++ (int) spent->tm.tv_usec); + } ++ + fprintf(options.output, "\n"); + + #if defined(HAVE_LIBUNWIND) +@@ -746,7 +694,7 @@ again: + } + #endif /* defined(HAVE_LIBUNWIND) */ + +- current_proc = 0; ++ current_proc = NULL; + current_column = 0; + } + +diff --git a/output.h b/output.h +index b9f0518..2e74d61 100644 +--- a/output.h ++++ b/output.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011, 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011, 2012, 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -28,7 +28,8 @@ void output_line(struct process *proc, const char *fmt, ...); + void output_left(enum tof type, struct process *proc, + struct library_symbol *libsym); + void output_right(enum tof type, struct process *proc, +- struct library_symbol *libsym); ++ struct library_symbol *libsym, ++ struct timedelta *spent); + + /* This function is for emitting lists of comma-separated strings. + * +diff --git a/proc.h b/proc.h +index e8032fa..64f8fe2 100644 +--- a/proc.h ++++ b/proc.h +@@ -66,7 +66,7 @@ struct callstack_element { + } c_un; + int is_syscall; + arch_addr_t return_addr; +- struct timeval time_spent; ++ struct timeval enter_time; + struct fetch_context *fetch_context; + struct value_dict *arguments; + struct output_state out; +diff --git a/summary.c b/summary.c +index 9e22086..9103f71 100644 +--- a/summary.c ++++ b/summary.c +@@ -22,11 +22,15 @@ + + #include "config.h" + ++#include + #include + #include +-#include ++#include + +-#include "common.h" ++#include "summary.h" ++#include "dict.h" ++#include "library.h" ++#include "options.h" + + struct entry_st { + const char *name; +@@ -40,6 +44,32 @@ struct fill_struct_data { + unsigned long tot_usecs; + }; + ++struct opt_c_struct { ++ int count; ++ struct timeval tv; ++}; ++ ++static struct dict *dict_opt_c; ++ ++struct timedelta ++calc_time_spent(struct timeval start) ++{ ++ struct timeval tv; ++ gettimeofday(&tv, NULL); ++ ++ struct timeval diff; ++ diff.tv_sec = tv.tv_sec - start.tv_sec; ++ if (tv.tv_usec >= start.tv_usec) { ++ diff.tv_usec = tv.tv_usec - start.tv_usec; ++ } else { ++ diff.tv_sec--; ++ diff.tv_usec = 1000000 + tv.tv_usec - start.tv_usec; ++ } ++ ++ struct timedelta ret = { diff }; ++ return ret; ++} ++ + static enum callback_status + fill_struct(const char **namep, struct opt_c_struct *st, void *u) + { +@@ -114,3 +144,58 @@ show_summary(void) + + vect_destroy(&cdata.entries, NULL, NULL); + } ++ ++static void ++free_stringp_cb(const char **stringp, void *data) ++{ ++ free((char *)*stringp); ++} ++ ++void ++summary_account_call(struct library_symbol *libsym, struct timedelta spent) ++{ ++ assert(options.summary); ++ ++ if (dict_opt_c == NULL) { ++ dict_opt_c = malloc(sizeof(*dict_opt_c)); ++ if (dict_opt_c == NULL) { ++ oom: ++ fprintf(stderr, ++ "Can't allocate memory for " ++ "keeping track of -c.\n"); ++ free(dict_opt_c); ++ options.summary = 0; ++ return; ++ } ++ DICT_INIT(dict_opt_c, char *, struct opt_c_struct, ++ dict_hash_string, dict_eq_string, NULL); ++ } ++ ++ struct opt_c_struct *st = DICT_FIND_REF(dict_opt_c, &libsym->name, ++ struct opt_c_struct); ++ if (st == NULL) { ++ const char *na = strdup(libsym->name); ++ struct opt_c_struct new_st = {.count = 0, .tv = {0, 0}}; ++ if (na == NULL ++ || DICT_INSERT(dict_opt_c, &na, &new_st) < 0) { ++ free((char *) na); ++ DICT_DESTROY(dict_opt_c, const char *, ++ struct opt_c_struct, ++ free_stringp_cb, NULL, NULL); ++ goto oom; ++ } ++ st = DICT_FIND_REF(dict_opt_c, &libsym->name, ++ struct opt_c_struct); ++ assert(st != NULL); ++ } ++ ++ if (st->tv.tv_usec + spent.tm.tv_usec > 1000000) { ++ st->tv.tv_usec += spent.tm.tv_usec - 1000000; ++ st->tv.tv_sec++; ++ } else { ++ st->tv.tv_usec += spent.tm.tv_usec; ++ } ++ st->count++; ++ st->tv.tv_sec += spent.tm.tv_sec; ++ return; ++} +diff --git a/summary.h b/summary.h +new file mode 100644 +index 0000000..f680ef9 +--- /dev/null ++++ b/summary.h +@@ -0,0 +1,35 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef _SUMMARY_H_ ++#define _SUMMARY_H_ ++ ++#include "forward.h" ++ ++struct timedelta { ++ struct timeval tm; ++}; ++ ++struct timedelta calc_time_spent(struct timeval start); ++void summary_account_call(struct library_symbol *libsym, ++ struct timedelta spent); ++void show_summary(void); ++ ++#endif /* _SUMMARY_H_ */ +diff --git a/testsuite/ltrace.minor/trace-exec.exp b/testsuite/ltrace.minor/trace-exec.exp +index 7a953de..57260f8 100644 +--- a/testsuite/ltrace.minor/trace-exec.exp ++++ b/testsuite/ltrace.minor/trace-exec.exp +@@ -1,5 +1,5 @@ + # This file is part of ltrace. +-# Copyright (C) 2012 Petr Machata, Red Hat Inc. ++# Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. + # + # This program is free software; you can redistribute it and/or + # modify it under the terms of the GNU General Public License as +@@ -16,22 +16,30 @@ + # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + # 02110-1301 USA + +-ltraceMatch [ltraceRun -xmain -- [ltraceCompile {} [ltraceSource c { ++set bin1 [ltraceCompile {} [ltraceSource c { + #include + #include + int main(int argc, char ** argv) { + execl(argv[1], argv[1], NULL); + abort(); + } +-}]] [ltraceCompile {} [ltraceSource c { ++}]] ++ ++set bin2 [ltraceCompile {} [ltraceSource c { + #include + int main(void) { + return puts("Hello, World."); + } +-}]]] { ++}]] ++ ++ltraceMatch [ltraceRun -xmain -- $bin1 $bin2] { + {{^execl\(} == 1} + {{^puts\(.*\) .*= 14} == 1} + {{^main\(} == 2} + } + ++ltraceMatch [ltraceRun -c -- $bin1 $bin2] { ++ {{exec} > 0} ++} ++ + ltraceDone +-- +1.7.6.5 + diff --git a/ltrace-0.7.91-arm.patch b/ltrace-0.7.91-arm.patch new file mode 100644 index 0000000..84c7d0d --- /dev/null +++ b/ltrace-0.7.91-arm.patch @@ -0,0 +1,610 @@ +diff --git a/ltrace-elf.c b/ltrace-elf.c +index 92b642b..6f86d56 100644 +--- a/ltrace-elf.c ++++ b/ltrace-elf.c +@@ -531,6 +531,38 @@ elf_read_relocs(struct ltelf *lte, Elf_Scn *scn, GElf_Shdr *shdr, + return 0; + } + ++int ++elf_load_dynamic_entry(struct ltelf *lte, int tag, GElf_Addr *valuep) ++{ ++ Elf_Scn *scn; ++ GElf_Shdr shdr; ++ if (elf_get_section_type(lte, SHT_DYNAMIC, &scn, &shdr) < 0 ++ || scn == NULL) { ++ fail: ++ fprintf(stderr, "Couldn't get SHT_DYNAMIC: %s\n", ++ elf_errmsg(-1)); ++ return -1; ++ } ++ ++ Elf_Data *data = elf_loaddata(scn, &shdr); ++ if (data == NULL) ++ goto fail; ++ ++ size_t j; ++ for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) { ++ GElf_Dyn dyn; ++ if (gelf_getdyn(data, j, &dyn) == NULL) ++ goto fail; ++ ++ if(dyn.d_tag == tag) { ++ *valuep = dyn.d_un.d_ptr; ++ return 0; ++ } ++ } ++ ++ return -1; ++} ++ + static int + ltelf_read_elf(struct ltelf *lte, const char *filename) + { +diff --git a/ltrace-elf.h b/ltrace-elf.h +index ea14512..db4ffe9 100644 +--- a/ltrace-elf.h ++++ b/ltrace-elf.h +@@ -139,6 +139,10 @@ struct elf_each_symbol_t { + int elf_read_relocs(struct ltelf *lte, Elf_Scn *scn, GElf_Shdr *shdr, + struct vect *rela_vec); + ++/* Read a given DT_ TAG from LTE. Value is returned in *VALUEP. ++ * Returns 0 on success or a negative value on failure. */ ++int elf_load_dynamic_entry(struct ltelf *lte, int tag, GElf_Addr *valuep); ++ + /* Read, respectively, 1, 2, 4, or 8 bytes from Elf data at given + * OFFSET, and store it in *RETP. Returns 0 on success or a negative + * value if there's not enough data. */ +diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h +index 58a7fdf..6d0d902 100644 +--- a/sysdeps/linux-gnu/arm/arch.h ++++ b/sysdeps/linux-gnu/arm/arch.h +@@ -22,6 +22,8 @@ + #ifndef LTRACE_ARM_ARCH_H + #define LTRACE_ARM_ARCH_H + ++#include ++ + #define ARCH_HAVE_ENABLE_BREAKPOINT 1 + #define ARCH_HAVE_DISABLE_BREAKPOINT 1 + +@@ -47,7 +49,7 @@ struct arch_breakpoint_data { + + #define ARCH_HAVE_LTELF_DATA + struct arch_ltelf_data { +- /* We have this only for the hooks. */ ++ Elf_Data *jmprel_data; + }; + + #define ARCH_HAVE_LIBRARY_DATA +diff --git a/sysdeps/linux-gnu/arm/fetch.c b/sysdeps/linux-gnu/arm/fetch.c +index 5081d78..b500448 100644 +--- a/sysdeps/linux-gnu/arm/fetch.c ++++ b/sysdeps/linux-gnu/arm/fetch.c +@@ -32,200 +32,12 @@ + #include "backend.h" + #include "fetch.h" + #include "library.h" +-#include "ltrace-elf.h" + #include "proc.h" + #include "ptrace.h" + #include "regs.h" + #include "type.h" + #include "value.h" + +-static int +-get_hardfp(uint64_t abi_vfp_args) +-{ +- if (abi_vfp_args == 2) +- fprintf(stderr, +- "Tag_ABI_VFP_args value 2 (tool chain-specific " +- "conventions) not supported.\n"); +- return abi_vfp_args == 1; +-} +- +-int +-arch_elf_init(struct ltelf *lte, struct library *lib) +-{ +- /* Nothing in this section is strictly critical. It's not +- * that much of a deal if we fail to guess right whether the +- * ABI is softfp or hardfp. */ +- unsigned hardfp = 0; +- +- Elf_Scn *scn; +- Elf_Data *data; +- GElf_Shdr shdr; +- if (elf_get_section_type(lte, SHT_ARM_ATTRIBUTES, &scn, &shdr) < 0 +- || (scn != NULL && (data = elf_loaddata(scn, &shdr)) == NULL)) { +- fprintf(stderr, +- "Error when obtaining ARM attribute section: %s\n", +- elf_errmsg(-1)); +- goto done; +- +- } else if (scn != NULL && data != NULL) { +- GElf_Xword offset = 0; +- uint8_t version; +- if (elf_read_next_u8(data, &offset, &version) < 0) { +- goto done; +- } else if (version != 'A') { +- fprintf(stderr, "Unsupported ARM attribute section " +- "version %d ('%c').\n", version, version); +- goto done; +- } +- +- do { +- const char signature[] = "aeabi"; +- /* N.B. LEN is including the length field +- * itself. */ +- uint32_t sec_len; +- if (elf_read_u32(data, offset, &sec_len) < 0 +- || !elf_can_read_next(data, offset, sec_len)) { +- goto done; +- } +- const GElf_Xword next_offset = offset + sec_len; +- offset += 4; +- +- if (sec_len < 4 + sizeof signature +- || strcmp(signature, data->d_buf + offset) != 0) +- goto skip; +- offset += sizeof signature; +- +- const GElf_Xword offset0 = offset; +- uint64_t tag; +- uint32_t sub_len; +- if (elf_read_next_uleb128(data, &offset, &tag) < 0 +- || elf_read_next_u32(data, &offset, &sub_len) < 0 +- || !elf_can_read_next(data, offset0, sub_len)) +- goto done; +- +- if (tag != 1) +- /* IHI0045D_ABI_addenda: "section and +- * symbol attributes are deprecated +- * [...] consumers are permitted to +- * ignore them." */ +- goto skip; +- +- while (offset < offset0 + sub_len) { +- if (elf_read_next_uleb128(data, +- &offset, &tag) < 0) +- goto done; +- +- switch (tag) { +- uint64_t v; +- case 6: /* Tag_CPU_arch */ +- case 7: /* Tag_CPU_arch_profile */ +- case 8: /* Tag_ARM_ISA_use */ +- case 9: /* Tag_THUMB_ISA_use */ +- case 10: /* Tag_FP_arch */ +- case 11: /* Tag_WMMX_arch */ +- case 12: /* Tag_Advanced_SIMD_arch */ +- case 13: /* Tag_PCS_config */ +- case 14: /* Tag_ABI_PCS_R9_use */ +- case 15: /* Tag_ABI_PCS_RW_data */ +- case 16: /* Tag_ABI_PCS_RO_data */ +- case 17: /* Tag_ABI_PCS_GOT_use */ +- case 18: /* Tag_ABI_PCS_wchar_t */ +- case 19: /* Tag_ABI_FP_rounding */ +- case 20: /* Tag_ABI_FP_denormal */ +- case 21: /* Tag_ABI_FP_exceptions */ +- case 22: /* Tag_ABI_FP_user_exceptions */ +- case 23: /* Tag_ABI_FP_number_model */ +- case 24: /* Tag_ABI_align_needed */ +- case 25: /* Tag_ABI_align_preserved */ +- case 26: /* Tag_ABI_enum_size */ +- case 27: /* Tag_ABI_HardFP_use */ +- case 28: /* Tag_ABI_VFP_args */ +- case 29: /* Tag_ABI_WMMX_args */ +- case 30: /* Tag_ABI_optimization_goals */ +- case 31: /* Tag_ABI_FP_optimization_goals */ +- case 32: /* Tag_compatibility */ +- case 34: /* Tag_CPU_unaligned_access */ +- case 36: /* Tag_FP_HP_extension */ +- case 38: /* Tag_ABI_FP_16bit_format */ +- case 42: /* Tag_MPextension_use */ +- case 70: /* Tag_MPextension_use as well */ +- case 44: /* Tag_DIV_use */ +- case 64: /* Tag_nodefaults */ +- case 66: /* Tag_T2EE_use */ +- case 68: /* Tag_Virtualization_use */ +- uleb128: +- if (elf_read_next_uleb128 +- (data, &offset, &v) < 0) +- goto done; +- if (tag == 28) +- hardfp = get_hardfp(v); +- if (tag != 32) +- continue; +- +- /* Tag 32 has two arguments, +- * fall through. */ +- +- case 4: /* Tag_CPU_raw_name */ +- case 5: /* Tag_CPU_name */ +- case 65: /* Tag_also_compatible_with */ +- case 67: /* Tag_conformance */ +- ntbs: +- offset += strlen(data->d_buf +- + offset) + 1; +- continue; +- } +- +- /* Handle unknown tags in a generic +- * manner, if possible. */ +- if (tag <= 32) { +- fprintf(stderr, +- "Unknown tag %lld " +- "at offset %#llx " +- "of ARM attribute section.", +- tag, offset); +- goto skip; +- } else if (tag % 2 == 0) { +- goto uleb128; +- } else { +- goto ntbs; +- } +- } +- +- skip: +- offset = next_offset; +- +- } while (elf_can_read_next(data, offset, 1)); +- +- } +- +-done: +- lib->arch.hardfp = hardfp; +- return 0; +-} +- +-void +-arch_elf_destroy(struct ltelf *lte) +-{ +-} +- +-int +-arch_library_init(struct library *lib) +-{ +- return 0; +-} +- +-void +-arch_library_destroy(struct library *lib) +-{ +-} +- +-int +-arch_library_clone(struct library *retp, struct library *lib) +-{ +- retp->arch = lib->arch; +- return 0; +-} +- + enum { + /* How many (double) VFP registers the AAPCS uses for + * parameter passing. */ +diff --git a/sysdeps/linux-gnu/arm/plt.c b/sysdeps/linux-gnu/arm/plt.c +index d1bf7ca..9e9e37f 100644 +--- a/sysdeps/linux-gnu/arm/plt.c ++++ b/sysdeps/linux-gnu/arm/plt.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Zach Welch, CodeSourcery + * Copyright (C) 2004,2008,2009 Juan Cespedes + * +@@ -20,20 +21,205 @@ + */ + + #include ++#include ++#include + + #include "proc.h" + #include "library.h" + #include "ltrace-elf.h" + + static int ++get_hardfp(uint64_t abi_vfp_args) ++{ ++ if (abi_vfp_args == 2) ++ fprintf(stderr, ++ "Tag_ABI_VFP_args value 2 (tool chain-specific " ++ "conventions) not supported.\n"); ++ return abi_vfp_args == 1; ++} ++ ++int ++arch_elf_init(struct ltelf *lte, struct library *lib) ++{ ++ GElf_Addr jmprel_addr; ++ Elf_Scn *jmprel_sec; ++ GElf_Shdr jmprel_shdr; ++ if (elf_load_dynamic_entry(lte, DT_JMPREL, &jmprel_addr) < 0 ++ || elf_get_section_covering(lte, jmprel_addr, ++ &jmprel_sec, &jmprel_shdr) < 0 ++ || jmprel_sec == NULL) ++ return -1; ++ ++ lte->arch.jmprel_data = elf_loaddata(jmprel_sec, &jmprel_shdr); ++ if (lte->arch.jmprel_data == NULL) ++ return -1; ++ ++ /* Nothing in this section is strictly critical. It's not ++ * that much of a deal if we fail to guess right whether the ++ * ABI is softfp or hardfp. */ ++ unsigned hardfp = 0; ++ ++ Elf_Scn *scn; ++ Elf_Data *data; ++ GElf_Shdr shdr; ++ if (elf_get_section_type(lte, SHT_ARM_ATTRIBUTES, &scn, &shdr) < 0 ++ || (scn != NULL && (data = elf_loaddata(scn, &shdr)) == NULL)) { ++ fprintf(stderr, ++ "Error when obtaining ARM attribute section: %s\n", ++ elf_errmsg(-1)); ++ goto done; ++ ++ } else if (scn != NULL && data != NULL) { ++ GElf_Xword offset = 0; ++ uint8_t version; ++ if (elf_read_next_u8(data, &offset, &version) < 0) { ++ goto done; ++ } else if (version != 'A') { ++ fprintf(stderr, "Unsupported ARM attribute section " ++ "version %d ('%c').\n", version, version); ++ goto done; ++ } ++ ++ do { ++ const char signature[] = "aeabi"; ++ /* N.B. LEN is including the length field ++ * itself. */ ++ uint32_t sec_len; ++ if (elf_read_u32(data, offset, &sec_len) < 0 ++ || !elf_can_read_next(data, offset, sec_len)) { ++ goto done; ++ } ++ const GElf_Xword next_offset = offset + sec_len; ++ offset += 4; ++ ++ if (sec_len < 4 + sizeof signature ++ || strcmp(signature, data->d_buf + offset) != 0) ++ goto skip; ++ offset += sizeof signature; ++ ++ const GElf_Xword offset0 = offset; ++ uint64_t tag; ++ uint32_t sub_len; ++ if (elf_read_next_uleb128(data, &offset, &tag) < 0 ++ || elf_read_next_u32(data, &offset, &sub_len) < 0 ++ || !elf_can_read_next(data, offset0, sub_len)) ++ goto done; ++ ++ if (tag != 1) ++ /* IHI0045D_ABI_addenda: "section and ++ * symbol attributes are deprecated ++ * [...] consumers are permitted to ++ * ignore them." */ ++ goto skip; ++ ++ while (offset < offset0 + sub_len) { ++ if (elf_read_next_uleb128(data, ++ &offset, &tag) < 0) ++ goto done; ++ ++ switch (tag) { ++ uint64_t v; ++ case 6: /* Tag_CPU_arch */ ++ case 7: /* Tag_CPU_arch_profile */ ++ case 8: /* Tag_ARM_ISA_use */ ++ case 9: /* Tag_THUMB_ISA_use */ ++ case 10: /* Tag_FP_arch */ ++ case 11: /* Tag_WMMX_arch */ ++ case 12: /* Tag_Advanced_SIMD_arch */ ++ case 13: /* Tag_PCS_config */ ++ case 14: /* Tag_ABI_PCS_R9_use */ ++ case 15: /* Tag_ABI_PCS_RW_data */ ++ case 16: /* Tag_ABI_PCS_RO_data */ ++ case 17: /* Tag_ABI_PCS_GOT_use */ ++ case 18: /* Tag_ABI_PCS_wchar_t */ ++ case 19: /* Tag_ABI_FP_rounding */ ++ case 20: /* Tag_ABI_FP_denormal */ ++ case 21: /* Tag_ABI_FP_exceptions */ ++ case 22: /* Tag_ABI_FP_user_exceptions */ ++ case 23: /* Tag_ABI_FP_number_model */ ++ case 24: /* Tag_ABI_align_needed */ ++ case 25: /* Tag_ABI_align_preserved */ ++ case 26: /* Tag_ABI_enum_size */ ++ case 27: /* Tag_ABI_HardFP_use */ ++ case 28: /* Tag_ABI_VFP_args */ ++ case 29: /* Tag_ABI_WMMX_args */ ++ case 30: /* Tag_ABI_optimization_goals */ ++ case 31: /* Tag_ABI_FP_optimization_goals */ ++ case 32: /* Tag_compatibility */ ++ case 34: /* Tag_CPU_unaligned_access */ ++ case 36: /* Tag_FP_HP_extension */ ++ case 38: /* Tag_ABI_FP_16bit_format */ ++ case 42: /* Tag_MPextension_use */ ++ case 70: /* Tag_MPextension_use as well */ ++ case 44: /* Tag_DIV_use */ ++ case 64: /* Tag_nodefaults */ ++ case 66: /* Tag_T2EE_use */ ++ case 68: /* Tag_Virtualization_use */ ++ uleb128: ++ if (elf_read_next_uleb128 ++ (data, &offset, &v) < 0) ++ goto done; ++ if (tag == 28) ++ hardfp = get_hardfp(v); ++ if (tag != 32) ++ continue; ++ ++ /* Tag 32 has two arguments, ++ * fall through. */ ++ ++ case 4: /* Tag_CPU_raw_name */ ++ case 5: /* Tag_CPU_name */ ++ case 65: /* Tag_also_compatible_with */ ++ case 67: /* Tag_conformance */ ++ ntbs: ++ offset += strlen(data->d_buf ++ + offset) + 1; ++ continue; ++ } ++ ++ /* Handle unknown tags in a generic ++ * manner, if possible. */ ++ if (tag <= 32) { ++ fprintf(stderr, ++ "Unknown tag %lld " ++ "at offset %#llx " ++ "of ARM attribute section.", ++ tag, offset); ++ goto skip; ++ } else if (tag % 2 == 0) { ++ goto uleb128; ++ } else { ++ goto ntbs; ++ } ++ } ++ ++ skip: ++ offset = next_offset; ++ ++ } while (elf_can_read_next(data, offset, 1)); ++ ++ } ++ ++done: ++ lib->arch.hardfp = hardfp; ++ return 0; ++} ++ ++void ++arch_elf_destroy(struct ltelf *lte) ++{ ++} ++ ++static int + arch_plt_entry_has_stub(struct ltelf *lte, size_t off) { +- uint16_t op = *(uint16_t *)((char *)lte->relplt->d_buf + off); ++ char *buf = (char *) lte->arch.jmprel_data->d_buf; ++ uint16_t op = *(uint16_t *) (buf + off); + return op == 0x4778; + } + + GElf_Addr + arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { +- size_t start = lte->relplt->d_size + 12; ++ size_t start = lte->arch.jmprel_data->d_size + 12; + size_t off = start + 20, i; + for (i = 0; i < ndx; i++) + off += arch_plt_entry_has_stub(lte, off) ? 16 : 12; +@@ -47,3 +233,21 @@ sym2addr(struct process *proc, struct library_symbol *sym) + { + return sym->enter_addr; + } ++ ++int ++arch_library_init(struct library *lib) ++{ ++ return 0; ++} ++ ++void ++arch_library_destroy(struct library *lib) ++{ ++} ++ ++int ++arch_library_clone(struct library *retp, struct library *lib) ++{ ++ retp->arch = lib->arch; ++ return 0; ++} +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 5e3ffe1..3ec1397 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -402,38 +402,6 @@ get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, Elf_Data *plt_data) + } + + static int +-load_dynamic_entry(struct ltelf *lte, int tag, GElf_Addr *valuep) +-{ +- Elf_Scn *scn; +- GElf_Shdr shdr; +- if (elf_get_section_type(lte, SHT_DYNAMIC, &scn, &shdr) < 0 +- || scn == NULL) { +- fail: +- fprintf(stderr, "Couldn't get SHT_DYNAMIC: %s\n", +- elf_errmsg(-1)); +- return -1; +- } +- +- Elf_Data *data = elf_loaddata(scn, &shdr); +- if (data == NULL) +- goto fail; +- +- size_t j; +- for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) { +- GElf_Dyn dyn; +- if (gelf_getdyn(data, j, &dyn) == NULL) +- goto fail; +- +- if(dyn.d_tag == tag) { +- *valuep = dyn.d_un.d_ptr; +- return 0; +- } +- } +- +- return -1; +-} +- +-static int + nonzero_data(Elf_Data *data) + { + /* We are not supposed to get here if there's no PLT. */ +@@ -488,8 +456,8 @@ arch_elf_init(struct ltelf *lte, struct library *lib) + Elf_Scn *rela_sec; + GElf_Shdr rela_shdr; + if ((lte->ehdr.e_machine == EM_PPC64 || lte->arch.secure_plt) +- && load_dynamic_entry(lte, DT_RELA, &rela) == 0 +- && load_dynamic_entry(lte, DT_RELASZ, &relasz) == 0 ++ && elf_load_dynamic_entry(lte, DT_RELA, &rela) == 0 ++ && elf_load_dynamic_entry(lte, DT_RELASZ, &relasz) == 0 + && elf_get_section_covering(lte, rela, &rela_sec, &rela_shdr) == 0 + && rela_sec != NULL) { + +@@ -509,7 +477,7 @@ arch_elf_init(struct ltelf *lte, struct library *lib) + + if (lte->ehdr.e_machine == EM_PPC && lte->arch.secure_plt) { + GElf_Addr ppcgot; +- if (load_dynamic_entry(lte, DT_PPC_GOT, &ppcgot) < 0) { ++ if (elf_load_dynamic_entry(lte, DT_PPC_GOT, &ppcgot) < 0) { + fprintf(stderr, "couldn't find DT_PPC_GOT\n"); + return -1; + } +@@ -522,7 +490,8 @@ arch_elf_init(struct ltelf *lte, struct library *lib) + + } else if (lte->ehdr.e_machine == EM_PPC64) { + GElf_Addr glink_vma; +- if (load_dynamic_entry(lte, DT_PPC64_GLINK, &glink_vma) < 0) { ++ if (elf_load_dynamic_entry(lte, DT_PPC64_GLINK, ++ &glink_vma) < 0) { + fprintf(stderr, "couldn't find DT_PPC64_GLINK\n"); + return -1; + } +@@ -532,8 +501,8 @@ arch_elf_init(struct ltelf *lte, struct library *lib) + + } else { + /* By exhaustion--PPC32 BSS. */ +- if (load_dynamic_entry(lte, DT_PLTGOT, +- &lib->arch.pltgot_addr) < 0) { ++ if (elf_load_dynamic_entry(lte, DT_PLTGOT, ++ &lib->arch.pltgot_addr) < 0) { + fprintf(stderr, "couldn't find DT_PLTGOT\n"); + return -1; + } diff --git a/ltrace-0.7.91-breakpoint-on_install.patch b/ltrace-0.7.91-breakpoint-on_install.patch new file mode 100644 index 0000000..15959a3 --- /dev/null +++ b/ltrace-0.7.91-breakpoint-on_install.patch @@ -0,0 +1,81 @@ +From 56134ff5442bee4e128b189bb86cfc97dcb6f60a Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Fri, 10 Jan 2014 20:05:15 +0100 +Subject: [PATCH 1/2] Add a new per-breakpoint callback on_install + +--- + breakpoint.h | 9 ++++++++- + breakpoints.c | 11 ++++++++++- + 2 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/breakpoint.h b/breakpoint.h +index 95964a8..c36f673 100644 +--- a/breakpoint.h ++++ b/breakpoint.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -46,6 +46,7 @@ + struct bp_callbacks { + void (*on_hit)(struct breakpoint *bp, struct process *proc); + void (*on_continue)(struct breakpoint *bp, struct process *proc); ++ void (*on_install)(struct breakpoint *bp, struct process *proc); + void (*on_retract)(struct breakpoint *bp, struct process *proc); + + /* Create a new breakpoint that should handle return from the +@@ -84,6 +85,12 @@ void breakpoint_on_continue(struct breakpoint *bp, struct process *proc); + * the instruction underneath it). */ + void breakpoint_on_retract(struct breakpoint *bp, struct process *proc); + ++/* Call ON_INSTALL handler of BP, if any is set. This should be ++ * called after the breakpoint is enabled for the first time, not ++ * every time it's enabled (such as after stepping over a site of a ++ * temporarily disabled breakpoint). */ ++void breakpoint_on_install(struct breakpoint *bp, struct process *proc); ++ + /* Call GET_RETURN_BP handler of BP, if any is set. If none is set, + * call CREATE_DEFAULT_RETURN_BP to obtain one. */ + int breakpoint_get_return_bp(struct breakpoint **ret, +diff --git a/breakpoints.c b/breakpoints.c +index 947cb71..c3fa275 100644 +--- a/breakpoints.c ++++ b/breakpoints.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2006,2007,2011,2012,2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2006,2007,2011,2012,2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * Copyright (C) 1998,2001,2002,2003,2007,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -85,6 +85,14 @@ breakpoint_on_retract(struct breakpoint *bp, struct process *proc) + (bp->cbs->on_retract)(bp, proc); + } + ++void ++breakpoint_on_install(struct breakpoint *bp, struct process *proc) ++{ ++ assert(bp != NULL); ++ if (bp->cbs != NULL && bp->cbs->on_install != NULL) ++ (bp->cbs->on_install)(bp, proc); ++} ++ + int + breakpoint_get_return_bp(struct breakpoint **ret, + struct breakpoint *bp, struct process *proc) +@@ -229,6 +237,7 @@ breakpoint_turn_on(struct breakpoint *bp, struct process *proc) + if (bp->enabled == 1) { + assert(proc->pid != 0); + enable_breakpoint(proc, bp); ++ breakpoint_on_install(bp, proc); + } + return 0; + } +-- +1.7.6.5 + diff --git a/ltrace-0.7.91-cant_open.patch b/ltrace-0.7.91-cant_open.patch new file mode 100644 index 0000000..4d3a0d4 --- /dev/null +++ b/ltrace-0.7.91-cant_open.patch @@ -0,0 +1,36 @@ +diff -urp ltrace-0.7.91/libltrace.c master/libltrace.c +--- ltrace-0.7.91/libltrace.c 2014-01-14 16:31:37.696174464 +0100 ++++ master/libltrace.c 2013-11-21 14:06:38.623701688 +0100 +@@ -113,9 +117,13 @@ ltrace_init(int argc, char **argv) { + if (command) { + /* Check that the binary ABI is supported before + * calling execute_program. */ +- struct ltelf lte; +- ltelf_init(<e, command); +- ltelf_destroy(<e); ++ { ++ struct ltelf lte; ++ if (ltelf_init(<e, command) == 0) ++ ltelf_destroy(<e); ++ else ++ exit(EXIT_FAILURE); ++ } + + pid_t pid = execute_program(command, argv); + struct process *proc = open_program(command, pid); +diff -urp ltrace-0.7.91/ltrace-elf.c master/ltrace-elf.c +--- ltrace-0.7.91/ltrace-elf.c 2014-01-14 16:31:37.688174420 +0100 ++++ master/ltrace-elf.c 2013-11-22 18:17:11.767721609 +0100 +@@ -361,8 +361,11 @@ ltelf_init(struct ltelf *lte, const char + { + memset(lte, 0, sizeof *lte); + lte->fd = open(filename, O_RDONLY); +- if (lte->fd == -1) ++ if (lte->fd == -1) { ++ fprintf(stderr, "Can't open %s: %s\n", filename, ++ strerror(errno)); + return 1; ++ } + + elf_version(EV_CURRENT); + diff --git a/ltrace-0.7.91-cet.patch b/ltrace-0.7.91-cet.patch new file mode 100644 index 0000000..a320682 --- /dev/null +++ b/ltrace-0.7.91-cet.patch @@ -0,0 +1,121 @@ +diff -rup a/ltrace-elf.c b/ltrace-elf.c +--- a/ltrace-elf.c 2019-02-28 17:32:49.873659818 -0500 ++++ b/ltrace-elf.c 2019-02-28 17:36:32.426779439 -0500 +@@ -639,7 +639,21 @@ ltelf_read_elf(struct ltelf *lte, const + } + } else if (shdr.sh_type == SHT_PROGBITS + || shdr.sh_type == SHT_NOBITS) { +- if (strcmp(name, ".plt") == 0) { ++ if (strcmp(name, ".plt") == 0 ++ && lte->second_plt_seen == 0) { ++ lte->plt_addr = shdr.sh_addr; ++ lte->plt_size = shdr.sh_size; ++ lte->plt_data = elf_loaddata(scn, &shdr); ++ if (lte->plt_data == NULL) ++ fprintf(stderr, ++ "Can't load .plt data\n"); ++ lte->plt_flags = shdr.sh_flags; ++ } ++ /* An Intel CET binary has two PLTs; the ++ initial PLTGOT points to the second ++ one. */ ++ else if (strcmp(name, ".plt.sec") == 0) { ++ lte->second_plt_seen = 1; + lte->plt_addr = shdr.sh_addr; + lte->plt_size = shdr.sh_size; + lte->plt_data = elf_loaddata(scn, &shdr); +diff -rup a/ltrace-elf.h b/ltrace-elf.h +--- a/ltrace-elf.h 2019-02-28 17:32:49.874660328 -0500 ++++ b/ltrace-elf.h 2019-02-28 17:36:32.428779868 -0500 +@@ -45,6 +45,7 @@ struct ltelf { + Elf_Data *dynsym; + size_t dynsym_count; + const char *dynstr; ++ int second_plt_seen; + GElf_Addr plt_addr; + GElf_Word plt_flags; + size_t plt_size; +diff -rup a/sysdeps/linux-gnu/x86/plt.c b/sysdeps/linux-gnu/x86/plt.c +--- a/sysdeps/linux-gnu/x86/plt.c 2019-02-28 17:32:49.991720041 -0500 ++++ b/sysdeps/linux-gnu/x86/plt.c 2019-02-28 17:36:32.429780083 -0500 +@@ -28,18 +28,18 @@ + #include "trace.h" + + static GElf_Addr +-x86_plt_offset(uint32_t i) ++x86_plt_offset(struct ltelf *lte, uint32_t i) + { + /* Skip the first PLT entry, which contains a stub to call the + * resolver. */ +- return (i + 1) * 16; ++ return (i + (lte->second_plt_seen ? 0 : 1)) * 16; + } + + GElf_Addr + arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) + { + uint32_t i = *VECT_ELEMENT(<e->arch.plt_map, uint32_t, ndx); +- return x86_plt_offset(i) + lte->plt_addr; ++ return x86_plt_offset(lte, i) + lte->plt_addr; + } + + void * +@@ -116,6 +116,13 @@ arch_elf_init(struct ltelf *lte, struct + * 400426: 68 00 00 00 00 pushq $0x0 + * 40042b: e9 e0 ff ff ff jmpq 400410 <_init+0x18> + * ++ * For CET binaries it is the following: ++ * ++ * 13d0: f3 0f 1e fa endbr64 ++ * 13d4: 68 27 00 00 00 pushq $0x27 <-- index ++ * 13d9: f2 e9 71 fd ff ff bnd jmpq 1150 <.plt> ++ * 13df: 90 nop ++ * + * On i386, the argument to push is an offset of relocation to + * use. The first PLT slot has an offset of 0x0, the second + * 0x8, etc. On x86_64, it's directly the index that we are +@@ -128,11 +135,33 @@ arch_elf_init(struct ltelf *lte, struct + unsigned int i, sz = vect_size(<e->plt_relocs); + for (i = 0; i < sz; ++i) { + +- GElf_Addr offset = x86_plt_offset(i); ++ GElf_Addr offset = x86_plt_offset(lte, i); ++ uint32_t reloc_arg; + + uint8_t byte; +- if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 +- || byte != 0xff ++ if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0) ++ continue; ++ ++ ++ if (byte == 0xf3 ++ && elf_read_next_u8(lte->plt_data, &offset, &byte) >= 0 ++ && byte == 0x0f ++ && elf_read_next_u8(lte->plt_data, &offset, &byte) >= 0 ++ && byte == 0x1e ++ && elf_read_next_u8(lte->plt_data, &offset, &byte) >= 0 ++ && byte == 0xfa ++ && elf_read_next_u8(lte->plt_data, &offset, &byte) >= 0 ++ && byte == 0x68 ++ && elf_read_next_u32(lte->plt_data, ++ &offset, &reloc_arg) >= 0) ++ { ++ /* CET */ ++ fprintf(stderr, "%d: reloc_arg is %lx\n", i, (long)reloc_arg); ++ *VECT_ELEMENT(<e->arch.plt_map, unsigned int, reloc_arg) = i; ++ continue; ++ } ++ ++ if (byte != 0xff + || elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 + || (byte != 0xa3 && byte != 0x25)) + continue; +@@ -140,7 +169,6 @@ arch_elf_init(struct ltelf *lte, struct + /* Skip immediate argument in the instruction. */ + offset += 4; + +- uint32_t reloc_arg; + if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 + || byte != 0x68 + || elf_read_next_u32(lte->plt_data, diff --git a/ltrace-0.7.91-man.patch b/ltrace-0.7.91-man.patch new file mode 100644 index 0000000..d7a7dbf --- /dev/null +++ b/ltrace-0.7.91-man.patch @@ -0,0 +1,45 @@ +diff -up ltrace-0.7.91/options.c\~ ltrace-0.7.91/options.c +--- ltrace-0.7.91/options.c~ 2013-10-22 11:54:21.000000000 +0200 ++++ ltrace-0.7.91/options.c 2014-01-13 15:38:51.362221740 +0100 +@@ -128,6 +128,8 @@ usage_debug(void) { + "\n" + "Debugging options are mixed using bitwise-or.\n" + "Note that the meanings and values are subject to change.\n" ++ "Also note that these values are used inconsistently in ltrace, and the\n" ++ "only debuglevel that you can rely on is -D77 that will show everything.\n" + ); + } + +diff -up ltrace-0.7.91/ltrace.1\~ ltrace-0.7.91/ltrace.1 +--- ltrace-0.7.91/ltrace.1~ 2013-10-23 17:44:13.000000000 +0200 ++++ ltrace-0.7.91/ltrace.1 2014-01-13 15:51:24.236730677 +0100 +@@ -1,5 +1,5 @@ + .\" -*-nroff-*- +-.\" Copyright (c) 2012, 2013 Petr Machata, Red Hat Inc. ++.\" Copyright (c) 2012,2013,2014 Petr Machata, Red Hat Inc. + .\" Copyright (c) 1997-2005 Juan Cespedes + .\" + .\" This program is free software; you can redistribute it and/or +@@ -118,9 +118,9 @@ Besides removing any initial underscore + this makes C++ function names readable. + .IP "\-D, \-\-debug \fRmask\fI" + Show debugging output of \fBltrace\fR itself. \fImask\fR is a number +-with internal meaning that's not really well defined at all. +-\fImask\fR of 77 shows all debug messages, which is what you usually +-need. ++describing which debug messages should be displayed. Use the option ++\-Dh to see what can be used, but note that currently the only ++reliable debugmask is 77, which shows all debug messages. + .IP "\-e \fIfilter" + A qualifying expression which modifies which library calls to trace. + The format of the filter expression is described in the section +@@ -156,7 +156,8 @@ dependency ordering. If you want to mak + library are actually called, use \fB-x @\fIlibrary_pattern\fR instead. + .IP \-L + When no -e option is given, don't assume the default action of +-\fB@MAIN\fR. ++\fB@MAIN\fR. In practice this means that library calls will not be ++traced. + .IP "\-n, \-\-indent \fInr" + Indent trace output by \fInr\fR spaces for each level of call + nesting. Using this option makes the program flow visualization easy diff --git a/ltrace-0.7.91-multithread-no-f-1.patch b/ltrace-0.7.91-multithread-no-f-1.patch new file mode 100644 index 0000000..0589f5e --- /dev/null +++ b/ltrace-0.7.91-multithread-no-f-1.patch @@ -0,0 +1,175 @@ +From 4724bd5a4a19db117a1d280b9d1a3508fd4e03fa Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Wed, 8 Apr 2015 07:11:52 -0400 +Subject: [PATCH 1/2] Convert main-threaded test case to new style + +--- + testsuite/ltrace.main/Makefile.am | 4 +- + testsuite/ltrace.main/main-threaded.c | 30 ---------- + testsuite/ltrace.main/main-threaded.exp | 103 ++++++++++++++++++++------------ + 3 files changed, 66 insertions(+), 71 deletions(-) + delete mode 100644 testsuite/ltrace.main/main-threaded.c + +diff --git a/testsuite/ltrace.main/Makefile.am b/testsuite/ltrace.main/Makefile.am +index 23ab8ab..06ad613 100644 +--- a/testsuite/ltrace.main/Makefile.am ++++ b/testsuite/ltrace.main/Makefile.am +@@ -1,4 +1,4 @@ +-# Copyright (C) 1992 - 2001, 2012, 2013 Free Software Foundation, Inc. ++# Copyright (C) 1992 - 2001, 2012, 2013, 2015 Free Software Foundation, Inc. + # + # This program is free software; you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by +@@ -17,7 +17,7 @@ + + EXTRA_DIST = branch_func.c branch_func.exp filters.exp hello-vfork.c \ + hello-vfork.exp main.c main.exp main-internal.exp main-lib.c \ +- main-threaded.c main-threaded.exp main-vfork.c main-vfork.exp \ ++ main-threaded.exp main-vfork.c main-vfork.exp \ + parameters.c parameters.conf parameters.exp parameters-lib.c \ + parameters2.exp parameters3.exp signals.c signals.exp \ + system_calls.c system_calls.exp system_call_params.exp +diff --git a/testsuite/ltrace.main/main-threaded.c b/testsuite/ltrace.main/main-threaded.c +deleted file mode 100644 +index 2992d1e..0000000 +--- a/testsuite/ltrace.main/main-threaded.c ++++ /dev/null +@@ -1,29 +0,0 @@ +-#include +- +-extern void print (char *); +- +-#define PRINT_LOOP 10 +- +-void * +-th_main (void *arg) +-{ +- int i; +- for (i=0; i. ++# This file is part of ltrace. ++# Copyright (C) 2011, 2015 Petr Machata, Red Hat Inc. ++# Copyright (C) 2006 Yao Qi . ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License as ++# published by the Free Software Foundation; either version 2 of the ++# License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++# 02110-1301 USA ++ ++set libprint [ltraceCompile libprint.so [ltraceSource c { ++ #include ++ ++ void ++ print(char* s) ++ { ++ printf("%s\n",s); ++ } ++}]] ++ ++set bin [ltraceCompile {} $libprint -lpthread [ltraceSource c { ++ #include ++ ++ extern void print (char *); ++ ++ #define PRINT_LOOP 10 ++ ++ void * ++ th_main (void *arg) ++ { ++ int i; ++ for (i=0; i +Date: Wed, 8 Apr 2015 07:14:10 -0400 +Subject: [PATCH 2/2] Fix tracing multi-threaded processes without -f + +- In handle_syscall, we avoid touching stack of ignored processes. + But in handle_sysret, we require a sysret-like stack entry even + for ignored processes, even though we then go ahead to not act + on that stack entry. Instead, for ignored processes, avoid looking + at stack trace at all. +--- + handle_event.c | 10 +++++----- + testsuite/ltrace.main/main-threaded.exp | 1 + + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/handle_event.c b/handle_event.c +index 6fa7e98..c2550ad 100644 +--- a/handle_event.c ++++ b/handle_event.c +@@ -619,12 +619,12 @@ handle_x_sysret(Event *event, char *(*name_cb)(struct process *, int)) + debug(DEBUG_FUNCTION, "handle_x_sysret(pid=%d, sysnum=%d)", + event->proc->pid, event->e_un.sysnum); + +- unsigned d = event->proc->callstack_depth; +- assert(d > 0); +- struct callstack_element *elem = &event->proc->callstack[d - 1]; +- assert(elem->is_syscall); +- + if (event->proc->state != STATE_IGNORED) { ++ unsigned d = event->proc->callstack_depth; ++ assert(d > 0); ++ struct callstack_element *elem = &event->proc->callstack[d - 1]; ++ assert(elem->is_syscall); ++ + struct timedelta spent = calc_time_spent(elem->enter_time); + if (options.syscalls) + output_syscall_right(event->proc, +diff --git a/testsuite/ltrace.main/main-threaded.exp b/testsuite/ltrace.main/main-threaded.exp +index cead82d..aca7afd 100644 +--- a/testsuite/ltrace.main/main-threaded.exp ++++ b/testsuite/ltrace.main/main-threaded.exp +@@ -60,5 +60,6 @@ set bin [ltraceCompile {} $libprint -lpthread [ltraceSource c { + }]] + + ltraceMatch1 [ltraceRun -f -l libprint.so -- $bin] {print\(} == 30 ++ltraceMatch1 [ltraceRun -L -- $bin] exited == 1 + + ltraceDone +-- +2.1.0 diff --git a/ltrace-0.7.91-null.patch b/ltrace-0.7.91-null.patch new file mode 100644 index 0000000..4ff7e6a --- /dev/null +++ b/ltrace-0.7.91-null.patch @@ -0,0 +1,14 @@ +diff --git a/output.c b/output.c +index 7cab383..18f9cf0 100644 +--- a/output.c ++++ b/output.c +@@ -598,6 +598,9 @@ frame_callback (Dwfl_Frame *state, void *arg) + NULL, NULL, NULL); + symname = dwfl_module_addrinfo(mod, pc, &off, &sym, + NULL, NULL, NULL); ++ } else { ++ modname = "unknown"; ++ symname = "unknown"; + } + + /* This mimics the output produced by libunwind below. */ diff --git a/ltrace-0.7.91-parser-ws_after_id.patch b/ltrace-0.7.91-parser-ws_after_id.patch new file mode 100644 index 0000000..bc6e96e --- /dev/null +++ b/ltrace-0.7.91-parser-ws_after_id.patch @@ -0,0 +1,144 @@ +From 2e9f9f1f5d0fb223b109429b9c904504b7f638e2 Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Fri, 8 Aug 2014 16:53:41 +0200 +Subject: [PATCH] In config files, allow whitespace between identifier and + opening paren + +--- + read_config_file.c | 61 ++++++-------------------------- + testsuite/ltrace.main/parameters2.exp | 14 +++++++- + 2 files changed, 25 insertions(+), 50 deletions(-) + +diff --git a/read_config_file.c b/read_config_file.c +index ea3ab88..05ff283 100644 +--- a/read_config_file.c ++++ b/read_config_file.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 1998,1999,2003,2007,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand + * Copyright (C) 2006 Steve Fink +@@ -168,38 +168,6 @@ parse_ident(struct locus *loc, char **str) + return xstrndup(ident, *str - ident); + } + +-/* +- Returns position in string at the left parenthesis which starts the +- function's argument signature. Returns NULL on error. +-*/ +-static char * +-start_of_arg_sig(char *str) { +- char *pos; +- int stacked = 0; +- +- if (!strlen(str)) +- return NULL; +- +- pos = &str[strlen(str)]; +- do { +- pos--; +- if (pos < str) +- return NULL; +- while ((pos > str) && (*pos != ')') && (*pos != '(')) +- pos--; +- +- if (*pos == ')') +- stacked++; +- else if (*pos == '(') +- stacked--; +- else +- return NULL; +- +- } while (stacked > 0); +- +- return (stacked == 0) ? pos : NULL; +-} +- + static int + parse_int(struct locus *loc, char **str, long *ret) + { +@@ -1110,7 +1078,6 @@ static int + process_line(struct protolib *plib, struct locus *loc, char *buf) + { + char *str = buf; +- char *tmp; + + debug(3, "Reading line %d of `%s'", loc->line_no, loc->filename); + eat_spaces(&str); +@@ -1148,22 +1115,13 @@ process_line(struct protolib *plib, struct locus *loc, char *buf) + debug(4, " return_type = %d", fun.return_info->type); + + eat_spaces(&str); +- tmp = start_of_arg_sig(str); +- if (tmp == NULL) { +- report_error(loc->filename, loc->line_no, "syntax error"); ++ proto_name = parse_ident(loc, &str); ++ if (proto_name == NULL) + goto err; +- } +- *tmp = '\0'; + +- proto_name = strdup(str); +- if (proto_name == NULL) { +- oom: +- report_error(loc->filename, loc->line_no, +- "%s", strerror(errno)); ++ eat_spaces(&str); ++ if (parse_char(loc, &str, '(') < 0) + goto err; +- } +- +- str = tmp + 1; + debug(3, " name = %s", proto_name); + + struct param *extra_param = NULL; +@@ -1177,8 +1135,13 @@ process_line(struct protolib *plib, struct locus *loc, char *buf) + if (have_stop == 0) { + struct param param; + param_init_stop(¶m); +- if (prototype_push_param(&fun, ¶m) < 0) +- goto oom; ++ if (prototype_push_param(&fun, ¶m) < 0) { ++ oom: ++ report_error(loc->filename, ++ loc->line_no, ++ "%s", strerror(errno)); ++ goto err; ++ } + have_stop = 1; + } + str++; +diff --git a/testsuite/ltrace.main/parameters2.exp b/testsuite/ltrace.main/parameters2.exp +index 6318fc5..9850079 100644 +--- a/testsuite/ltrace.main/parameters2.exp ++++ b/testsuite/ltrace.main/parameters2.exp +@@ -1,5 +1,5 @@ + # This file is part of ltrace. +-# Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. ++# Copyright (C) 2012, 2013, 2014 Petr Machata, Red Hat Inc. + # + # This program is free software; you can redistribute it and/or + # modify it under the terms of the GNU General Public License as +@@ -259,4 +259,16 @@ ltraceMatch1 [ltraceLibTest { + somefunc(); + }] {somefunc\(\) *= nil} == 1 + ++# Test that spaces in function name make no difference. ++ ++ltraceMatch1 [ltraceLibTest { ++ void somefunc (); ++} { ++ void somefunc(void); ++} { ++ void somefunc(void) {} ++} { ++ somefunc(); ++}] {somefunc\(\)} == 1 ++ + ltraceDone +-- +1.7.6.5 + diff --git a/ltrace-0.7.91-ppc-bias.patch b/ltrace-0.7.91-ppc-bias.patch new file mode 100644 index 0000000..c0c0abd --- /dev/null +++ b/ltrace-0.7.91-ppc-bias.patch @@ -0,0 +1,91 @@ +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 45ed7fb..5f81889 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -274,14 +274,15 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) + + assert(rela->r_addend != 0); + /* XXX double cast */ +- arch_addr_t res_addr = (arch_addr_t) (uintptr_t) rela->r_addend; ++ arch_addr_t res_addr ++ = (arch_addr_t) (uintptr_t) (rela->r_addend + lte->bias); + if (arch_translate_address(lte, res_addr, &res_addr) < 0) { + fprintf(stderr, "Couldn't OPD-translate IRELATIVE " + "resolver address.\n"); + return 0; + } + /* XXX double cast */ +- return (GElf_Addr) (uintptr_t) res_addr; ++ return (GElf_Addr) (uintptr_t) (res_addr - lte->bias); + + } else { + /* We put brakpoints to PLT entries the same as the +@@ -453,7 +454,7 @@ arch_elf_init(struct ltelf *lte, struct library *lib) + #ifndef EF_PPC64_ABI + assert (! (lte->ehdr.e_flags & 3 ) == 2) + #else +- lte->arch.elfv2_abi=((lte->ehdr.e_flags & EF_PPC64_ABI) == 2) ; ++ lte->arch.elfv2_abi = ((lte->ehdr.e_flags & EF_PPC64_ABI) == 2); + #endif + + if (lte->ehdr.e_machine == EM_PPC64 +@@ -827,15 +828,15 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + assert(plt_slot_addr >= lte->plt_addr + || plt_slot_addr < lte->plt_addr + lte->plt_size); + ++ plt_entry_addr += lte->bias; ++ plt_slot_addr += lte->bias; ++ + /* Should avoid to do read if dynamic linker hasn't run yet + * or allow -1 a valid return code. */ + GElf_Addr plt_slot_value; +- if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) { +- if (!lte->arch.elfv2_abi) +- goto fail; +- else +- return PPC_PLT_UNRESOLVED; +- } ++ int rc = read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value); ++ if (rc < 0 && !lte->arch.elfv2_abi) ++ goto fail; + + struct library_symbol *libsym = malloc(sizeof(*libsym)); + if (libsym == NULL) { +@@ -854,8 +855,9 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + goto fail2; + libsym->arch.plt_slot_addr = plt_slot_addr; + +- if (! is_irelative +- && (plt_slot_value == plt_entry_addr || plt_slot_value == 0)) { ++ if (rc < 0 || (! is_irelative ++ && (plt_slot_value == plt_entry_addr ++ || plt_slot_value == 0))) { + libsym->arch.type = PPC_PLT_UNRESOLVED; + libsym->arch.resolved_value = plt_entry_addr; + } else { +@@ -1166,8 +1168,8 @@ ppc_plt_bp_install(struct breakpoint *bp, struct process *proc) + libsym->arch.resolved_value = plt_entry_addr; + } + } else { +- fprintf(stderr, "Couldn't unresolve %s@%p. Not tracing" +- " this symbol.\n", ++ fprintf(stderr, "Couldn't unresolve %s@%p. Will not" ++ " trace this symbol.\n", + breakpoint_name(bp), bp->addr); + proc_remove_breakpoint(proc, bp); + } +@@ -1222,6 +1224,14 @@ arch_library_symbol_clone(struct library_symbol *retp, + struct library_symbol *libsym) + { + retp->arch = libsym->arch; ++ if (libsym->arch.type == PPC_PLT_NEED_UNRESOLVE) { ++ assert(libsym->arch.data->self == libsym->arch.data); ++ retp->arch.data = malloc(sizeof *retp->arch.data); ++ if (retp->arch.data == NULL) ++ return -1; ++ *retp->arch.data = *libsym->arch.data; ++ retp->arch.data->self = retp->arch.data; ++ } + return 0; + } + diff --git a/ltrace-0.7.91-ppc64-fork.patch b/ltrace-0.7.91-ppc64-fork.patch new file mode 100644 index 0000000..53c66fd --- /dev/null +++ b/ltrace-0.7.91-ppc64-fork.patch @@ -0,0 +1,52 @@ +From 35742523e3daa0e59de0c1c3fdd8e5ff52891967 Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Thu, 9 Jan 2014 23:41:50 +0100 +Subject: [PATCH] Fix a problem in tracing across fork on PPC64 + +In order to avoid single-stepping through large portions of the +dynamic linker, ltrace remembers at which address the instruction that +resolved a PLT slot is. It then puts a breakpoint to this address so +that it can fast-forward to that address next time it needs to catch a +PLT slot being resolved. + +When a process is cloned, the pointer to this breakpoint is simply +copied over to the new process, instead of being looked up in the new +process structures. This patches fixes this. +--- + sysdeps/linux-gnu/ppc/plt.c | 14 +++++++++++++- + 1 files changed, 13 insertions(+), 1 deletions(-) + +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 3ec1397..8715da6 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 2004,2008,2009 Juan Cespedes + * Copyright (C) 2006 Paul Gilliam + * +@@ -1157,6 +1157,18 @@ int + arch_process_clone(struct process *retp, struct process *proc) + { + retp->arch = proc->arch; ++ ++ if (retp->arch.dl_plt_update_bp != NULL) { ++ /* Point it to the corresponding breakpoint in RETP. ++ * It must be there, this part of PROC has already ++ * been cloned to RETP. */ ++ retp->arch.dl_plt_update_bp ++ = address2bpstruct(retp, ++ retp->arch.dl_plt_update_bp->addr); ++ ++ assert(retp->arch.dl_plt_update_bp != NULL); ++ } ++ + return 0; + } + +-- +1.7.6.5 + diff --git a/ltrace-0.7.91-ppc64-unprelink.patch b/ltrace-0.7.91-ppc64-unprelink.patch new file mode 100644 index 0000000..a5929e3 --- /dev/null +++ b/ltrace-0.7.91-ppc64-unprelink.patch @@ -0,0 +1,221 @@ +From a0093ca43cf40d7e5f6cebeb64156062d2de46d9 Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Fri, 10 Jan 2014 20:06:51 +0100 +Subject: [PATCH 2/2] Don't crash untraced calls via PLT in prelinked PPC64 + binaries + +In prelinked binaries, ltrace has to unprelinks PLT slots in order to +catch calls done through PLT. This makes the calls done through these +slots invalid, because the special first PLT slot is not initialized, +and dynamic linker SIGSEGVs because of this. Ltrace relies on +arranging breakpoints such that the dynamic linker is not actually +entered, and moves PC around itself to simulate the effects of a call +through PLT. + +Originally, arch_elf_add_plt_entry was called only for symbols that +were actually traced. Later this was changed and it's now called for +all PLT entries, and the resulting candidate list is filtered +afterwards. This gives backends a chance to rename the symbol, as is +useful with IRELATIVE PLT calls, where symbol name may not be +available at all. But the PPC backend was never updated to reflect +this, and unresolved all symbols for which arch_elf_add_plt_entry was +called, thus rendering _all_ PLT slots invalid, even those that +weren't later procted by breakpoints. Thus calls done through any +untraced slots failed. + +This patch fixes this problem by deferring the unprelinking of PLT +slots into the on_install hook of breakpoints. +--- + sysdeps/linux-gnu/ppc/arch.h | 21 ++++++++- + sysdeps/linux-gnu/ppc/plt.c | 94 +++++++++++++++++++++++++++++++++-------- + 2 files changed, 94 insertions(+), 21 deletions(-) + +diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h +index 2add3b8..bf9b5dc 100644 +--- a/sysdeps/linux-gnu/ppc/arch.h ++++ b/sysdeps/linux-gnu/ppc/arch.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012,2013 Petr Machata ++ * Copyright (C) 2012,2013,2014 Petr Machata + * Copyright (C) 2006 Paul Gilliam + * Copyright (C) 2002,2004 Juan Cespedes + * +@@ -87,12 +87,29 @@ enum ppc64_plt_type { + /* Very similar to PPC_PLT_UNRESOLVED, but for JMP_IREL + * slots. */ + PPC_PLT_IRELATIVE, ++ ++ /* Transitional state before the breakpoint is enabled. */ ++ PPC_PLT_NEED_UNRESOLVE, + }; + + #define ARCH_HAVE_LIBRARY_SYMBOL_DATA ++struct ppc_unresolve_data; + struct arch_library_symbol_data { + enum ppc64_plt_type type; +- GElf_Addr resolved_value; ++ ++ /* State Contents ++ * ++ * PPC_DEFAULT N/A ++ * PPC64_PLT_STUB N/A ++ * PPC_PLT_UNRESOLVED PLT entry address. ++ * PPC_PLT_IRELATIVE Likewise. ++ * PPC_PLT_RESOLVED The original value the slot was resolved to. ++ * PPC_PLT_NEED_UNRESOLVE DATA. ++ */ ++ union { ++ GElf_Addr resolved_value; ++ struct ppc_unresolve_data *data; ++ }; + + /* Address of corresponding slot in .plt. */ + GElf_Addr plt_slot_addr; +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 8715da6..332daa8 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -679,6 +679,14 @@ arch_elf_add_func_entry(struct process *proc, struct ltelf *lte, + return PLT_OK; + } + ++struct ppc_unresolve_data { ++ struct ppc_unresolve_data *self; /* A canary. */ ++ GElf_Addr plt_entry_addr; ++ GElf_Addr plt_slot_addr; ++ GElf_Addr plt_slot_value; ++ bool is_irelative; ++}; ++ + enum plt_status + arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, +@@ -778,28 +786,23 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + && (plt_slot_value == plt_entry_addr || plt_slot_value == 0)) { + libsym->arch.type = PPC_PLT_UNRESOLVED; + libsym->arch.resolved_value = plt_entry_addr; +- + } else { +- /* Unresolve the .plt slot. If the binary was +- * prelinked, this makes the code invalid, because in +- * case of prelinked binary, the dynamic linker +- * doesn't update .plt[0] and .plt[1] with addresses +- * of the resover. But we don't care, we will never +- * need to enter the resolver. That just means that +- * we have to un-un-resolve this back before we +- * detach. */ +- +- if (unresolve_plt_slot(proc, plt_slot_addr, plt_entry_addr) < 0) { +- library_symbol_destroy(libsym); ++ /* Mark the symbol for later unresolving. We may not ++ * do this right away, as this is called by ltrace ++ * core for all symbols, and only later filtered. We ++ * only unresolve the symbol before the breakpoint is ++ * enabled. */ ++ ++ libsym->arch.type = PPC_PLT_NEED_UNRESOLVE; ++ libsym->arch.data = malloc(sizeof *libsym->arch.data); ++ if (libsym->arch.data == NULL) + goto fail2; +- } + +- if (! is_irelative) { +- mark_as_resolved(libsym, plt_slot_value); +- } else { +- libsym->arch.type = PPC_PLT_IRELATIVE; +- libsym->arch.resolved_value = plt_entry_addr; +- } ++ libsym->arch.data->self = libsym->arch.data; ++ libsym->arch.data->plt_entry_addr = plt_entry_addr; ++ libsym->arch.data->plt_slot_addr = plt_slot_addr; ++ libsym->arch.data->plt_slot_value = plt_slot_value; ++ libsym->arch.data->is_irelative = is_irelative; + } + + *ret = libsym; +@@ -999,6 +1002,7 @@ ppc_plt_bp_continue(struct breakpoint *bp, struct process *proc) + return; + + case PPC64_PLT_STUB: ++ case PPC_PLT_NEED_UNRESOLVE: + /* These should never hit here. */ + break; + } +@@ -1050,6 +1054,52 @@ ppc_plt_bp_retract(struct breakpoint *bp, struct process *proc) + } + } + ++static void ++ppc_plt_bp_install(struct breakpoint *bp, struct process *proc) ++{ ++ /* This should not be an artificial breakpoint. */ ++ struct library_symbol *libsym = bp->libsym; ++ if (libsym == NULL) ++ libsym = bp->arch.irel_libsym; ++ assert(libsym != NULL); ++ ++ if (libsym->arch.type == PPC_PLT_NEED_UNRESOLVE) { ++ /* Unresolve the .plt slot. If the binary was ++ * prelinked, this makes the code invalid, because in ++ * case of prelinked binary, the dynamic linker ++ * doesn't update .plt[0] and .plt[1] with addresses ++ * of the resover. But we don't care, we will never ++ * need to enter the resolver. That just means that ++ * we have to un-un-resolve this back before we ++ * detach. */ ++ ++ struct ppc_unresolve_data *data = libsym->arch.data; ++ libsym->arch.data = NULL; ++ assert(data->self == data); ++ ++ GElf_Addr plt_slot_addr = data->plt_slot_addr; ++ GElf_Addr plt_slot_value = data->plt_slot_value; ++ GElf_Addr plt_entry_addr = data->plt_entry_addr; ++ ++ if (unresolve_plt_slot(proc, plt_slot_addr, ++ plt_entry_addr) == 0) { ++ if (! data->is_irelative) { ++ mark_as_resolved(libsym, plt_slot_value); ++ } else { ++ libsym->arch.type = PPC_PLT_IRELATIVE; ++ libsym->arch.resolved_value = plt_entry_addr; ++ } ++ } else { ++ fprintf(stderr, "Couldn't unresolve %s@%p. Not tracing" ++ " this symbol.\n", ++ breakpoint_name(bp), bp->addr); ++ proc_remove_breakpoint(proc, bp); ++ } ++ ++ free(data); ++ } ++} ++ + int + arch_library_init(struct library *lib) + { +@@ -1080,6 +1130,11 @@ arch_library_symbol_init(struct library_symbol *libsym) + void + arch_library_symbol_destroy(struct library_symbol *libsym) + { ++ if (libsym->arch.type == PPC_PLT_NEED_UNRESOLVE) { ++ assert(libsym->arch.data->self == libsym->arch.data); ++ free(libsym->arch.data); ++ libsym->arch.data = NULL; ++ } + } + + int +@@ -1115,6 +1170,7 @@ arch_breakpoint_init(struct process *proc, struct breakpoint *bp) + static struct bp_callbacks cbs = { + .on_continue = ppc_plt_bp_continue, + .on_retract = ppc_plt_bp_retract, ++ .on_install = ppc_plt_bp_install, + }; + breakpoint_set_callbacks(bp, &cbs); + +-- +1.7.6.5 + diff --git a/ltrace-0.7.91-ppc64le-configure.patch b/ltrace-0.7.91-ppc64le-configure.patch new file mode 100644 index 0000000..74f0987 --- /dev/null +++ b/ltrace-0.7.91-ppc64le-configure.patch @@ -0,0 +1,44 @@ +From eea6091f8672b01f7f022b0fc367e0f568225ffc Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Fri, 8 Aug 2014 17:09:58 +0200 +Subject: [PATCH] Recognize powerpc64le in configure + +--- + configure.ac | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 4f360c8..6fe5e3b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -43,7 +43,7 @@ case "${host_cpu}" in + arm*|sa110) HOST_CPU="arm" ;; + cris*) HOST_CPU="cris" ;; + mips*) HOST_CPU="mips" ;; +- powerpc|powerpc64) HOST_CPU="ppc" ;; ++ powerpc|powerpc64|powerpc64le) HOST_CPU="ppc" ;; + sun4u|sparc64) HOST_CPU="sparc" ;; + s390x) HOST_CPU="s390" ;; + i?86|x86_64) HOST_CPU="x86" ;; +@@ -210,12 +210,12 @@ AC_MSG_RESULT([$enable_libunwind]) + + if test x"$enable_libunwind" = xyes; then + case "${host_cpu}" in +- arm*|sa110) UNWIND_ARCH="arm" ;; +- i?86) UNWIND_ARCH="x86" ;; +- powerpc) UNWIND_ARCH="ppc32" ;; +- powerpc64) UNWIND_ARCH="ppc64" ;; +- mips*) UNWIND_ARCH="mips" ;; +- *) UNWIND_ARCH="${host_cpu}" ;; ++ arm*|sa110) UNWIND_ARCH="arm" ;; ++ i?86) UNWIND_ARCH="x86" ;; ++ powerpc) UNWIND_ARCH="ppc32" ;; ++ powerpc64|powerpc64le) UNWIND_ARCH="ppc64" ;; ++ mips*) UNWIND_ARCH="mips" ;; ++ *) UNWIND_ARCH="${host_cpu}" ;; + esac + + saved_LDFLAGS="${LDFLAGS}" +-- +2.1.0 + diff --git a/ltrace-0.7.91-ppc64le-fixes.patch b/ltrace-0.7.91-ppc64le-fixes.patch new file mode 100644 index 0000000..82fb474 --- /dev/null +++ b/ltrace-0.7.91-ppc64le-fixes.patch @@ -0,0 +1,421 @@ +diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h +index d5ad759..a8b67bb 100644 +--- a/sysdeps/linux-gnu/ppc/arch.h ++++ b/sysdeps/linux-gnu/ppc/arch.h +@@ -32,36 +32,45 @@ + #define LT_ELF_MACHINE EM_PPC + + #ifdef __powerpc64__ // Says 'ltrace' is 64 bits, says nothing about target. +-#define LT_ELFCLASS2 ELFCLASS64 +-#define LT_ELF_MACHINE2 EM_PPC64 ++# define LT_ELFCLASS2 ELFCLASS64 ++# define LT_ELF_MACHINE2 EM_PPC64 + + # ifdef __LITTLE_ENDIAN__ +-# define BREAKPOINT_VALUE { 0x08, 0x00, 0xe0, 0x7f } +-# define ARCH_ENDIAN_LITTLE ++# define BREAKPOINT_VALUE { 0x08, 0x00, 0xe0, 0x7f } ++# define ARCH_ENDIAN_LITTLE + # else +-# define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } +-# define ARCH_SUPPORTS_OPD +-# define ARCH_ENDIAN_BIG ++# define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } ++# define ARCH_SUPPORTS_OPD ++# define ARCH_ENDIAN_BIG + # endif + +-# if _CALL_ELF != 2 +-# define ARCH_SUPPORTS_OPD +-# define STACK_FRAME_OVERHEAD 112 ++# if !defined(_CALL_ELF) || _CALL_ELF < 2 ++# define ARCH_SUPPORTS_OPD ++# define STACK_FRAME_OVERHEAD 112 + # ifndef EF_PPC64_ABI +-# define EF_PPC64_ABI 3 ++# define EF_PPC64_ABI 3 + # endif +-# else /* _CALL_ELF == 2 ABIv2 */ +-# define STACK_FRAME_OVERHEAD 32 ++# elif _CALL_ELF == 2 /* ELFv2 ABI */ ++# define STACK_FRAME_OVERHEAD 32 ++# else ++# error Unsupported PowerPC64 ABI. + # endif /* CALL_ELF */ + + #else +-#define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } +-#define ARCH_ENDIAN_BIG ++# define STACK_FRAME_OVERHEAD 112 ++# define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } ++# define ARCH_ENDIAN_BIG + # ifndef EF_PPC64_ABI +-# define EF_PPC64_ABI 3 ++# define EF_PPC64_ABI 3 + # endif + #endif /* __powerpc64__ */ + ++#ifdef _CALL_ELF ++enum { ppc64_call_elf_abi = _CALL_ELF }; ++#else ++enum { ppc64_call_elf_abi = 0 }; ++#endif ++ + #define ARCH_HAVE_SW_SINGLESTEP + #define ARCH_HAVE_ADD_PLT_ENTRY + #define ARCH_HAVE_ADD_FUNC_ENTRY +diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c +index c9381c3..c6cbd71 100644 +--- a/sysdeps/linux-gnu/ppc/fetch.c ++++ b/sysdeps/linux-gnu/ppc/fetch.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012, 2014 Petr Machata, Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #include "backend.h" + #include "fetch.h" +@@ -57,7 +58,7 @@ struct fetch_context { + arch_addr_t stack_pointer; + int greg; + int freg; +- int ret_struct; ++ bool ret_struct; + + union { + gregs32_t r32; +@@ -65,11 +66,29 @@ struct fetch_context { + } regs; + struct fpregs_t fpregs; + int vgreg; +- int struct_size; +- int struct_hfa_size; +- int struct_hfa_count; + }; + ++static bool ++is_eligible_hfa(struct arg_type_info *info, ++ struct arg_type_info **hfa_infop, size_t *hfa_countp) ++{ ++ size_t hfa_count; ++ struct arg_type_info *hfa_info = type_get_hfa_type(info, &hfa_count); ++ ++ if (hfa_info != NULL && hfa_count <= 8 ++ && (hfa_info->type == ARGTYPE_FLOAT ++ || hfa_info->type == ARGTYPE_DOUBLE)) { ++ ++ if (hfa_infop != NULL) ++ *hfa_infop = hfa_info; ++ if (hfa_countp != NULL) ++ *hfa_countp = hfa_count; ++ return true; ++ } ++ ++ return false; ++} ++ + static int + fetch_context_init(struct process *proc, struct fetch_context *context) + { +@@ -125,30 +144,37 @@ arch_fetch_arg_init(enum tof type, struct process *proc, + } + + context->vgreg = context->greg; +- context->struct_size = 0; +- context->struct_hfa_size = 0; +- context->struct_hfa_count = 0; + + /* Aggregates or unions of any length, and character strings + * of length longer than 8 bytes, will be returned in a + * storage buffer allocated by the caller. The caller will + * pass the address of this buffer as a hidden first argument + * in r3, causing the first explicit argument to be passed in +- * r4. */ +- context->ret_struct = ret_info->type == ARGTYPE_STRUCT; +- if (context->ret_struct) { +-#if _CALL_ELF == 2 +- /* if R3 points to stack, parameters will be in R4. */ +- uint64_t pstack_end = ptrace(PTRACE_PEEKTEXT, proc->pid, +- proc->stack_pointer, 0); +- if (((arch_addr_t)context->regs.r64[3] > proc->stack_pointer) +- && (context->regs.r64[3] < pstack_end)) { ++ * r4. ++ */ ++ ++ context->ret_struct = false; ++ ++ if (ppc64_call_elf_abi == 2) { ++ /* With ELFv2 ABI, aggregates that consist ++ * (recursively) only of members of the same ++ * floating-point or vector type, are passed in a ++ * series of floating-point resp. vector registers. ++ * Additionally, when returning any aggregate of up to ++ * 16 bytes, general-purpose registers are used. */ ++ ++ if (ret_info->type == ARGTYPE_STRUCT ++ && ! is_eligible_hfa(ret_info, NULL, NULL) ++ && type_sizeof(proc, ret_info) > 16) { ++ ++ context->ret_struct = true; + context->greg++; + context->stack_pointer += 8; + } +-#else ++ ++ } else if (ret_info->type == ARGTYPE_STRUCT) { ++ context->ret_struct = true; + context->greg++; +-#endif + } + + return context; +@@ -176,17 +202,16 @@ allocate_stack_slot(struct fetch_context *ctx, struct process *proc, + + size_t a = type_alignof(proc, info); + size_t off = 0; +- if (proc->e_machine == EM_PPC && a < 4) +- a = 4; +-#if _CALL_ELF == 2 +- else if (proc->e_machine == EM_PPC64 && sz == 4 && is_hfa_type) ++ if (proc->e_machine == EM_PPC && a < 4) { + a = 4; +- else +- a = 8; +-#else +- else if (proc->e_machine == EM_PPC64 && a < 8) +-#endif ++ } else if (ppc64_call_elf_abi == 2) { ++ if (proc->e_machine == EM_PPC64 && sz == 4 && is_hfa_type) { ++ a = 4; ++ } else ++ a = 8; ++ } else if (proc->e_machine == EM_PPC64 && a < 8) { + a = 8; ++ } + + /* XXX Remove the two double casts when arch_addr_t + * becomes integral type. */ +@@ -259,18 +284,19 @@ allocate_gpr(struct fetch_context *ctx, struct process *proc, + if (sz == (size_t)-1) + return -1; + assert(sz == 1 || sz == 2 || sz == 4 || sz == 8); +-#if _CALL_ELF == 2 +- /* Consume the stack slot corresponding to this arg. */ +- if ((sz + off) >= 8) +- ctx->greg++; + +- if (is_hfa_type) +- ctx->stack_pointer += sz; +- else +- ctx->stack_pointer += 8; +-#else +- ctx->greg++; +-#endif ++ if (ppc64_call_elf_abi == 2) { ++ /* Consume the stack slot corresponding to this arg. */ ++ if ((sz + off) >= 8) ++ ctx->greg++; ++ ++ if (is_hfa_type) ++ ctx->stack_pointer += sz; ++ else ++ ctx->stack_pointer += 8; ++ } else { ++ ctx->greg++; ++ } + + if (valuep == NULL) + return 0; +@@ -326,7 +352,6 @@ allocate_float(struct fetch_context *ctx, struct process *proc, + return allocate_stack_slot(ctx, proc, info, valuep, is_hfa_type); + } + +-#if _CALL_ELF == 2 + static int + allocate_hfa(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, +@@ -336,27 +361,27 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, + if (sz == (size_t)-1) + return -1; + +- ctx->struct_hfa_size += sz; +- + /* There are two changes regarding structure return types: +- * * heterogeneous float/vector structs are returned +- * in (multiple) FP/vector registers, +- * instead of via implicit reference. +- * * small structs (up to 16 bytes) are return +- * in one or two GPRs, instead of via implicit reference. ++ * * heterogeneous float/vector structs are returned in ++ * (multiple) FP/vector registers, instead of via implicit ++ * reference. ++ * * small structs (up to 16 bytes) are return in one or two ++ * GPRs, instead of via implicit reference. + * + * Other structures (larger than 16 bytes, not heterogeneous) + * are still returned via implicit reference (i.e. a pointer + * to memory where to return the struct being passed in r3). +- * Of course, whether or not an implicit reference pointer +- * is present will shift the remaining arguments, +- * so you need to get this right for ELFv2 in order +- * to get the arguments correct. ++ * Of course, whether or not an implicit reference pointer is ++ * present will shift the remaining arguments, so you need to ++ * get this right for ELFv2 in order to get the arguments ++ * correct. ++ * + * If an actual parameter is known to correspond to an HFA + * formal parameter, each element is passed in the next + * available floating-point argument register starting at fp1 + * until the fp13. The remaining elements of the aggregate are +- * passed on the stack. */ ++ * passed on the stack. ++ */ + size_t slot_off = 0; + + unsigned char *buf = value_reserve(valuep, sz); +@@ -366,26 +391,17 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *hfa_info = type_get_simple(hfa_type); + size_t hfa_sz = type_sizeof(proc, hfa_info); + +- if (hfa_count > 8) +- ctx->struct_hfa_count += hfa_count; +- + while (hfa_count > 0 && ctx->freg <= 13) { +- int rc; + struct value tmp; +- + value_init(&tmp, proc, NULL, hfa_info, 0); ++ int rc = allocate_float(ctx, proc, hfa_info, ++ &tmp, slot_off, true); ++ if (rc == 0) ++ memcpy(buf, value_get_data(&tmp, NULL), hfa_sz); ++ value_destroy(&tmp); + +- /* Hetereogeneous struct - get value on GPR or stack. */ +- if (((hfa_type == ARGTYPE_FLOAT +- || hfa_type == ARGTYPE_DOUBLE) +- && hfa_count <= 8)) +- rc = allocate_float(ctx, proc, hfa_info, &tmp, +- slot_off, true); +- else +- rc = allocate_gpr(ctx, proc, hfa_info, &tmp, +- slot_off, true); +- +- memcpy(buf, value_get_data(&tmp, NULL), hfa_sz); ++ if (rc < 0) ++ return -1; + + slot_off += hfa_sz; + buf += hfa_sz; +@@ -394,17 +410,13 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, + slot_off = 0; + ctx->vgreg++; + } +- +- value_destroy(&tmp); +- if (rc < 0) +- return -1; + } + if (hfa_count == 0) + return 0; + + /* if no remaining FP, GPR corresponding to slot is used +- * Mostly it is in part of r10. */ +- if (ctx->struct_hfa_size <= 64 && ctx->vgreg == 10) { ++ * Mostly it is in part of r10. */ ++ if (ctx->vgreg == 10) { + while (ctx->vgreg <= 10) { + struct value tmp; + value_init(&tmp, proc, NULL, hfa_info, 0); +@@ -428,11 +440,8 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, + } + } + +- if (hfa_count == 0) +- return 0; +- + /* Remaining values are on stack */ +- while (hfa_count) { ++ while (hfa_count > 0) { + struct value tmp; + value_init(&tmp, proc, NULL, hfa_info, 0); + +@@ -444,7 +453,6 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, + } + return 0; + } +-#endif + + static int + allocate_argument(struct fetch_context *ctx, struct process *proc, +@@ -459,24 +467,20 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, + case ARGTYPE_FLOAT: + case ARGTYPE_DOUBLE: + return allocate_float(ctx, proc, info, valuep, +- 8 - type_sizeof(proc,info), false); ++ 8 - type_sizeof(proc,info), false); + + case ARGTYPE_STRUCT: + if (proc->e_machine == EM_PPC) { + if (value_pass_by_reference(valuep) < 0) + return -1; +- } else { +-#if _CALL_ELF == 2 ++ } else if (ppc64_call_elf_abi == 2) { + struct arg_type_info *hfa_info; +- size_t hfa_size; +- hfa_info = type_get_hfa_type(info, &hfa_size); +- if (hfa_info != NULL ) { +- size_t sz = type_sizeof(proc, info); +- ctx->struct_size += sz; ++ size_t hfa_count; ++ if (is_eligible_hfa(info, &hfa_info, &hfa_count)) { + return allocate_hfa(ctx, proc, info, valuep, +- hfa_info->type, hfa_size); ++ hfa_info->type, hfa_count); + } +-#endif ++ } else { + /* PPC64: Fixed size aggregates and unions passed by + * value are mapped to as many doublewords of the + * parameter save area as the value uses in memory. +@@ -510,9 +514,6 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, + if (sz == (size_t)-1) + return -1; + +- if (ctx->ret_struct) +- ctx->struct_size += sz; +- + size_t slots = (sz + width - 1) / width; /* Round up. */ + unsigned char *buf = value_reserve(valuep, slots * width); + if (buf == NULL) +@@ -605,19 +606,7 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, + if (fetch_context_init(proc, ctx) < 0) + return -1; + +-#if _CALL_ELF == 2 +- void *ptr = (void *)(ctx->regs.r64[1]+32); +- uint64_t val = ptrace(PTRACE_PEEKTEXT, proc->pid, ptr, 0); +- +- if (ctx->ret_struct +- && ((ctx->struct_size > 64 +- || ctx->struct_hfa_count > 8 +- || (ctx->struct_hfa_size == 0 && ctx->struct_size > 56) +- || (ctx->regs.r64[3] == ctx->regs.r64[1]+32) +- || (ctx->regs.r64[3] == val )))) { +-#else + if (ctx->ret_struct) { +-#endif + assert(info->type == ARGTYPE_STRUCT); + + uint64_t addr = read_gpr(ctx, proc, 3); diff --git a/ltrace-0.7.91-ppc64le-support.patch b/ltrace-0.7.91-ppc64le-support.patch new file mode 100644 index 0000000..e2d8daa --- /dev/null +++ b/ltrace-0.7.91-ppc64le-support.patch @@ -0,0 +1,786 @@ +From eea4ad2cce289753aaa35b4e0258a76d8f8f367c Mon Sep 17 00:00:00 2001 +From: Thierry Fauck +Date: Tue, 13 May 2014 07:48:24 -0400 +Subject: [PATCH] Support for powerpc64 arch ppc64el + +Signed-off-by: Thierry Fauck + + Add support for ppc64le proc and ELF ABIv2. + Provides support for irelative and wchar +--- + ltrace-elf.c | 2 +- + ltrace-elf.h | 1 + + sysdeps/linux-gnu/ppc/arch.h | 35 ++++- + sysdeps/linux-gnu/ppc/fetch.c | 244 +++++++++++++++++++++++++++++--- + sysdeps/linux-gnu/ppc/plt.c | 98 ++++++++++++-- + sysdeps/linux-gnu/ppc/trace.c | 10 ++ + testsuite/ltrace.main/system_calls.exp | 2 +- + 7 files changed, 356 insertions(+), 36 deletions(-) + +diff --git a/ltrace-elf.c b/ltrace-elf.c +index 8997518..f638342 100644 +--- a/ltrace-elf.c ++++ b/ltrace-elf.c +@@ -859,7 +859,7 @@ populate_plt(struct process *proc, const char *filename, + return 0; + } + +-static void ++void + delete_symbol_chain(struct library_symbol *libsym) + { + while (libsym != NULL) { +diff --git a/ltrace-elf.h b/ltrace-elf.h +index db4ffe9..4a824c4 100644 +--- a/ltrace-elf.h ++++ b/ltrace-elf.h +@@ -166,6 +166,7 @@ int elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp); + /* Return whether there's AMOUNT more bytes after OFFSET in DATA. */ + int elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword amount); + ++void delete_symbol_chain(struct library_symbol *); + #if __WORDSIZE == 32 + #define PRI_ELF_ADDR PRIx32 + #define GELF_ADDR_CAST(x) (void *)(uint32_t)(x) +diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h +index bf9b5dc..7918a13 100644 +--- a/sysdeps/linux-gnu/ppc/arch.h ++++ b/sysdeps/linux-gnu/ppc/arch.h +@@ -23,8 +23,8 @@ + #define LTRACE_PPC_ARCH_H + + #include ++#include + +-#define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } + #define BREAKPOINT_LENGTH 4 + #define DECR_PC_AFTER_BREAK 0 + +@@ -34,8 +34,33 @@ + #ifdef __powerpc64__ // Says 'ltrace' is 64 bits, says nothing about target. + #define LT_ELFCLASS2 ELFCLASS64 + #define LT_ELF_MACHINE2 EM_PPC64 +-#define ARCH_SUPPORTS_OPD +-#endif ++ ++# ifdef __LITTLE_ENDIAN__ ++# define BREAKPOINT_VALUE { 0x08, 0x00, 0xe0, 0x7f } ++# define ARCH_ENDIAN_LITTLE ++# else ++# define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } ++# define ARCH_SUPPORTS_OPD ++# define ARCH_ENDIAN_BIG ++# endif ++ ++# if _CALL_ELF != 2 ++# define ARCH_SUPPORTS_OPD ++# define STACK_FRAME_OVERHEAD 112 ++# ifndef EF_PPC64_ABI ++# define EF_PPC64_ABI 3 ++# endif ++# else /* _CALL_ELF == 2 ABIv2 */ ++# define STACK_FRAME_OVERHEAD 32 ++# endif /* CALL_ELF */ ++ ++#else ++#define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } ++#define ARCH_ENDIAN_BIG ++# ifndef EF_PPC64_ABI ++# define EF_PPC64_ABI 3 ++# endif ++#endif /* __powerpc64__ */ + + #define ARCH_HAVE_SW_SINGLESTEP + #define ARCH_HAVE_ADD_PLT_ENTRY +@@ -43,7 +68,6 @@ + #define ARCH_HAVE_TRANSLATE_ADDRESS + #define ARCH_HAVE_DYNLINK_DONE + #define ARCH_HAVE_FETCH_ARG +-#define ARCH_ENDIAN_BIG + #define ARCH_HAVE_SIZEOF + #define ARCH_HAVE_ALIGNOF + +@@ -56,7 +80,8 @@ struct arch_ltelf_data { + Elf_Data *opd_data; + GElf_Addr opd_base; + GElf_Xword opd_size; +- int secure_plt; ++ bool secure_plt : 1; ++ bool elfv2_abi : 1; + + Elf_Data *reladyn; + size_t reladyn_count; +diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c +index ed38336..c9381c3 100644 +--- a/sysdeps/linux-gnu/ppc/fetch.c ++++ b/sysdeps/linux-gnu/ppc/fetch.c +@@ -30,9 +30,11 @@ + #include "ptrace.h" + #include "proc.h" + #include "value.h" ++#include "ltrace-elf.h" + + static int allocate_gpr(struct fetch_context *ctx, struct process *proc, +- struct arg_type_info *info, struct value *valuep); ++ struct arg_type_info *info, struct value *valuep, ++ size_t off, bool is_hfa_type); + + /* Floating point registers have the same width on 32-bit as well as + * 64-bit PPC, but presents a different API depending on +@@ -62,7 +64,10 @@ struct fetch_context { + gregs64_t r64; + } regs; + struct fpregs_t fpregs; +- ++ int vgreg; ++ int struct_size; ++ int struct_hfa_size; ++ int struct_hfa_count; + }; + + static int +@@ -74,7 +79,8 @@ fetch_context_init(struct process *proc, struct fetch_context *context) + if (proc->e_machine == EM_PPC) + context->stack_pointer = proc->stack_pointer + 8; + else +- context->stack_pointer = proc->stack_pointer + 112; ++ context->stack_pointer = proc->stack_pointer ++ + STACK_FRAME_OVERHEAD; + + /* When ltrace is 64-bit, we might use PTRACE_GETREGS to + * obtain 64-bit as well as 32-bit registers. But if we do it +@@ -118,6 +124,11 @@ arch_fetch_arg_init(enum tof type, struct process *proc, + return NULL; + } + ++ context->vgreg = context->greg; ++ context->struct_size = 0; ++ context->struct_hfa_size = 0; ++ context->struct_hfa_count = 0; ++ + /* Aggregates or unions of any length, and character strings + * of length longer than 8 bytes, will be returned in a + * storage buffer allocated by the caller. The caller will +@@ -125,8 +136,20 @@ arch_fetch_arg_init(enum tof type, struct process *proc, + * in r3, causing the first explicit argument to be passed in + * r4. */ + context->ret_struct = ret_info->type == ARGTYPE_STRUCT; +- if (context->ret_struct) ++ if (context->ret_struct) { ++#if _CALL_ELF == 2 ++ /* if R3 points to stack, parameters will be in R4. */ ++ uint64_t pstack_end = ptrace(PTRACE_PEEKTEXT, proc->pid, ++ proc->stack_pointer, 0); ++ if (((arch_addr_t)context->regs.r64[3] > proc->stack_pointer) ++ && (context->regs.r64[3] < pstack_end)) { ++ context->greg++; ++ context->stack_pointer += 8; ++ } ++#else + context->greg++; ++#endif ++ } + + return context; + } +@@ -144,7 +167,8 @@ arch_fetch_arg_clone(struct process *proc, + + static int + allocate_stack_slot(struct fetch_context *ctx, struct process *proc, +- struct arg_type_info *info, struct value *valuep) ++ struct arg_type_info *info, struct value *valuep, ++ bool is_hfa_type) + { + size_t sz = type_sizeof(proc, info); + if (sz == (size_t)-1) +@@ -154,7 +178,14 @@ allocate_stack_slot(struct fetch_context *ctx, struct process *proc, + size_t off = 0; + if (proc->e_machine == EM_PPC && a < 4) + a = 4; ++#if _CALL_ELF == 2 ++ else if (proc->e_machine == EM_PPC64 && sz == 4 && is_hfa_type) ++ a = 4; ++ else ++ a = 8; ++#else + else if (proc->e_machine == EM_PPC64 && a < 8) ++#endif + a = 8; + + /* XXX Remove the two double casts when arch_addr_t +@@ -164,7 +195,7 @@ allocate_stack_slot(struct fetch_context *ctx, struct process *proc, + + if (valuep != NULL) + value_in_inferior(valuep, ctx->stack_pointer + off); +- ctx->stack_pointer += sz; ++ ctx->stack_pointer += a; + + return 0; + } +@@ -216,19 +247,34 @@ align_small_int(unsigned char *buf, size_t w, size_t sz) + + static int + allocate_gpr(struct fetch_context *ctx, struct process *proc, +- struct arg_type_info *info, struct value *valuep) ++ struct arg_type_info *info, struct value *valuep, ++ size_t off, bool is_hfa_type) + { + if (ctx->greg > 10) +- return allocate_stack_slot(ctx, proc, info, valuep); ++ return allocate_stack_slot(ctx, proc, info, valuep, is_hfa_type); + +- int reg_num = ctx->greg++; +- if (valuep == NULL) +- return 0; ++ int reg_num = ctx->greg; + + size_t sz = type_sizeof(proc, info); + if (sz == (size_t)-1) + return -1; + assert(sz == 1 || sz == 2 || sz == 4 || sz == 8); ++#if _CALL_ELF == 2 ++ /* Consume the stack slot corresponding to this arg. */ ++ if ((sz + off) >= 8) ++ ctx->greg++; ++ ++ if (is_hfa_type) ++ ctx->stack_pointer += sz; ++ else ++ ctx->stack_pointer += 8; ++#else ++ ctx->greg++; ++#endif ++ ++ if (valuep == NULL) ++ return 0; ++ + if (value_reserve(valuep, sz) == NULL) + return -1; + +@@ -240,13 +286,14 @@ allocate_gpr(struct fetch_context *ctx, struct process *proc, + u.i64 = read_gpr(ctx, proc, reg_num); + if (proc->e_machine == EM_PPC) + align_small_int(u.buf, 8, sz); +- memcpy(value_get_raw_data(valuep), u.buf, sz); ++ memcpy(value_get_raw_data(valuep), u.buf + off, sz); + return 0; + } + + static int + allocate_float(struct fetch_context *ctx, struct process *proc, +- struct arg_type_info *info, struct value *valuep) ++ struct arg_type_info *info, struct value *valuep, ++ size_t off, bool is_hfa_type) + { + int pool = proc->e_machine == EM_PPC64 ? 13 : 8; + if (ctx->freg <= pool) { +@@ -257,8 +304,12 @@ allocate_float(struct fetch_context *ctx, struct process *proc, + } u = { .d = ctx->fpregs.fpregs[ctx->freg] }; + + ctx->freg++; ++ ++ if (!is_hfa_type) ++ ctx->vgreg++; ++ + if (proc->e_machine == EM_PPC64) +- allocate_gpr(ctx, proc, info, NULL); ++ allocate_gpr(ctx, proc, info, NULL, off, is_hfa_type); + + size_t sz = sizeof(double); + if (info->type == ARGTYPE_FLOAT) { +@@ -272,8 +323,128 @@ allocate_float(struct fetch_context *ctx, struct process *proc, + memcpy(value_get_raw_data(valuep), u.buf, sz); + return 0; + } +- return allocate_stack_slot(ctx, proc, info, valuep); ++ return allocate_stack_slot(ctx, proc, info, valuep, is_hfa_type); ++} ++ ++#if _CALL_ELF == 2 ++static int ++allocate_hfa(struct fetch_context *ctx, struct process *proc, ++ struct arg_type_info *info, struct value *valuep, ++ enum arg_type hfa_type, size_t hfa_count) ++{ ++ size_t sz = type_sizeof(proc, info); ++ if (sz == (size_t)-1) ++ return -1; ++ ++ ctx->struct_hfa_size += sz; ++ ++ /* There are two changes regarding structure return types: ++ * * heterogeneous float/vector structs are returned ++ * in (multiple) FP/vector registers, ++ * instead of via implicit reference. ++ * * small structs (up to 16 bytes) are return ++ * in one or two GPRs, instead of via implicit reference. ++ * ++ * Other structures (larger than 16 bytes, not heterogeneous) ++ * are still returned via implicit reference (i.e. a pointer ++ * to memory where to return the struct being passed in r3). ++ * Of course, whether or not an implicit reference pointer ++ * is present will shift the remaining arguments, ++ * so you need to get this right for ELFv2 in order ++ * to get the arguments correct. ++ * If an actual parameter is known to correspond to an HFA ++ * formal parameter, each element is passed in the next ++ * available floating-point argument register starting at fp1 ++ * until the fp13. The remaining elements of the aggregate are ++ * passed on the stack. */ ++ size_t slot_off = 0; ++ ++ unsigned char *buf = value_reserve(valuep, sz); ++ if (buf == NULL) ++ return -1; ++ ++ struct arg_type_info *hfa_info = type_get_simple(hfa_type); ++ size_t hfa_sz = type_sizeof(proc, hfa_info); ++ ++ if (hfa_count > 8) ++ ctx->struct_hfa_count += hfa_count; ++ ++ while (hfa_count > 0 && ctx->freg <= 13) { ++ int rc; ++ struct value tmp; ++ ++ value_init(&tmp, proc, NULL, hfa_info, 0); ++ ++ /* Hetereogeneous struct - get value on GPR or stack. */ ++ if (((hfa_type == ARGTYPE_FLOAT ++ || hfa_type == ARGTYPE_DOUBLE) ++ && hfa_count <= 8)) ++ rc = allocate_float(ctx, proc, hfa_info, &tmp, ++ slot_off, true); ++ else ++ rc = allocate_gpr(ctx, proc, hfa_info, &tmp, ++ slot_off, true); ++ ++ memcpy(buf, value_get_data(&tmp, NULL), hfa_sz); ++ ++ slot_off += hfa_sz; ++ buf += hfa_sz; ++ hfa_count--; ++ if (slot_off == 8) { ++ slot_off = 0; ++ ctx->vgreg++; ++ } ++ ++ value_destroy(&tmp); ++ if (rc < 0) ++ return -1; ++ } ++ if (hfa_count == 0) ++ return 0; ++ ++ /* if no remaining FP, GPR corresponding to slot is used ++ * Mostly it is in part of r10. */ ++ if (ctx->struct_hfa_size <= 64 && ctx->vgreg == 10) { ++ while (ctx->vgreg <= 10) { ++ struct value tmp; ++ value_init(&tmp, proc, NULL, hfa_info, 0); ++ union { ++ uint64_t i64; ++ unsigned char buf[0]; ++ } u; ++ ++ u.i64 = read_gpr(ctx, proc, ctx->vgreg); ++ ++ memcpy(buf, u.buf + slot_off, hfa_sz); ++ slot_off += hfa_sz; ++ buf += hfa_sz; ++ hfa_count--; ++ ctx->stack_pointer += hfa_sz; ++ if (slot_off >= 8 ) { ++ slot_off = 0; ++ ctx->vgreg++; ++ } ++ value_destroy(&tmp); ++ } ++ } ++ ++ if (hfa_count == 0) ++ return 0; ++ ++ /* Remaining values are on stack */ ++ while (hfa_count) { ++ struct value tmp; ++ value_init(&tmp, proc, NULL, hfa_info, 0); ++ ++ value_in_inferior(&tmp, ctx->stack_pointer); ++ memcpy(buf, value_get_data(&tmp, NULL), hfa_sz); ++ ctx->stack_pointer += hfa_sz; ++ buf += hfa_sz; ++ hfa_count--; ++ } ++ return 0; + } ++#endif + + static int + allocate_argument(struct fetch_context *ctx, struct process *proc, +@@ -287,13 +458,25 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, + + case ARGTYPE_FLOAT: + case ARGTYPE_DOUBLE: +- return allocate_float(ctx, proc, info, valuep); ++ return allocate_float(ctx, proc, info, valuep, ++ 8 - type_sizeof(proc,info), false); + + case ARGTYPE_STRUCT: + if (proc->e_machine == EM_PPC) { + if (value_pass_by_reference(valuep) < 0) + return -1; + } else { ++#if _CALL_ELF == 2 ++ struct arg_type_info *hfa_info; ++ size_t hfa_size; ++ hfa_info = type_get_hfa_type(info, &hfa_size); ++ if (hfa_info != NULL ) { ++ size_t sz = type_sizeof(proc, info); ++ ctx->struct_size += sz; ++ return allocate_hfa(ctx, proc, info, valuep, ++ hfa_info->type, hfa_size); ++ } ++#endif + /* PPC64: Fixed size aggregates and unions passed by + * value are mapped to as many doublewords of the + * parameter save area as the value uses in memory. +@@ -326,6 +509,10 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, + size_t sz = type_sizeof(proc, valuep->type); + if (sz == (size_t)-1) + return -1; ++ ++ if (ctx->ret_struct) ++ ctx->struct_size += sz; ++ + size_t slots = (sz + width - 1) / width; /* Round up. */ + unsigned char *buf = value_reserve(valuep, slots * width); + if (buf == NULL) +@@ -346,9 +533,11 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *fp_info + = type_get_fp_equivalent(valuep->type); + if (fp_info != NULL) +- rc = allocate_float(ctx, proc, fp_info, &val); ++ rc = allocate_float(ctx, proc, fp_info, &val, ++ 8-type_sizeof(proc,info), false); + else +- rc = allocate_gpr(ctx, proc, long_info, &val); ++ rc = allocate_gpr(ctx, proc, long_info, &val, ++ 0, false); + + if (rc >= 0) { + memcpy(ptr, value_get_data(&val, NULL), width); +@@ -363,6 +552,7 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, + return rc; + } + ++#ifndef __LITTLE_ENDIAN__ + /* Small values need post-processing. */ + if (sz < width) { + switch (info->type) { +@@ -394,6 +584,7 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, + break; + } + } ++#endif + + return 0; + } +@@ -411,7 +602,22 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, + struct process *proc, struct arg_type_info *info, + struct value *valuep) + { ++ if (fetch_context_init(proc, ctx) < 0) ++ return -1; ++ ++#if _CALL_ELF == 2 ++ void *ptr = (void *)(ctx->regs.r64[1]+32); ++ uint64_t val = ptrace(PTRACE_PEEKTEXT, proc->pid, ptr, 0); ++ ++ if (ctx->ret_struct ++ && ((ctx->struct_size > 64 ++ || ctx->struct_hfa_count > 8 ++ || (ctx->struct_hfa_size == 0 && ctx->struct_size > 56) ++ || (ctx->regs.r64[3] == ctx->regs.r64[1]+32) ++ || (ctx->regs.r64[3] == val )))) { ++#else + if (ctx->ret_struct) { ++#endif + assert(info->type == ARGTYPE_STRUCT); + + uint64_t addr = read_gpr(ctx, proc, 3); +@@ -424,8 +630,6 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, + return 0; + } + +- if (fetch_context_init(proc, ctx) < 0) +- return -1; + return allocate_argument(ctx, proc, info, valuep); + } + +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 332daa8..45ed7fb 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -136,7 +136,11 @@ + */ + + #define PPC_PLT_STUB_SIZE 16 +-#define PPC64_PLT_STUB_SIZE 8 //xxx ++#if _CALL_ELF != 2 ++#define PPC64_PLT_STUB_SIZE 8 ++#else ++#define PPC64_PLT_STUB_SIZE 4 ++#endif + + static inline int + host_powerpc64() +@@ -186,8 +190,13 @@ ppc32_delayed_symbol(struct library_symbol *libsym) + if ((insn1 & BRANCH_MASK) == B_INSN + || ((insn2 & BRANCH_MASK) == B_INSN + /* XXX double cast */ ++#ifdef __LITTLE_ENDIAN__ ++ && (ppc_branch_dest(libsym->enter_addr + 4, insn1) ++ == (arch_addr_t) (long) libsym->lib->arch.pltgot_addr))) ++#else + && (ppc_branch_dest(libsym->enter_addr + 4, insn2) + == (arch_addr_t) (long) libsym->lib->arch.pltgot_addr))) ++#endif + { + mark_as_resolved(libsym, libsym->arch.resolved_value); + } +@@ -206,7 +215,7 @@ arch_dynlink_done(struct process *proc) + "couldn't read PLT value for %s(%p): %s\n", + libsym->name, libsym->enter_addr, + strerror(errno)); +- return; ++ return; + } + + if (proc->e_machine == EM_PPC) +@@ -227,8 +236,14 @@ reloc_is_irelative(int machine, GElf_Rela *rela) + { + bool irelative = false; + if (machine == EM_PPC64) { +-#ifdef R_PPC64_JMP_IREL ++#ifdef __LITTLE_ENDIAN__ ++# ifdef R_PPC64_IRELATIVE ++ irelative = GELF_R_TYPE(rela->r_info) == R_PPC64_IRELATIVE; ++# endif ++#else ++# ifdef R_PPC64_JMP_IREL + irelative = GELF_R_TYPE(rela->r_info) == R_PPC64_JMP_IREL; ++# endif + #endif + } else { + assert(machine == EM_PPC); +@@ -285,6 +300,7 @@ arch_translate_address_dyn(struct process *proc, + arch_addr_t addr, arch_addr_t *ret) + { + if (proc->e_machine == EM_PPC64) { ++#if _CALL_ELF != 2 + uint64_t value; + if (proc_read_64(proc, addr, &value) < 0) { + fprintf(stderr, +@@ -296,6 +312,7 @@ arch_translate_address_dyn(struct process *proc, + * arch_addr_t becomes integral type. */ + *ret = (arch_addr_t)(uintptr_t)value; + return 0; ++#endif + } + + *ret = addr; +@@ -306,7 +323,8 @@ int + arch_translate_address(struct ltelf *lte, + arch_addr_t addr, arch_addr_t *ret) + { +- if (lte->ehdr.e_machine == EM_PPC64) { ++ if (lte->ehdr.e_machine == EM_PPC64 ++ && !lte->arch.elfv2_abi) { + /* XXX The double cast should be removed when + * arch_addr_t becomes integral type. */ + GElf_Xword offset +@@ -430,7 +448,16 @@ reloc_copy_if_irelative(GElf_Rela *rela, void *data) + int + arch_elf_init(struct ltelf *lte, struct library *lib) + { ++ ++ /* Check for ABIv2 in ELF header processor specific flag. */ ++#ifndef EF_PPC64_ABI ++ assert (! (lte->ehdr.e_flags & 3 ) == 2) ++#else ++ lte->arch.elfv2_abi=((lte->ehdr.e_flags & EF_PPC64_ABI) == 2) ; ++#endif ++ + if (lte->ehdr.e_machine == EM_PPC64 ++ && !lte->arch.elfv2_abi + && load_opd_data(lte, lib) < 0) + return -1; + +@@ -599,7 +626,7 @@ read_plt_slot_value(struct process *proc, GElf_Addr addr, GElf_Addr *valp) + uint64_t l; + /* XXX double cast. */ + if (proc_read_64(proc, (arch_addr_t)(uintptr_t)addr, &l) < 0) { +- fprintf(stderr, "ptrace .plt slot value @%#" PRIx64": %s\n", ++ debug(DEBUG_EVENT, "ptrace .plt slot value @%#" PRIx64": %s", + addr, strerror(errno)); + return -1; + } +@@ -616,7 +643,7 @@ unresolve_plt_slot(struct process *proc, GElf_Addr addr, GElf_Addr value) + * pointers intact. Hence the only adjustment that we need to + * do is to IP. */ + if (ptrace(PTRACE_POKETEXT, proc->pid, addr, value) < 0) { +- fprintf(stderr, "failed to unresolve .plt slot: %s\n", ++ debug(DEBUG_EVENT, "failed to unresolve .plt slot: %s", + strerror(errno)); + return -1; + } +@@ -629,9 +656,48 @@ arch_elf_add_func_entry(struct process *proc, struct ltelf *lte, + arch_addr_t addr, const char *name, + struct library_symbol **ret) + { +- if (lte->ehdr.e_machine != EM_PPC || lte->ehdr.e_type == ET_DYN) ++#ifndef PPC64_LOCAL_ENTRY_OFFSET ++ assert(! lte->arch.elfv2_abi); ++#else ++ /* With ABIv2 st_other field contains an offset. */ ++ if (lte->arch.elfv2_abi) ++ addr += PPC64_LOCAL_ENTRY_OFFSET(sym->st_other); ++#endif ++ ++ int st_info = GELF_ST_TYPE(sym->st_info); ++ ++ if ((lte->ehdr.e_machine != EM_PPC && sym->st_other == 0) ++ || lte->ehdr.e_type == ET_DYN ++ || (st_info == STT_FUNC && ! sym->st_other)) + return PLT_DEFAULT; + ++ if (st_info == STT_FUNC) { ++ /* Put the default symbol to the chain. ++ * The addr has already been updated with ++ * symbol offset */ ++ char *full_name = strdup(name); ++ if (full_name == NULL) { ++ fprintf(stderr, "couldn't copy name of %s: %s\n", ++ name, strerror(errno)); ++ free(full_name); ++ return PLT_FAIL; ++ } ++ struct library_symbol *libsym = malloc(sizeof *libsym); ++ if (libsym == NULL ++ || library_symbol_init(libsym, addr, full_name, 1, ++ LS_TOPLT_NONE) < 0) { ++ free(libsym); ++ delete_symbol_chain(libsym); ++ libsym = NULL; ++ fprintf(stderr, "Couldn't add symbol %s" ++ "for tracing.\n", name); ++ } ++ full_name = NULL; ++ libsym->next = *ret; ++ *ret = libsym; ++ return PLT_OK; ++ } ++ + bool ifunc = false; + #ifdef STT_GNU_IFUNC + ifunc = GELF_ST_TYPE(sym->st_info) == STT_GNU_IFUNC; +@@ -761,9 +827,15 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + assert(plt_slot_addr >= lte->plt_addr + || plt_slot_addr < lte->plt_addr + lte->plt_size); + ++ /* Should avoid to do read if dynamic linker hasn't run yet ++ * or allow -1 a valid return code. */ + GElf_Addr plt_slot_value; +- if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) +- goto fail; ++ if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) { ++ if (!lte->arch.elfv2_abi) ++ goto fail; ++ else ++ return PPC_PLT_UNRESOLVED; ++ } + + struct library_symbol *libsym = malloc(sizeof(*libsym)); + if (libsym == NULL) { +@@ -997,8 +1069,12 @@ ppc_plt_bp_continue(struct breakpoint *bp, struct process *proc) + return; + } + ++#if _CALL_ELF == 2 ++ continue_after_breakpoint(proc, bp); ++#else + jump_to_entry_point(proc, bp); + continue_process(proc->pid); ++#endif + return; + + case PPC64_PLT_STUB: +@@ -1123,7 +1199,11 @@ arch_library_symbol_init(struct library_symbol *libsym) + /* We set type explicitly in the code above, where we have the + * necessary context. This is for calls from ltrace-elf.c and + * such. */ ++#if _CALL_ELF == 2 ++ libsym->arch.type = PPC_PLT_UNRESOLVED; ++#else + libsym->arch.type = PPC_DEFAULT; ++#endif + return 0; + } + +diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c +index ee9a6b5..5aab538 100644 +--- a/sysdeps/linux-gnu/ppc/trace.c ++++ b/sysdeps/linux-gnu/ppc/trace.c +@@ -65,9 +65,15 @@ syscall_p(struct process *proc, int status, int *sysnum) + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + long pc = (long)get_instruction_pointer(proc); ++#ifndef __LITTLE_ENDIAN__ + int insn = + (int)ptrace(PTRACE_PEEKTEXT, proc->pid, pc - sizeof(long), + 0); ++#else ++ int insn = ++ (int)ptrace(PTRACE_PEEKTEXT, proc->pid, pc - sizeof(int), ++ 0); ++#endif + + if (insn == SYSCALL_INSN) { + *sysnum = +diff -up ltrace-0.7.91/sysdeps/linux-gnu/ppc/trace.c\~ ltrace-0.7.91/sysdeps/linux-gnu/ppc/trace.c +--- ltrace-0.7.91/sysdeps/linux-gnu/ppc/trace.c~ 2014-08-08 14:05:58.000000000 +0200 ++++ ltrace-0.7.91/sysdeps/linux-gnu/ppc/trace.c 2014-08-08 14:07:55.000000000 +0200 +@@ -133,7 +133,11 @@ arch_sw_singlestep(struct process *proc, + return SWS_FAIL; + uint32_t insn; + #ifdef __powerpc64__ ++# ifdef __LITTLE_ENDIAN__ ++ insn = (uint32_t) l; ++# else + insn = l >> 32; ++# endif + #else + insn = l; + #endif +diff -up ltrace-0.7.91/configure\~ ltrace-0.7.91/configure +--- ltrace-0.7.91/configure~ 2014-08-08 14:09:12.000000000 +0200 ++++ ltrace-0.7.91/configure 2014-08-08 14:18:30.000000000 +0200 +@@ -2555,7 +2555,7 @@ case "${host_cpu}" in + arm*|sa110) HOST_CPU="arm" ;; + cris*) HOST_CPU="cris" ;; + mips*) HOST_CPU="mips" ;; +- powerpc|powerpc64) HOST_CPU="ppc" ;; ++ powerpc|powerpc64|powerpc64le) HOST_CPU="ppc" ;; + sun4u|sparc64) HOST_CPU="sparc" ;; + s390x) HOST_CPU="s390" ;; + i?86|x86_64) HOST_CPU="x86" ;; +@@ -12094,7 +12094,7 @@ if test x"$enable_libunwind" = xyes; the + arm*|sa110) UNWIND_ARCH="arm" ;; + i?86) UNWIND_ARCH="x86" ;; + powerpc) UNWIND_ARCH="ppc32" ;; +- powerpc64) UNWIND_ARCH="ppc64" ;; ++ powerpc64|powerpc64le) UNWIND_ARCH="ppc64" ;; + mips*) UNWIND_ARCH="mips" ;; + *) UNWIND_ARCH="${host_cpu}" ;; + esac diff --git a/ltrace-0.7.91-rh1799619.patch b/ltrace-0.7.91-rh1799619.patch new file mode 100644 index 0000000..ceef540 --- /dev/null +++ b/ltrace-0.7.91-rh1799619.patch @@ -0,0 +1,16 @@ +diff -rup a/expr.c b/expr.c +--- a/expr.c 2013-10-10 08:43:55.000000000 -0400 ++++ b/expr.c 2020-02-06 17:05:40.658679755 -0500 +@@ -189,10 +189,10 @@ int + expr_clone(struct expr_node *retp, const struct expr_node *node) + { + *retp = *node; ++ struct expr_node *nlhs; ++ struct expr_node *nrhs = NULL; + + switch (node->kind) { +- struct expr_node *nlhs; +- struct expr_node *nrhs; + + case EXPR_OP_ARGNO: + case EXPR_OP_SELF: diff --git a/ltrace-0.7.91-s390-fetch-syscall.patch b/ltrace-0.7.91-s390-fetch-syscall.patch new file mode 100644 index 0000000..da50b48 --- /dev/null +++ b/ltrace-0.7.91-s390-fetch-syscall.patch @@ -0,0 +1,69 @@ +@@ -, +, @@ + exe->mount("source", "target", "filesystemtype", 0, nil + mount@SYS("", "target", "filesystemtype", 0, nil) = -2 + <... mount resumed> = -1 +--- + sysdeps/linux-gnu/s390/fetch.c | 17 ++++++++++++----- + 1 files changed, 12 insertions(+), 5 deletions(-) +--- a/sysdeps/linux-gnu/s390/fetch.c ++++ a/sysdeps/linux-gnu/s390/fetch.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -61,7 +62,8 @@ s390x(struct fetch_context *ctx) + } + + static int +-fetch_register_banks(struct process *proc, struct fetch_context *ctx) ++fetch_register_banks(struct process *proc, struct fetch_context *ctx, ++ bool syscall_enter) + { + ptrace_area parea; + parea.len = sizeof(ctx->regs); +@@ -72,15 +74,20 @@ fetch_register_banks(struct process *proc, struct fetch_context *ctx) + strerror(errno)); + return -1; + } ++ ++ if (syscall_enter) ++ ctx->regs.gprs[2] = ctx->regs.orig_gpr2; ++ + return 0; + } + + static int +-fetch_context_init(struct process *proc, struct fetch_context *context) ++fetch_context_init(struct process *proc, struct fetch_context *context, ++ bool syscall_enter) + { + context->greg = 2; + context->freg = 0; +- return fetch_register_banks(proc, context); ++ return fetch_register_banks(proc, context, syscall_enter); + } + + struct fetch_context * +@@ -89,7 +96,7 @@ arch_fetch_arg_init(enum tof type, struct process *proc, + { + struct fetch_context *context = malloc(sizeof(*context)); + if (context == NULL +- || fetch_context_init(proc, context) < 0) { ++ || fetch_context_init(proc, context, type == LT_TOF_SYSCALL) < 0) { + fprintf(stderr, "arch_fetch_arg_init: %s\n", + strerror(errno)); + free(context); +@@ -277,7 +284,7 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, + return 0; + } + +- if (fetch_context_init(proc, ctx) < 0) ++ if (fetch_context_init(proc, ctx, false) < 0) + return -1; + return arch_fetch_arg_next(ctx, type, proc, info, valuep); + } +-- diff --git a/ltrace-0.7.91-s390-irelative.patch b/ltrace-0.7.91-s390-irelative.patch new file mode 100644 index 0000000..29222c9 --- /dev/null +++ b/ltrace-0.7.91-s390-irelative.patch @@ -0,0 +1,67 @@ +@@ -, +, @@ +--- + sysdeps/linux-gnu/s390/arch.h | 2 ++ + sysdeps/linux-gnu/s390/plt.c | 22 ++++++++++++++++++++++ + 2 files changed, 24 insertions(+) +--- a/sysdeps/linux-gnu/s390/arch.h ++++ a/sysdeps/linux-gnu/s390/arch.h +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2001 IBM Poughkeepsie, IBM Corporation + * + * This program is free software; you can redistribute it and/or +@@ -25,6 +26,7 @@ + #define ARCH_HAVE_FETCH_ARG + #define ARCH_HAVE_SIZEOF + #define ARCH_HAVE_ALIGNOF ++#define ARCH_HAVE_ADD_PLT_ENTRY + + #define LT_ELFCLASS ELFCLASS32 + #define LT_ELF_MACHINE EM_S390 +--- a/sysdeps/linux-gnu/s390/plt.c ++++ a/sysdeps/linux-gnu/s390/plt.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2004,2008,2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -19,9 +20,12 @@ + */ + + #include ++#include ++ + #include "proc.h" + #include "common.h" + #include "library.h" ++#include "trace.h" + + GElf_Addr + arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { +@@ -33,3 +37,21 @@ sym2addr(struct process *proc, struct library_symbol *sym) + { + return sym->enter_addr; + } ++ ++enum plt_status ++arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, ++ const char *a_name, GElf_Rela *rela, size_t ndx, ++ struct library_symbol **ret) ++{ ++#ifdef R_390_IRELATIVE ++ bool irelative = GELF_R_TYPE(rela->r_info) == R_390_IRELATIVE; ++#else ++ bool irelative = false; ++#endif ++ ++ if (irelative) ++ return linux_elf_add_plt_entry_irelative(proc, lte, rela, ++ ndx, ret); ++ ++ return PLT_DEFAULT; ++} +-- diff --git a/ltrace-0.7.91-tautology.patch b/ltrace-0.7.91-tautology.patch new file mode 100644 index 0000000..24ff020 --- /dev/null +++ b/ltrace-0.7.91-tautology.patch @@ -0,0 +1,96 @@ +diff -r -U3 -p ltrace-0.7.91.orig/filter.c ltrace-0.7.91.dj/filter.c +--- ltrace-0.7.91.orig/filter.c 2012-12-16 20:53:44.000000000 -0500 ++++ ltrace-0.7.91.dj/filter.c 2016-06-17 19:07:23.678490985 -0400 +@@ -79,7 +79,7 @@ filter_lib_matcher_name_init(struct filt + { + switch (type) { + case FLM_MAIN: +- assert(type != type); ++ //assert(type != type); + abort(); + + case FLM_SONAME: +@@ -137,7 +137,7 @@ matcher_matches_library(struct filter_li + case FLM_MAIN: + return lib->type == LT_LIBTYPE_MAIN; + } +- assert(matcher->type != matcher->type); ++ //assert(matcher->type != matcher->type); + abort(); + } + +Only in ltrace-0.7.91.dj/: filter.c~ +diff -r -U3 -p ltrace-0.7.91.orig/sysdeps/linux-gnu/proc.c ltrace-0.7.91.dj/sysdeps/linux-gnu/proc.c +--- ltrace-0.7.91.orig/sysdeps/linux-gnu/proc.c 2013-10-11 15:27:11.000000000 -0400 ++++ ltrace-0.7.91.dj/sysdeps/linux-gnu/proc.c 2016-06-17 18:59:42.333774042 -0400 +@@ -242,9 +242,10 @@ process_tasks(pid_t pid, pid_t **ret_tas + size_t alloc = 0; + + while (1) { +- struct dirent entry; + struct dirent *result; +- if (readdir_r(d, &entry, &result) != 0) { ++ errno = 0; ++ result = readdir(d); ++ if (result == NULL && errno != 0) { + fail: + free(tasks); + closedir(d); +Only in ltrace-0.7.91.dj/sysdeps/linux-gnu: proc.c~ +diff -r -U3 -p ltrace-0.7.91.orig/sysdeps/linux-gnu/x86/fetch.c ltrace-0.7.91.dj/sysdeps/linux-gnu/x86/fetch.c +--- ltrace-0.7.91.orig/sysdeps/linux-gnu/x86/fetch.c 2013-10-24 08:33:35.000000000 -0400 ++++ ltrace-0.7.91.dj/sysdeps/linux-gnu/x86/fetch.c 2016-06-17 18:52:33.962842191 -0400 +@@ -523,7 +523,7 @@ classify(struct process *proc, struct fe + + default: + /* Unsupported type. */ +- assert(info->type != info->type); ++ //assert(info->type != info->type); + abort(); + } + abort(); +Only in ltrace-0.7.91.dj/sysdeps/linux-gnu/x86: fetch.c~ +diff -r -U3 -p ltrace-0.7.91.orig/sysdeps/linux-gnu/x86/trace.c ltrace-0.7.91.dj/sysdeps/linux-gnu/x86/trace.c +--- ltrace-0.7.91.orig/sysdeps/linux-gnu/x86/trace.c 2012-12-16 20:53:45.000000000 -0500 ++++ ltrace-0.7.91.dj/sysdeps/linux-gnu/x86/trace.c 2016-06-17 18:52:16.699844065 -0400 +@@ -145,7 +145,7 @@ arch_type_sizeof(struct process *proc, s + return (size_t)-2; + + default: +- assert(info->type != info->type); ++ //assert(info->type != info->type); + abort(); + } + } +@@ -158,7 +158,7 @@ arch_type_alignof(struct process *proc, + + switch (info->type) { + default: +- assert(info->type != info->type); ++ //assert(info->type != info->type); + abort(); + break; + +Only in ltrace-0.7.91.dj/sysdeps/linux-gnu/x86: trace.c~ +diff -r -U3 -p ltrace-0.7.91.orig/value.c ltrace-0.7.91.dj/value.c +--- ltrace-0.7.91.orig/value.c 2013-10-10 08:43:55.000000000 -0400 ++++ ltrace-0.7.91.dj/value.c 2016-06-17 19:11:43.441047589 -0400 +@@ -363,7 +363,7 @@ value_set_word(struct value *value, long + u.u64 = word; + break; + default: +- assert(sz != sz); ++ //assert(sz != sz); + abort(); + } + +@@ -414,7 +414,7 @@ value_extract_word(struct value *value, + *retp = (long)u.u64; + return 0; + default: +- assert(sz != sz); ++ //assert(sz != sz); + abort(); + } + } +Only in ltrace-0.7.91.dj/: value.c~ diff --git a/ltrace-0.7.91-testsuite-includes-2.patch b/ltrace-0.7.91-testsuite-includes-2.patch new file mode 100644 index 0000000..100443a --- /dev/null +++ b/ltrace-0.7.91-testsuite-includes-2.patch @@ -0,0 +1,48 @@ +From 57dbe34ea7aa54b97e11406e1cfb2e427a68779e Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Wed, 8 Apr 2015 16:04:13 +0200 +Subject: [PATCH] Fix warnings in compilation of test-suite cases + +--- + testsuite/ltrace.main/signals.c | 5 +++-- + testsuite/ltrace.minor/wchar.exp | 3 ++- + testsuite/ltrace.torture/signals.c | 5 +++-- + 3 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/testsuite/ltrace.main/signals.c b/testsuite/ltrace.main/signals.c +index a02e795..ab23fc7 100644 +--- a/testsuite/ltrace.main/signals.c ++++ b/testsuite/ltrace.main/signals.c +@@ -2,9 +2,10 @@ + Objectives : Verify that ltrace can trace user defined signal. + This file was written by Yao Qi . */ + +-#include +-#include ++#include ++#include + #include ++#include + + #define LOOP 7 + +diff --git a/testsuite/ltrace.torture/signals.c b/testsuite/ltrace.torture/signals.c +index b786c81..c66416e 100644 +--- a/testsuite/ltrace.torture/signals.c ++++ b/testsuite/ltrace.torture/signals.c +@@ -2,9 +2,10 @@ + Objectives : Verify that ltrace can trace user defined signal. + This file was written by Yao Qi . */ + +-#include +-#include ++#include ++#include + #include ++#include + + #define LOOP 20 + +-- +2.3.5 + diff --git a/ltrace-0.7.91-testsuite-includes.patch b/ltrace-0.7.91-testsuite-includes.patch new file mode 100644 index 0000000..0c5a324 --- /dev/null +++ b/ltrace-0.7.91-testsuite-includes.patch @@ -0,0 +1,216 @@ +From 694d19ff14017926454771cbb63a22355b72f1bf Mon Sep 17 00:00:00 2001 +From: Faraz Shahbazker +Date: Tue, 3 Feb 2015 13:07:55 -0800 +Subject: [PATCH] Fix missing includes and return statements in test sources + +Fix warnings while compiling test cases by adding missing #includes and +return statements. Missing arguments provided for functions wait()/wcswidth() +--- + testsuite/ltrace.main/filters.exp | 1 + + testsuite/ltrace.main/main-internal.exp | 4 +++- + testsuite/ltrace.main/main-threaded.c | 1 + + testsuite/ltrace.main/parameters.c | 1 + + testsuite/ltrace.main/parameters2.exp | 2 +- + testsuite/ltrace.main/parameters3.exp | 2 ++ + testsuite/ltrace.main/system_call_params.exp | 4 ++++ + testsuite/ltrace.minor/attach-process.exp | 1 + + testsuite/ltrace.minor/libdl-simple.c | 2 ++ + testsuite/ltrace.minor/time-record.c | 1 + + testsuite/ltrace.minor/trace-clone.c | 2 ++ + testsuite/ltrace.minor/trace-fork.c | 4 +++- + testsuite/ltrace.minor/wchar.exp | 3 ++- + testsuite/ltrace.torture/vfork-thread.c | 1 + + 14 files changed, 25 insertions(+), 4 deletions(-) + +diff --git a/testsuite/ltrace.main/filters.exp b/testsuite/ltrace.main/filters.exp +index 988346f..f7f4140 100644 +--- a/testsuite/ltrace.main/filters.exp ++++ b/testsuite/ltrace.main/filters.exp +@@ -22,6 +22,7 @@ set libfilt1 [ltraceCompile libfilt1.so [ltraceSource c { + }]] + + set libfilt2 [ltraceCompile libfilt2.so [ltraceSource c { ++ #include + void func2(void) { puts("func2"); } + }]] + +diff --git a/testsuite/ltrace.main/main-internal.exp b/testsuite/ltrace.main/main-internal.exp +index 0ca5e14..112c69b 100644 +--- a/testsuite/ltrace.main/main-internal.exp ++++ b/testsuite/ltrace.main/main-internal.exp +@@ -19,11 +19,13 @@ + set bin [ltraceCompile {} [ltraceSource c { + __attribute__((noinline)) void this(void) {} + __attribute__((noinline)) void that(void) {} +- int main(int i) { ++ int main() { ++ int i; + for (i = 0; i < 12; ++i) { + this(); + that(); + } ++ return 0; + } + }]] + +diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c +index a3d8bb5..aa862b9 100644 +--- a/testsuite/ltrace.main/parameters.c ++++ b/testsuite/ltrace.main/parameters.c +@@ -17,6 +17,7 @@ void func_intptr_ret(int *i); + int func_strlen(char*); + void func_strfixed(char*); + void func_ppp(int***); ++void func_string(char*); + void func_stringp(char**); + void func_short(short, short); + void func_ushort(unsigned short, unsigned short); +diff --git a/testsuite/ltrace.main/parameters2.exp b/testsuite/ltrace.main/parameters2.exp +index 9850079..1c7b3b4 100644 +--- a/testsuite/ltrace.main/parameters2.exp ++++ b/testsuite/ltrace.main/parameters2.exp +@@ -17,7 +17,7 @@ + # 02110-1301 USA + + set trivial [ltraceCompile {} [ltraceSource c { +- int main(void) {} ++ int main(void) {return 0;} + }]] + + ltraceMatch1 [ltraceRun -L -F [ltraceSource conf { +diff --git a/testsuite/ltrace.main/parameters3.exp b/testsuite/ltrace.main/parameters3.exp +index 693c219..f6d9116 100644 +--- a/testsuite/ltrace.main/parameters3.exp ++++ b/testsuite/ltrace.main/parameters3.exp +@@ -29,8 +29,10 @@ set liba [ltraceCompile liba.so [ltraceSource c { + }]] + + set bin [ltraceCompile {} $liba [ltraceSource c { ++ extern void fun(void); + int main(void) { + fun(); ++ return 0; + } + }]] + +diff --git a/testsuite/ltrace.main/system_call_params.exp b/testsuite/ltrace.main/system_call_params.exp +index 2ccf840..f3a55d2 100644 +--- a/testsuite/ltrace.main/system_call_params.exp ++++ b/testsuite/ltrace.main/system_call_params.exp +@@ -17,12 +17,15 @@ + # 02110-1301 USA + + set bin [ltraceCompile {} [ltraceSource c { ++ #ifndef _GNU_SOURCE + #define _GNU_SOURCE ++ #endif + #include + #include + #include + #include + #include /* For SYS_xxx definitions */ ++ #include + + #ifndef SYS_open + # if defined(__aarch64__) +@@ -38,6 +41,7 @@ set bin [ltraceCompile {} [ltraceSource c { + syscall(SYS_open, "/some/path", O_RDONLY); + write(1, "something", 10); + mount("source", "target", "filesystemtype", 0, 0); ++ return 0; + } + }]] + +diff --git a/testsuite/ltrace.minor/attach-process.exp b/testsuite/ltrace.minor/attach-process.exp +index 2c7d20c..c050f21 100644 +--- a/testsuite/ltrace.minor/attach-process.exp ++++ b/testsuite/ltrace.minor/attach-process.exp +@@ -21,6 +21,7 @@ set bin [ltraceCompile {} [ltraceSource c { + int main(void) { + sleep(5); + sleep(1); ++ return 0; + } + }]] + +diff --git a/testsuite/ltrace.minor/libdl-simple.c b/testsuite/ltrace.minor/libdl-simple.c +index 0bef5cf..b1be002 100644 +--- a/testsuite/ltrace.minor/libdl-simple.c ++++ b/testsuite/ltrace.minor/libdl-simple.c +@@ -1,6 +1,7 @@ + #include + #include + #include ++#include + + int main(int argc, char **argv) { + void *handle; +@@ -21,4 +22,5 @@ int main(int argc, char **argv) { + + printf("%d\n", test(5)); + dlclose(handle); ++ return 0; + } +diff --git a/testsuite/ltrace.minor/time-record.c b/testsuite/ltrace.minor/time-record.c +index a66b838..7d5e5e3 100644 +--- a/testsuite/ltrace.minor/time-record.c ++++ b/testsuite/ltrace.minor/time-record.c +@@ -5,6 +5,7 @@ + This file was written by Yao Qi . */ + #include + #include ++#include + + #define SLEEP_COUNT 2 + #define NANOSLEEP_COUNT 50 +diff --git a/testsuite/ltrace.minor/trace-clone.c b/testsuite/ltrace.minor/trace-clone.c +index ded930c..6aab235 100644 +--- a/testsuite/ltrace.minor/trace-clone.c ++++ b/testsuite/ltrace.minor/trace-clone.c +@@ -3,7 +3,9 @@ + clone called. + + This file was written by Yao Qi . */ ++#ifndef _GNU_SOURCE + #define _GNU_SOURCE ++#endif + #include + #include + #include +diff --git a/testsuite/ltrace.minor/trace-fork.c b/testsuite/ltrace.minor/trace-fork.c +index c5f0c71..9611184 100644 +--- a/testsuite/ltrace.minor/trace-fork.c ++++ b/testsuite/ltrace.minor/trace-fork.c +@@ -6,6 +6,8 @@ + + #include + #include ++#include ++#include + + void + child () +@@ -27,7 +29,7 @@ main () + else + { + printf("My child pid is %d\n",pid); +- wait(); ++ wait(NULL); + } + return 0; + } +diff --git a/testsuite/ltrace.torture/vfork-thread.c b/testsuite/ltrace.torture/vfork-thread.c +index f909bd3..4c118a6 100644 +--- a/testsuite/ltrace.torture/vfork-thread.c ++++ b/testsuite/ltrace.torture/vfork-thread.c +@@ -13,6 +13,7 @@ routine (void *data) + puts ("bleble"); + sleep (1); + } ++ return NULL; + } + + +-- +2.1.0 + diff --git a/ltrace-0.7.91-testsuite-system_call_params.patch b/ltrace-0.7.91-testsuite-system_call_params.patch new file mode 100644 index 0000000..233b5c8 --- /dev/null +++ b/ltrace-0.7.91-testsuite-system_call_params.patch @@ -0,0 +1,68 @@ +diff -rup a/testsuite/Makefile.am b/testsuite/Makefile.am +--- a/testsuite/Makefile.am 2012-12-16 20:53:45.000000000 -0500 ++++ b/testsuite/Makefile.am 2019-06-28 16:59:19.935602953 -0400 +@@ -39,6 +39,7 @@ env.exp: Makefile + rm -f env.exp + echo set libelf_LD_LIBRARY_PATH '"$(libelf_LD_LIBRARY_PATH)"' >> $@ + echo set libunwind_LD_LIBRARY_PATH '"$(libunwind_LD_LIBRARY_PATH)"' >> $@ ++ echo set PREFIX '"$(prefix)"' >> $@ + + CLEANFILES = *.o *.so *.log *.sum *.ltrace site.bak setval.tmp site.exp env.exp + +diff -rup a/testsuite/Makefile.in b/testsuite/Makefile.in +--- a/testsuite/Makefile.in 2013-11-04 20:22:47.000000000 -0500 ++++ b/testsuite/Makefile.in 2019-06-28 16:59:12.075602806 -0400 +@@ -648,6 +648,7 @@ env.exp: Makefile + rm -f env.exp + echo set libelf_LD_LIBRARY_PATH '"$(libelf_LD_LIBRARY_PATH)"' >> $@ + echo set libunwind_LD_LIBRARY_PATH '"$(libunwind_LD_LIBRARY_PATH)"' >> $@ ++ echo set PREFIX '"$(prefix)"' >> $@ + + # Tell versions [3.59,3.63) of GNU make to not export all variables. + # Otherwise a system limit (for SysV at least) may be exceeded. +diff -rup a/testsuite/ltrace.main/system_call_params.exp b/testsuite/ltrace.main/system_call_params.exp +--- a/testsuite/ltrace.main/system_call_params.exp 2019-06-28 16:44:07.542584754 -0400 ++++ b/testsuite/ltrace.main/system_call_params.exp 2019-06-28 17:00:35.811604355 -0400 +@@ -1,5 +1,5 @@ + # This file is part of ltrace. +-# Copyright (C) 2013, 2014 Petr Machata, Red Hat Inc. ++# Copyright (C) 2013, 2014, 2015 Petr Machata, Red Hat Inc. + # + # This program is free software; you can redistribute it and/or + # modify it under the terms of the GNU General Public License as +@@ -60,8 +60,35 @@ set conf [ltraceNamedSource "$dir/syscal + # somelib.conf is passed, and syscalls.conf is not available, or + # doesn't list readdir, that would be taken from somelib.conf with a + # wrong prototype. ++# ++# This test relies on the fact that there is no global config file ++# that would provide legitimate system call prototypes. But that ++# doesn't have to be true, maybe ltrace is already installed on the ++# system with the right prefix. So first compile a wrapper that we ++# use to redirect fopen calls. ++ ++set libfopen_so [ltraceCompile libfopen.so -ldl \ ++ [ltraceSource c [string map [list "@PREFIX@" "$PREFIX"] { ++ #define _GNU_SOURCE ++ #include ++ #include ++ #include ++ ++ FILE * ++ fopen(const char *path, const char *mode) ++ { ++ #define PATH "@PREFIX@/share" ++ if (strncmp(path, PATH, sizeof(PATH) - 1) == 0) ++ path = "/dev/null"; + ++ return ((FILE *(*)(const char *, const char *)) ++ dlsym(RTLD_NEXT, "fopen")) (path, mode); ++ } ++}]]] ++ ++setenv LD_PRELOAD $libfopen_so + ltraceMatch1 [ltraceRun -L -S -F $conf -- $bin] {^open@SYS\("/some/path", 0\)} == 0 ++unsetenv LD_PRELOAD + + # On the other hand, if -F somedir/ is given, we want to accept + # syscalls.conf found there. diff --git a/ltrace-0.7.91-unwind-elfutils.patch b/ltrace-0.7.91-unwind-elfutils.patch new file mode 100644 index 0000000..3b46a76 --- /dev/null +++ b/ltrace-0.7.91-unwind-elfutils.patch @@ -0,0 +1,422 @@ +Common subdirectories: ltrace-0.7.91/config and ltrace-0.7.91-pm/config +diff -u ltrace-0.7.91/configure.ac ltrace-0.7.91-pm/configure.ac +--- ltrace-0.7.91/configure.ac 2015-01-09 00:38:17.977190726 +0100 ++++ ltrace-0.7.91-pm/configure.ac 2015-01-09 00:37:40.261910548 +0100 +@@ -128,6 +128,51 @@ + AC_CHECK_HEADERS(selinux/selinux.h) + AC_CHECK_LIB(selinux, security_get_boolean_active) + ++dnl Whether (and which) elfutils libdw.so to use for unwinding. ++AC_ARG_WITH(elfutils, ++ AS_HELP_STRING([--with-elfutils], [Use elfutils libdwfl unwinding support]), ++ [case "${withval}" in ++ (yes|no) enable_elfutils=$withval;; ++ (*) enable_elfutils=yes ++ AM_CPPFLAGS="${AM_CPPFLAGS} -I${withval}/include" ++ AM_LDFLAGS="${AM_LDFLAGS} -L${withval}/lib" ++ elfutils_LD_LIBRARY_PATH="${withval}/lib:${withval}/lib/elfutils" ++ ;; ++esac],[enable_elfutils=maybe]) ++ ++dnl Check whether we have the elfutils libdwfl.h header installed. ++saved_CPPFLAGS="${CPPFLAGS}" ++CPPFLAGS="${CPPFLAGS} ${AM_CPPFLAGS}" ++AC_CHECK_HEADERS([elfutils/libdwfl.h],[have_libdwfl_h=yes]) ++CPPFLAGS="${saved_CPPFLAGS}" ++ ++dnl And whether libdw.so provides the unwinding functions. ++saved_LDFLAGS="${LDFLAGS}" ++LDFLAGS="${LDFLAGS} ${AM_LDFLAGS}" ++AC_CHECK_LIB([dw], [dwfl_getthread_frames], [have_libdw_dwfl_frames=yes]) ++LDFLAGS="${saved_LDFLAGS}" ++ ++AC_MSG_CHECKING([whether to use elfutils libdwfl unwinding support]) ++case "${enable_elfutils}" in ++(yes|maybe) ++ if test x$have_libdwfl_h = xyes -a x$have_libdw_dwfl_frames = xyes; then ++ enable_elfutils=yes ++ elif test $enable_elfutils = maybe; then ++ enable_elfutils=no ++ else ++ AC_MSG_RESULT([$enable_elfutils]) ++ AC_MSG_ERROR([Missing elfutils/libdwfl.h or dwfl_getthread_frames not in libdw.so]) ++ fi ++ ;; ++(*) ;; ++esac ++AC_MSG_RESULT([$enable_elfutils]) ++ ++if test x"$enable_elfutils" = xyes; then ++ libdw_LIBS=-ldw ++ AC_SUBST(libdw_LIBS) ++ AC_DEFINE([HAVE_LIBDW], [1], [we have elfutils libdw]) ++fi + + # HAVE_LIBUNWIND + AC_ARG_WITH(libunwind, +@@ -193,6 +238,13 @@ + LDFLAGS="${saved_LDFLAGS}" + fi + ++if test x"$enable_elfutils" = xyes -a x"$enable_libunwind" = xyes; then ++ AC_MSG_ERROR([Cannot enable both --with-libunwind and --with-elfutils]) ++fi ++ ++if test x"$enable_elfutils" = xyes -o x"$enable_libunwind" = xyes; then ++ AC_DEFINE([HAVE_UNWINDER], [1], [we have an unwinder available]) ++fi + + saved_CPPFLAGS="${CPPFLAGS}" + saved_LDFLAGS="${LDFLAGS}" +@@ -340,6 +392,7 @@ + AC_SUBST(AM_CFLAGS) + AC_SUBST(AM_LDFLAGS) + AC_SUBST(libelf_LD_LIBRARY_PATH) ++AC_SUBST(elfutils_LD_LIBRARY_PATH) + AC_SUBST(libunwind_LD_LIBRARY_PATH) + + AC_CONFIG_FILES([ +Common subdirectories: ltrace-0.7.91/debian and ltrace-0.7.91-pm/debian +Common subdirectories: ltrace-0.7.91/etc and ltrace-0.7.91-pm/etc +diff -u ltrace-0.7.91/ltrace.1 ltrace-0.7.91-pm/ltrace.1 +--- ltrace-0.7.91/ltrace.1 2015-01-09 00:38:17.975190764 +0100 ++++ ltrace-0.7.91-pm/ltrace.1 2015-01-09 00:37:40.261910548 +0100 +@@ -196,7 +196,8 @@ + correct execution of setuid and/or setgid binaries. + .IP "\-w, --where \fInr" + Show backtrace of \fInr\fR stack frames for each traced function. This +-option enabled only if libunwind support was enabled at compile time. ++option enabled only if elfutils or libunwind support was enabled at compile ++time. + .IP "\-x \fIfilter" + A qualifying expression which modifies which symbol table entry points + to trace. The format of the filter expression is described in the +Only in ltrace-0.7.91-pm/: ltrace.1.orig +diff -u ltrace-0.7.91/Makefile.am ltrace-0.7.91-pm/Makefile.am +--- ltrace-0.7.91/Makefile.am 2015-01-09 00:38:17.965190955 +0100 ++++ ltrace-0.7.91-pm/Makefile.am 2015-01-09 00:37:40.260910568 +0100 +@@ -40,6 +40,7 @@ + $(liberty_LIBS) \ + $(libsupcxx_LIBS) \ + $(libstdcxx_LIBS) \ ++ $(libdw_LIBS) \ + $(libunwind_LIBS) \ + sysdeps/libos.la + +diff -u ltrace-0.7.91/options.c ltrace-0.7.91-pm/options.c +--- ltrace-0.7.91/options.c 2015-01-09 00:38:17.974190783 +0100 ++++ ltrace-0.7.91-pm/options.c 2015-01-09 00:37:40.261910548 +0100 +@@ -107,9 +107,9 @@ + " -T show the time spent inside each call.\n" + " -u USERNAME run command with the userid, groupid of username.\n" + " -V, --version output version information and exit.\n" +-#if defined(HAVE_LIBUNWIND) ++#if defined(HAVE_UNWINDER) + " -w, --where=NR print backtrace showing NR stack frames at most.\n" +-#endif /* defined(HAVE_LIBUNWIND) */ ++#endif /* defined(HAVE_UNWINDER) */ + " -x FILTER modify which static functions to trace.\n" + "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n", + progname); +@@ -519,9 +519,9 @@ + progname = argv[0]; + options.output = stderr; + options.no_signals = 0; +-#if defined(HAVE_LIBUNWIND) ++#if defined(HAVE_UNWINDER) + options.bt_depth = -1; +-#endif /* defined(HAVE_LIBUNWIND) */ ++#endif /* defined(HAVE_UNWINDER) */ + + guess_cols(); + +@@ -545,9 +545,9 @@ + {"output", 1, 0, 'o'}, + {"version", 0, 0, 'V'}, + {"no-signals", 0, 0, 'b'}, +-# if defined(HAVE_LIBUNWIND) ++# if defined(HAVE_UNWINDER) + {"where", 1, 0, 'w'}, +-# endif /* defined(HAVE_LIBUNWIND) */ ++# endif /* defined(HAVE_UNWINDER) */ + {0, 0, 0, 0} + }; + #endif +@@ -556,7 +556,7 @@ + #ifdef USE_DEMANGLE + "C" + #endif +-#if defined(HAVE_LIBUNWIND) ++#if defined(HAVE_UNWINDER) + "w:" + #endif + "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:X:"; +@@ -681,11 +681,11 @@ + "There is NO WARRANTY, to the extent permitted by law.\n"); + exit(0); + break; +-#if defined(HAVE_LIBUNWIND) ++#if defined(HAVE_UNWINDER) + case 'w': + options.bt_depth = parse_int(optarg, 'w', 1, 0); + break; +-#endif /* defined(HAVE_LIBUNWIND) */ ++#endif /* defined(HAVE_UNWINDER) */ + + case 'x': + parse_filter_chain(optarg, &options.static_filter); +Only in ltrace-0.7.91-pm/: options.c.orig +diff -u ltrace-0.7.91/options.h ltrace-0.7.91-pm/options.h +--- ltrace-0.7.91/options.h 2015-01-09 00:38:17.966190936 +0100 ++++ ltrace-0.7.91-pm/options.h 2015-01-09 00:37:40.261910548 +0100 +@@ -44,9 +44,9 @@ + size_t strlen; /* default maximum # of bytes printed in strings */ + int follow; /* trace child processes */ + int no_signals; /* don't print signals */ +-#if defined(HAVE_LIBUNWIND) ++#if defined(HAVE_UNWINDER) + int bt_depth; /* how may levels of stack frames to show */ +-#endif /* defined(HAVE_LIBUNWIND) */ ++#endif /* defined(HAVE_UNWINDER) */ + struct filter *plt_filter; + struct filter *static_filter; + +diff -u ltrace-0.7.91/output.c ltrace-0.7.91-pm/output.c +--- ltrace-0.7.91/output.c 2015-01-09 00:38:17.966190936 +0100 ++++ ltrace-0.7.91-pm/output.c 2015-01-09 00:37:40.261910548 +0100 +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + #include "output.h" + #include "demangle.h" +@@ -567,6 +568,73 @@ + stel->out.need_delim = need_delim; + } + ++#if defined(HAVE_LIBDW) ++/* Prints information about one frame of a thread. Called by ++ dwfl_getthread_frames in output_right. Returns 1 when done (max ++ number of frames reached). Returns -1 on error. Returns 0 on ++ success (if there are more frames in the thread, call us again). */ ++static int ++frame_callback (Dwfl_Frame *state, void *arg) ++{ ++ Dwarf_Addr pc; ++ bool isactivation; ++ ++ int *frames = (int *) arg; ++ ++ if (!dwfl_frame_pc(state, &pc, &isactivation)) ++ return -1; ++ ++ if (!isactivation) ++ pc--; ++ ++ Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state)); ++ Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc); ++ const char *modname = NULL; ++ const char *symname = NULL; ++ GElf_Off off = 0; ++ if (mod != NULL) { ++ GElf_Sym sym; ++ modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL); ++ symname = dwfl_module_addrinfo(mod, pc, &off, &sym, ++ NULL, NULL, NULL); ++ } ++ ++ /* This mimics the output produced by libunwind below. */ ++ fprintf(options.output, " > %s(%s+0x%" PRIx64 ") [%" PRIx64 "]\n", ++ modname, symname, off, pc); ++ ++ /* See if we can extract the source line too and print it on ++ the next line if we can find it. */ ++ if (mod != NULL) { ++ Dwfl_Line *l = dwfl_module_getsrc(mod, pc); ++ if (l != NULL) { ++ int line, col; ++ line = col = -1; ++ const char *src = dwfl_lineinfo(l, NULL, &line, &col, ++ NULL, NULL); ++ if (src != NULL) { ++ fprintf(options.output, "\t%s", src); ++ if (line > 0) { ++ fprintf(options.output, ":%d", line); ++ if (col > 0) ++ fprintf(options.output, ++ ":%d", col); ++ } ++ fprintf(options.output, "\n"); ++ } ++ ++ } ++ } ++ ++ /* Max number of frames to print reached? */ ++ if ((*frames)-- == 0) ++ return 1; ++ ++ return 0; ++} ++#endif /* defined(HAVE_LIBDW) */ ++ + void + output_right(enum tof type, struct process *proc, struct library_symbol *libsym, + struct timedelta *spent) +@@ -694,6 +762,24 @@ + } + #endif /* defined(HAVE_LIBUNWIND) */ + ++#if defined(HAVE_LIBDW) ++ if (options.bt_depth > 0 && proc->leader->dwfl != NULL) { ++ int frames = options.bt_depth; ++ if (dwfl_getthread_frames(proc->leader->dwfl, proc->pid, ++ frame_callback, &frames) < 0) { ++ // Only print an error if we couldn't show anything. ++ // Otherwise just show there might be more... ++ if (frames == options.bt_depth) ++ fprintf(stderr, ++ "dwfl_getthread_frames tid %d: %s\n", ++ proc->pid, dwfl_errmsg(-1)); ++ else ++ fprintf(options.output, " > [...]\n"); ++ } ++ fprintf(options.output, "\n"); ++ } ++#endif /* defined(HAVE_LIBDW) */ ++ + current_proc = NULL; + current_column = 0; + } +Only in ltrace-0.7.91-pm/: output.c.orig +diff -u ltrace-0.7.91/proc.c ltrace-0.7.91-pm/proc.c +--- ltrace-0.7.91/proc.c 2015-01-09 00:38:17.981190650 +0100 ++++ ltrace-0.7.91-pm/proc.c 2015-01-09 00:37:40.261910548 +0100 +@@ -111,6 +111,11 @@ + if (proc->unwind_as != NULL) + unw_destroy_addr_space(proc->unwind_as); + #endif /* defined(HAVE_LIBUNWIND) */ ++ ++#if defined(HAVE_LIBDW) ++ if (proc->dwfl != NULL) ++ dwfl_end(proc->dwfl); ++#endif /* defined(HAVE_LIBDW) */ + } + + static int +@@ -172,6 +177,10 @@ + } + #endif /* defined(HAVE_LIBUNWIND) */ + ++#if defined(HAVE_LIBDW) ++ proc->dwfl = NULL; /* Initialize for leader only on first library. */ ++#endif /* defined(HAVE_LIBDW) */ ++ + return 0; + } + +@@ -887,6 +896,59 @@ + debug(DEBUG_PROCESS, "added library %s@%p (%s) to %d", + lib->soname, lib->base, lib->pathname, proc->pid); + ++#if defined(HAVE_LIBDW) ++ if (options.bt_depth > 0) { ++ /* Setup module tracking for libdwfl unwinding. */ ++ struct process *leader = proc->leader; ++ Dwfl *dwfl = leader->dwfl; ++ if (dwfl == NULL) { ++ static const Dwfl_Callbacks proc_callbacks = { ++ .find_elf = dwfl_linux_proc_find_elf, ++ .find_debuginfo = dwfl_standard_find_debuginfo ++ }; ++ dwfl = dwfl_begin(&proc_callbacks); ++ if (dwfl == NULL) ++ fprintf(stderr, ++ "Couldn't initialize libdwfl unwinding " ++ "for process %d: %s\n", leader->pid, ++ dwfl_errmsg (-1)); ++ } ++ ++ if (dwfl != NULL) { ++ dwfl_report_begin_add(dwfl); ++ if (dwfl_report_elf(dwfl, lib->soname, ++ lib->pathname, -1, ++ (GElf_Addr) lib->base, ++ false) == NULL) ++ fprintf(stderr, ++ "dwfl_report_elf %s@%p (%s) %d: %s\n", ++ lib->soname, lib->base, lib->pathname, ++ proc->pid, dwfl_errmsg (-1)); ++ dwfl_report_end(dwfl, NULL, NULL); ++ ++ if (leader->dwfl == NULL) { ++ int r = dwfl_linux_proc_attach(dwfl, ++ leader->pid, ++ true); ++ if (r == 0) ++ leader->dwfl = dwfl; ++ else { ++ const char *msg; ++ dwfl_end(dwfl); ++ if (r < 0) ++ msg = dwfl_errmsg(-1); ++ else ++ msg = strerror(r); ++ fprintf(stderr, "Couldn't initialize " ++ "libdwfl unwinding for " ++ "process %d: %s\n", ++ leader->pid, msg); ++ } ++ } ++ } ++ } ++#endif /* defined(HAVE_LIBDW) */ ++ + /* Insert breakpoints for all active (non-latent) symbols. */ + struct library_symbol *libsym = NULL; + while ((libsym = library_each_symbol(lib, libsym, +diff -u ltrace-0.7.91/proc.c.orig ltrace-0.7.91-pm/proc.c.orig +--- ltrace-0.7.91/proc.h 2015-01-09 00:38:17.966190936 +0100 ++++ ltrace-0.7.91-pm/proc.h 2015-01-09 00:37:40.261910548 +0100 +@@ -28,6 +28,10 @@ + #include + #include + ++#if defined(HAVE_LIBDW) ++# include ++#endif ++ + #if defined(HAVE_LIBUNWIND) + # include + #endif /* defined(HAVE_LIBUNWIND) */ +@@ -113,6 +117,11 @@ + short e_machine; + char e_class; + ++#if defined(HAVE_LIBDW) ++ /* Unwind info for leader, NULL for non-leader procs. */ ++ Dwfl *dwfl; ++#endif /* defined(HAVE_LIBDW) */ ++ + #if defined(HAVE_LIBUNWIND) + /* libunwind address space */ + unw_addr_space_t unwind_as; +Only in ltrace-0.7.91-pm/: proc.h.orig +Common subdirectories: ltrace-0.7.91/sysdeps and ltrace-0.7.91-pm/sysdeps +Common subdirectories: ltrace-0.7.91/testsuite and ltrace-0.7.91-pm/testsuite +diff -up ltrace-0.7.91/proc.c\~ ltrace-0.7.91/proc.c +--- ltrace-0.7.91/proc.c~ 2015-01-09 01:55:38.289864078 +0100 ++++ ltrace-0.7.91/proc.c 2015-01-09 01:56:29.818881935 +0100 +@@ -918,7 +918,8 @@ proc_add_library(struct process *proc, s + dwfl_report_begin_add(dwfl); + if (dwfl_report_elf(dwfl, lib->soname, + lib->pathname, -1, +- (GElf_Addr) lib->base, ++ /* XXX double cast */ ++ (GElf_Addr) (uintptr_t) lib->base, + false) == NULL) + fprintf(stderr, + "dwfl_report_elf %s@%p (%s) %d: %s\n", diff --git a/ltrace-0.7.91-x86-plt_map.patch b/ltrace-0.7.91-x86-plt_map.patch new file mode 100644 index 0000000..9983cc7 --- /dev/null +++ b/ltrace-0.7.91-x86-plt_map.patch @@ -0,0 +1,101 @@ +From fba95ad936f1d8c1052259bae811f1fc07f9a215 Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Thu, 30 Oct 2014 01:48:17 +0100 +Subject: [PATCH] Initialize the PLT slot map correctly on x86 and x86_64 + +The PLT slot map translates relocation numbers to PLT slot numbers, +but was actually initialized in the opposite direction. Fix the way +it's initialized. This bug can be seen on glibc in particular: + + $ ltrace -e free ls + libc.so.6->free(0x5) = + libc.so.6->free(0x78) = + libc.so.6->free(0xc) = + libc.so.6->free(0x308) = + +Note the nonsense values passed to free. The problem is that these +are not free calls at all, but malloc calls that are assigned to wrong +PLT slots due to above bug. +--- + sysdeps/linux-gnu/x86/plt.c | 38 +++++++++++++++++++++----------------- + 1 file changed, 21 insertions(+), 17 deletions(-) + +diff --git a/sysdeps/linux-gnu/x86/plt.c b/sysdeps/linux-gnu/x86/plt.c +index c860af6..97f6c3e 100644 +--- a/sysdeps/linux-gnu/x86/plt.c ++++ b/sysdeps/linux-gnu/x86/plt.c +@@ -77,6 +77,18 @@ arch_elf_init(struct ltelf *lte, struct library *lib) + { + VECT_INIT(<e->arch.plt_map, unsigned int); + ++ if (vect_reserve(<e->arch.plt_map, vect_size(<e->plt_relocs)) < 0) { ++ fail: ++ arch_elf_destroy(lte); ++ return -1; ++ } ++ ++ { ++ unsigned int i, sz = vect_size(<e->plt_relocs); ++ for (i = 0; i < sz; ++i) ++ vect_pushback (<e->arch.plt_map, &i); ++ } ++ + /* IRELATIVE slots may make the whole situation a fair deal + * more complex. On x86{,_64}, the PLT slots are not + * presented in the order of the corresponding relocations, +@@ -114,43 +126,35 @@ arch_elf_init(struct ltelf *lte, struct library *lib) + /* Here we scan the PLT table and initialize a map of + * relocation->slot number in lte->arch.plt_map. */ + +- size_t i; +- for (i = 0; i < vect_size(<e->plt_relocs); ++i) { ++ unsigned int i, sz = vect_size(<e->plt_relocs); ++ for (i = 0; i < sz; ++i) { + + GElf_Addr offset = x86_plt_offset(i); +- uint32_t reloc_arg = 0; + + uint8_t byte; + if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 + || byte != 0xff + || elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 + || (byte != 0xa3 && byte != 0x25)) +- goto next; ++ continue; + + /* Skip immediate argument in the instruction. */ + offset += 4; + ++ uint32_t reloc_arg; + if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 + || byte != 0x68 + || elf_read_next_u32(lte->plt_data, +- &offset, &reloc_arg) < 0) { +- reloc_arg = 0; +- goto next; +- } ++ &offset, &reloc_arg) < 0) ++ continue; + + if (lte->ehdr.e_machine == EM_386) { +- if (reloc_arg % 8 != 0) { +- reloc_arg = 0; +- goto next; +- } ++ if (reloc_arg % 8 != 0) ++ continue; + reloc_arg /= 8; + } + +- next: +- if (VECT_PUSHBACK(<e->arch.plt_map, &reloc_arg) < 0) { +- arch_elf_destroy(lte); +- return -1; +- } ++ *VECT_ELEMENT(<e->arch.plt_map, unsigned int, reloc_arg) = i; + } + + return 0; +-- +2.1.0 + diff --git a/ltrace-0.7.91-x86-unused_label.patch b/ltrace-0.7.91-x86-unused_label.patch new file mode 100644 index 0000000..e56bda2 --- /dev/null +++ b/ltrace-0.7.91-x86-unused_label.patch @@ -0,0 +1,32 @@ +From e16a28f1b6e5a15368f8ed98dc29a6da714dc5fa Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Tue, 9 Dec 2014 17:44:30 +0100 +Subject: [PATCH] Drop unused label in x86 backend + +--- + sysdeps/linux-gnu/x86/plt.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/sysdeps/linux-gnu/x86/plt.c b/sysdeps/linux-gnu/x86/plt.c +index 97f6c3e..44ea260 100644 +--- a/sysdeps/linux-gnu/x86/plt.c ++++ b/sysdeps/linux-gnu/x86/plt.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 2004,2008,2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -78,7 +78,6 @@ arch_elf_init(struct ltelf *lte, struct library *lib) + VECT_INIT(<e->arch.plt_map, unsigned int); + + if (vect_reserve(<e->arch.plt_map, vect_size(<e->plt_relocs)) < 0) { +- fail: + arch_elf_destroy(lte); + return -1; + } +-- +2.1.0 + diff --git a/ltrace-0.7.91-x86_64-irelative.patch b/ltrace-0.7.91-x86_64-irelative.patch new file mode 100644 index 0000000..949b5fb --- /dev/null +++ b/ltrace-0.7.91-x86_64-irelative.patch @@ -0,0 +1,156 @@ +@@ -, +, @@ + relocation +- In general they are. But IRELATIVE relocations are sorted to come + last, and PLT entries are not sorted accordingly. +--- + sysdeps/linux-gnu/x86/arch.h | 11 +++++ + sysdeps/linux-gnu/x86/plt.c | 101 +++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 111 insertions(+), 1 deletions(-) +--- a/sysdeps/linux-gnu/x86/arch.h ++++ a/sysdeps/linux-gnu/x86/arch.h +@@ -19,6 +19,10 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ ++#ifndef LTRACE_X86_ARCH_H ++#define LTRACE_X86_ARCH_H ++ ++#include "vect.h" + + #define BREAKPOINT_VALUE {0xcc} + #define BREAKPOINT_LENGTH 1 +@@ -30,9 +34,16 @@ + + #define ARCH_HAVE_ADD_PLT_ENTRY + ++#define ARCH_HAVE_LTELF_DATA ++struct arch_ltelf_data { ++ struct vect plt_map; ++}; ++ + #ifdef __x86_64__ + #define LT_ELFCLASS ELFCLASS64 + #define LT_ELF_MACHINE EM_X86_64 + #endif + #define LT_ELFCLASS2 ELFCLASS32 + #define LT_ELF_MACHINE2 EM_386 ++ ++#endif /* LTRACE_X86_ARCH_H */ +--- a/sysdeps/linux-gnu/x86/plt.c ++++ a/sysdeps/linux-gnu/x86/plt.c +@@ -27,10 +27,19 @@ + #include "library.h" + #include "trace.h" + ++static GElf_Addr ++x86_plt_offset(uint32_t i) ++{ ++ /* Skip the first PLT entry, which contains a stub to call the ++ * resolver. */ ++ return (i + 1) * 16; ++} ++ + GElf_Addr + arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) + { +- return lte->plt_addr + (ndx + 1) * 16; ++ uint32_t i = *VECT_ELEMENT(<e->arch.plt_map, uint32_t, ndx); ++ return x86_plt_offset(i) + lte->plt_addr; + } + + void * +@@ -62,3 +71,93 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + + return PLT_DEFAULT; + } ++ ++int ++arch_elf_init(struct ltelf *lte, struct library *lib) ++{ ++ VECT_INIT(<e->arch.plt_map, unsigned int); ++ ++ /* IRELATIVE slots may make the whole situation a fair deal ++ * more complex. On x86{,_64}, the PLT slots are not ++ * presented in the order of the corresponding relocations, ++ * but in the order it which these symbols are in the symbol ++ * table. That's static symbol table, which may be stripped ++ * off, not dynsym--that doesn't contain IFUNC symbols at all. ++ * So we have to decode each PLT entry to figure out what ++ * entry it corresponds to. We need to interpret the PLT ++ * table to figure this out. ++ * ++ * On i386, the PLT entry format is as follows: ++ * ++ * 8048300: ff 25 0c a0 04 08 jmp *0x804a00c ++ * 8048306: 68 20 00 00 00 push $0x20 ++ * 804830b: e9 e0 ff ff ff jmp 80482f0 <_init+0x30> ++ * ++ * For PIE binaries it is the following: ++ * ++ * 410: ff a3 10 00 00 00 jmp *0x10(%ebx) ++ * 416: 68 00 00 00 00 push $0x0 ++ * 41b: e9 d0 ff ff ff jmp 3f0 <_init+0x30> ++ * ++ * On x86_64, it is: ++ * ++ * 400420: ff 25 f2 0b 20 00 jmpq *0x200bf2(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> ++ * 400426: 68 00 00 00 00 pushq $0x0 ++ * 40042b: e9 e0 ff ff ff jmpq 400410 <_init+0x18> ++ * ++ * On i386, the argument to push is an offset of relocation to ++ * use. The first PLT slot has an offset of 0x0, the second ++ * 0x8, etc. On x86_64, it's directly the index that we are ++ * looking for. ++ */ ++ ++ /* Here we scan the PLT table and initialize a map of ++ * relocation->slot number in lte->arch.plt_map. */ ++ ++ size_t i; ++ for (i = 0; i < vect_size(<e->plt_relocs); ++i) { ++ ++ GElf_Addr offset = x86_plt_offset(i); ++ uint32_t reloc_arg = 0; ++ ++ uint8_t byte; ++ if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 ++ || byte != 0xff ++ || elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 ++ || (byte != 0xa3 && byte != 0x25)) ++ goto next; ++ ++ /* Skip immediate argument in the instruction. */ ++ offset += 4; ++ ++ if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 ++ || byte != 0x68 ++ || elf_read_next_u32(lte->plt_data, ++ &offset, &reloc_arg) < 0) { ++ reloc_arg = 0; ++ goto next; ++ } ++ ++ if (lte->ehdr.e_machine == EM_386) { ++ if (reloc_arg % 8 != 0) { ++ reloc_arg = 0; ++ goto next; ++ } ++ reloc_arg /= 8; ++ } ++ ++ next: ++ if (VECT_PUSHBACK(<e->arch.plt_map, &reloc_arg) < 0) { ++ arch_elf_destroy(lte); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++void ++arch_elf_destroy(struct ltelf *lte) ++{ ++ VECT_DESTROY(<e->arch.plt_map, uint32_t, NULL, NULL); ++} +-- diff --git a/ltrace-rh1225568.patch b/ltrace-rh1225568.patch new file mode 100644 index 0000000..612abc1 --- /dev/null +++ b/ltrace-rh1225568.patch @@ -0,0 +1,12 @@ +diff -Nrup a/testsuite/ltrace.minor/trace-irelative.exp b/testsuite/ltrace.minor/trace-irelative.exp +--- a/testsuite/ltrace.minor/trace-irelative.exp 2013-11-04 18:08:03.000000000 -0700 ++++ b/testsuite/ltrace.minor/trace-irelative.exp 2015-06-01 12:30:59.737371166 -0600 +@@ -54,6 +54,8 @@ set src [ltraceSource c { + }] + + foreach ext {{} .pie} { ++ # ltrace does not yet support AARCH64's ifuncs ++ setup_xfail aarch64*-*-* + set bin1 [ltraceCompile $ext $src] + do_tests $bin1 "" + } diff --git a/ltrace-rh1307754.patch b/ltrace-rh1307754.patch new file mode 100644 index 0000000..ac3560a --- /dev/null +++ b/ltrace-rh1307754.patch @@ -0,0 +1,12 @@ +diff -Nrup a/testsuite/ltrace.main/system_calls.exp b/testsuite/ltrace.main/system_calls.exp +--- a/testsuite/ltrace.main/system_calls.exp 2016-02-17 19:39:51.433134376 -0700 ++++ b/testsuite/ltrace.main/system_calls.exp 2016-02-17 19:40:26.220402747 -0700 +@@ -133,7 +133,7 @@ Match [Diff [Calls [ltraceRun -L -S -- $ + { {^write$} == 1 } + { {^unlink(at)?$} >= 2 } + { {^open(at)?$} == 1 } +- { {^(new|f)?stat(64)?$} == 1 } ++ { {^(new|f)?stat(64)?$} >= 1 } + { {^close$} == 1 } + { {^getcwd$} == 1 } + { {^chdir$} == 1 } diff --git a/ltrace-rh1423913.patch b/ltrace-rh1423913.patch new file mode 100644 index 0000000..f328dd9 --- /dev/null +++ b/ltrace-rh1423913.patch @@ -0,0 +1,20 @@ +diff -Nrup a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c +--- a/sysdeps/linux-gnu/arm/trace.c 2013-03-11 17:23:39.000000000 -0600 ++++ b/sysdeps/linux-gnu/arm/trace.c 2017-02-17 09:39:42.233547101 -0700 +@@ -155,6 +155,8 @@ arm_get_next_pcs(struct process *proc, + const unsigned cond = BITS(this_instr, 28, 31); + const unsigned opcode = BITS(this_instr, 24, 27); + ++ uint32_t operand1, operand2, result = 0; ++ + if (cond == COND_NV) + switch (opcode) { + arch_addr_t addr; +@@ -170,7 +172,6 @@ arm_get_next_pcs(struct process *proc, + } + else + switch (opcode) { +- uint32_t operand1, operand2, result = 0; + case 0x0: + case 0x1: /* data processing */ + case 0x2: diff --git a/ltrace.spec b/ltrace.spec new file mode 100644 index 0000000..7766f83 --- /dev/null +++ b/ltrace.spec @@ -0,0 +1,709 @@ +Summary: Tracks runtime library calls from dynamically linked executables +Name: ltrace +Version: 0.7.91 +Release: 39%{?dist} +URL: http://ltrace.alioth.debian.org/ +License: GPLv2+ + +BuildRequires: elfutils-devel dejagnu +BuildRequires: libselinux-devel +BuildRequires: autoconf automake libtool +BuildRequires: gcc-c++ + +# Note: this URL needs to be updated for each release, as the file +# number changes for each file. Full list of released files is at: +# https://alioth.debian.org/frs/?group_id=30892 +Source: ltrace-%{version}.tar.bz2 + +# Merge of several upstream commits that fixes compilation on ARM. +Patch0: ltrace-0.7.91-arm.patch + +# Upstream patch that fixes accounting of exec, __libc_start_main and +# others in -c output. +Patch1: ltrace-0.7.91-account_execl.patch + +# Upstream patch that fixes interpretation of PLT on x86_64 when +# IRELATIVE slots are present. +Patch2: ltrace-0.7.91-x86_64-irelative.patch + +# Upstream patch that fixes fetching of system call arguments on s390. +Patch3: ltrace-0.7.91-s390-fetch-syscall.patch + +# Upstream patch that enables tracing of IRELATIVE PLT slots on s390. +Patch4: ltrace-0.7.91-s390-irelative.patch + +# Fix for a regression in tracing across fork. Upstream patch. +Patch5: ltrace-0.7.91-ppc64-fork.patch + +# Fix crashing a prelinked PPC64 binary which makes PLT calls through +# slots that ltrace doesn't trace. +# https://bugzilla.redhat.com/show_bug.cgi?id=1051221 +Patch6: ltrace-0.7.91-breakpoint-on_install.patch +Patch7: ltrace-0.7.91-ppc64-unprelink.patch + +# Man page nits. Backport of an upstream patch. +Patch8: ltrace-0.7.91-man.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1044766 +Patch9: ltrace-0.7.91-cant_open.patch + +# Support Aarch64 architecture. +Patch10: ltrace-0.7.91-aarch64.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1064406 +Patch11: ltrace-0.7.2-e_machine.patch + +# Support for ppc64le, backported from upstream. +# http://anonscm.debian.org/gitweb/?p=collab-maint/ltrace.git;a=commit;h=eea4ad2cce289753aaa35b4e0258a76d8f8f367c +# https://bugzilla.redhat.com/show_bug.cgi?id=1131956 +Patch13: ltrace-0.7.91-ppc64le-support.patch +# 35a9677dc9dcb7909ebd28f30200474d7e8b660f, +# 437d2377119036346f4dbd93039c847b4cc9d0be, +# eb3993420734f091cde9a6053ca6b4edcf9ae334 +Patch14: ltrace-0.7.91-ppc64le-fixes.patch + +# http://anonscm.debian.org/gitweb/?p=collab-maint/ltrace.git;a=commit;h=2e9f9f1f5d0fb223b109429b9c904504b7f638e2 +# http://anonscm.debian.org/gitweb/?p=collab-maint/ltrace.git;a=commit;h=f96635a03b3868057db5c2d7972d5533e2068345 +Patch15: ltrace-0.7.91-parser-ws_after_id.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1171165 +# http://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=d8f1287b85e2c2b2ae0235809e956f4365e53c45 +# http://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=d80c5371454383e3f9978622e5578cf02af8c44c +# http://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=bf82100966deda9c7d26ad085d97c08126a8ae88 +Patch16: ltrace-0.7.91-ppc-bias.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1158714 +Patch17: ltrace-0.7.91-x86-plt_map.patch +Patch18: ltrace-0.7.91-x86-unused_label.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1170315 +Patch19: ltrace-0.7.91-unwind-elfutils.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1208351 +# http://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=4724bd5a4a19db117a1d280b9d1a3508fd4e03fa +# http://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=72ee29639c55b5942bc07c8ed0013005f8fc5a97 +Patch20: ltrace-0.7.91-multithread-no-f-1.patch +Patch21: ltrace-0.7.91-multithread-no-f-2.patch + +# Fix problems with building a number of test cases. +# http://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=694d19ff14017926454771cbb63a22355b72f1bf +# http://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=a3a03622fb4ca9772dca13eae724a94ba1e728f4 +Patch22: ltrace-0.7.91-testsuite-includes.patch +Patch23: ltrace-0.7.91-testsuite-includes-2.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1210653 +# http://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=eea6091f8672b01f7f022b0fc367e0f568225ffc +Patch24: ltrace-0.7.91-ppc64le-configure.patch + +Patch25: ltrace-rh1307754.patch + +# GCC now warns (errors) on "tautological compares", and readdir_r is deprecated. +Patch26: ltrace-0.7.91-tautology.patch + +# ARM code has unreachable code after switch statement, move initialization +Patch27: ltrace-rh1423913.patch + +# AARCH64 large parameters and syscall testsuite fixes. +Patch28: ltrace-0.7.91-aarch64-params.patch + +# gcc-9 fix. Avoid passing NULL as argument to %s +Patch29: ltrace-0.7.91-null.patch + +# Adds support for CET PLTs via second-plt lookups. +Patch30: ltrace-0.7.91-cet.patch + +# Extra #includes for gcc 9 +Patch31: ltrace-0.7.91-aarch64-headers.patch +# Testsuite: AARCH64 ifuncs not supported yet yet. +Patch32: ltrace-rh1225568.patch + +# testsuite fixes for pre-installed config files +Patch33: ltrace-0.7.91-testsuite-system_call_params.patch + +# Ignore bogus files from the environment +Patch34: ltrace-0.7.91-XDG_CONFIG_DIRS.patch + +# GCC erroneously warns about uninitialized values +Patch35: ltrace-0.7.91-rh1799619.patch + +%description +Ltrace is a debugging program which runs a specified command until the +command exits. While the command is executing, ltrace intercepts and +records both the dynamic library calls called by the executed process +and the signals received by the executed process. Ltrace can also +intercept and print system calls executed by the process. + +You should install ltrace if you need a sysadmin tool for tracking the +execution of processes. + +%prep +%setup -q -n %{name}-%{version} +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 + +%build +autoreconf -i +%configure --docdir=%{?_pkgdocdir}%{!?_pkgdocdir:%{_docdir}/%{name}-%{version}} +%make_build + +%install +%make_install bindir=%{_bindir} + +# The testsuite is useful for development in real world, but fails in +# koji for some reason. Disable it, but have it handy. +%check +echo ====================TESTING========================= +# The ppc64 testsuite hangs rpmbuild hard in koji, disable until fixed. +%ifnarch ppc64le +timeout 180 make check ||: +%endif +echo ====================TESTING END===================== + +%files +%doc NEWS COPYING CREDITS INSTALL README TODO +%{_bindir}/ltrace +%{_mandir}/man1/ltrace.1* +%{_mandir}/man5/ltrace.conf.5* +%{_datadir}/ltrace + +%changelog +* Tue Jul 28 2020 Fedora Release Engineering - 0.7.91-39 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue Jul 14 2020 Tom Stellard - 0.7.91-38 +- Use make macros +- https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro + +* Thu Feb 6 2020 DJ Delorie - 0.7.91-37 +- Initialize some variables to avoid gcc warning (#1799619) + +* Wed Jan 29 2020 Fedora Release Engineering - 0.7.91-36 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Tue Jul 23 2019 DJ Delorie - 0.7.91-35 +- Skip ppc64 testsuite until the hangs in koji can be fixed. + +* Wed Jul 17 2019 DJ Delorie - 0.7.91-34 +- Add fixes in handling of bogus paths that come from XDG_CONFIG_DIRS. +- Testsuite fixes for pre-installed config files. +- Extra AARCH64 includes for gcc 9. +- Testsuite: AARCH64 ifuncs not supported yet yet. + +* Thu Apr 4 2019 DJ Delorie - 0.7.91-33 +- Add Intel CET support. + +* Tue Mar 12 2019 DJ Delorie - 0.7.91-32 +- Revert previous patch, redundant + +* Tue Mar 5 2019 Eugene Syromiatnikov - 0.7.91-31 +- Fix "Too many return value classes" assert + +* Fri Feb 01 2019 Fedora Release Engineering - 0.7.91-30 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Wed Jan 16 2019 Jeff Law - 0.7.91-29 +- Avoid passing NULL as argument to %s in printf call + +* Fri Jul 13 2018 Fedora Release Engineering - 0.7.91-28 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Thu Jul 05 2018 DJ Delorie - 0.7.91-27 +- Fix aarch64 long parameters (via $r8) support. +- Make system_call_params test compare more exactly. + +* Fri Feb 09 2018 Igor Gnatenko - 0.7.91-26 +- Escape macros in %%changelog + +* Thu Feb 08 2018 Fedora Release Engineering - 0.7.91-25 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Thu Aug 03 2017 Fedora Release Engineering - 0.7.91-24 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 0.7.91-23 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Feb 17 2017 Jeff Law - 0.7.91-22 +- Fix FTBFS due to invalid code in ARM support (#1423913). + +* Fri Feb 10 2017 Fedora Release Engineering - 0.7.91-21 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Jul 20 2016 DJ Delorie - 0.7.91-20 +- Fix FTBFS due to new gcc 6 warnings, deprecated readdir_r, and bogus chunk in unwind-elf patch. + +* Fri Feb 19 2016 Jeff Law - 0.7.91-19 +- Fix FTBFS due to testsuite failure (#1307754) . Add missing files to %%doc + +* Thu Feb 04 2016 Fedora Release Engineering - 0.7.91-18 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Wed Jun 17 2015 Fedora Release Engineering - 0.7.91-17 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Mon Apr 13 2015 Petr Machata - 0.7.91-16 +- Add upstream fix to map of powerpc64le architecture to ppc backend. + (ltrace-0.7.91-ppc64le-configure.patch) + +* Wed Apr 8 2015 Petr Machata - 0.7.91-15 +- Add upstream fixes for compilation of test cases + (ltrace-0.7.91-testsuite-includes.patch, + ltrace-0.7.91-testsuite-includes-2.patch) + +* Wed Apr 8 2015 Petr Machata - 0.7.91-14 +- Add upstream fixes for tracing multi-threaded processes without -f + (ltrace-0.7.91-multithread-no-f-1.patch, + ltrace-0.7.91-multithread-no-f-2.patch) + +* Fri Jan 9 2015 Petr Machata - 0.7.91-13 +- Add upstream fix for support of elfutils unwinder + (ltrace-0.7.91-unwind-elfutils.patch) + +* Wed Jan 7 2015 Petr Machata - 0.7.91-12 +- Add upstream fix for a bug in labeling PLT slots + (ltrace-0.7.91-x86-plt_map.patch) + +* Tue Dec 9 2014 Petr Machata - 0.7.91-11 +- Fix bias handling in PPC backend +- Fix cloning of unresolved breakpoints in PPC backend + (ltrace-0.7.91-ppc-bias.patch) + +* Wed Aug 20 2014 Petr Machata - 0.7.91-10 +- Backported PowerPC64 ELFv2 support. + (ltrace-0.7.91-ppc64le-support.patch, + ltrace-0.7.91-ppc64le-fixes.patch) + +* Sun Aug 17 2014 Fedora Release Engineering - 0.7.91-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sat Jun 07 2014 Fedora Release Engineering - 0.7.91-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Thu Feb 13 2014 Petr Machata - 0.7.91-7 +- Add an upstream patch that fixes missed initialization of some + fields in struct process after atteching to a multi-threaded + process. (ltrace-0.7.2-e_machine.patch) +- Add upstream patch-set that implements support for the new aarch64 + architecture. (ltrace-0.7.91-aarch64.patch) + +* Tue Jan 14 2014 Petr Machata - 0.7.91-6 +- Fix a problem when an invalid command has been found + (ltrace-0.7.91-cant_open.patch) + +* Tue Jan 14 2014 Petr Machata - 0.7.91-5 +- Fix interpretation of x86_64 PLT with IRELATIVE slots. + (ltrace-0.7.91-x86_64-irelative.patch) +- Fix fetching of system call arguments on s390. + (ltrace-0.7.91-s390-fetch-syscall.patch) +- Enable tracing of IRELATIVE PLT slots on s390. + (ltrace-0.7.91-s390-irelative.patch) +- Fix a couple nits in ltrace.1 (ltrace-0.7.91-man.patch) + +* Fri Jan 10 2014 Petr Machata - 0.7.91-4 +- Fix crashing a prelinked PPC64 binary which makes PLT calls through + slots that ltrace doesn't trace. + (ltrace-0.7.91-breakpoint-on_install.patch, + ltrace-0.7.91-ppc64-unprelink.patch) + +* Thu Jan 9 2014 Petr Machata - 0.7.91-3 +- Fix a problem in tracing across fork on PPC64 + (ltrace-0.7.91-ppc64-fork.patch) + +* Thu Nov 21 2013 Petr Machata - 0.7.91-2 +- Fix a problem in including in summary (-c) function calls that don't + finish before exec or exit (ltrace-0.7.91-account_execl.patch) + +* Tue Nov 5 2013 Petr Machata - 0.7.91-1 +- Rebase to a pre-release 0.8 +- Drop BR on autoconf and friends + +* Wed Aug 7 2013 Ville Skyttä - 0.7.2-7 +- Install docs to %%{_pkgdocdir} where available (#992149). + +* Sat Aug 03 2013 Fedora Release Engineering - 0.7.2-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Mar 26 2013 Petr Machata - 0.7.2-5 +- On s390, the highest bit in PC address is used to distinguish + between 24-bit and 31-bit addressing modes. Linux used to do this + for us, but not anymore. + (ltrace-0.7.2-s390-set_instruction_pointer.patch) + +* Wed Feb 6 2013 Petr Machata - 0.7.2-4 +- Update the ARM patch (ltrace-0.7.2-arm.patch) with support for + parameter passing conventions. + +* Thu Jan 31 2013 Petr Machata - 0.7.2-3 +- Bring small fixes from master branch + (ltrace-0.7.2-bits.patch; drop ltrace-0.7.2-man.patch) +- Add a patch that implements ARM sofware singlestepping. This mostly + fixes test suite on ARM, though parameter passing conventions are + still not implemented. (ltrace-0.7.2-arm.patch) +- Work around a new GCC warning (ltrace-0.7.2-unused-typedef.patch) + +* Fri Jan 11 2013 Petr Machata - 0.7.2-2 +- Improve documentation: better correlation between ltrace(1) and + --help, other minor improvements in ltrace(1). + (ltrace-0.7.2-man.patch) + +* Mon Dec 10 2012 Petr Machata - 0.7.2-1 +- Upstream 0.7.2 + - Drop all the patches + +* Sat Nov 10 2012 Petr Machata - 0.7.0-1 +- Upstream 0.7.0 + - Drop all the patches + - Upstream patch for missing sysdeps/linux-gnu/ppc/insn.h + (ltrace-0.7.0-ppc-insn.h.patch) + - Upstream patch for installing ltrace.conf.5 to man5 + (ltrace-0.7.0-man5.patch) + +* Mon Oct 1 2012 Petr Machata - 0.6.0-19 +- Upstream patch for ia64 parameter passing + (ltrace-0.6.0-abi-ia64.patch) +- Upstream fix for a bug in computation of time spent in a syscall + (ltrace-0.6.0-syscall-time.patch) +- Upstream fix for a bug in passing struct(float,struct(float,float)) + on x86_64 (ltrace-0.6.0-x86_64-flatten.patch) +- Upstream patch for support of -l option (ltrace-0.6.0-dash-l.patch) +- Several more upstream patches with random cleanups. Those were + brought to Fedora to make porting of other patches easier. + (ltrace-0.6.0-cleanups.patch) + +* Thu Aug 30 2012 Petr Machata - 0.6.0-18 +- PPC64 passes floating point equivalent structures in registers + +* Thu Jul 19 2012 Fedora Release Engineering - 0.6.0-17 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Fri Jun 1 2012 Petr Machata - 0.6.0-16 +- Look for __cxa_demangle in libstdc++ as well +- Demangle test case should report it's unsupported if demangling + support isn't compiled in (ltrace-0.6.0-demangle.patch) +- Resolves: #827422 + +* Thu May 31 2012 Petr Machata - 0.6.0-15 +- Add upstream patches for parameter passing. Apart from a couple of + fixes, this brings in s390 support (ltrace-0.6.0-abi-s390.patch) + +* Fri May 18 2012 Petr Machata - 0.6.0-14 +- Add upstream patch that improves parameter passing support (the + upstream "revamp" branch) (ltrace-0.6.0-abi.patch) + +* Thu May 3 2012 Petr Machata - 0.6.0-13 +- Check -n argument for validity (ltrace-0.6.0-dash-n.patch) +- Resolves: #818529 +- ltrace-0.6.0-libs-fixes-1.patch + - Fix double free when process initialization fails for some reason + - Don't indent first level of calls + +* Mon Apr 30 2012 Petr Machata - 0.6.0-12 +- Fix 32-bit builds + +* Mon Apr 30 2012 Petr Machata - 0.6.0-11 +- Fix detach from sleeping process +- Add limited support for return from tail call +- Fix singlestep over atomic instruction sequence on PPC +- Add extensive upstream patch that implements + - tracing calls done from DSOs + - better tools for filtering symbol tables + - support for tracing PLT calls on PPC64 (not entry points read from .plt) + - support for PPC32 old-style (BSS) PLT table +- Drop ppc-shift patch that was superseded by the above +- Drop demangle patch that hasn't been applied for some time now + +* Wed Apr 11 2012 Peter Robinson - 0.6.0-10 +- Drop ExclusiveArch as all current Primary/Secondary Arches are supported + +* Wed Apr 11 2012 Petr Machata - 0.6.0-9 +- And patch configure and config.h, not just configure.ac +- Resolves: #810973 + +* Wed Apr 11 2012 Petr Machata - 0.6.0-7 +- Add libselinux-devel BR +- Resolves: #810973 + +* Tue Apr 10 2012 Petr Machata - 0.6.0-6 +- If we fail to attach to traced process, check whether deny_ptrace + isn't enabled. If yes, warn about it. +- Resolves: #810973 + +* Tue Feb 7 2012 Petr Machata - 0.6.0-5 +- Add upstream patches for initial breakpoint insertion. This mostly + fixes tracing on PPC. +- Resolves: #773050 + +* Fri Jan 13 2012 Fedora Release Engineering - 0.6.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Fri Nov 25 2011 Petr Machata - 0.6.0-3 +- Add several upstream patches that fix various races in tracing + multi-threaded processes +- Add upstream patches for support of tracing across vfork +- Add upstream patches for ppc: excessive shift, and fetching + function arguments + +* Fri Sep 2 2011 Petr Machata - 0.6.0-2 +- Add upstream patches for tracing multi-threaded processes, endian + fixes, and a test suite fixlet + +* Tue Feb 15 2011 Petr Machata - 0.6.0-1 +- Update to 0.6.0 + - Drop most patches + - Port exec-stripped patch + - Add return-string-n patch + - Leave just the testsuite part in ia64-sigill patch + +* Tue Feb 08 2011 Fedora Release Engineering - 0.5-19.45svn +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Thu Dec 9 2010 Petr Machata - 0.5-18.45svn +- Add memmove to /etc/ltrace.conf +- Resolves: #658311 + +* Wed Sep 8 2010 Petr Machata - 0.5-17.45svn +- Fix demangler resolution. Libiberty is not in the default install + anymore, and the fallback configure check for __cxa_demangle doesn't + take into account the possibility that the symbol might be in + libstdc++ instead. +- Resolves: #631069 FTBFS + +* Wed May 19 2010 Petr Machata - 0.5-16.45svn.1 +- When the value of undefined symbol in PPC 32-bit binary is 0, use + PPC-specific magic to compute the PLT slots. +- Fix a problem with tracing stripped binary after execl on + architectures that need PLT reinitalisation breakpoint. +- Support tracing of 31-bit binaries with 64-bit ltrace +- Fix handling of the case where forked child is reported before + parent's fork event +- Patch from Supriya Kannery implements fetching 5th and further + function arguments on s390 + +* Sat Jul 25 2009 Fedora Release Engineering - 0.5-14.45svn +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Wed Feb 25 2009 Fedora Release Engineering - 0.5-13.45svn +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Tue Oct 7 2008 Petr Machata - 0.5-12.45svn +- Fix fork & exec patches to apply cleanly under --fuzz=0 +- Resolves: #465036 + +* Fri May 23 2008 Petr Machata - 0.5-11.45svn +- Patch from James M. Leddy, fixes interaction of -c and -o +- Fix compilation by using -D_LARGEFILE64_SOURCE +- related: #447404 + +* Tue Feb 19 2008 Fedora Release Engineering - 0.5-10.45svn +- Autorebuild for GCC 4.3 + +* Wed Sep 12 2007 Petr Machata - 0.5-9.45svn +- Cleanup spec. +- Fix parallel make bug in Makefile. +- resolves: #226109 + +* Thu Aug 16 2007 Petr Machata - 0.5-8.45svn +- Fix licensing tag. + +* Fri May 4 2007 Petr Machata - 0.5-7.45svn +- added fork/exec patches, mostly IBM's work +- added trace-exec tests into suite +- added ia64 sigill patch + +* Thu Jan 25 2007 Petr Machata - 0.5-6.45svn +- tidy up the specfile per rpmlint comments +- fix man page + +* Mon Sep 4 2006 Petr Machata - 0.5-5.45svn +- fix plt handling on ppc32 (symval patch) +- fix attaching to process (attach patch) +- add fork & exec patches from IBM +- adjust weak symbol handling (ppc32fc5 patch) + +* Wed Aug 23 2006 Petr Machata - 0.5-3.45svn +- use "{X}.{release}svn" release string per naming guidelines + +* Tue Aug 22 2006 Petr Machata - 0.5-1.1.45svn +- using dist tag + +* Fri Aug 18 2006 Jesse Keating - 0.5-1.0.45svn.6 +- rebuilt with latest binutils to pick up 64K -z commonpagesize on ppc* + (#203001) + +* Fri Jul 14 2006 Petr Machata - 0.5-1.0.45svn.5 +- adding .gnu.hash patch to support new ELF hash table section +- adding testsuite patch to silent some bogus failures + +* Fri Jul 14 2006 Petr Machata - 0.5-1.0.45svn +- adding upstream (svn) version. It contains most of patches that we + already use, and has support for secure PLTs. + +* Wed Jul 12 2006 Jesse Keating - 0.4-1.7.1 +- rebuild + +* Wed Jun 14 2006 Petr Machata - 0.4-1.7 +- drop broken ppc support + +* Thu Jun 1 2006 Petr Machata - 0.4-1.6 +- e_entry patch: use elf's e_entry field instead of looking up _start + symbol, which failed on stripped binaries. + +* Wed May 3 2006 Petr Machata - 0.4-1.5 +- Correct a typo that prevented the inclusion of "demangle.h" +- Adding -Wl,-z,relro + +* Mon Apr 24 2006 Petr Machata - 0.4-1.4 +- turn off opd translation on ia64, GElf already gives us function + address. +- turn on main-internal test, it should pass now. + +* Wed Apr 12 2006 Petr Machata - 0.4-1.2 +- svn fix for opt_x patch +- patches for testsuite for s390{,x} +- turning off main-internal test. Fails on ia64, needs investigation. + +* Fri Apr 7 2006 Petr Machata - 0.4-1 +- Upstream 0.4 +- opt_x patch: New structure for opt_x list elements, now with + 'found'. Using it in options.c, elf.c. +- testsuite patch: Automated testsuite for ltrace. + +* Wed Mar 1 2006 Petr Machata - 0.3.36-4.3 +- include %%{ix86} to ExclusiveArch, instead of mere i386 + +* Fri Feb 10 2006 Jesse Keating - 0.3.36-4.2 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Jesse Keating - 0.3.36-4.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Mon Jan 9 2006 Jakub Jelinek 0.3.36-4 +- added ppc64 and s390x support (IBM) +- added ia64 support (Ian Wienand) + +* Sat Mar 5 2005 Jakub Jelinek 0.3.36-3 +- rebuilt with GCC 4 + +* Tue Dec 14 2004 Jakub Jelinek 0.3.36-2 +- make x86_64 ltrace trace both 32-bit and 64-bit binaries (#141955, + IT#55600) +- fix tracing across execve +- fix printf-style format handling on 64-bit arches + +* Thu Nov 18 2004 Jakub Jelinek 0.3.36-1 +- update to 0.3.36 + +* Mon Oct 11 2004 Jakub Jelinek 0.3.35-1 +- update to 0.3.35 +- update syscall tables from latest kernel source + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Tue Jun 8 2004 Jakub Jelinek 0.3.32-3 +- buildreq elfutils-libelf-devel (#124921) + +* Thu Apr 22 2004 Jakub Jelinek 0.3.32-2 +- fix demangling + +* Thu Apr 22 2004 Jakub Jelinek 0.3.32-1 +- update to 0.3.32 + - fix dict.c assertion (#114359) + - x86_64 support +- rewrite elf.[ch] using libelf +- don't rely on st_value of SHN_UNDEF symbols in binaries, + instead walk .rel{,a}.plt and compute the addresses (#115299) +- fix x86-64 support +- some ltrace.conf additions +- some format string printing fixes + +* Fri Feb 13 2004 Elliot Lee +- rebuilt + +* Mon Feb 3 2003 Jakub Jelinek 0.3.29-1 +- update to 0.3.29 + +* Wed Jan 22 2003 Tim Powers +- rebuilt + +* Sun Sep 1 2002 Jakub Jelinek 0.3.10-12 +- add a bunch of missing functions to ltrace.conf + (like strlen, ugh) + +* Fri Jun 21 2002 Tim Powers +- automated rebuild + +* Tue May 28 2002 Phil Knirsch +- Added the 'official' s390 patch. + +* Thu May 23 2002 Tim Powers +- automated rebuild + +* Wed Jan 09 2002 Tim Powers +- automated rebuild + +* Fri Jul 20 2001 Jakub Jelinek +- fix stale symlink in documentation directory (#47749) + +* Sun Jun 24 2001 Elliot Lee +- Bump release + rebuild. + +* Wed Aug 2 2000 Tim Waugh +- fix off-by-one problem in checking syscall number + +* Wed Jul 12 2000 Prospector +- automatic rebuild + +* Mon Jun 19 2000 Matt Wilson +- rebuilt for next release +- patched Makefile.in to take a hint on mandir (patch2) +- use %%{_mandir} and %%makeinstall + +* Wed Feb 02 2000 Cristian Gafton +- fix description + +* Fri Jan 7 2000 Jeff Johnson +- update to 0.3.10. +- include (but don't apply) sparc patch from Jakub Jellinek. + +* Sun Mar 21 1999 Cristian Gafton +- auto rebuild in the new build environment (release 2) + +* Fri Mar 12 1999 Jeff Johnson +- update to 0.3.6. + +* Mon Sep 21 1998 Preston Brown +- upgraded to 0.3.4 diff --git a/sources b/sources new file mode 100644 index 0000000..a8420d6 --- /dev/null +++ b/sources @@ -0,0 +1 @@ +9db3bdee7cf3e11c87d8cc7673d4d25b ltrace-0.7.91.tar.bz2