Projects
home:Eustace:branches:Eulaceura:Factory
ltrace
_service:obs_scm:ltrace-0.7.91-add-support-for-...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:obs_scm:ltrace-0.7.91-add-support-for-loongarch.patch of Package ltrace
From 8f52c1af112acbe5ebfd49f4d55e0017903ced89 Mon Sep 17 00:00:00 2001 From: Hui Li <lihui@loongson.cn> Date: Thu, 15 Dec 2022 07:39:41 +0800 Subject: [PATCH] ltrace-0.7.91: add support for loongarch Signed-off-by: Hui Li <lihui@loongson.cn> --- configure.ac | 3 + sysdeps/linux-gnu/Makefile.am | 4 +- sysdeps/linux-gnu/loongarch/Makefile.am | 25 + sysdeps/linux-gnu/loongarch/arch.h | 48 ++ sysdeps/linux-gnu/loongarch/fetch.c | 770 +++++++++++++++++++++++ sysdeps/linux-gnu/loongarch/plt.c | 58 ++ sysdeps/linux-gnu/loongarch/ptrace.h | 23 + sysdeps/linux-gnu/loongarch/regs.c | 61 ++ sysdeps/linux-gnu/loongarch/signalent.h | 52 ++ sysdeps/linux-gnu/loongarch/syscallent.h | 471 ++++++++++++++ sysdeps/linux-gnu/loongarch/trace.c | 274 ++++++++ 11 files changed, 1787 insertions(+), 2 deletions(-) create mode 100644 sysdeps/linux-gnu/loongarch/Makefile.am create mode 100644 sysdeps/linux-gnu/loongarch/arch.h create mode 100644 sysdeps/linux-gnu/loongarch/fetch.c create mode 100644 sysdeps/linux-gnu/loongarch/plt.c create mode 100644 sysdeps/linux-gnu/loongarch/ptrace.h create mode 100644 sysdeps/linux-gnu/loongarch/regs.c create mode 100644 sysdeps/linux-gnu/loongarch/signalent.h create mode 100644 sysdeps/linux-gnu/loongarch/syscallent.h create mode 100644 sysdeps/linux-gnu/loongarch/trace.c diff --git a/configure.ac b/configure.ac index 63fd950..205457e 100644 --- a/configure.ac +++ b/configure.ac @@ -43,6 +43,7 @@ case "${host_cpu}" in arm*|sa110) HOST_CPU="arm" ;; aarch64_be) HOST_CPU="aarch64" ;; cris*) HOST_CPU="cris" ;; + loongarch*) HOST_CPU="loongarch" ;; mips*) HOST_CPU="mips" ;; powerpc|powerpc64) HOST_CPU="ppc" ;; sun4u|sparc64) HOST_CPU="sparc" ;; @@ -216,6 +217,7 @@ if test x"$enable_libunwind" = xyes; then powerpc) UNWIND_ARCH="ppc32" ;; powerpc64) UNWIND_ARCH="ppc64" ;; mips*) UNWIND_ARCH="mips" ;; + loongarch*) UNWIND_ARCH="loongarch" ;; *) UNWIND_ARCH="${host_cpu}" ;; esac @@ -405,6 +407,7 @@ AC_CONFIG_FILES([ sysdeps/linux-gnu/arm/Makefile sysdeps/linux-gnu/cris/Makefile sysdeps/linux-gnu/ia64/Makefile + sysdeps/linux-gnu/loongarch/Makefile sysdeps/linux-gnu/m68k/Makefile sysdeps/linux-gnu/metag/Makefile sysdeps/linux-gnu/mips/Makefile diff --git a/sysdeps/linux-gnu/Makefile.am b/sysdeps/linux-gnu/Makefile.am index ec26162..7d17020 100644 --- a/sysdeps/linux-gnu/Makefile.am +++ b/sysdeps/linux-gnu/Makefile.am @@ -17,8 +17,8 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA -DIST_SUBDIRS = aarch64 alpha arm cris ia64 m68k metag mips ppc s390 \ - sparc x86 +DIST_SUBDIRS = aarch64 alpha arm cris ia64 loongarch m68k metag mips \ + ppc s390 sparc x86 SUBDIRS = \ $(HOST_CPU) diff --git a/sysdeps/linux-gnu/loongarch/Makefile.am b/sysdeps/linux-gnu/loongarch/Makefile.am new file mode 100644 index 0000000..f4da7cf --- /dev/null +++ b/sysdeps/linux-gnu/loongarch/Makefile.am @@ -0,0 +1,25 @@ +# This file is part of ltrace. +# Copyright (C) 2022-2023 Loongson Technology Corporation Limited. +# +# 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/loongarch/arch.h b/sysdeps/linux-gnu/loongarch/arch.h new file mode 100644 index 0000000..52c8b2a --- /dev/null +++ b/sysdeps/linux-gnu/loongarch/arch.h @@ -0,0 +1,48 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited. + * + * 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_LOONGARCH_ARCH_H +#define LTRACE_LOONGARCH_ARCH_H + + +/* | 31 15 | 14 0 | + * | 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 | code | */ +#define BREAKPOINT_VALUE { 0x00, 0x00, 0x2A, 0x00 } +#define BREAKPOINT_LENGTH 4 +#define DECR_PC_AFTER_BREAK 0 +#define ARCH_ENDIAN_LITTLE + +#define LT_ELFCLASS ELFCLASS64 +#define LT_ELF_MACHINE EM_LOONGARCH + +#define ARCH_HAVE_SIZEOF +#define ARCH_HAVE_ALIGNOF +#define ARCH_HAVE_ADD_PLT_ENTRY +#define ARCH_HAVE_SW_SINGLESTEP +#define ARCH_HAVE_FETCH_ARG +#define ARCH_HAVE_FETCH_PACK + +#define RLEN 8 +#define ARG_GAR_START 4 +#define ARG_GAR_END 11 +#define ARG_FAR_START 0 +#define ARG_FAR_END 7 + +#endif /* LTRACE_LOONGARCH_ARCH_H */ diff --git a/sysdeps/linux-gnu/loongarch/fetch.c b/sysdeps/linux-gnu/loongarch/fetch.c new file mode 100644 index 0000000..779e841 --- /dev/null +++ b/sysdeps/linux-gnu/loongarch/fetch.c @@ -0,0 +1,770 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited. + * + * 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 <sys/ptrace.h> +#include <asm/ptrace.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <linux/uio.h> +#include "fetch.h" +#include "proc.h" +#include "type.h" +#include "value.h" +#include "arch.h" +#include "expr.h" + +enum fetch_method { + FETCH_NOP, + FETCH_STACK, + FETCH_GAR, + FETCH_FAR, +}; + +struct small_struct_data_t { + char fixed_member; + char float_member; + bool first_member_is_float; +}; + +struct fetch_context { + struct user_pt_regs gregs; + struct user_fp_state fpregs; + unsigned int ngr; + unsigned int nfr; + arch_addr_t stack_pointer; + arch_addr_t retval; + bool in_varargs; +}; + +static int +loongarch_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; +} + +static int +loongarch_read_fregs(struct process *proc, struct user_fp_state *regs) +{ + *regs = (struct user_fp_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; +} + +static void +get_array_member(struct arg_type_info *info, + struct small_struct_data_t *small_struct) +{ + long len; + struct arg_type_info *array_type = info->u.array_info.elt_type; + expr_eval_constant(info->u.array_info.length, &len); + switch (array_type->type) { + case ARGTYPE_STRUCT: + break; + case ARGTYPE_FLOAT: + case ARGTYPE_DOUBLE: + small_struct->float_member += len; + break; + default: + if (small_struct->float_member > 0 + && small_struct->fixed_member == 0) + small_struct->first_member_is_float = true; + small_struct->fixed_member += len; + break; + } +} + +static void +get_struct_member(struct arg_type_info *info, + struct small_struct_data_t *small_struct) +{ + for (size_t i = 0; i < type_struct_size(info); i++) { + struct arg_type_info *field = type_struct_get(info, i); + assert(field != NULL); + switch (field->type) { + case ARGTYPE_STRUCT: + get_struct_member(field, small_struct); + break; + case ARGTYPE_ARRAY: + get_array_member(field, small_struct); + break; + case ARGTYPE_FLOAT: + case ARGTYPE_DOUBLE: + small_struct->float_member++; + break; + default: + if (small_struct->float_member > 0 + && small_struct->fixed_member == 0) + small_struct->first_member_is_float = true; + small_struct->fixed_member++; + break; + } + } +} + +static int +context_init(struct fetch_context *context, struct process *proc, + struct arg_type_info *ret_info) +{ + if (loongarch_read_gregs(proc, &context->gregs) < 0 + || loongarch_read_fregs(proc, &context->fpregs) < 0) + return -1; + + context->ngr = ARG_GAR_START; + context->nfr = ARG_FAR_START; + context->stack_pointer = (arch_addr_t)context->gregs.regs[3]; + context->retval = 0; + context->in_varargs = false; + + return 0; +} + +static int +fetch_gar(struct fetch_context *context, struct value *value, + size_t offset, size_t len) +{ + unsigned char *buf = value_get_raw_data(value); + unsigned long u = context->gregs.regs[context->ngr++]; + memcpy(buf + offset, &u, len); + + return 0; +} + +static int +fetch_far(struct fetch_context *context, struct value *value, + size_t offset, size_t len) +{ + unsigned char *buf = value_get_raw_data(value); + uint64_t u = context->fpregs.fpr[context->nfr++]; + memcpy(buf + offset, &u, len); + + 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; + uintptr_t sp = (uintptr_t) context->stack_pointer; + sp = ((sp + align - 1) / align) * align; + + value_in_inferior(value, (arch_addr_t) sp); + + sp += amount; + context->stack_pointer = (arch_addr_t) sp; + + return 0; +} + +static void +classify_struct_argument(struct fetch_context const *context, + struct small_struct_data_t small_struct, + enum fetch_method methods[], size_t sz) +{ + /* "big" structs are dealt with in arch_fetch_arg_init(). */ + if (RLEN < sz && sz <= 2 * RLEN) { + /* Only fixed-point members, the argument is passed in a + * pair of available GAR,with the low-order bits in the + * lower-numbered GAR and the high-order bits in the + * higher-numbered GAR. If only one GAR is available, the + * low-order bits are in the GAR and the high-order bits + * are on the stack, and passed on the stack if no GAR is + * available. */ + if (small_struct.fixed_member > 0 + && small_struct.float_member == 0) { + if (context->ngr < ARG_GAR_END) + methods[0] = methods[1] = FETCH_GAR; + else if (context->ngr == ARG_GAR_END) { + methods[0] = FETCH_GAR; + methods[1] = FETCH_STACK; + } + else + methods[0] = methods[1] = FETCH_STACK; + } else if (small_struct.fixed_member == 0 + && small_struct.float_member > 0) { + /* The structure has one long double member or one + * double member and two adjacent float members or + * 3-4 float members. The argument is passed in a + * pair of available GAR, with the low-order bits + * in the lower-numbered GAR and the high-order bits + * in the higher-numbered GAR. If only one GAR is + * available, the low-order bits are in the GAR and + * the high-order bits are on the stack, and passed + * on the stack if no GAR is available. */ + if (small_struct.float_member > 2) { + if (context->ngr < ARG_GAR_END) + methods[0] = methods[1] = FETCH_GAR; + else if (context->ngr == ARG_GAR_END) { + methods[0] = FETCH_GAR; + methods[1] = FETCH_STACK; + } + else + methods[0] = methods[1] = FETCH_STACK; + } + if (small_struct.float_member == 1) { + if (context->ngr < ARG_GAR_END) + methods[0] = methods[1] = FETCH_GAR; + else if (context->ngr == ARG_GAR_END) { + methods[0] = FETCH_GAR; + methods[1] = FETCH_STACK; + } + else + methods[0] = methods[1] = FETCH_STACK; + } else if (small_struct.float_member == 2) { + /* The structure with two double members is + * passed in a pair of available FARs. If no a + * pair of available FARs, it’s passed in GARs. + * If only one GAR is available, the low-order + * bits are in the GAR and the high-order bits + * are on the stack, and passed on the stack if + * no GAR available, structure with one double + * member and one float member is same. */ + if (context->nfr < ARG_FAR_END + && !context->in_varargs) { + methods[0] = methods[1] = FETCH_FAR; + } else { + if (context->ngr < ARG_GAR_END) + methods[0] = methods[1] = FETCH_GAR; + else if (context->ngr == ARG_GAR_END) { + methods[0] = FETCH_GAR; + methods[1] = FETCH_STACK; + } + else + methods[0] = methods[1] = FETCH_STACK; + } + } + } else if (small_struct.fixed_member > 0 + && small_struct.float_member > 0) { + /* The structure has one floating-point member and + * one fixed-point member. If one FAR and one GAR + * are available, the floating-point member of the + * structure is passed in the FAR, and the integer + * member of the structure is passed in the GAR; + * If no floating-point registers but two GARs are + * available, it’s passed in the two GARs; If only + * one GAR is available, the low-order bits are in + * the GAR and the high-order bits are on the stack; + * it’s passed on the stack if no GAR is available. */ + if (small_struct.fixed_member == 1 + && small_struct.float_member == 1) { + if (context->nfr <= ARG_FAR_END + && context->nfr <= ARG_FAR_END + && !context->in_varargs) { + if (small_struct.first_member_is_float) { + methods[0] = FETCH_FAR; + methods[1] = FETCH_GAR; + } else { + methods[0] = FETCH_GAR; + methods[1] = FETCH_FAR; + } + } else { + if (context->ngr < ARG_GAR_END) + methods[0] = methods[1] = FETCH_GAR; + else if (context->ngr == ARG_GAR_END) { + methods[0] = FETCH_GAR; + methods[1] = FETCH_STACK; + } + else + methods[0] = methods[1] = FETCH_STACK; + } + } else { + /* Others, the argument is passed in a pair of + * available GAR, with the low-order bits in the + * lower-numbered GAR and the high-order bits in + * the higher-numbered GAR. If only one GAR is + * available, the low-order bits are in the GAR + * and the high-order bits are on the stack, and + * passed on the stack if no GAR is available. */ + if (context->ngr < ARG_GAR_END) { + methods[0] = methods[1] = FETCH_GAR; + } + else if (context->ngr == ARG_GAR_END) { + methods[0] = FETCH_GAR; + methods[1] = FETCH_STACK; + } + else + methods[0] = methods[1] = FETCH_STACK; + } + } + } else if (sz <= RLEN) { + /* The structure has only fixed-point members. If there + * is an available GAR, the structure is passed through + * the GAR by value passing; If no GAR is available, + * it’s passed on the stack. */ + if (small_struct.fixed_member > 0 + && small_struct.float_member == 0) { + if (context->ngr <= ARG_GAR_END) + methods[0] = FETCH_GAR; + else + methods[0] = FETCH_STACK; + } else if (small_struct.fixed_member == 0 + && small_struct.float_member > 0) { + /* One floating-point member. The argument is passed + * in a FAR; If no FAR is available, the value is + * passed in a GAR; if no GAR is available, the value + * is passed on the stack. */ + if (small_struct.float_member == 1) { + if (context->nfr <= ARG_FAR_END + && !context->in_varargs) { + methods[0] = FETCH_FAR; + } else { + if (context->ngr <= ARG_GAR_END) + methods[0] = FETCH_GAR; + else + methods[0] = FETCH_STACK; + } + } else if (small_struct.float_member == 2) { + /* Two floating-point members. argument is + * passed in a pair of available FAR, with + * the low-order float member bits in the + * lower-numbered FAR and the high-order + * float member bits in the higher-numbered + * FAR. If the number of available FAR is + * less than 2, it’s passed in a GAR, and + * passed on stack if no GAR available. */ + if (context->nfr < ARG_FAR_END + && !context->in_varargs) { + methods[0] = methods[1] = FETCH_FAR; + } else { + if (context->ngr <= ARG_GAR_END) + methods[0] = FETCH_GAR; + else + methods[0] = FETCH_STACK; + } + } + } else if (small_struct.fixed_member > 0 + && small_struct.float_member == 1) { + /* Multiple fixed-point members. If there are + * available GAR, the structure passed in a GAR, + * and passed on the stack if no GAR is available. */ + if (small_struct.fixed_member > 1) { + if (context->ngr <= ARG_GAR_END) + methods[0] = FETCH_GAR; + else + methods[0] = FETCH_STACK; + } else if (small_struct.fixed_member == 1) { + /* Only one fixed-point member. If one FAR + * and one GAR are available, floating-point + * member of the structure is passed in FAR, + * and the integer member is passed in GAR; + * If no floating-point register but one GAR + * is available, it’s passed in GAR; If no + * GAR is available, it’s passed on stack. */ + if (context->nfr <= ARG_FAR_END + && context->nfr <= ARG_FAR_END + && !context->in_varargs) { + if (small_struct.first_member_is_float) { + methods[0] = FETCH_FAR; + methods[1] = FETCH_GAR; + } else { + methods[0] = FETCH_FAR; + methods[1] = FETCH_GAR; + } + } else { + if (context->ngr <= ARG_GAR_END) + methods[0] = FETCH_GAR; + else + methods[0] = FETCH_STACK; + } + } + } + } +} + +static int +classify_argument(struct fetch_context const *context, + struct process *proc, struct arg_type_info *info, + enum fetch_method methods[]) +{ + struct small_struct_data_t small_struct = {0, 0, false}; + size_t sz = type_sizeof(proc, info); + if (sz == (size_t) -1) + return -1; + + switch (info->type) { + case ARGTYPE_VOID: + return -1; + + case ARGTYPE_STRUCT: + get_struct_member (info, &small_struct); + classify_struct_argument(context, small_struct, methods, sz); + return 0; + case ARGTYPE_POINTER: + case ARGTYPE_ARRAY: + case ARGTYPE_INT: + case ARGTYPE_UINT: + case ARGTYPE_LONG: + case ARGTYPE_ULONG: + case ARGTYPE_CHAR: + case ARGTYPE_SHORT: + case ARGTYPE_USHORT: + if (context->ngr <= ARG_GAR_END) + methods[0] = FETCH_GAR; + else + methods[0] = FETCH_STACK; + return 0; + case ARGTYPE_FLOAT: + case ARGTYPE_DOUBLE: + if (context->nfr <= ARG_FAR_END && !context->in_varargs) + methods[0] = FETCH_FAR; + else if (context->ngr <= ARG_GAR_END) + methods[0] = FETCH_GAR; + else + methods[0] = FETCH_STACK; + return 0; + } + + assert(!"Failed to classify argument."); + abort(); +} + + +static int +classify_return_value(struct fetch_context const *context, + struct process *proc, struct arg_type_info *info, + enum fetch_method methods[]) +{ + struct small_struct_data_t small_struct = {0, 0, false}; + size_t sz = type_sizeof(proc, info); + if (sz == (size_t) -1) + return -1; + + switch (info->type) { + case ARGTYPE_VOID: + return 0; + case ARGTYPE_STRUCT: + get_struct_member (info, &small_struct); + /* sz <= RLEN */ + if (sz <= RLEN) { + /* The structure has only fixed-point members. + * passed on $v0. */ + if (small_struct.fixed_member > 0 + && small_struct.float_member == 0) + methods[0] = FETCH_GAR; + /* The structure has only floating-point members. */ + else if (small_struct.fixed_member == 0 + && small_struct.float_member > 0) { + /* One floating-point member. passed on $fv0 */ + if (small_struct.float_member == 1) + methods[0] = FETCH_FAR; + /* Two floating-point members. passed on $fv0 + * and $fv1 */ + else if (small_struct.float_member == 2) { + methods[0] = FETCH_FAR; + methods[1] = FETCH_FAR; + } + } + /* The structure has both fixed-point and floating + * point members */ + else if (small_struct.fixed_member > 0 + && small_struct.float_member == 1) { + /* Multiple fixed-point members. passed on + * $v0. */ + if (small_struct.fixed_member > 1) + methods[0] = FETCH_GAR; + /* Only one fixed-point member. float-point + * member is passed on $fv0, fixed-point member + * is passed on $v0. */ + else if (small_struct.fixed_member == 1) { + if (small_struct.first_member_is_float) { + methods[0] = FETCH_FAR; + methods[1] = FETCH_GAR; + } else { + methods[0] = FETCH_GAR; + methods[1] = FETCH_FAR; + } + } + } + } + /* RLEN < sz && sz <= 2 * RLEN */ + else if (RLEN < sz && sz <= 2 * RLEN) { + /* Only fixed-point members, passed on $v0 and $v1 */ + if (small_struct.fixed_member > 0 + && small_struct.float_member == 0) { + methods[0] = FETCH_GAR; + methods[1] = FETCH_GAR; + } + /* Only floating-point members. */ + else if (small_struct.fixed_member == 0 + && small_struct.float_member > 0) { + /* The structure has one long double member + * or one double member and two adjacent + * float members or 3-4 float members. passed + * on $v0 and $v1. */ + if (small_struct.float_member == 1 + || small_struct.float_member > 2) { + methods[0] = FETCH_GAR; + methods[1] = FETCH_GAR; + } + /* The structure two double member, passed on + * $fv0 and $fv1. */ + if (small_struct.float_member == 2) { + methods[0] = FETCH_FAR; + methods[1] = FETCH_FAR; + } + } + /* Both fixed-point and floating-point members. */ + else if (small_struct.fixed_member > 0 + && small_struct.float_member > 0) { + /* The structure has one floating-point member + * and one fixed-point member. float-point + * member is passed on $fv0, fixed-point member + * is passed on $v0.*/ + if (small_struct.fixed_member == 1 + && small_struct.float_member == 1) { + if (small_struct.first_member_is_float) { + methods[0] = FETCH_FAR; + methods[1] = FETCH_GAR; + } else { + methods[0] = FETCH_GAR; + methods[1] = FETCH_FAR; + } + } + /* Others, passed on $v0 and $v1. */ + else { + methods[0] = FETCH_GAR; + methods[1] = FETCH_GAR; + } + } + } + return 0; + case ARGTYPE_POINTER: + case ARGTYPE_ARRAY: + case ARGTYPE_INT: + case ARGTYPE_UINT: + case ARGTYPE_LONG: + case ARGTYPE_ULONG: + case ARGTYPE_CHAR: + case ARGTYPE_SHORT: + case ARGTYPE_USHORT: + methods[0] = FETCH_GAR; + return 0; + case ARGTYPE_FLOAT: + case ARGTYPE_DOUBLE: + methods[0] = FETCH_FAR; + return 0; + } + + assert(!"Failed to classify retval."); + abort(); +} + +static int +fetch_argument(struct fetch_context *context, + struct process *proc, struct arg_type_info *info, + struct value *value, enum fetch_method method, + size_t offset, size_t len) +{ + switch (method) { + case FETCH_NOP: + return 0; + + case FETCH_STACK: + return fetch_stack(context, value, RLEN, RLEN); + + case FETCH_GAR: + return fetch_gar(context, value, offset, len); + + case FETCH_FAR: + return fetch_far(context, value, offset, len); + + } + + assert(!"Don't know how to fetch argument."); + abort(); +} + +static int +fetch_return_value(struct fetch_context *context, struct process *proc, + struct arg_type_info *info, struct value *value, + enum fetch_method method, size_t offset, size_t len) +{ + + switch (method) { + case FETCH_NOP: + return 0; + case FETCH_STACK: + return 0; + + case FETCH_GAR: + return fetch_gar(context, value, offset, len); + + case FETCH_FAR: + return fetch_far(context, value, offset, len); + + } + + assert(!"Don't know how to fetch retval."); + abort(); +} + +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)); +} + +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, ret_info) < 0) { +fail: + free(context); + return NULL; + } + + size_t sz = type_sizeof(proc, ret_info); + if (sz == (size_t) -1) + goto fail; + + if (sz > 2 * RLEN) { + /* The reference of the return value is stored in GAR a0 + * if the size of return value is larger than 2*GRLEN bits */ + context->retval = (arch_addr_t) context->gregs.regs[context->ngr++]; + } + + 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) +{ + enum fetch_method methods[2] = {FETCH_NOP, FETCH_NOP}; + size_t len = RLEN; + size_t sz = type_sizeof(proc, info); + if (sz == (size_t) -1) + return -1; + if (sz > 2 * RLEN) { + sz = 8; + value_pass_by_reference(value); + if (context->ngr <= ARG_GAR_END) + methods[0] = FETCH_GAR; + else + methods[0] = FETCH_STACK; + } else { + if (classify_argument(context, proc, info, methods) != 0) + return -1; + } + + if (value_reserve(value, sz) == NULL) + return -1; + + if (methods[1] == FETCH_NOP) { + fetch_argument(context, proc, info, value, methods[0], 0, RLEN); + } else { + if (sz <= RLEN) + len = RLEN / 2; + + fetch_argument(context, proc, info, value, methods[0], 0, len); + fetch_argument(context, proc, info, value, methods[1], len, len); + } + + return 0; +} + +int +arch_fetch_retval(struct fetch_context *context, enum tof type, + struct process *proc, struct arg_type_info *info, + struct value *value) +{ + size_t len = RLEN; + size_t sz = type_sizeof(proc, info); + if (sz == (size_t) -1) + return -1; + + if (type == LT_TOF_FUNCTIONR) { + enum fetch_method methods[2] = {FETCH_NOP, FETCH_NOP}; + if (context->retval != 0) { + /* return value is larger than 2*GRLEN + * was extracted when in fetch init. */ + value_in_inferior(value, context->retval); + return 0; + } + + if (context_init(context, proc, info) < 0) + return -1; + + if (classify_return_value(context, proc, info, methods) != 0) + return -1; + + if (value_reserve(value, sz) == NULL) + return -1; + + if (methods[1] == FETCH_NOP) { + fetch_return_value(context, proc, info, value, + methods[0], 0, RLEN); + } else { + if (sz <= RLEN) + len = RLEN / 2; + + fetch_return_value(context, proc, info, value, + methods[0], 0, len); + fetch_return_value(context, proc, info, value, + methods[1], len, len); + } + + } + /* SYSCALLR,return value in GAR a0 */ + else if (type == LT_TOF_SYSCALLR) + value_in_inferior(value, (arch_addr_t) context->gregs.regs[4]); + + return 0; +} + +void +arch_fetch_arg_done(struct fetch_context *context) +{ + if (context != NULL) + 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/loongarch/plt.c b/sysdeps/linux-gnu/loongarch/plt.c new file mode 100644 index 0000000..4dd9235 --- /dev/null +++ b/sysdeps/linux-gnu/loongarch/plt.c @@ -0,0 +1,58 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited. + * + * 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 <gelf.h> +#include <stdbool.h> +#include "backend.h" +#include "proc.h" +#include "library.h" +#include "ltrace-elf.h" +#include "trace.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; +} + + +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_LARCH_IRELATIVE + bool irelative = GELF_R_TYPE(rela->r_info) == R_LARCH_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/sysdeps/linux-gnu/loongarch/ptrace.h b/sysdeps/linux-gnu/loongarch/ptrace.h new file mode 100644 index 0000000..3685186 --- /dev/null +++ b/sysdeps/linux-gnu/loongarch/ptrace.h @@ -0,0 +1,23 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited. + * + * 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 <sys/ptrace.h> +#include <asm/ptrace.h> +#include <sys/user.h> diff --git a/sysdeps/linux-gnu/loongarch/regs.c b/sysdeps/linux-gnu/loongarch/regs.c new file mode 100644 index 0000000..304b2b4 --- /dev/null +++ b/sysdeps/linux-gnu/loongarch/regs.c @@ -0,0 +1,61 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited. + * + * 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 "config.h" +#include <stddef.h> +#include <sys/types.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> +#include <linux/uio.h> +#include "proc.h" +#include "common.h" +#include "backend.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void * +get_instruction_pointer(struct process *proc) +{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, PC, 0); +} + +void +set_instruction_pointer(struct process *proc, void *addr) +{ + ptrace(PTRACE_POKEUSER, proc->pid, PC, addr); +} + +void * +get_stack_pointer(struct process *proc) +{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, GPR_BASE + 3, 0); +} + +void * +get_return_addr(struct process *proc, void *stack_pointer) +{ + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, GPR_BASE + 1, 0); +} diff --git a/sysdeps/linux-gnu/loongarch/signalent.h b/sysdeps/linux-gnu/loongarch/signalent.h new file mode 100644 index 0000000..4c0a466 --- /dev/null +++ b/sysdeps/linux-gnu/loongarch/signalent.h @@ -0,0 +1,52 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited. + * + * 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/loongarch/syscallent.h b/sysdeps/linux-gnu/loongarch/syscallent.h new file mode 100644 index 0000000..4d50cad --- /dev/null +++ b/sysdeps/linux-gnu/loongarch/syscallent.h @@ -0,0 +1,471 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited. + * + * 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 */ + "sendfile64", /* 71 */ + "pselect6", /* 72 */ + "ppoll", /* 73 */ + "signalfd4", /* 74 */ + "vmsplice", /* 75 */ + "splice", /* 76 */ + "tee", /* 77 */ + "readlinkat", /* 78 */ + "79", /* 79 */ + "80", /* 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_64", /* 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 */ + "sched_setattr", /* 274 */ + "sched_getattr", /* 275 */ + "renameat2", /* 276 */ + "seccomp", /* 277 */ + "getrandom", /* 278 */ + "memfd_create", /* 279 */ + "bpf", /* 280 */ + "execveat", /* 281 */ + "userfaultfd", /* 282 */ + "membarrier", /* 283 */ + "mlock2", /* 284 */ + "copy_file_range", /* 285 */ + "preadv2", /* 286 */ + "pwritev2", /* 287 */ + "pkey_mprotect", /* 288 */ + "pkey_alloc", /* 289 */ + "pkey_free", /* 290 */ + "statx", /* 291 */ + "io_pgetevents", /* 292 */ + "rseq", /* 293 */ + "kexec_file_load", /* 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 */ + "pidfd_send_signal", /* 424 */ + "io_uring_setup", /* 425 */ + "io_uring_enter", /* 426 */ + "io_uring_register", /* 427 */ + "open_tree", /* 428 */ + "move_mount", /* 429 */ + "fsopen", /* 430 */ + "fsconfig", /* 431 */ + "fsmount", /* 432 */ + "fspick", /* 433 */ + "pidfd_open", /* 434 */ + "clone3", /* 435 */ + "close_range", /* 436 */ + "openat2", /* 437 */ + "pidfd_getfd", /* 438 */ + "faccessat2", /* 439 */ + "process_madvise", /* 440 */ + "epoll_pwait2", /* 441 */ + "mount_setattr", /* 442 */ + "quotactl_fd", /* 443 */ + "landlock_create_ruleset", /* 444 */ + "landlock_add_rule", /* 445 */ + "landlock_restrict_self", /* 446 */ + "memfd_secret", /* 447 */ + "process_mrelease", /* 448 */ + "futex_waitv", /* 449 */ + "set_mempolicy_home_node", /* 450 */ diff --git a/sysdeps/linux-gnu/loongarch/trace.c b/sysdeps/linux-gnu/loongarch/trace.c new file mode 100644 index 0000000..e0bf634 --- /dev/null +++ b/sysdeps/linux-gnu/loongarch/trace.c @@ -0,0 +1,274 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited. + * + * 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 <sys/ptrace.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <asm/ptrace.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include "fetch.h" +#include "backend.h" +#include "proc.h" +#include "type.h" +#include "debug.h" + +#define BRANCH_MASK 0xfc000000 +#define OPCODE_JIRL 0x4c000000 +#define OPCODE_B 0x50000000 +#define OPCODE_BL 0x54000000 +#define OPCODE_BEQ 0x58000000 +#define OPCODE_BNE 0x5c000000 +#define OPCODE_BLT 0x60000000 +#define OPCODE_BGE 0x64000000 +#define OPCODE_BLTU 0x68000000 +#define OPCODE_BGEU 0x6c000000 +#define OPCODE_BEQZ 0x40000000 +#define OPCODE_BNEZ 0x44000000 + +void +get_arch_dep(struct process *proc) +{ + +} + +/* Sign-extend the number in the bottom B bits of X to a 64-bit integer. + * Requires 0 < B < 64 */ +static inline int64_t sign_extend64(uint64_t X, unsigned B) +{ + assert(B > 0 && "Bit width can't be 0."); + assert(B <= 64 && "Bit width out of range."); + return (int64_t)(X << (64 - B)) >> (64 - B); +} + +/* Return the bit field(s) from the most significant bit (msbit) to the + * least significant bit (lsbit) of a 32-bit unsigned value. */ +static inline uint32_t bits32(const uint32_t bits, const uint32_t msbit, + const uint32_t lsbit) +{ + assert(msbit < 32 && lsbit <= msbit); + return (bits >> lsbit) & ((1u << (msbit - lsbit + 1)) - 1); +} + +static int +loongarch_get_next_pcs(struct process *proc, + arch_addr_t pc, arch_addr_t next_pcs[2]) +{ + uint32_t insn; + uint32_t op; + uint32_t rj, imm; + int64_t rj_value, signext_imm; + int nr = 0; + + insn = (uint32_t)ptrace(PTRACE_PEEKTEXT, proc->pid, pc, 0); + op = insn & BRANCH_MASK; + + switch (op) { + case OPCODE_JIRL: + rj = bits32(insn, 9, 5); + rj_value = ptrace(PTRACE_PEEKUSER, proc->pid, rj, 0); + imm = bits32(insn, 25, 10); + signext_imm = sign_extend64(imm << 2, 18); + next_pcs[nr++] = (arch_addr_t)(rj_value + signext_imm); + next_pcs[nr++] = pc + 4; + break; + case OPCODE_B: + case OPCODE_BL: + imm = bits32(insn, 25, 10) + (bits32(insn, 9, 0) << 16); + signext_imm = sign_extend64(imm << 2, 28); + next_pcs[nr++] = pc + signext_imm; + next_pcs[nr++] = pc + 4; + break; + case OPCODE_BEQ: + case OPCODE_BNE: + case OPCODE_BLT: + case OPCODE_BGE: + case OPCODE_BLTU: + case OPCODE_BGEU: + imm = bits32(insn, 25, 10); + signext_imm = sign_extend64(imm << 2, 18); + next_pcs[nr++] = pc + signext_imm; + next_pcs[nr++] = pc + 4; + break; + case OPCODE_BEQZ: + case OPCODE_BNEZ: + imm = bits32(insn, 25, 10) + (bits32(insn, 4, 0) << 16); + signext_imm = sign_extend64(imm << 2, 23); + next_pcs[nr++] = pc + signext_imm; + next_pcs[nr++] = pc + 4; + break; + default: + next_pcs[nr++] = pc + 4; + break; + } + if (nr <= 0 || nr > 2) + goto fail; + if (nr == 2) { + if (next_pcs[1] == 0) + goto fail; + } + if (next_pcs[0] == 0) + goto fail; + + assert(nr == 1 || nr == 2); + return nr; + +fail: + printf("nr=%d pc=%p\n", nr, pc); + printf("next_pcs=%p %p\n", next_pcs[0], next_pcs[1]); + + return nr; + +} + +enum sw_singlestep_status +arch_sw_singlestep(struct process *proc, struct breakpoint *sbp, + int (*add_cb)(arch_addr_t, struct sw_singlestep_data *), + struct sw_singlestep_data *add_cb_data) +{ + int nr; + arch_addr_t next_pcs[2] = {}; + arch_addr_t pc = get_instruction_pointer(proc); + nr = loongarch_get_next_pcs(proc, pc, next_pcs); + + while (nr-- > 0) { + arch_addr_t baddr = next_pcs[nr]; + if (DICT_HAS_KEY(proc->leader->breakpoints, &baddr)) { + fprintf(stderr, "skip %p %p\n", baddr, add_cb_data); + continue; + } + + if (add_cb(baddr, add_cb_data) < 0) + return SWS_FAIL; + } + debug(1, "PTRACE_CONT"); + ptrace(PTRACE_CONT, proc->pid, 0, 0); + return SWS_OK; +} + +int +syscall_p(struct process *proc, int status, int *sysnum) +{ + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + struct callstack_element *elem = NULL; + if (proc->callstack_depth > 0) + elem = proc->callstack + proc->callstack_depth - 1; + /* sysnum in $a7(r11) on loongarch */ + long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, + GPR_BASE + 11, 0); + if (ret == -1) { + if (errno) + return -1; + } + + *sysnum = ret; + + if (elem != NULL && elem->is_syscall + && elem->c_un.syscall == *sysnum) + return 2; + + if (*sysnum >= 0) + return 1; + } + return 0; +} + +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: + return 4; + + case ARGTYPE_LONG: + case ARGTYPE_ULONG: + case ARGTYPE_POINTER: + return 8; + + 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) +{ + if (proc == NULL) + return (size_t)-2; + + switch (info->type) { + + case ARGTYPE_CHAR: + return 1; + + case ARGTYPE_SHORT: + case ARGTYPE_USHORT: + return 2; + + case ARGTYPE_INT: + case ARGTYPE_UINT: + return 4; + + case ARGTYPE_LONG: + case ARGTYPE_ULONG: + case ARGTYPE_POINTER: + return 8; + + 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(); + } +} -- 2.37.1
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2