| From 5f6dfafb80bdc2566fe91d5fde96769175fabf35 Mon Sep 17 00:00:00 2001 |
| From: Faraz Shahbazker <faraz.shahbazker@imgtec.com> |
| Date: Sat, 1 Jul 2017 10:56:59 -0700 |
| Subject: [PATCH] Add support for mips64 n32/n64 |
| |
| Signed-off-by: Khem Raj <raj.khem@gmail.com> |
| --- |
| Upstream-Status: Pending |
| Source: http://lists.alioth.debian.org/pipermail/ltrace-devel/2015-May/001327.html |
| |
| backend.h | 8 + |
| ltrace-elf.c | 12 ++ |
| proc.h | 1 + |
| sysdeps/linux-gnu/mips/Makefile.am | 1 + |
| sysdeps/linux-gnu/mips/abi.c | 64 +++++++ |
| sysdeps/linux-gnu/mips/arch.h | 24 ++- |
| sysdeps/linux-gnu/mips/plt.c | 68 ++++++-- |
| sysdeps/linux-gnu/mips/signalent1.h | 52 ++++++ |
| sysdeps/linux-gnu/mips/syscallent1.h | 328 +++++++++++++++++++++++++++++++++++ |
| sysdeps/linux-gnu/mips/trace.c | 241 ++++++++++++++++++------- |
| sysdeps/linux-gnu/mksyscallent_mips | 9 +- |
| 11 files changed, 728 insertions(+), 80 deletions(-) |
| create mode 100644 sysdeps/linux-gnu/mips/abi.c |
| create mode 100644 sysdeps/linux-gnu/mips/signalent1.h |
| create mode 100644 sysdeps/linux-gnu/mips/syscallent1.h |
| |
| diff --git a/backend.h b/backend.h |
| index e25daa0..0d6926a 100644 |
| --- a/backend.h |
| +++ b/backend.h |
| @@ -314,6 +314,14 @@ int arch_process_exec(struct process *proc); |
| int arch_get_sym_info(struct ltelf *lte, const char *filename, size_t sym_index, |
| GElf_Rela *rela, GElf_Sym *sym); |
| |
| +/* The following callback has to be implemented in backend if arch.h |
| + * defines ARCH_HAVE_GET_ABI |
| + * |
| + * This is called from read_module just once, when reading the main module. |
| + * The value returned is an architecture specific ID for the current ABI |
| + * to be used later for ABI-specific operations. */ |
| +char arch_get_abi(GElf_Ehdr ehdr); |
| + |
| enum plt_status { |
| PLT_FAIL, |
| PLT_OK, |
| diff --git a/ltrace-elf.c b/ltrace-elf.c |
| index f439cb0..a85edca 100644 |
| --- a/ltrace-elf.c |
| +++ b/ltrace-elf.c |
| @@ -1131,6 +1131,14 @@ populate_symtab(struct process *proc, const char *filename, |
| only_exported_names); |
| } |
| |
| +#ifndef ARCH_HAVE_GET_ABI |
| +char |
| +arch_get_abi(GElf_Ehdr ehdr) |
| +{ |
| + return 0; |
| +} |
| +#endif |
| + |
| static int |
| read_module(struct library *lib, struct process *proc, |
| const char *filename, GElf_Addr bias, int main) |
| @@ -1151,6 +1159,10 @@ read_module(struct library *lib, struct process *proc, |
| * with 32-bit ltrace. It is desirable to preserve this. */ |
| proc->e_machine = lte.ehdr.e_machine; |
| proc->e_class = lte.ehdr.e_ident[EI_CLASS]; |
| + /* Another candidate for the ABI module. We probably |
| + * want to do all of the e_* stuff only once, for main */ |
| + if (main) |
| + proc->e_abi = arch_get_abi(lte.ehdr); |
| get_arch_dep(proc); |
| |
| /* Find out the base address. For PIE main binaries we look |
| diff --git a/proc.h b/proc.h |
| index a611456..00094e1 100644 |
| --- a/proc.h |
| +++ b/proc.h |
| @@ -117,6 +117,7 @@ struct process { |
| * nauseam. */ |
| short e_machine; |
| char e_class; |
| + char e_abi; |
| |
| #if defined(HAVE_LIBDW) |
| /* Unwind info for leader, NULL for non-leader procs. */ |
| diff --git a/sysdeps/linux-gnu/mips/Makefile.am b/sysdeps/linux-gnu/mips/Makefile.am |
| index 1fd8c2a..571ee0d 100644 |
| --- a/sysdeps/linux-gnu/mips/Makefile.am |
| +++ b/sysdeps/linux-gnu/mips/Makefile.am |
| @@ -20,6 +20,7 @@ noinst_LTLIBRARIES = \ |
| ../libcpu.la |
| |
| ___libcpu_la_SOURCES = \ |
| + abi.c \ |
| plt.c \ |
| regs.c \ |
| trace.c |
| diff --git a/sysdeps/linux-gnu/mips/abi.c b/sysdeps/linux-gnu/mips/abi.c |
| new file mode 100644 |
| index 0000000..64e3c10 |
| --- /dev/null |
| +++ b/sysdeps/linux-gnu/mips/abi.c |
| @@ -0,0 +1,64 @@ |
| +/* |
| + * This file is part of ltrace. |
| + * Copyright (C) 2015 Imagination Technologies 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 <stdio.h> |
| +#include <gelf.h> |
| +#include "arch.h" |
| + |
| +/* |
| + * There is no bit in the header-flags to mark N64 ABI, it must be |
| + * determined by exclusion of other ABIs. The following values are |
| + * from elfcpp/mips.h in binutils sources |
| + */ |
| +enum |
| +{ |
| + E_MIPS_ABI_MASK = 0x0000F000, |
| + E_MIPS_ABI_N32 = 0x00000020, |
| + E_MIPS_ABI_O32 = 0x00001000, |
| + E_MIPS_ABI_O64 = 0x00002000, |
| + E_MIPS_ABI_EABI32 = 0x00003000, |
| + E_MIPS_ABI_EABI64 = 0x00004000, |
| +}; |
| + |
| +char |
| +arch_get_abi(GElf_Ehdr ehdr) |
| +{ |
| + enum mips_abi_type abi; |
| + switch (ehdr.e_flags & E_MIPS_ABI_MASK) { |
| + case E_MIPS_ABI_O32: |
| + abi = ABI_O32; break; |
| + case E_MIPS_ABI_O64: |
| + abi = ABI_O64; break; |
| + case E_MIPS_ABI_EABI32: |
| + case E_MIPS_ABI_EABI64: |
| + fprintf(stderr, "%s: MIPS EABI is not supported\n", __func__); |
| + abi = -1; |
| + break; |
| + default: |
| + if (ehdr.e_flags & E_MIPS_ABI_N32) |
| + abi = ABI_N32; |
| + else |
| + abi = ABI_N64; |
| + } |
| + |
| + return abi; |
| +} |
| + |
| +/**@}*/ |
| diff --git a/sysdeps/linux-gnu/mips/arch.h b/sysdeps/linux-gnu/mips/arch.h |
| index 16273d2..8b75df2 100644 |
| --- a/sysdeps/linux-gnu/mips/arch.h |
| +++ b/sysdeps/linux-gnu/mips/arch.h |
| @@ -1,5 +1,6 @@ |
| /* |
| * This file is part of ltrace. |
| + * Copyright (C) 2015 Imagination Technologies Limited |
| * Copyright (C) 2013,2014 Petr Machata, Red Hat Inc. |
| * Copyright (C) 2006 Eric Vaitl |
| * |
| @@ -38,8 +39,12 @@ |
| #define BREAKPOINT_LENGTH 4 |
| #define DECR_PC_AFTER_BREAK 0 |
| |
| -#define LT_ELFCLASS ELFCLASS32 |
| +#ifdef __LP64__ |
| +#define LT_ELFCLASS ELFCLASS64 |
| #define LT_ELF_MACHINE EM_MIPS |
| +#endif /* __LP64__ */ |
| +#define LT_ELFCLASS2 ELFCLASS32 |
| +#define LT_ELF_MACHINE2 EM_MIPS |
| |
| #define ARCH_HAVE_LTELF_DATA |
| struct arch_ltelf_data { |
| @@ -53,8 +58,14 @@ struct arch_ltelf_data { |
| #define ARCH_HAVE_ADD_PLT_ENTRY |
| #define ARCH_HAVE_SW_SINGLESTEP |
| #define ARCH_HAVE_SYMBOL_RET |
| - |
| +#define ARCH_HAVE_GET_ABI |
| #define ARCH_HAVE_LIBRARY_SYMBOL_DATA |
| + |
| +#ifdef __LP64__ |
| +#define ARCH_HAVE_SIZEOF |
| +#define ARCH_HAVE_ALIGNOF |
| +#endif /* __LP64__ */ |
| + |
| enum mips_plt_type |
| { |
| /* A symbol has associated PLT entry. */ |
| @@ -73,7 +84,14 @@ enum mips_plt_type |
| MIPS_PLT_NEED_UNRESOLVE, |
| }; |
| |
| -struct mips_unresolve_data; |
| +enum mips_abi_type |
| +{ |
| + ABI_O32, |
| + ABI_N32, |
| + ABI_N64, |
| + ABI_O64, |
| +}; |
| + |
| struct arch_library_symbol_data { |
| enum mips_plt_type type; |
| union { |
| diff --git a/sysdeps/linux-gnu/mips/plt.c b/sysdeps/linux-gnu/mips/plt.c |
| index f3c12da..2d85ad9 100644 |
| --- a/sysdeps/linux-gnu/mips/plt.c |
| +++ b/sysdeps/linux-gnu/mips/plt.c |
| @@ -1,5 +1,6 @@ |
| /* |
| * This file is part of ltrace. |
| + * Copyright (C) 2015 Imagination Technologies Limited |
| * Copyright (C) 2012,2013,2014 Petr Machata, Red Hat Inc. |
| * Copyright (C) 2012 Edgar E. Iglesias, Axis Communications |
| * Copyright (C) 2008,2009 Juan Cespedes |
| @@ -182,6 +183,11 @@ arch_find_dl_debug(struct process *proc, arch_addr_t dyn_addr, |
| { |
| arch_addr_t rld_addr; |
| int r; |
| +#ifdef __LP64__ |
| + size_t addrsize = proc->mask_32bit ? 4 : (sizeof *ret); |
| +#else /* !__LP64__ */ |
| + size_t addrsize = sizeof *ret; |
| +#endif /* !__LP64__ */ |
| |
| /* MIPS puts the address of the r_debug structure into the |
| * DT_MIPS_RLD_MAP entry instead of into the DT_DEBUG entry. */ |
| @@ -189,7 +195,7 @@ arch_find_dl_debug(struct process *proc, arch_addr_t dyn_addr, |
| DT_MIPS_RLD_MAP, &rld_addr); |
| if (r == 0) { |
| if (umovebytes(proc, rld_addr, |
| - ret, sizeof *ret) != sizeof *ret) { |
| + ret, addrsize) != addrsize) { |
| r = -1; |
| } |
| } |
| @@ -295,14 +301,25 @@ arch_elf_init(struct ltelf *lte, struct library *lib) |
| |
| for (j = 0; j < data->d_size / 16; ++j) { |
| uint32_t insn; |
| + int got_size = 4; |
| + uint32_t load_inst = 0x24180000U; /* addui t8,0,xx */ |
| + |
| +#ifdef __LP64__ |
| + if (arch_get_abi(lte->ehdr) == ABI_N64 |
| + || arch_get_abi(lte->ehdr) == ABI_O64) { |
| + got_size = 8; |
| + load_inst = 0x64180000U; /* daddui t8,0,xx */ |
| + } |
| +#endif /* __LP64__ */ |
| + |
| if (elf_read_u32(data, j * 16 + 12, &insn) < 0) |
| goto fail_stubs; |
| |
| if (insn == 0) |
| continue; |
| |
| - /* 0x2418XXXX encodes lbu 0,t8,XXXX or li t8,XXXX. */ |
| - if ((insn & 0xffff0000U) != 0x24180000U) |
| + /* 0x[62]418XXXX encodes [d]addiu t8, 0, XXXX. */ |
| + if ((insn & 0xffff0000U) != load_inst) |
| goto fail_stubs; |
| |
| unsigned idx = insn & 0xffff; |
| @@ -323,8 +340,8 @@ arch_elf_init(struct ltelf *lte, struct library *lib) |
| + lte->arch.mips_local_gotno; |
| /* XXX Double cast. */ |
| arch_addr_t got_entry_addr |
| - = (arch_addr_t) (uintptr_t) lte->arch.pltgot_addr |
| - + got_idx * 4; |
| + = (arch_addr_t) (uintptr_t) (lte->arch.pltgot_addr |
| + + got_idx * got_size); |
| |
| GElf_Rela rela = { |
| /* XXX double cast. */ |
| @@ -336,7 +353,7 @@ arch_elf_init(struct ltelf *lte, struct library *lib) |
| if (VECT_PUSHBACK(<e->plt_relocs, &rela) < 0) |
| goto fail_stubs; |
| |
| - fprintf(stderr, |
| + debug(2, |
| "added stub entry for symbol %u at %#lx, GOT @%p\n", |
| idx, (unsigned long) rela.r_addend, got_entry_addr); |
| } |
| @@ -362,8 +379,17 @@ read_got_entry(struct process *proc, GElf_Addr addr, GElf_Addr *valp) |
| { |
| /* XXX double cast. */ |
| arch_addr_t a = (arch_addr_t) (uintptr_t) addr; |
| - uint32_t l; |
| - if (proc_read_32(proc, a, &l) < 0) { |
| + uint64_t l = 0; |
| + int result; |
| + |
| +#ifdef __LP64__ |
| + if (!proc->mask_32bit) |
| + result = proc_read_64(proc, a, &l); |
| + else |
| +#endif /* __LP64__ */ |
| + result = proc_read_32(proc, a, (uint32_t *) &l); |
| + |
| + if (result < 0) { |
| fprintf(stderr, "ptrace read got entry @%#" PRIx64 ": %s\n", |
| addr, strerror(errno)); |
| return -1; |
| @@ -426,13 +452,13 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, |
| GElf_Addr stub_addr = rela->r_addend + lte->bias; |
| |
| debug(2, "PLT-less arch_elf_add_plt_entry %s = %#llx\n", |
| - a_name, stub_addr); |
| + a_name, (unsigned long long) stub_addr); |
| |
| struct library_symbol *libsym = NULL; |
| if (default_elf_add_plt_entry(proc, lte, a_name, rela, ndx, |
| &libsym) < 0) { |
| - fprintf(stderr, "%s: failed %s(%#llx): %s\n", __func__, |
| - a_name, stub_addr, strerror(errno)); |
| + fprintf(stderr, "%s: failed %s(%#lx): %s\n", __func__, |
| + a_name, (unsigned long) stub_addr, strerror(errno)); |
| goto fail; |
| } |
| |
| @@ -503,13 +529,27 @@ jump_to_entry_point(struct process *proc, struct breakpoint *bp) |
| static int |
| unresolve_got_entry(struct process *proc, GElf_Addr addr, GElf_Addr value) |
| { |
| - uint32_t v32 = (uint32_t) value; |
| - uint32_t a32 = (uint32_t) addr; |
| - if (ptrace(PTRACE_POKETEXT, proc->pid, a32, v32) < 0) { |
| + arch_addr_t a = (arch_addr_t) (uintptr_t) addr; |
| +#ifdef __LP64__ |
| + /* To write 32-bit value in 64-bit mode, we must read-modify-write |
| + the 64-bit value with only the lower 32 bits modified. */ |
| + if (proc->mask_32bit) { |
| + GElf_Addr orig = ptrace(PTRACE_PEEKTEXT, proc->pid, a, 0); |
| + char *obytes = (char *) &orig; |
| + char *nbytes = (char *) &value; |
| + unsigned i; |
| + |
| + for (i = 0; i < 4; i++) |
| + obytes[i] = nbytes[i]; |
| + value = orig; |
| + } |
| +#endif /* __LP64__ */ |
| + if (ptrace(PTRACE_POKETEXT, proc->pid, a, (unsigned long) value) < 0) { |
| fprintf(stderr, "failed to unresolve GOT entry: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| + |
| return 0; |
| } |
| |
| diff --git a/sysdeps/linux-gnu/mips/signalent1.h b/sysdeps/linux-gnu/mips/signalent1.h |
| new file mode 100644 |
| index 0000000..9e9d1f7 |
| --- /dev/null |
| +++ b/sysdeps/linux-gnu/mips/signalent1.h |
| @@ -0,0 +1,52 @@ |
| +/* |
| + * This file is part of ltrace. |
| + * Copyright (C) 2015 Imagination Technologies 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 */ |
| + "SIGIOT", /* 6 */ |
| + "SIGEMT", /* 7 */ |
| + "SIGFPE", /* 8 */ |
| + "SIGKILL", /* 9 */ |
| + "SIGBUS", /* 10 */ |
| + "SIGSEGV", /* 11 */ |
| + "SIGSYS", /* 12 */ |
| + "SIGPIPE", /* 13 */ |
| + "SIGALRM", /* 14 */ |
| + "SIGTERM", /* 15 */ |
| + "SIGUSR1", /* 16 */ |
| + "SIGUSR2", /* 17 */ |
| + "SIGCHLD", /* 18 */ |
| + "SIGPWR", /* 19 */ |
| + "SIGWINCH", /* 20 */ |
| + "SIGURG", /* 21 */ |
| + "SIGIO", /* 22 */ |
| + "SIGSTOP", /* 23 */ |
| + "SIGTSTP", /* 24 */ |
| + "SIGCONT", /* 25 */ |
| + "SIGTTIN", /* 26 */ |
| + "SIGTTOU", /* 27 */ |
| + "SIGVTALRM", /* 28 */ |
| + "SIGPROF", /* 29 */ |
| + "SIGXCPU", /* 30 */ |
| + "SIGXFSZ", /* 31 */ |
| diff --git a/sysdeps/linux-gnu/mips/syscallent1.h b/sysdeps/linux-gnu/mips/syscallent1.h |
| new file mode 100644 |
| index 0000000..dfa4954 |
| --- /dev/null |
| +++ b/sysdeps/linux-gnu/mips/syscallent1.h |
| @@ -0,0 +1,328 @@ |
| +/* |
| + * This file is part of ltrace. |
| + * Copyright (C) 2015 Imagination Technologies 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 |
| + */ |
| + |
| +/* MIPS64 */ |
| + |
| + "read", /* 0 */ |
| + "write", /* 1 */ |
| + "open", /* 2 */ |
| + "close", /* 3 */ |
| + "stat", /* 4 */ |
| + "fstat", /* 5 */ |
| + "lstat", /* 6 */ |
| + "poll", /* 7 */ |
| + "lseek", /* 8 */ |
| + "mmap", /* 9 */ |
| + "mprotect", /* 10 */ |
| + "munmap", /* 11 */ |
| + "brk", /* 12 */ |
| + "rt_sigaction", /* 13 */ |
| + "rt_sigprocmask", /* 14 */ |
| + "ioctl", /* 15 */ |
| + "pread64", /* 16 */ |
| + "pwrite64", /* 17 */ |
| + "readv", /* 18 */ |
| + "writev", /* 19 */ |
| + "access", /* 20 */ |
| + "pipe", /* 21 */ |
| + "_newselect", /* 22 */ |
| + "sched_yield", /* 23 */ |
| + "mremap", /* 24 */ |
| + "msync", /* 25 */ |
| + "mincore", /* 26 */ |
| + "madvise", /* 27 */ |
| + "shmget", /* 28 */ |
| + "shmat", /* 29 */ |
| + "shmctl", /* 30 */ |
| + "dup", /* 31 */ |
| + "dup2", /* 32 */ |
| + "pause", /* 33 */ |
| + "nanosleep", /* 34 */ |
| + "getitimer", /* 35 */ |
| + "setitimer", /* 36 */ |
| + "alarm", /* 37 */ |
| + "getpid", /* 38 */ |
| + "sendfile", /* 39 */ |
| + "socket", /* 40 */ |
| + "connect", /* 41 */ |
| + "accept", /* 42 */ |
| + "sendto", /* 43 */ |
| + "recvfrom", /* 44 */ |
| + "sendmsg", /* 45 */ |
| + "recvmsg", /* 46 */ |
| + "shutdown", /* 47 */ |
| + "bind", /* 48 */ |
| + "listen", /* 49 */ |
| + "getsockname", /* 50 */ |
| + "getpeername", /* 51 */ |
| + "socketpair", /* 52 */ |
| + "setsockopt", /* 53 */ |
| + "getsockopt", /* 54 */ |
| + "clone", /* 55 */ |
| + "fork", /* 56 */ |
| + "execve", /* 57 */ |
| + "exit", /* 58 */ |
| + "wait4", /* 59 */ |
| + "kill", /* 60 */ |
| + "uname", /* 61 */ |
| + "semget", /* 62 */ |
| + "semop", /* 63 */ |
| + "semctl", /* 64 */ |
| + "shmdt", /* 65 */ |
| + "msgget", /* 66 */ |
| + "msgsnd", /* 67 */ |
| + "msgrcv", /* 68 */ |
| + "msgctl", /* 69 */ |
| + "fcntl", /* 70 */ |
| + "flock", /* 71 */ |
| + "fsync", /* 72 */ |
| + "fdatasync", /* 73 */ |
| + "truncate", /* 74 */ |
| + "ftruncate", /* 75 */ |
| + "getdents", /* 76 */ |
| + "getcwd", /* 77 */ |
| + "chdir", /* 78 */ |
| + "fchdir", /* 79 */ |
| + "rename", /* 80 */ |
| + "mkdir", /* 81 */ |
| + "rmdir", /* 82 */ |
| + "creat", /* 83 */ |
| + "link", /* 84 */ |
| + "unlink", /* 85 */ |
| + "symlink", /* 86 */ |
| + "readlink", /* 87 */ |
| + "chmod", /* 88 */ |
| + "fchmod", /* 89 */ |
| + "chown", /* 90 */ |
| + "fchown", /* 91 */ |
| + "lchown", /* 92 */ |
| + "umask", /* 93 */ |
| + "gettimeofday", /* 94 */ |
| + "getrlimit", /* 95 */ |
| + "getrusage", /* 96 */ |
| + "sysinfo", /* 97 */ |
| + "times", /* 98 */ |
| + "ptrace", /* 99 */ |
| + "getuid", /* 100 */ |
| + "syslog", /* 101 */ |
| + "getgid", /* 102 */ |
| + "setuid", /* 103 */ |
| + "setgid", /* 104 */ |
| + "geteuid", /* 105 */ |
| + "getegid", /* 106 */ |
| + "setpgid", /* 107 */ |
| + "getppid", /* 108 */ |
| + "getpgrp", /* 109 */ |
| + "setsid", /* 110 */ |
| + "setreuid", /* 111 */ |
| + "setregid", /* 112 */ |
| + "getgroups", /* 113 */ |
| + "setgroups", /* 114 */ |
| + "setresuid", /* 115 */ |
| + "getresuid", /* 116 */ |
| + "setresgid", /* 117 */ |
| + "getresgid", /* 118 */ |
| + "getpgid", /* 119 */ |
| + "setfsuid", /* 120 */ |
| + "setfsgid", /* 121 */ |
| + "getsid", /* 122 */ |
| + "capget", /* 123 */ |
| + "capset", /* 124 */ |
| + "rt_sigpending", /* 125 */ |
| + "rt_sigtimedwait", /* 126 */ |
| + "rt_sigqueueinfo", /* 127 */ |
| + "rt_sigsuspend", /* 128 */ |
| + "sigaltstack", /* 129 */ |
| + "utime", /* 130 */ |
| + "mknod", /* 131 */ |
| + "personality", /* 132 */ |
| + "ustat", /* 133 */ |
| + "statfs", /* 134 */ |
| + "fstatfs", /* 135 */ |
| + "sysfs", /* 136 */ |
| + "getpriority", /* 137 */ |
| + "setpriority", /* 138 */ |
| + "sched_setparam", /* 139 */ |
| + "sched_getparam", /* 140 */ |
| + "sched_setscheduler", /* 141 */ |
| + "sched_getscheduler", /* 142 */ |
| + "sched_get_priority_max", /* 143 */ |
| + "sched_get_priority_min", /* 144 */ |
| + "sched_rr_get_interval", /* 145 */ |
| + "mlock", /* 146 */ |
| + "munlock", /* 147 */ |
| + "mlockall", /* 148 */ |
| + "munlockall", /* 149 */ |
| + "vhangup", /* 150 */ |
| + "pivot_root", /* 151 */ |
| + "_sysctl", /* 152 */ |
| + "prctl", /* 153 */ |
| + "adjtimex", /* 154 */ |
| + "setrlimit", /* 155 */ |
| + "chroot", /* 156 */ |
| + "sync", /* 157 */ |
| + "acct", /* 158 */ |
| + "settimeofday", /* 159 */ |
| + "mount", /* 160 */ |
| + "umount2", /* 161 */ |
| + "swapon", /* 162 */ |
| + "swapoff", /* 163 */ |
| + "reboot", /* 164 */ |
| + "sethostname", /* 165 */ |
| + "setdomainname", /* 166 */ |
| + "create_module", /* 167 */ |
| + "init_module", /* 168 */ |
| + "delete_module", /* 169 */ |
| + "get_kernel_syms", /* 170 */ |
| + "query_module", /* 171 */ |
| + "quotactl", /* 172 */ |
| + "nfsservctl", /* 173 */ |
| + "getpmsg", /* 174 */ |
| + "putpmsg", /* 175 */ |
| + "afs_syscall", /* 176 */ |
| + "reserved177", /* 177 */ |
| + "gettid", /* 178 */ |
| + "readahead", /* 179 */ |
| + "setxattr", /* 180 */ |
| + "lsetxattr", /* 181 */ |
| + "fsetxattr", /* 182 */ |
| + "getxattr", /* 183 */ |
| + "lgetxattr", /* 184 */ |
| + "fgetxattr", /* 185 */ |
| + "listxattr", /* 186 */ |
| + "llistxattr", /* 187 */ |
| + "flistxattr", /* 188 */ |
| + "removexattr", /* 189 */ |
| + "lremovexattr", /* 190 */ |
| + "fremovexattr", /* 191 */ |
| + "tkill", /* 192 */ |
| + "reserved193", /* 193 */ |
| + "futex", /* 194 */ |
| + "sched_setaffinity", /* 195 */ |
| + "sched_getaffinity", /* 196 */ |
| + "cacheflush", /* 197 */ |
| + "cachectl", /* 198 */ |
| + "sysmips", /* 199 */ |
| + "io_setup", /* 200 */ |
| + "io_destroy", /* 201 */ |
| + "io_getevents", /* 202 */ |
| + "io_submit", /* 203 */ |
| + "io_cancel", /* 204 */ |
| + "exit_group", /* 205 */ |
| + "lookup_dcookie", /* 206 */ |
| + "epoll_create", /* 207 */ |
| + "epoll_ctl", /* 208 */ |
| + "epoll_wait", /* 209 */ |
| + "remap_file_pages", /* 210 */ |
| + "rt_sigreturn", /* 211 */ |
| + "set_tid_address", /* 212 */ |
| + "restart_syscall", /* 213 */ |
| + "semtimedop", /* 214 */ |
| + "fadvise64", /* 215 */ |
| + "timer_create", /* 216 */ |
| + "timer_settime", /* 217 */ |
| + "timer_gettime", /* 218 */ |
| + "timer_getoverrun", /* 219 */ |
| + "timer_delete", /* 220 */ |
| + "clock_settime", /* 221 */ |
| + "clock_gettime", /* 222 */ |
| + "clock_getres", /* 223 */ |
| + "clock_nanosleep", /* 224 */ |
| + "tgkill", /* 225 */ |
| + "utimes", /* 226 */ |
| + "mbind", /* 227 */ |
| + "get_mempolicy", /* 228 */ |
| + "set_mempolicy", /* 229 */ |
| + "mq_open", /* 230 */ |
| + "mq_unlink", /* 231 */ |
| + "mq_timedsend", /* 232 */ |
| + "mq_timedreceive", /* 233 */ |
| + "mq_notify", /* 234 */ |
| + "mq_getsetattr", /* 235 */ |
| + "vserver", /* 236 */ |
| + "waitid", /* 237 */ |
| + "238", /* 238 */ |
| + "add_key", /* 239 */ |
| + "request_key", /* 240 */ |
| + "keyctl", /* 241 */ |
| + "set_thread_area", /* 242 */ |
| + "inotify_init", /* 243 */ |
| + "inotify_add_watch", /* 244 */ |
| + "inotify_rm_watch", /* 245 */ |
| + "migrate_pages", /* 246 */ |
| + "openat", /* 247 */ |
| + "mkdirat", /* 248 */ |
| + "mknodat", /* 249 */ |
| + "fchownat", /* 250 */ |
| + "futimesat", /* 251 */ |
| + "newfstatat", /* 252 */ |
| + "unlinkat", /* 253 */ |
| + "renameat", /* 254 */ |
| + "linkat", /* 255 */ |
| + "symlinkat", /* 256 */ |
| + "readlinkat", /* 257 */ |
| + "fchmodat", /* 258 */ |
| + "faccessat", /* 259 */ |
| + "pselect6", /* 260 */ |
| + "ppoll", /* 261 */ |
| + "unshare", /* 262 */ |
| + "splice", /* 263 */ |
| + "sync_file_range", /* 264 */ |
| + "tee", /* 265 */ |
| + "vmsplice", /* 266 */ |
| + "move_pages", /* 267 */ |
| + "set_robust_list", /* 268 */ |
| + "get_robust_list", /* 269 */ |
| + "kexec_load", /* 270 */ |
| + "getcpu", /* 271 */ |
| + "epoll_pwait", /* 272 */ |
| + "ioprio_set", /* 273 */ |
| + "ioprio_get", /* 274 */ |
| + "utimensat", /* 275 */ |
| + "signalfd", /* 276 */ |
| + "timerfd", /* 277 */ |
| + "eventfd", /* 278 */ |
| + "fallocate", /* 279 */ |
| + "timerfd_create", /* 280 */ |
| + "timerfd_gettime", /* 281 */ |
| + "timerfd_settime", /* 282 */ |
| + "signalfd4", /* 283 */ |
| + "eventfd2", /* 284 */ |
| + "epoll_create1", /* 285 */ |
| + "dup3", /* 286 */ |
| + "pipe2", /* 287 */ |
| + "inotify_init1", /* 288 */ |
| + "preadv", /* 289 */ |
| + "pwritev", /* 290 */ |
| + "rt_tgsigqueueinfo", /* 291 */ |
| + "perf_event_open", /* 292 */ |
| + "accept4", /* 293 */ |
| + "recvmmsg", /* 294 */ |
| + "fanotify_init", /* 295 */ |
| + "fanotify_mark", /* 296 */ |
| + "prlimit64", /* 297 */ |
| + "name_to_handle_at", /* 298 */ |
| + "open_by_handle_at", /* 299 */ |
| + "clock_adjtime", /* 300 */ |
| + "syncfs", /* 301 */ |
| + "sendmmsg", /* 302 */ |
| + "setns", /* 303 */ |
| + "process_vm_readv", /* 304 */ |
| + "process_vm_writev", /* 305 */ |
| diff --git a/sysdeps/linux-gnu/mips/trace.c b/sysdeps/linux-gnu/mips/trace.c |
| index e81b374..d54818e 100644 |
| --- a/sysdeps/linux-gnu/mips/trace.c |
| +++ b/sysdeps/linux-gnu/mips/trace.c |
| @@ -1,5 +1,6 @@ |
| /* |
| * This file is part of ltrace. |
| + * Copyright (C) 2015 Imagination Technologies Limited |
| * Copyright (C) 2013 Petr Machata, Red Hat Inc. |
| * Copyright (C) 2012 Edgar E. Iglesias, Axis Communications |
| * Copyright (C) 2010 Arnaud Patard, Mandriva SA |
| @@ -29,6 +30,7 @@ |
| #include <signal.h> |
| #include <sys/ptrace.h> |
| #include <asm/ptrace.h> |
| +#include <asm/unistd.h> |
| #include <assert.h> |
| #include <asm/unistd.h> |
| |
| @@ -68,6 +70,44 @@ |
| void |
| get_arch_dep(struct process *proc) |
| { |
| +#ifdef __LP64__ |
| + proc->mask_32bit = (proc->e_class == ELFCLASS32); |
| +#endif /* __LP64__ */ |
| + /* n32 personality is best approximated by n64, |
| + at least for syscall numbers */ |
| + proc->personality = (proc->e_class == ELFCLASS64 |
| + || proc->e_abi == ABI_N32); |
| +} |
| + |
| +/** |
| + \param abi ABI of current process, from mips_abi_type enum |
| + \param list An array of 4 elements, each corresponding to an ABI, in |
| + the order: o32, n32, n64, o64 |
| + |
| + return value from array corresponding to requested ABI |
| + */ |
| +static int |
| +abi_select(const int abi, const int list[]) |
| +{ |
| + int retval; |
| + switch (abi) |
| + { |
| + case ABI_N32: |
| + retval = list[1]; |
| + break; |
| + case ABI_N64: |
| + retval = list[2]; |
| + break; |
| + case ABI_O64: |
| + retval = list[3]; |
| + break; |
| + case ABI_O32: |
| + default: |
| + retval = list[0]; |
| + break; |
| + } |
| + |
| + return retval; |
| } |
| |
| /** |
| @@ -90,53 +130,94 @@ get_arch_dep(struct process *proc) |
| int |
| syscall_p(struct process *proc, int status, int *sysnum) |
| { |
| - if (WIFSTOPPED(status) |
| - && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { |
| - /* get the user's pc (plus 8) */ |
| - long pc = (long)get_instruction_pointer(proc); |
| - /* fetch the SWI instruction */ |
| - int insn = ptrace(PTRACE_PEEKTEXT, proc->pid, pc - 4, 0); |
| - int num = ptrace(PTRACE_PEEKTEXT, proc->pid, pc - 8, 0); |
| - |
| - /* |
| - On a mips, syscall looks like: |
| - 24040fa1 li v0, 0x0fa1 # 4001 --> _exit syscall |
| - 0000000c syscall |
| - */ |
| - if(insn!=0x0000000c){ |
| - /* sigreturn returns control to the point |
| - where the signal was received; skip check |
| - for preceeding syscall instruction */ |
| - int depth = proc->callstack_depth; |
| - struct callstack_element *top = NULL; |
| - if (depth > 0) |
| - top = &proc->callstack[depth - 1]; |
| - |
| - if (top != NULL && top->is_syscall && |
| - (top->c_un.syscall == (__NR_rt_sigreturn - |
| - __NR_Linux) || |
| - top->c_un.syscall == (__NR_sigreturn - |
| - __NR_Linux))) { |
| - *sysnum = top->c_un.syscall; |
| - return 2; |
| - } |
| - else |
| - return 0; |
| - } |
| - |
| - *sysnum = (num & 0xFFFF) - 4000; |
| - /* if it is a syscall, return 1 or 2 */ |
| - if (proc->callstack_depth > 0 && |
| - proc->callstack[proc->callstack_depth - 1].is_syscall && |
| - proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { |
| + unsigned long pc; |
| + int insn, prev; |
| + int min_syscall, max_syscall, sigreturn, rt_sigreturn; |
| + struct callstack_element *top = NULL; |
| + int depth = proc->callstack_depth; |
| + const int syscallbase[] = {__NR_O32_Linux, __NR_N32_Linux, |
| + __NR_64_Linux, __NR_O32_Linux}; |
| + const int syscallnum[] = {__NR_O32_Linux_syscalls, |
| + __NR_N32_Linux_syscalls, |
| + __NR_64_Linux_syscalls, |
| + __NR_O32_Linux_syscalls}; |
| + const int rt_sigreturn_list[] = {193, 211, 211, 193}; |
| + const int sigreturn_list[] = {119, -1, -1, 119}; |
| + |
| + if (!WIFSTOPPED(status) |
| + || WSTOPSIG(status) != (SIGTRAP | proc->tracesysgood)) |
| + return 0; |
| + |
| + /* get the user's pc (plus 8) */ |
| + pc = (unsigned long)get_instruction_pointer(proc); |
| + /* fetch the SWI instruction */ |
| + insn = ptrace(PTRACE_PEEKTEXT, proc->pid, pc - 4, 0); |
| + prev = ptrace(PTRACE_PEEKTEXT, proc->pid, pc - 8, 0); |
| + |
| + if (depth > 0) |
| + top = &proc->callstack[depth - 1]; |
| + |
| + /* Range of syscall numbers varies with ABI; ref:asm/unistd.h */ |
| + min_syscall = abi_select(proc->e_abi, syscallbase); |
| + max_syscall = min_syscall + abi_select(proc->e_abi, syscallnum); |
| + sigreturn = min_syscall + abi_select(proc->e_abi, sigreturn_list); |
| + rt_sigreturn = min_syscall + abi_select(proc->e_abi, rt_sigreturn_list); |
| + |
| + /* not a syscall instruction */ |
| + if(insn!=0x0000000c){ |
| + /* sigreturn returns control to the point where the signal was |
| + received; skip check for preceeding syscall instruction */ |
| + if (top != NULL && top->is_syscall |
| + && (top->c_un.syscall == (rt_sigreturn - min_syscall) |
| + || top->c_un.syscall == (sigreturn - min_syscall))) { |
| + *sysnum = top->c_un.syscall; |
| return 2; |
| } |
| + else |
| + return 0; |
| + } |
| |
| - if (*sysnum >= 0) { |
| - return 1; |
| - } |
| + /* |
| + On a mips, syscall looks like: |
| + 24020fa1 li v0, 0x0fa1 # 4001 --> _exit syscall |
| + 0000000c syscall |
| + */ |
| + if ((prev & 0xFFFF0000) == 0x24020000) { |
| + *sysnum = (prev & 0xFFFF) - min_syscall; |
| } |
| - return 0; |
| + /* |
| + The above is not necessary in Linux kernel > v2.6.35. Recent |
| + kernels have a fancy-pants method of restarting syscalls. |
| + We must read v0 instead, to get the syscall number. |
| + |
| + Unfortunately, v0 is not preserved till the point of return. |
| + If already in syscall and v0 is invalid, assume this event |
| + to be a return without attempting to match previous syscall. |
| + |
| + Caveat: logic fails if v0 incidentally contains a valid |
| + syscall number, distinct from the current syscall number, |
| + at the point of return from a nested syscall. |
| + */ |
| + else { |
| + int v0 = ptrace(PTRACE_PEEKUSER, proc->pid, off_v0, 0); |
| + |
| + if ((v0 >= min_syscall) && (v0 <= max_syscall)) |
| + *sysnum = v0 - min_syscall; |
| + else if (depth > 0 && top->is_syscall) |
| + *sysnum = top->c_un.syscall; |
| + else /* syscall instruction without valid number - ignored */ |
| + return 0; |
| + } |
| + |
| + /* if it is a syscall, return 1 or 2 */ |
| + if (depth > 0 && top->is_syscall && top->c_un.syscall == *sysnum) { |
| + return 2; |
| + } |
| + |
| + if (*sysnum >= 0) |
| + return 1; |
| + else |
| + return 0; |
| } |
| |
| /* Based on GDB code. */ |
| @@ -162,9 +243,11 @@ mips32_relative_offset (uint32_t inst) |
| return ((itype_immediate(inst) ^ 0x8000) - 0x8000) << 2; |
| } |
| |
| -int mips_next_pcs(struct process *proc, uint32_t pc, uint32_t *newpc) |
| +int mips_next_pcs(struct process *proc, unsigned long pc, |
| + unsigned long *newpc) |
| { |
| - uint32_t inst, rx; |
| + uint32_t inst; |
| + unsigned long rx; |
| int op; |
| int rn; |
| int nr = 0; |
| @@ -277,8 +360,8 @@ int mips_next_pcs(struct process *proc, uint32_t pc, uint32_t *newpc) |
| return nr; |
| |
| fail: |
| - printf("nr=%d pc=%x\n", nr, pc); |
| - printf("pc=%x %x\n", newpc[0], newpc[1]); |
| + printf("nr=%d pc=%lx\n", nr, pc); |
| + printf("pc=%lx %lx\n", newpc[0], newpc[1]); |
| return 0; |
| } |
| |
| @@ -304,17 +387,27 @@ fail: |
| * branches within the LL-SC sequence. |
| */ |
| #define inrange(x,lo,hi) ((x)<=(hi) && (x)>=(lo)) |
| +/* Instruction encodings for atomic operations */ |
| +#ifdef __mips64 |
| +#define op_SC_p(op) (op == 0x38 || op == 0x3c) |
| +#define op_LL_p(op) (op == 0x30 || op == 0x34) |
| +#else /* !__mips64 */ |
| +#define op_SC_p(op) (op == 0x38) |
| +#define op_LL_p(op) (op == 0x30) |
| +#endif /* !__mips64 */ |
| + |
| static int |
| -mips_atomic_next_pcs(struct process *proc, uint32_t lladdr, uint32_t *newpcs) |
| +mips_atomic_next_pcs(struct process *proc, unsigned long lladdr, |
| + unsigned long *newpcs) |
| { |
| int nr = 0; |
| |
| - uint32_t scaddr; |
| + unsigned long scaddr; |
| for (scaddr = lladdr + 4; scaddr - lladdr <= 2048; scaddr += 4) { |
| /* Found SC, now stepover trailing branch */ |
| uint32_t inst; |
| if (proc_read_32(proc, (arch_addr_t)scaddr, &inst) >= 0 && |
| - itype_op(inst) == 0x38) { |
| + op_SC_p (itype_op(inst))) { |
| newpcs[nr++] = scaddr + 4; |
| break; |
| } |
| @@ -327,16 +420,16 @@ mips_atomic_next_pcs(struct process *proc, uint32_t lladdr, uint32_t *newpcs) |
| } |
| |
| /* Scan LL<->SC range for branches going outside that range */ |
| - uint32_t spc; |
| + unsigned long spc; |
| for (spc = lladdr + 4; spc < scaddr; spc += 4) { |
| - uint32_t scanpcs[2]; |
| + unsigned long scanpcs[2]; |
| int snr = mips_next_pcs(proc, spc, scanpcs); |
| |
| int i; |
| for (i = 0; i < snr; ++i) { |
| if (!inrange(scanpcs[i], lladdr, scaddr)) { |
| - uint32_t *tmp = realloc(newpcs, (nr + 1) * |
| - sizeof *newpcs); |
| + unsigned long *tmp = realloc(newpcs, (nr + 1) |
| + * sizeof *newpcs); |
| if (tmp == NULL) { |
| perror("malloc atomic next pcs"); |
| return -1; |
| @@ -357,8 +450,8 @@ arch_sw_singlestep(struct process *proc, struct breakpoint *bp, |
| int (*add_cb)(arch_addr_t, struct sw_singlestep_data *), |
| struct sw_singlestep_data *add_cb_data) |
| { |
| - uint32_t pc = (uint32_t) get_instruction_pointer(proc); |
| - uint32_t *newpcs; |
| + unsigned long pc = (unsigned long) get_instruction_pointer(proc); |
| + unsigned long *newpcs; |
| int nr; |
| uint32_t inst; |
| |
| @@ -369,7 +462,7 @@ arch_sw_singlestep(struct process *proc, struct breakpoint *bp, |
| return SWS_FAIL; |
| |
| /* Starting an atomic read-modify-write sequence */ |
| - if (itype_op(inst) == 0x30) |
| + if (op_LL_p(itype_op(inst))) |
| nr = mips_atomic_next_pcs(proc, pc, newpcs); |
| else |
| nr = mips_next_pcs(proc, pc, newpcs); |
| @@ -462,7 +555,7 @@ gimme_arg(enum tof type, struct process *proc, int arg_num, |
| debug(2,"ret = %#lx",addr); |
| return addr; |
| } |
| - ret = addr + 4*arg_num; |
| + ret = addr + sizeof(long) * arg_num; |
| ret=ptrace(PTRACE_PEEKTEXT,proc->pid,addr,0); |
| debug(2,"ret = %#lx",ret); |
| return ret; |
| @@ -474,7 +567,7 @@ gimme_arg(enum tof type, struct process *proc, int arg_num, |
| debug(2,"ret = %#lx",addr); |
| return addr; |
| } |
| - ret = addr + 4*arg_num; |
| + ret = addr + sizeof(long) * arg_num; |
| ret=ptrace(PTRACE_PEEKTEXT,proc->pid,addr,0); |
| debug(2,"ret = %#lx",ret); |
| return ret; |
| @@ -483,4 +576,34 @@ gimme_arg(enum tof type, struct process *proc, int arg_num, |
| return 0; |
| } |
| |
| +#ifdef __LP64__ |
| +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_LONG: |
| + case ARGTYPE_ULONG: |
| + return proc->mask_32bit ? 4 : sizeof (long); |
| + |
| + case ARGTYPE_POINTER: |
| + return proc->mask_32bit ? 4 : sizeof (void *); |
| + |
| + default: |
| + /* Use default value. */ |
| + return (size_t)-2; |
| + } |
| +} |
| + |
| +size_t |
| +arch_type_alignof(struct process *proc, struct arg_type_info *info) |
| +{ |
| + if (proc == NULL) |
| + return (size_t)-2; |
| + |
| + return arch_type_sizeof(proc, info); |
| +} |
| +#endif /* __LP64__ */ |
| /**@}*/ |
| diff --git a/sysdeps/linux-gnu/mksyscallent_mips b/sysdeps/linux-gnu/mksyscallent_mips |
| index f3961b4..f8dcfe1 100755 |
| --- a/sysdeps/linux-gnu/mksyscallent_mips |
| +++ b/sysdeps/linux-gnu/mksyscallent_mips |
| @@ -19,9 +19,7 @@ |
| |
| # hack expression to generate arch/syscallent.h from <asm/unistd.h> |
| # It reads from stdin and writes to stdout |
| -# It should work OK on i386,m68k,arm,ia64 |
| -# It does NOT work in mips, s390 |
| -# It is untested in other architectures |
| +# Default is o32; arch=mips64 generates n64 syscalls |
| |
| BEGIN { |
| max=0; |
| @@ -31,7 +29,10 @@ BEGIN { |
| { |
| #debug |
| #printf("/%s/%s/%s/%s/\n", $1, $2, $3, $4); |
| - if ($2 ~ /__NR_Linux/ && $3 ~ /4000/) { |
| + min=4000 |
| + if (arch ~ "mips64") min=5000 |
| + |
| + if ($2 ~ /__NR_Linux/ && $3 ~ min) { |
| syscall=1; |
| } |
| if ($2 ~ /__NR_Linux_syscalls/) { |
| -- |
| 2.13.2 |
| |