diff --git a/.devtoolset-10-ltrace.metadata b/.devtoolset-10-ltrace.metadata new file mode 100644 index 0000000..5e7fbca --- /dev/null +++ b/.devtoolset-10-ltrace.metadata @@ -0,0 +1 @@ +74029042af10b0e9fca6acccb016ce096460a176 SOURCES/ltrace-0.7.91.tar.bz2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5592bd6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/ltrace-0.7.91.tar.bz2 diff --git a/SOURCES/ltrace-0.7.2-e_machine.patch b/SOURCES/ltrace-0.7.2-e_machine.patch new file mode 100644 index 0000000..2cfc15a --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-aarch64-params.patch b/SOURCES/ltrace-0.7.91-aarch64-params.patch new file mode 100644 index 0000000..29767c5 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-aarch64.patch b/SOURCES/ltrace-0.7.91-aarch64.patch new file mode 100644 index 0000000..123cfe1 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-account_execl.patch b/SOURCES/ltrace-0.7.91-account_execl.patch new file mode 100644 index 0000000..2aa4ba0 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-arm.patch b/SOURCES/ltrace-0.7.91-arm.patch new file mode 100644 index 0000000..84c7d0d --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-breakpoint-on_install.patch b/SOURCES/ltrace-0.7.91-breakpoint-on_install.patch new file mode 100644 index 0000000..15959a3 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-cant_open.patch b/SOURCES/ltrace-0.7.91-cant_open.patch new file mode 100644 index 0000000..4d3a0d4 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-cet.patch b/SOURCES/ltrace-0.7.91-cet.patch new file mode 100644 index 0000000..a320682 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-man.patch b/SOURCES/ltrace-0.7.91-man.patch new file mode 100644 index 0000000..d7a7dbf --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-multithread-no-f-1.patch b/SOURCES/ltrace-0.7.91-multithread-no-f-1.patch new file mode 100644 index 0000000..0589f5e --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-null.patch b/SOURCES/ltrace-0.7.91-null.patch new file mode 100644 index 0000000..4ff7e6a --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-parser-ws_after_id.patch b/SOURCES/ltrace-0.7.91-parser-ws_after_id.patch new file mode 100644 index 0000000..bc6e96e --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-ppc-bias.patch b/SOURCES/ltrace-0.7.91-ppc-bias.patch new file mode 100644 index 0000000..c0c0abd --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-ppc64-fork.patch b/SOURCES/ltrace-0.7.91-ppc64-fork.patch new file mode 100644 index 0000000..53c66fd --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-ppc64-unprelink.patch b/SOURCES/ltrace-0.7.91-ppc64-unprelink.patch new file mode 100644 index 0000000..a5929e3 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-ppc64le-configure.patch b/SOURCES/ltrace-0.7.91-ppc64le-configure.patch new file mode 100644 index 0000000..74f0987 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-ppc64le-fixes.patch b/SOURCES/ltrace-0.7.91-ppc64le-fixes.patch new file mode 100644 index 0000000..82fb474 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-ppc64le-support.patch b/SOURCES/ltrace-0.7.91-ppc64le-support.patch new file mode 100644 index 0000000..e2d8daa --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-s390-fetch-syscall.patch b/SOURCES/ltrace-0.7.91-s390-fetch-syscall.patch new file mode 100644 index 0000000..da50b48 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-s390-irelative.patch b/SOURCES/ltrace-0.7.91-s390-irelative.patch new file mode 100644 index 0000000..29222c9 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-tautology.patch b/SOURCES/ltrace-0.7.91-tautology.patch new file mode 100644 index 0000000..24ff020 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-testsuite-includes-2.patch b/SOURCES/ltrace-0.7.91-testsuite-includes-2.patch new file mode 100644 index 0000000..100443a --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-testsuite-includes.patch b/SOURCES/ltrace-0.7.91-testsuite-includes.patch new file mode 100644 index 0000000..0c5a324 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-unwind-elfutils.patch b/SOURCES/ltrace-0.7.91-unwind-elfutils.patch new file mode 100644 index 0000000..3b46a76 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-x86-plt_map.patch b/SOURCES/ltrace-0.7.91-x86-plt_map.patch new file mode 100644 index 0000000..9983cc7 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-x86-unused_label.patch b/SOURCES/ltrace-0.7.91-x86-unused_label.patch new file mode 100644 index 0000000..e56bda2 --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-0.7.91-x86_64-irelative.patch b/SOURCES/ltrace-0.7.91-x86_64-irelative.patch new file mode 100644 index 0000000..949b5fb --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-rh1307754.patch b/SOURCES/ltrace-rh1307754.patch new file mode 100644 index 0000000..ac3560a --- /dev/null +++ b/SOURCES/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/SOURCES/ltrace-rh1423913.patch b/SOURCES/ltrace-rh1423913.patch new file mode 100644 index 0000000..f328dd9 --- /dev/null +++ b/SOURCES/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/SPECS/ltrace.spec b/SPECS/ltrace.spec new file mode 100644 index 0000000..7e5fb46 --- /dev/null +++ b/SPECS/ltrace.spec @@ -0,0 +1,186 @@ +%global __python /usr/bin/python3 +%{?scl:%{?scl_package:%scl_package ltrace}} + +Summary: Tracks runtime library calls from dynamically linked executables +Name: %{?scl_prefix}ltrace +Version: 0.7.91 +Release: 1%{?dist} +URL: http://ltrace.alioth.debian.org/ +License: GPLv2+ + +BuildRequires: elfutils-devel dejagnu +BuildRequires: libselinux-devel +BuildRequires: autoconf automake libtool + +# 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 + +%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 ltrace-%{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 + +%build +autoreconf -i +%configure --docdir=%{?_pkgdocdir}%{!?_pkgdocdir:%{_docdir}/%{name}-%{version}} +make %{?_smp_mflags} + +%install +make DESTDIR=$RPM_BUILD_ROOT bindir=%{_bindir} install + +# 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========================= +timeout 180 make -k check ||: +echo ====================TESTING END===================== + +%files +%defattr(-,root,root) +%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 DJ Delorie - 0.7.91-1 +- Initial sources (#1851544) +