From cb7483942c2d8ff8f495d45339113ee4cd694e2d Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Feb 06 2013 23:15:40 +0000 Subject: Add patches for ARM parameter passing convention --- diff --git a/ltrace-0.7.2-arm.patch b/ltrace-0.7.2-arm.patch index a469224..72859a0 100644 --- a/ltrace-0.7.2-arm.patch +++ b/ltrace-0.7.2-arm.patch @@ -25,6 +25,40 @@ index c3356de..141ff85 100644 backend.h \ breakpoint.h \ common.h \ +diff --git a/README b/README +index 3db5bc8..95871d1 100644 +--- a/README ++++ b/README +@@ -24,6 +24,8 @@ The following targets are currently (at least somewhat) supported. + Some of them may be more or less broken in reality, it is not feasible + to test each release comprehensively on each target. + ++ armv6l-*-linux-gnueabi ++ armv7l-*-linux-gnueabihf + i[4567]86-*-linux-gnu + ia64-*-linux-gnu + m68k-*-linux-gnu +@@ -41,11 +43,6 @@ current status is unknown: + sparc64*-*-linux-gnu + alpha*-*-linux-gnu + +-Support of the following systems is known to be broken and requires +-fixing: +- +- arm-*-linux-gnueabi +- + + Bug Reports + ----------- +@@ -83,7 +80,7 @@ quick one-liner), it is advisable to send an e-mail beforehand. + + + ------------------------------------------------------------------------------- +-Copyright (C) 2012 Petr Machata ++Copyright (C) 2012,2013 Petr Machata + Copyright (C) 1997-2009 Juan Cespedes + This file is part of ltrace. + diff --git a/backend.h b/backend.h index cfac65e..a9de3b4 100644 --- a/backend.h @@ -307,6 +341,180 @@ index ed3d0e1..47b8c70 100644 static int bitvect_lens_format_cb(struct lens *lens, FILE *stream, struct value *value, struct value_dict *arguments) +diff --git a/ltrace-elf.c b/ltrace-elf.c +index 1d0f769..af25f8f 100644 +--- a/ltrace-elf.c ++++ b/ltrace-elf.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2006,2010,2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2006,2010,2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Zachary T Welch, CodeSourcery + * Copyright (C) 2010 Joe Damato + * Copyright (C) 1997,1998,2001,2004,2007,2008,2009 Juan Cespedes +@@ -141,8 +141,9 @@ elf_get_section_if(struct ltelf *lte, Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr, + return 0; + } + } +- return -1; + ++ *tgt_sec = NULL; ++ return 0; + } + + static int +@@ -203,23 +204,23 @@ elf_get_section_named(struct ltelf *lte, const char *name, + &name_p, &data); + } + +-static int +-need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size) ++int ++elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword size) + { + assert(data != NULL); + if (data->d_size < size || offset > data->d_size - size) { + debug(1, "Not enough data to read %"PRId64"-byte value" + " at offset %"PRId64".", size, offset); +- return -1; ++ return 0; + } +- return 0; ++ return 1; + } + + #define DEF_READER(NAME, SIZE) \ + int \ + NAME(Elf_Data *data, GElf_Xword offset, uint##SIZE##_t *retp) \ + { \ +- if (!need_data(data, offset, SIZE / 8) < 0) \ ++ if (!elf_can_read_next(data, offset, SIZE / 8)) \ + return -1; \ + \ + if (data->d_buf == NULL) /* NODATA section */ { \ +@@ -236,12 +237,63 @@ need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size) + return 0; \ + } + ++DEF_READER(elf_read_u8, 8) + DEF_READER(elf_read_u16, 16) + DEF_READER(elf_read_u32, 32) + DEF_READER(elf_read_u64, 64) + + #undef DEF_READER + ++#define DEF_READER(NAME, SIZE) \ ++ int \ ++ NAME(Elf_Data *data, GElf_Xword *offset, uint##SIZE##_t *retp) \ ++ { \ ++ int rc = elf_read_u##SIZE(data, *offset, retp); \ ++ if (rc < 0) \ ++ return rc; \ ++ *offset += SIZE / 8; \ ++ return 0; \ ++ } ++ ++DEF_READER(elf_read_next_u8, 8) ++DEF_READER(elf_read_next_u16, 16) ++DEF_READER(elf_read_next_u32, 32) ++DEF_READER(elf_read_next_u64, 64) ++ ++#undef DEF_READER ++ ++int ++elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp) ++{ ++ uint64_t result = 0; ++ int shift = 0; ++ int size = 8 * sizeof result; ++ ++ while (1) { ++ uint8_t byte; ++ if (elf_read_next_u8(data, offset, &byte) < 0) ++ return -1; ++ ++ uint8_t payload = byte & 0x7f; ++ result |= (uint64_t)payload << shift; ++ shift += 7; ++ if (shift > size && byte != 0x1) ++ return -1; ++ if ((byte & 0x80) == 0) ++ break; ++ } ++ ++ if (retp != NULL) ++ *retp = result; ++ return 0; ++} ++ ++int ++elf_read_uleb128(Elf_Data *data, GElf_Xword offset, uint64_t *retp) ++{ ++ return elf_read_next_uleb128(data, &offset, retp); ++} ++ + int + open_elf(struct ltelf *lte, const char *filename) + { +diff --git a/ltrace-elf.h b/ltrace-elf.h +index b76d1eb..178258b 100644 +--- a/ltrace-elf.h ++++ b/ltrace-elf.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2006,2010,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2006,2010,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Zachary T Welch + * Copyright (C) 2001,2004,2007,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -95,6 +95,12 @@ int elf_get_sym_info(struct ltelf *lte, const char *filename, + size_t sym_index, GElf_Rela *rela, GElf_Sym *sym); + + Elf_Data *elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr); ++ ++/* The following three look for sections based on various criteria. ++ * They return 0 if there was no error, or a negative value if there ++ * was. If the section was found, it is returned in *TGT_SEC, and the ++ * header is stored te TGT_SHDR. If it wasn't found, *TGT_SEC is set ++ * to NULL. */ + int elf_get_section_covering(struct ltelf *lte, GElf_Addr addr, + Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr); + int elf_get_section_type(struct ltelf *lte, GElf_Word type, +@@ -102,13 +108,29 @@ int elf_get_section_type(struct ltelf *lte, GElf_Word type, + int elf_get_section_named(struct ltelf *lte, const char *name, + Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr); + +-/* Read, respectively, 2, 4, or 8 bytes from Elf data at given OFFSET, +- * and store it in *RETP. Returns 0 on success or a negative value if +- * there's not enough data. */ ++/* Read, respectively, 1, 2, 4, or 8 bytes from Elf data at given ++ * OFFSET, and store it in *RETP. Returns 0 on success or a negative ++ * value if there's not enough data. */ ++int elf_read_u8(Elf_Data *data, GElf_Xword offset, uint8_t *retp); + int elf_read_u16(Elf_Data *data, GElf_Xword offset, uint16_t *retp); + int elf_read_u32(Elf_Data *data, GElf_Xword offset, uint32_t *retp); + int elf_read_u64(Elf_Data *data, GElf_Xword offset, uint64_t *retp); + ++/* Read at most 64-bit quantity recorded in an ULEB128 variable-length ++ * encoding. */ ++int elf_read_uleb128(Elf_Data *data, GElf_Xword offset, uint64_t *retp); ++ ++/* These are same as above, but update *OFFSET with the width ++ * of read datum. */ ++int elf_read_next_u8(Elf_Data *data, GElf_Xword *offset, uint8_t *retp); ++int elf_read_next_u16(Elf_Data *data, GElf_Xword *offset, uint16_t *retp); ++int elf_read_next_u32(Elf_Data *data, GElf_Xword *offset, uint32_t *retp); ++int elf_read_next_u64(Elf_Data *data, GElf_Xword *offset, uint64_t *retp); ++int elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp); ++ ++/* Return whether there's AMOUNT more bytes after OFFSET in DATA. */ ++int elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword amount); ++ + #if __WORDSIZE == 32 + #define PRI_ELF_ADDR PRIx32 + #define GELF_ADDR_CAST(x) (void *)(uint32_t)(x) diff --git a/output.c b/output.c index fe62bb4..f046df8 100644 --- a/output.c @@ -428,7 +636,7 @@ index c197225..9ccd8f2 100644 - ptrace(PTRACE_POKEUSER, proc->pid, 26 /* RA */ , addr); -} diff --git a/sysdeps/linux-gnu/arm/Makefile.am b/sysdeps/linux-gnu/arm/Makefile.am -index 385424c..4b858ec 100644 +index 385424c..2c180c6 100644 --- a/sysdeps/linux-gnu/arm/Makefile.am +++ b/sysdeps/linux-gnu/arm/Makefile.am @@ -1,4 +1,5 @@ @@ -450,7 +658,7 @@ index 385424c..4b858ec 100644 - plt.c \ - regs.c \ - trace.c -+___libcpu_la_SOURCES = breakpoint.c plt.c regs.c trace.c ++___libcpu_la_SOURCES = breakpoint.c fetch.c plt.c regs.c trace.c -noinst_HEADERS = \ - arch.h \ @@ -465,7 +673,7 @@ index 385424c..4b858ec 100644 - Makefile.in +MAINTAINERCLEANFILES = Makefile.in diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h -index 291443a..71cfd5e 100644 +index 291443a..58a7fdf 100644 --- a/sysdeps/linux-gnu/arm/arch.h +++ b/sysdeps/linux-gnu/arm/arch.h @@ -1,5 +1,6 @@ @@ -475,14 +683,41 @@ index 291443a..71cfd5e 100644 * Copyright (C) 1998,2004,2008 Juan Cespedes * * This program is free software; you can redistribute it and/or -@@ -31,6 +32,7 @@ +@@ -18,6 +19,9 @@ + * 02110-1301 USA + */ + ++#ifndef LTRACE_ARM_ARCH_H ++#define LTRACE_ARM_ARCH_H ++ + #define ARCH_HAVE_ENABLE_BREAKPOINT 1 + #define ARCH_HAVE_DISABLE_BREAKPOINT 1 + +@@ -31,7 +35,24 @@ #define LT_ELFCLASS ELFCLASS32 #define LT_ELF_MACHINE EM_ARM +#define ARCH_HAVE_SW_SINGLESTEP ++#define ARCH_HAVE_FETCH_ARG ++#define ARCH_HAVE_FETCH_PACK ++#define ARCH_HAVE_SIZEOF ++#define ARCH_HAVE_ALIGNOF #define ARCH_HAVE_BREAKPOINT_DATA struct arch_breakpoint_data { int thumb_mode; + }; ++ ++#define ARCH_HAVE_LTELF_DATA ++struct arch_ltelf_data { ++ /* We have this only for the hooks. */ ++}; ++ ++#define ARCH_HAVE_LIBRARY_DATA ++struct arch_library_data { ++ unsigned int hardfp:1; ++}; ++ ++#endif /* LTRACE_ARM_ARCH_H */ diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c index 2fb9578..fcd43a7 100644 --- a/sysdeps/linux-gnu/arm/breakpoint.c @@ -507,8 +742,543 @@ index 2fb9578..fcd43a7 100644 return 0; } +diff --git a/sysdeps/linux-gnu/arm/fetch.c b/sysdeps/linux-gnu/arm/fetch.c +new file mode 100644 +index 0000000..0064d91 +--- /dev/null ++++ b/sysdeps/linux-gnu/arm/fetch.c +@@ -0,0 +1,529 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "backend.h" ++#include "fetch.h" ++#include "library.h" ++#include "ltrace-elf.h" ++#include "proc.h" ++#include "ptrace.h" ++#include "regs.h" ++#include "type.h" ++#include "value.h" ++ ++static int ++get_hardfp(uint64_t abi_vfp_args) ++{ ++ if (abi_vfp_args == 2) ++ fprintf(stderr, ++ "Tag_ABI_VFP_args value 2 (tool chain-specific " ++ "conventions) not supported.\n"); ++ return abi_vfp_args == 1; ++} ++ ++int ++arch_elf_init(struct ltelf *lte, struct library *lib) ++{ ++ /* Nothing in this section is strictly critical. It's not ++ * that much of a deal if we fail to guess right whether the ++ * ABI is softfp or hardfp. */ ++ unsigned hardfp = 0; ++ ++ Elf_Scn *scn; ++ Elf_Data *data; ++ GElf_Shdr shdr; ++ if (elf_get_section_type(lte, SHT_ARM_ATTRIBUTES, &scn, &shdr) < 0 ++ || (scn != NULL && (data = elf_loaddata(scn, &shdr)) == NULL)) { ++ fprintf(stderr, ++ "Error when obtaining ARM attribute section: %s\n", ++ elf_errmsg(-1)); ++ goto done; ++ ++ } else if (scn != NULL && data != NULL) { ++ GElf_Xword offset = 0; ++ uint8_t version; ++ if (elf_read_next_u8(data, &offset, &version) < 0) { ++ goto done; ++ } else if (version != 'A') { ++ fprintf(stderr, "Unsupported ARM attribute section " ++ "version %d ('%c').\n", version, version); ++ goto done; ++ } ++ ++ do { ++ const char signature[] = "aeabi"; ++ /* N.B. LEN is including the length field ++ * itself. */ ++ uint32_t sec_len; ++ if (elf_read_u32(data, offset, &sec_len) < 0 ++ || !elf_can_read_next(data, offset, sec_len)) { ++ goto done; ++ } ++ const GElf_Xword next_offset = offset + sec_len; ++ offset += 4; ++ ++ if (sec_len < 4 + sizeof signature ++ || strcmp(signature, data->d_buf + offset) != 0) ++ goto skip; ++ offset += sizeof signature; ++ ++ const GElf_Xword offset0 = offset; ++ uint64_t tag; ++ uint32_t sub_len; ++ if (elf_read_next_uleb128(data, &offset, &tag) < 0 ++ || elf_read_next_u32(data, &offset, &sub_len) < 0 ++ || !elf_can_read_next(data, offset0, sub_len)) ++ goto done; ++ ++ if (tag != 1) ++ /* IHI0045D_ABI_addenda: "section and ++ * symbol attributes are deprecated ++ * [...] consumers are permitted to ++ * ignore them." */ ++ goto skip; ++ ++ while (offset < offset0 + sub_len) { ++ if (elf_read_next_uleb128(data, ++ &offset, &tag) < 0) ++ goto done; ++ ++ switch (tag) { ++ uint64_t v; ++ case 6: /* Tag_CPU_arch */ ++ case 7: /* Tag_CPU_arch_profile */ ++ case 8: /* Tag_ARM_ISA_use */ ++ case 9: /* Tag_THUMB_ISA_use */ ++ case 10: /* Tag_FP_arch */ ++ case 11: /* Tag_WMMX_arch */ ++ case 12: /* Tag_Advanced_SIMD_arch */ ++ case 13: /* Tag_PCS_config */ ++ case 14: /* Tag_ABI_PCS_R9_use */ ++ case 15: /* Tag_ABI_PCS_RW_data */ ++ case 16: /* Tag_ABI_PCS_RO_data */ ++ case 17: /* Tag_ABI_PCS_GOT_use */ ++ case 18: /* Tag_ABI_PCS_wchar_t */ ++ case 19: /* Tag_ABI_FP_rounding */ ++ case 20: /* Tag_ABI_FP_denormal */ ++ case 21: /* Tag_ABI_FP_exceptions */ ++ case 22: /* Tag_ABI_FP_user_exceptions */ ++ case 23: /* Tag_ABI_FP_number_model */ ++ case 24: /* Tag_ABI_align_needed */ ++ case 25: /* Tag_ABI_align_preserved */ ++ case 26: /* Tag_ABI_enum_size */ ++ case 27: /* Tag_ABI_HardFP_use */ ++ case 28: /* Tag_ABI_VFP_args */ ++ case 29: /* Tag_ABI_WMMX_args */ ++ case 30: /* Tag_ABI_optimization_goals */ ++ case 31: /* Tag_ABI_FP_optimization_goals */ ++ case 32: /* Tag_compatibility */ ++ case 34: /* Tag_CPU_unaligned_access */ ++ case 36: /* Tag_FP_HP_extension */ ++ case 38: /* Tag_ABI_FP_16bit_format */ ++ case 42: /* Tag_MPextension_use */ ++ case 70: /* Tag_MPextension_use as well */ ++ case 44: /* Tag_DIV_use */ ++ case 64: /* Tag_nodefaults */ ++ case 66: /* Tag_T2EE_use */ ++ case 68: /* Tag_Virtualization_use */ ++ uleb128: ++ if (elf_read_next_uleb128 ++ (data, &offset, &v) < 0) ++ goto done; ++ if (tag == 28) ++ hardfp = get_hardfp(v); ++ if (tag != 32) ++ continue; ++ ++ /* Tag 32 has two arguments, ++ * fall through. */ ++ ++ case 4: /* Tag_CPU_raw_name */ ++ case 5: /* Tag_CPU_name */ ++ case 65: /* Tag_also_compatible_with */ ++ case 67: /* Tag_conformance */ ++ ntbs: ++ offset += strlen(data->d_buf ++ + offset) + 1; ++ continue; ++ } ++ ++ /* Handle unknown tags in a generic ++ * manner, if possible. */ ++ if (tag <= 32) { ++ fprintf(stderr, ++ "Unknown tag %lld " ++ "at offset %#llx " ++ "of ARM attribute section.", ++ tag, offset); ++ goto skip; ++ } else if (tag % 2 == 0) { ++ goto uleb128; ++ } else { ++ goto ntbs; ++ } ++ } ++ ++ skip: ++ offset = next_offset; ++ ++ } while (elf_can_read_next(data, offset, 1)); ++ ++ } ++ ++done: ++ lib->arch.hardfp = hardfp; ++ return 0; ++} ++ ++void ++arch_elf_destroy(struct ltelf *lte) ++{ ++} ++ ++void ++arch_library_init(struct library *lib) ++{ ++} ++ ++void ++arch_library_destroy(struct library *lib) ++{ ++} ++ ++void ++arch_library_clone(struct library *retp, struct library *lib) ++{ ++ retp->arch = lib->arch; ++} ++ ++enum { ++ /* How many (double) VFP registers the AAPCS uses for ++ * parameter passing. */ ++ NUM_VFP_REGS = 8, ++}; ++ ++struct fetch_context { ++ struct pt_regs regs; ++ ++ struct { ++ union { ++ double d[32]; ++ float s[64]; ++ }; ++ uint32_t fpscr; ++ } fpregs; ++ ++ /* VFP register allocation. ALLOC.S tracks whether the ++ * corresponding FPREGS.S register is taken, ALLOC.D the same ++ * for FPREGS.D. We only track 8 (16) registers, because ++ * that's what the ABI uses for parameter passing. */ ++ union { ++ int16_t d[NUM_VFP_REGS]; ++ int8_t s[NUM_VFP_REGS * 2]; ++ } alloc; ++ ++ unsigned ncrn; ++ arch_addr_t sp; ++ arch_addr_t nsaa; ++ arch_addr_t ret_struct; ++ ++ bool hardfp:1; ++ bool in_varargs:1; ++}; ++ ++static int ++fetch_register_banks(struct process *proc, struct fetch_context *context) ++{ ++ if (ptrace(PTRACE_GETREGS, proc->pid, NULL, &context->regs) == -1) ++ return -1; ++ ++ if (context->hardfp ++ && ptrace(PTRACE_GETVFPREGS, proc->pid, ++ NULL, &context->fpregs) == -1) ++ return -1; ++ ++ context->ncrn = 0; ++ context->nsaa = context->sp = get_stack_pointer(proc); ++ memset(&context->alloc, 0, sizeof(context->alloc)); ++ ++ return 0; ++} ++ ++struct fetch_context * ++arch_fetch_arg_init(enum tof type, struct process *proc, ++ struct arg_type_info *ret_info) ++{ ++ struct fetch_context *context = malloc(sizeof(*context)); ++ ++ { ++ struct process *mainp = proc; ++ while (mainp->libraries == NULL && mainp->parent != NULL) ++ mainp = mainp->parent; ++ context->hardfp = mainp->libraries->arch.hardfp; ++ } ++ ++ if (context == NULL ++ || fetch_register_banks(proc, context) < 0) { ++ free(context); ++ return NULL; ++ } ++ ++ if (ret_info->type == ARGTYPE_STRUCT ++ || ret_info->type == ARGTYPE_ARRAY) { ++ size_t sz = type_sizeof(proc, ret_info); ++ assert(sz != (size_t)-1); ++ if (sz > 4) { ++ /* XXX double cast */ ++ context->ret_struct ++ = (arch_addr_t)context->regs.uregs[0]; ++ context->ncrn++; ++ } ++ } ++ ++ return context; ++} ++ ++struct fetch_context * ++arch_fetch_arg_clone(struct process *proc, ++ struct fetch_context *context) ++{ ++ struct fetch_context *clone = malloc(sizeof(*context)); ++ if (clone == NULL) ++ return NULL; ++ *clone = *context; ++ return clone; ++} ++ ++/* 0 is success, 1 is failure, negative value is an error. */ ++static int ++pass_in_vfp(struct fetch_context *ctx, struct process *proc, ++ enum arg_type type, size_t count, struct value *valuep) ++{ ++ assert(type == ARGTYPE_FLOAT || type == ARGTYPE_DOUBLE); ++ unsigned max = type == ARGTYPE_DOUBLE ? NUM_VFP_REGS : 2 * NUM_VFP_REGS; ++ if (count > max) ++ return 1; ++ ++ size_t i; ++ size_t j; ++ for (i = 0; i < max; ++i) { ++ for (j = i; j < i + count; ++j) ++ if ((type == ARGTYPE_DOUBLE && ctx->alloc.d[j] != 0) ++ || (type == ARGTYPE_FLOAT && ctx->alloc.s[j] != 0)) ++ goto next; ++ ++ /* Found COUNT consecutive unallocated registers at I. */ ++ const size_t sz = (type == ARGTYPE_FLOAT ? 4 : 8) * count; ++ unsigned char *data = value_reserve(valuep, sz); ++ if (data == NULL) ++ return -1; ++ ++ for (j = i; j < i + count; ++j) ++ if (type == ARGTYPE_DOUBLE) ++ ctx->alloc.d[j] = -1; ++ else ++ ctx->alloc.s[j] = -1; ++ ++ if (type == ARGTYPE_DOUBLE) ++ memcpy(data, ctx->fpregs.d + i, sz); ++ else ++ memcpy(data, ctx->fpregs.s + i, sz); ++ ++ return 0; ++ ++ next: ++ continue; ++ } ++ return 1; ++} ++ ++/* 0 is success, 1 is failure, negative value is an error. */ ++static int ++consider_vfp(struct fetch_context *ctx, struct process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ struct arg_type_info *float_info = NULL; ++ size_t hfa_size = 1; ++ if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) ++ float_info = info; ++ else ++ float_info = type_get_hfa_type(info, &hfa_size); ++ ++ if (float_info != NULL && hfa_size <= 4) ++ return pass_in_vfp(ctx, proc, float_info->type, ++ hfa_size, valuep); ++ return 1; ++} ++ ++int ++arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, ++ struct process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ const size_t sz = type_sizeof(proc, info); ++ assert(sz != (size_t)-1); ++ ++ if (ctx->hardfp && !ctx->in_varargs) { ++ int rc; ++ if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1) ++ return rc; ++ } ++ ++ /* IHI0042E_aapcs: If the argument requires double-word ++ * alignment (8-byte), the NCRN is rounded up to the next even ++ * register number. */ ++ const size_t al = type_alignof(proc, info); ++ assert(al != (size_t)-1); ++ if (al == 8) ++ ctx->ncrn = ((ctx->ncrn + 1) / 2) * 2; ++ ++ /* If the size in words of the argument is not more than r4 ++ * minus NCRN, the argument is copied into core registers, ++ * starting at the NCRN. */ ++ /* If the NCRN is less than r4 and the NSAA is equal to the ++ * SP, the argument is split between core registers and the ++ * stack. */ ++ ++ const size_t words = (sz + 3) / 4; ++ if (ctx->ncrn < 4 && ctx->nsaa == ctx->sp) { ++ unsigned char *data = value_reserve(valuep, words * 4); ++ if (data == NULL) ++ return -1; ++ size_t i; ++ for (i = 0; i < words && ctx->ncrn < 4; ++i) { ++ memcpy(data, &ctx->regs.uregs[ctx->ncrn++], 4); ++ data += 4; ++ } ++ const size_t rest = (words - i) * 4; ++ if (rest > 0) { ++ umovebytes(proc, ctx->nsaa, data, rest); ++ ctx->nsaa += rest; ++ } ++ return 0; ++ } ++ ++ assert(ctx->ncrn == 4); ++ ++ /* If the argument required double-word alignment (8-byte), ++ * then the NSAA is rounded up to the next double-word ++ * address. */ ++ if (al == 8) ++ /* XXX double cast. */ ++ ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 7) / 8) * 8); ++ else ++ ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 3) / 4) * 4); ++ ++ value_in_inferior(valuep, ctx->nsaa); ++ ctx->nsaa += sz; ++ ++ return 0; ++} ++ ++int ++arch_fetch_retval(struct fetch_context *ctx, enum tof type, ++ struct process *proc, struct arg_type_info *info, ++ struct value *valuep) ++{ ++ if (fetch_register_banks(proc, ctx) < 0) ++ return -1; ++ ++ if (ctx->hardfp && !ctx->in_varargs) { ++ int rc; ++ if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1) ++ return rc; ++ } ++ ++ size_t sz = type_sizeof(proc, info); ++ assert(sz != (size_t)-1); ++ ++ switch (info->type) { ++ unsigned char *data; ++ ++ case ARGTYPE_VOID: ++ return 0; ++ ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ if (ctx->hardfp && !ctx->in_varargs) { ++ unsigned char *data = value_reserve(valuep, sz); ++ if (data == NULL) ++ return -1; ++ memmove(data, &ctx->fpregs, sz); ++ return 0; ++ } ++ goto pass_in_registers; ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ if (sz > 4) { ++ value_in_inferior(valuep, ctx->ret_struct); ++ return 0; ++ } ++ /* Fall through. */ ++ ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ pass_in_registers: ++ if ((data = value_reserve(valuep, sz)) == NULL) ++ return -1; ++ memmove(data, ctx->regs.uregs, sz); ++ return 0; ++ } ++ assert(info->type != info->type); ++ abort(); ++} ++ ++void ++arch_fetch_arg_done(struct fetch_context *context) ++{ ++ free(context); ++} ++ ++int ++arch_fetch_param_pack_start(struct fetch_context *context, ++ enum param_pack_flavor ppflavor) ++{ ++ if (ppflavor == PARAM_PACK_VARARGS) ++ context->in_varargs = true; ++ return 0; ++} ++ ++void ++arch_fetch_param_pack_end(struct fetch_context *context) ++{ ++ context->in_varargs = false; ++} diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c -index 377df62..bd86370 100644 +index 377df62..e9e825e 100644 --- a/sysdeps/linux-gnu/arm/regs.c +++ b/sysdeps/linux-gnu/arm/regs.c @@ -1,5 +1,6 @@ @@ -530,14 +1300,13 @@ index 377df62..bd86370 100644 #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) # define PTRACE_PEEKUSER PTRACE_PEEKUSR -@@ -37,13 +40,91 @@ +@@ -36,50 +39,119 @@ + # define PTRACE_POKEUSER PTRACE_POKEUSR #endif - #define off_pc ((void *)60) +-#define off_pc ((void *)60) -#define off_lr ((void *)56) - #define off_sp ((void *)52) - --void * +-#define off_sp ((void *)52) +int +arm_get_register(struct process *proc, enum arm_register reg, uint32_t *lp) +{ @@ -548,22 +1317,38 @@ index 377df62..bd86370 100644 + *lp = (uint32_t)l; + return 0; +} -+ + +-void * +-get_instruction_pointer(struct process *proc) ++int ++arm_set_register(struct process *proc, enum arm_register reg, uint32_t lp) + { +- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); ++ return ptrace(PTRACE_PEEKUSER, proc->pid, ++ (void *)(reg * 4L), (void *)lp); + } + +-void +-set_instruction_pointer(struct process *proc, void *addr) +int +arm_get_register_offpc(struct process *proc, enum arm_register reg, + uint32_t *lp) -+{ + { +- ptrace(PTRACE_POKEUSER, proc->pid, off_pc, addr); + if (arm_get_register(proc, reg, lp) < 0) + return -1; + if (reg == ARM_REG_PC) + *lp += 8; + return 0; -+} -+ + } + +-void * +-get_stack_pointer(struct process *proc) +int +arm_get_shifted_register(struct process *proc, uint32_t inst, int carry, + arch_addr_t pc_val, uint32_t *lp) -+{ + { +- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0); + enum arm_register rm = BITS(inst, 0, 3); + unsigned long shifttype = BITS(inst, 5, 6); + @@ -610,30 +1395,15 @@ index 377df62..bd86370 100644 + + *lp = res & 0xffffffff; + return 0; -+} -+ -+arch_addr_t - get_instruction_pointer(struct process *proc) - { -- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); -+ uint32_t reg; -+ if (arm_get_register(proc, ARM_REG_PC, ®) < 0) -+ /* XXX double cast. */ -+ return (arch_addr_t)-1; -+ /* XXX double cast. */ -+ return (arch_addr_t)(uintptr_t)reg; - } - - void -@@ -58,28 +139,13 @@ get_stack_pointer(struct process *proc) - return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0); } -/* really, this is given the *stack_pointer expecting - * a CISC architecture; in our case, we don't need that */ -void * -get_return_addr(struct process *proc, void *stack_pointer) --{ ++static arch_addr_t ++get_register_nocheck(struct process *proc, enum arm_register r) + { - long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0); - - /* Remember & unset the thumb mode bit. XXX This is really a @@ -646,28 +1416,47 @@ index 377df62..bd86370 100644 - addr &= ~1; - - return (void *)addr; --} -- --void ++ uint32_t reg; ++ if (arm_get_register(proc, r, ®) < 0) ++ /* XXX double cast. */ ++ return (arch_addr_t)-1; ++ /* XXX double cast. */ ++ return (arch_addr_t)(uintptr_t)reg; ++} ++ ++arch_addr_t ++get_instruction_pointer(struct process *proc) ++{ ++ return get_register_nocheck(proc, ARM_REG_PC); + } + + void -set_return_addr(struct process *proc, void *addr) ++set_instruction_pointer(struct process *proc, arch_addr_t addr) ++{ ++ /* XXX double cast. */ ++ arm_set_register(proc, ARM_REG_PC, (uint32_t)addr); ++} ++ ++void * ++get_stack_pointer(struct process *proc) ++{ ++ return get_register_nocheck(proc, ARM_REG_SP); ++} ++ +arch_addr_t +get_return_addr(struct process *proc, arch_addr_t stack_pointer) { - long iaddr = (int)addr | proc->thumb_mode; - ptrace(PTRACE_POKEUSER, proc->pid, off_lr, (void *)iaddr); -+ uint32_t reg; -+ if (arm_get_register(proc, ARM_REG_LR, ®) < 0) -+ /* XXX double cast. */ -+ return (arch_addr_t)-1; -+ /* XXX double cast. */ -+ return (arch_addr_t)(uintptr_t)reg; ++ return get_register_nocheck(proc, ARM_REG_LR); } diff --git a/sysdeps/linux-gnu/arm/regs.h b/sysdeps/linux-gnu/arm/regs.h new file mode 100644 -index 0000000..1566f92 +index 0000000..f9a5a86 --- /dev/null +++ b/sysdeps/linux-gnu/arm/regs.h -@@ -0,0 +1,45 @@ +@@ -0,0 +1,47 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2013 Petr Machata, Red Hat Inc. @@ -695,6 +1484,8 @@ index 0000000..1566f92 + ((long) (BITS(obj,st,fn) | ((long) BIT(obj,fn) * ~ SUBMASK(fn - st)))) + +enum arm_register { ++ ARM_REG_R7 = 7, ++ ARM_REG_IP = 12, + ARM_REG_SP = 13, + ARM_REG_LR = 14, + ARM_REG_PC = 15, @@ -714,7 +1505,7 @@ index 0000000..1566f92 +int arm_get_shifted_register(struct process *proc, uint32_t inst, int carry, + arch_addr_t pc, uint32_t *lp); diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c -index fbbf676..8b0734e 100644 +index fbbf676..5e51e91 100644 --- a/sysdeps/linux-gnu/arm/trace.c +++ b/sysdeps/linux-gnu/arm/trace.c @@ -1,6 +1,6 @@ @@ -725,7 +1516,7 @@ index fbbf676..8b0734e 100644 * Copyright (C) 1998,2004,2008,2009 Juan Cespedes * Copyright (C) 2006 Ian Wienand * -@@ -29,10 +29,12 @@ +@@ -29,10 +29,13 @@ #include #include @@ -736,36 +1527,89 @@ index fbbf676..8b0734e 100644 #include "output.h" #include "ptrace.h" +#include "regs.h" ++#include "type.h" #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) # define PTRACE_PEEKUSER PTRACE_PEEKUSR -@@ -46,6 +48,7 @@ - #define off_r7 ((void *)28) - #define off_ip ((void *)48) - #define off_pc ((void *)60) -+#define off_cpsr ((void *)64) +@@ -42,11 +45,6 @@ + # define PTRACE_POKEUSER PTRACE_POKEUSR + #endif +-#define off_r0 ((void *)0) +-#define off_r7 ((void *)28) +-#define off_ip ((void *)48) +-#define off_pc ((void *)60) +- void get_arch_dep(struct process *proc) -@@ -149,3 +152,560 @@ gimme_arg(enum tof type, struct process *proc, int arg_num, + { +@@ -68,18 +66,24 @@ syscall_p(struct process *proc, int status, int *sysnum) + { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { +- /* get the user's pc (plus 8) */ +- unsigned pc = ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); ++ uint32_t pc, ip; ++ if (arm_get_register(proc, ARM_REG_PC, &pc) < 0 ++ || arm_get_register(proc, ARM_REG_IP, &ip) < 0) ++ return -1; ++ + pc = pc - 4; ++ + /* fetch the SWI instruction */ + unsigned insn = ptrace(PTRACE_PEEKTEXT, proc->pid, + (void *)pc, 0); +- int ip = ptrace(PTRACE_PEEKUSER, proc->pid, off_ip, 0); + if (insn == 0xef000000 || insn == 0x0f000000 + || (insn & 0xffff0000) == 0xdf000000) { + /* EABI syscall */ +- *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, off_r7, 0); ++ uint32_t r7; ++ if (arm_get_register(proc, ARM_REG_R7, &r7) < 0) ++ return -1; ++ *sysnum = r7; + } else if ((insn & 0xfff00000) == 0xef900000) { + /* old ABI syscall */ + *sysnum = insn & 0xfffff; +@@ -105,47 +109,605 @@ syscall_p(struct process *proc, int status, int *sysnum) return 0; } -+ + +-long +-gimme_arg(enum tof type, struct process *proc, int arg_num, +- struct arg_type_info *info) +static arch_addr_t +arm_branch_dest(const arch_addr_t pc, const uint32_t insn) -+{ + { +- proc_archdep *a = (proc_archdep *) proc->arch_ptr; + /* Bits 0-23 are signed immediate value. */ + return pc + ((((insn & 0xffffff) ^ 0x800000) - 0x800000) << 2) + 8; +} -+ + +- if (arg_num == -1) { /* return value */ +- return ptrace(PTRACE_PEEKUSER, proc->pid, off_r0, 0); +- } +/* Addresses for calling Thumb functions have the bit 0 set. + Here are some macros to test, set, or clear bit 0 of addresses. */ +/* XXX double cast */ +#define IS_THUMB_ADDR(addr) ((uintptr_t)(addr) & 1) +#define MAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) | 1)) +#define UNMAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) & ~1)) -+ + +- /* deal with the ARM calling conventions */ +- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { +- if (arg_num < 4) { +- if (a->valid && type == LT_TOF_FUNCTION) +- return a->regs.uregs[arg_num]; +- if (a->valid && type == LT_TOF_FUNCTIONR) +- return a->func_arg[arg_num]; +- return ptrace(PTRACE_PEEKUSER, proc->pid, +- (void *)(4 * arg_num), 0); +- } else { +- return ptrace(PTRACE_PEEKDATA, proc->pid, +- proc->stack_pointer + 4 * (arg_num - 4), +- 0); +enum { + COND_ALWAYS = 0xe, + COND_NV = 0xf, @@ -810,7 +1654,19 @@ index fbbf676..8b0734e 100644 + | (((this_instr >> 24) & 0x1) << 1)); + next_pcs[nr++] = MAKE_THUMB_ADDR(addr); + break; -+ } + } +- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { +- if (arg_num < 5) { +- if (a->valid && type == LT_TOF_SYSCALL) +- return a->regs.uregs[arg_num]; +- if (a->valid && type == LT_TOF_SYSCALLR) +- return a->sysc_arg[arg_num]; +- return ptrace(PTRACE_PEEKUSER, proc->pid, +- (void *)(4 * arg_num), 0); +- } else { +- return ptrace(PTRACE_PEEKDATA, proc->pid, +- proc->stack_pointer + 4 * (arg_num - 5), +- 0); + else + switch (opcode) { + uint32_t operand1, operand2, result = 0; @@ -1268,15 +2124,18 @@ index fbbf676..8b0734e 100644 + return -1; + + next_pcs[nr++] = pc + 2 * length; -+ } -+ } -+ + } +- } else { +- fprintf(stderr, "gimme_arg called with wrong arguments\n"); +- exit(1); + } + + + /* Otherwise take the next instruction. */ + if (nr == 0) + next_pcs[nr++] = pc + thumb_insn_size(inst1); -+ return 0; -+} + return 0; + } + +enum sw_singlestep_status +arch_sw_singlestep(struct process *proc, struct breakpoint *sbp, @@ -1308,6 +2167,140 @@ index fbbf676..8b0734e 100644 + ptrace(PTRACE_CONT, proc->pid, 0, 0); + return SWS_OK; +} ++ ++size_t ++arch_type_sizeof(struct process *proc, struct arg_type_info *info) ++{ ++ if (proc == NULL) ++ return (size_t)-2; ++ ++ switch (info->type) { ++ case ARGTYPE_VOID: ++ return 0; ++ ++ case ARGTYPE_CHAR: ++ return 1; ++ ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ return 2; ++ ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ return 4; ++ ++ case ARGTYPE_FLOAT: ++ return 4; ++ case ARGTYPE_DOUBLE: ++ return 8; ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ /* Use default value. */ ++ return (size_t)-2; ++ ++ default: ++ assert(info->type != info->type); ++ abort(); ++ } ++} ++ ++size_t ++arch_type_alignof(struct process *proc, struct arg_type_info *info) ++{ ++ return arch_type_sizeof(proc, info); ++} +diff --git a/sysdeps/linux-gnu/ia64/fetch.c b/sysdeps/linux-gnu/ia64/fetch.c +index e90dbed..171c7a2 100644 +--- a/sysdeps/linux-gnu/ia64/fetch.c ++++ b/sysdeps/linux-gnu/ia64/fetch.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2008,2009 Juan Cespedes + * Copyright (C) 2006 Steve Fink + * Copyright (C) 2006 Ian Wienand +@@ -249,37 +249,6 @@ allocate_float(struct fetch_context *ctx, struct process *proc, + return 0; + } + +-static enum arg_type +-get_hfa_type(struct arg_type_info *info, size_t *countp) +-{ +- size_t n = type_aggregate_size(info); +- if (n == (size_t)-1) +- return ARGTYPE_VOID; +- +- enum arg_type type = ARGTYPE_VOID; +- *countp = 0; +- +- while (n-- > 0) { +- struct arg_type_info *emt = type_element(info, n); +- +- enum arg_type emt_type = emt->type; +- size_t emt_count = 1; +- if (emt_type == ARGTYPE_STRUCT || emt_type == ARGTYPE_ARRAY) +- emt_type = get_hfa_type(emt, &emt_count); +- +- if (type == ARGTYPE_VOID) { +- if (emt_type != ARGTYPE_FLOAT +- && emt_type != ARGTYPE_DOUBLE) +- return ARGTYPE_VOID; +- type = emt_type; +- } +- if (emt_type != type) +- return ARGTYPE_VOID; +- *countp += emt_count; +- } +- return type; +-} +- + static int + allocate_hfa(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, +@@ -380,10 +349,11 @@ allocate_ret(struct fetch_context *ctx, struct process *proc, + * floating-point registers, beginning with f8. */ + if (info->type == ARGTYPE_STRUCT || info->type == ARGTYPE_ARRAY) { + size_t hfa_size; +- enum arg_type hfa_type = get_hfa_type(info, &hfa_size); +- if (hfa_type != ARGTYPE_VOID && hfa_size <= 8) ++ struct arg_type_info *hfa_info ++ = type_get_hfa_type(info, &hfa_size); ++ if (hfa_info != NULL && hfa_size <= 8) + return allocate_hfa(ctx, proc, info, valuep, +- hfa_type, hfa_size); ++ hfa_info->type, hfa_size); + } + + /* Integers and pointers are passed in r8. 128-bit integers +@@ -409,7 +379,7 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, + struct arg_type_info *info, struct value *valuep) + { + switch (info->type) { +- enum arg_type hfa_type; ++ struct arg_type_info *hfa_info; + size_t hfa_size; + + case ARGTYPE_VOID: +@@ -421,10 +391,10 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, + return allocate_float(ctx, proc, info, valuep, 1); + + case ARGTYPE_STRUCT: +- hfa_type = get_hfa_type(info, &hfa_size); +- if (hfa_type != ARGTYPE_VOID) ++ hfa_info = type_get_hfa_type(info, &hfa_size); ++ if (hfa_info != NULL) + return allocate_hfa(ctx, proc, info, valuep, +- hfa_type, hfa_size); ++ hfa_info->type, hfa_size); + /* Fall through. */ + case ARGTYPE_CHAR: + case ARGTYPE_SHORT: diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c index fb79e8a..67873ce 100644 --- a/sysdeps/linux-gnu/ia64/regs.c @@ -1372,6 +2365,32 @@ index 19f97cb..d6a7a50 100644 -{ - ptrace(PTRACE_POKEUSER, proc->pid, off_lr, addr); -} +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 439b8e8..fe1602a 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -262,7 +262,8 @@ load_opd_data(struct ltelf *lte, struct library *lib) + { + Elf_Scn *sec; + GElf_Shdr shdr; +- if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0) { ++ if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0 ++ || sec == NULL) { + fail: + fprintf(stderr, "couldn't find .opd data\n"); + return -1; +@@ -290,8 +291,9 @@ get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, Elf_Data *plt_data) + Elf_Scn *ppcgot_sec = NULL; + GElf_Shdr ppcgot_shdr; + if (ppcgot != 0 +- && elf_get_section_covering(lte, ppcgot, +- &ppcgot_sec, &ppcgot_shdr) < 0) ++ && (elf_get_section_covering(lte, ppcgot, ++ &ppcgot_sec, &ppcgot_shdr) < 0 ++ || ppcgot_sec == NULL)) + fprintf(stderr, + "DT_PPC_GOT=%#"PRIx64", but no such section found\n", + ppcgot); diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c index ed9b398..40d7e7a 100644 --- a/sysdeps/linux-gnu/ppc/regs.c @@ -1609,6 +2628,36 @@ index 3886e84..0a42c6e 100644 - addr = (void *)((long int)addr & 0xffffffff); - ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); -} +diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp +index e54086f..b585bc9 100644 +--- a/testsuite/ltrace.main/parameters.exp ++++ b/testsuite/ltrace.main/parameters.exp +@@ -35,9 +35,6 @@ if [regexp {ELF from incompatible architecture} $exec_output] { + return + } + +-set xfail_spec {"arm*-*" } +-set xfail_spec_arm {"arm*-*"} +- + # Verify the output + set pattern "func_intptr(17)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +@@ -63,7 +60,6 @@ set pattern "func_ushort(33, 34)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_float(3.40*, -3.40*).*= 3.40*" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-eval "setup_xfail $xfail_spec" + set pattern "func_double(3.40*, -3.40*).*= -3.40*" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_typedef(BLUE)" +@@ -86,7 +82,6 @@ set pattern "func_work(\\\"x\\\")" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_struct_2(17, { \\\"ABCDE\\\\\\\\0\\\", 0.250* }, 0.50*).*= { 0.250*, 'B', 'C' }" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-eval "setup_xfail $xfail_spec_arm" + set pattern "<... func_call resumed> \\\"x\\\", \\\"y\\\")" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + diff --git a/testsuite/ltrace.torture/Makefile.am b/testsuite/ltrace.torture/Makefile.am index daa772f..5a45265 100644 --- a/testsuite/ltrace.torture/Makefile.am @@ -1682,3 +2731,81 @@ index 0000000..0d633d9 +} + +ltraceDone +diff --git a/type.c b/type.c +index d80550b..e06a9c2 100644 +--- a/type.c ++++ b/type.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2007,2008 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -568,3 +568,39 @@ type_get_fp_equivalent(struct arg_type_info *info) + } + abort(); + } ++ ++struct arg_type_info * ++type_get_hfa_type(struct arg_type_info *info, size_t *countp) ++{ ++ assert(info != NULL); ++ if (info->type != ARGTYPE_STRUCT ++ && info->type != ARGTYPE_ARRAY) ++ return NULL; ++ ++ size_t n = type_aggregate_size(info); ++ if (n == (size_t)-1) ++ return NULL; ++ ++ struct arg_type_info *ret = NULL; ++ *countp = 0; ++ ++ while (n-- > 0) { ++ struct arg_type_info *emt = type_element(info, n); ++ ++ size_t emt_count = 1; ++ if (emt->type == ARGTYPE_STRUCT || emt->type == ARGTYPE_ARRAY) ++ emt = type_get_hfa_type(emt, &emt_count); ++ if (emt == NULL) ++ return NULL; ++ if (ret == NULL) { ++ if (emt->type != ARGTYPE_FLOAT ++ && emt->type != ARGTYPE_DOUBLE) ++ return NULL; ++ ret = emt; ++ } ++ if (emt->type != ret->type) ++ return NULL; ++ *countp += emt_count; ++ } ++ return ret; ++} +diff --git a/type.h b/type.h +index b92c1af..3210677 100644 +--- a/type.h ++++ b/type.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 1997-2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -142,4 +142,13 @@ int type_is_signed(enum arg_type type); + * type. */ + struct arg_type_info *type_get_fp_equivalent(struct arg_type_info *info); + ++/* If INFO is homogeneous floating-point aggregate, return the ++ * corresponding floating point type, and set *COUNTP to number of ++ * fields of the structure. Otherwise return NULL. INFO is a HFA if ++ * it's an aggregate whose each field is either a HFA, or a ++ * floating-point type. */ ++struct arg_type_info *type_get_hfa_type(struct arg_type_info *info, ++ size_t *countp); ++ ++ + #endif /* TYPE_H */ diff --git a/ltrace.spec b/ltrace.spec index adbba90..f6a6795 100644 --- a/ltrace.spec +++ b/ltrace.spec @@ -1,7 +1,7 @@ Summary: Tracks runtime library calls from dynamically linked executables Name: ltrace Version: 0.7.2 -Release: 3%{?dist} +Release: 4%{?dist} URL: http://ltrace.alioth.debian.org/ License: GPLv2+ Group: Development/Debuggers @@ -18,9 +18,7 @@ Source: http://alioth.debian.org/frs/download.php/3848/ltrace-0.7.2.tar.bz2 # Many small fixes brought from master branch. Patch0: ltrace-0.7.2-bits.patch -# Upstream patch which introduces singlestepping support on ARM. This -# makes ltrace essentially work on ARM, except for parameter passing -# conventions. +# Upstream patch which introduces support for ARM architecture. Patch1: ltrace-0.7.2-arm.patch # Work around a recently-added GCC warning. @@ -66,6 +64,10 @@ echo ====================TESTING END===================== %config(noreplace) %{_sysconfdir}/ltrace.conf %changelog +* Wed Feb 6 2013 Petr Machata - 0.7.2-4 +- Update the ARM patch (ltrace-0.7.2-arm.patch) with support for + parameter passing conventions. + * Thu Jan 31 2013 Petr Machata - 0.7.2-3 - Bring small fixes from master branch (ltrace-0.7.2-bits.patch; drop ltrace-0.7.2-man.patch)