ltrace: Add support for Imagination Technologies Meta
This patchset adds support for Imagination's Meta architecture.
The Meta Linux kernel port will be included in the Linux Kernel
v3.9. It also uses the generic system call numbers.
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
diff --git a/README b/README
index 95871d1..414bdfb 100644
--- a/README
+++ b/README
@@ -29,6 +29,7 @@
i[4567]86-*-linux-gnu
ia64-*-linux-gnu
m68k-*-linux-gnu
+ metag-*-linux-uclibc
mips-*-linux-gnu
powerpc-*-linux-gnu
powerpc64-*-linux-gnu
diff --git a/configure.ac b/configure.ac
index 82133ce..16c7a61 100644
--- a/configure.ac
+++ b/configure.ac
@@ -350,6 +350,7 @@
sysdeps/linux-gnu/cris/Makefile
sysdeps/linux-gnu/ia64/Makefile
sysdeps/linux-gnu/m68k/Makefile
+ sysdeps/linux-gnu/metag/Makefile
sysdeps/linux-gnu/mips/Makefile
sysdeps/linux-gnu/ppc/Makefile
sysdeps/linux-gnu/s390/Makefile
diff --git a/sysdeps/linux-gnu/metag/Makefile.am b/sysdeps/linux-gnu/metag/Makefile.am
new file mode 100644
index 0000000..a79d2f7
--- /dev/null
+++ b/sysdeps/linux-gnu/metag/Makefile.am
@@ -0,0 +1,16 @@
+noinst_LTLIBRARIES = \
+ ../libcpu.la
+
+___libcpu_la_SOURCES = \
+ 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/metag/arch.h b/sysdeps/linux-gnu/metag/arch.h
new file mode 100644
index 0000000..2699c62
--- /dev/null
+++ b/sysdeps/linux-gnu/metag/arch.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 1998,2004,2008 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#define LT_ELFCLASS ELFCLASS32
+#define LT_ELF_MACHINE EM_METAG
+
+#define BREAKPOINT_VALUE { 0x01, 0x00, 0x40, 0xAF }
+#define BREAKPOINT_LENGTH 4
+#define DECR_PC_AFTER_BREAK 0
+#define ARCH_ENDIAN_LITTLE
+#define ARCH_HAVE_SW_SINGLESTEP
diff --git a/sysdeps/linux-gnu/metag/plt.c b/sysdeps/linux-gnu/metag/plt.c
new file mode 100644
index 0000000..2d479a4
--- /dev/null
+++ b/sysdeps/linux-gnu/metag/plt.c
@@ -0,0 +1,38 @@
+/*
+ * This file is part of ltrace.
+ *
+ * Copyright (C) 2013 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 "debug.h"
+#include "proc.h"
+#include "library.h"
+#include "ltrace-elf.h"
+
+GElf_Addr
+arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela)
+{
+ return lte->plt_addr + ( ndx * 0x14 ) + 0x14;
+}
+
+void
+*sym2addr(struct process *proc, struct library_symbol *sym)
+{
+ return sym->enter_addr;
+}
diff --git a/sysdeps/linux-gnu/metag/ptrace.h b/sysdeps/linux-gnu/metag/ptrace.h
new file mode 100644
index 0000000..7a41e4a
--- /dev/null
+++ b/sysdeps/linux-gnu/metag/ptrace.h
@@ -0,0 +1,21 @@
+/*
+ * This file is part of ltrace.
+ *
+ * Copyright (C) 2013 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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>
diff --git a/sysdeps/linux-gnu/metag/regs.c b/sysdeps/linux-gnu/metag/regs.c
new file mode 100644
index 0000000..d4a6f2f
--- /dev/null
+++ b/sysdeps/linux-gnu/metag/regs.c
@@ -0,0 +1,89 @@
+/*
+ * This file is part of ltrace.
+ *
+ * Copyright (C) 2013 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 <sys/types.h>
+#include <sys/ptrace.h>
+#include <linux/uio.h>
+#include <asm/ptrace.h>
+
+#include "proc.h"
+#include "common.h"
+
+arch_addr_t
+get_instruction_pointer(struct process *proc)
+{
+ struct user_gp_regs regs;
+ struct iovec iov;
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ if (ptrace(PTRACE_GETREGSET, proc->pid, NT_PRSTATUS, (long)&iov))
+ return (void *)-1;
+
+ return (void *)regs.pc; /* PC */
+}
+
+void
+set_instruction_pointer(struct process *proc, arch_addr_t addr)
+{
+ struct user_gp_regs regs;
+ struct iovec iov;
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ if (ptrace(PTRACE_GETREGSET, proc->pid, NT_PRSTATUS, (long)&iov))
+ return;
+
+ regs.pc = (unsigned long)addr;
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ ptrace(PTRACE_SETREGSET, proc->pid, NT_PRSTATUS, (long)&iov);
+}
+
+arch_addr_t
+get_stack_pointer(struct process *proc)
+{
+ struct user_gp_regs regs;
+ struct iovec iov;
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ if (ptrace(PTRACE_GETREGSET, proc->pid, NT_PRSTATUS, (long)&iov))
+ return (void *)-1;
+
+ return (void *)regs.ax[0][0]; /* A0StP (A0.0) */
+}
+
+arch_addr_t
+get_return_addr(struct process *proc, void *stack_pointer)
+{
+ struct user_gp_regs regs;
+ struct iovec iov;
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ if (ptrace(PTRACE_GETREGSET, proc->pid, NT_PRSTATUS, (long)&iov))
+ return (void *)-1;
+
+ return (void *)regs.dx[4][1]; /* D1RtP (D1.4) */
+}
diff --git a/sysdeps/linux-gnu/metag/signalent.h b/sysdeps/linux-gnu/metag/signalent.h
new file mode 100644
index 0000000..80228b7
--- /dev/null
+++ b/sysdeps/linux-gnu/metag/signalent.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of ltrace.
+ *
+ * Copyright (C) 2013 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 */
+ "SIGRTMIN", /* 32 */
diff --git a/sysdeps/linux-gnu/metag/syscallent.h b/sysdeps/linux-gnu/metag/syscallent.h
new file mode 100644
index 0000000..447c550
--- /dev/null
+++ b/sysdeps/linux-gnu/metag/syscallent.h
@@ -0,0 +1,293 @@
+/*
+ * This file is part of ltrace.
+ *
+ * Copyright (C) 2013 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 */
+ "fcntl64", /* 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 */
+ "umount", /* 39 */
+ "mount", /* 40 */
+ "pivot_root", /* 41 */
+ "42", /* 42 */
+ "statfs64", /* 43 */
+ "fstatfs64", /* 44 */
+ "truncate64", /* 45 */
+ "ftruncate64", /* 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 */
+ "llseek", /* 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 */
+ "fstatat64", /* 79 */
+ "fstat64", /* 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 */
+ "newuname", /* 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 */
+ "mmap2", /* 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 */
+ "244", /* 244 */
+ "metag_setglobalbit", /* 245 */
+ "metag_set_fpu_flags", /* 246 */
+ "metag_set_tls", /* 247 */
+ "metag_get_tls", /* 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 */
diff --git a/sysdeps/linux-gnu/metag/trace.c b/sysdeps/linux-gnu/metag/trace.c
new file mode 100644
index 0000000..ad5fffe
--- /dev/null
+++ b/sysdeps/linux-gnu/metag/trace.c
@@ -0,0 +1,428 @@
+/*
+ * This file is part of ltrace.
+ *
+ * Copyright (C) 2013 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <linux/uio.h>
+#include <asm/ptrace.h>
+#include <assert.h>
+
+#include "proc.h"
+#include "common.h"
+
+#define METAG_INSN_SIZE 4
+#define N_UNITS 2
+#define REG_SIZE 4
+
+/* unit codes */
+enum metag_unitnum {
+ METAG_UNIT_CT, /* 0x0 */
+ METAG_UNIT_D0,
+ METAG_UNIT_D1,
+ METAG_UNIT_A0,
+ METAG_UNIT_A1, /* 0x4 */
+ METAG_UNIT_PC,
+ METAG_UNIT_RA,
+ METAG_UNIT_TR,
+ METAG_UNIT_TT, /* 0x8 */
+ METAG_UNIT_FX,
+ METAG_UNIT_MAX,
+};
+
+/**
+ \param proc The process that had an event.
+
+ Called by \c next_event() right after the return from wait.
+ */
+void
+get_arch_dep(struct process *proc)
+{
+
+}
+
+/**
+ \param proc Process that had event.
+ \param status From \c\ waitpid().
+ \param sysnum 0-based syscall number.
+ \return 1 if syscall, 2 if sysret, 0 otherwise.
+
+ Called by \c next_event() after the call to get_arch_dep().
+
+ */
+int
+syscall_p(struct process *proc, int status, int *sysnum)
+{
+ if (WIFSTOPPED(status)
+ && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
+ struct user_gp_regs regs;
+ struct iovec iov;
+
+ /* Get GP registers. */
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ if (ptrace(PTRACE_GETREGSET, proc->pid, NT_PRSTATUS,
+ (long)&iov))
+ return -1;
+
+ /* Fetch the SWITCH instruction. */
+ unsigned int insn = ptrace(PTRACE_PEEKTEXT, proc->pid, regs.pc,
+ 0);
+ *sysnum = regs.dx[0][1];
+
+ if (insn != 0xaf440001) {
+ /* Check if we're returning from the system call. */
+ insn = ptrace(PTRACE_PEEKTEXT, proc->pid, regs.pc - 4,
+ 0);
+ if (insn == 0xaf440001)
+ return 2;
+
+ return 0;
+ }
+
+ if (*sysnum >= 0)
+ return 1;
+ }
+ return 0;
+}
+
+/* 2-bit base unit (BU) mapping. */
+static enum metag_unitnum metag_bu_map[4] = {
+ METAG_UNIT_A1,
+ METAG_UNIT_D0,
+ METAG_UNIT_D1,
+ METAG_UNIT_A0,
+};
+
+static int
+get_regval_from_unit(enum metag_unitnum unit, unsigned int reg,
+ struct user_gp_regs *regs)
+{
+ /*
+ * Check if reg has a sane value.
+ * We do have N_UNITS, each one having X registers
+ * and each register is REG_SIZE bytes.
+ */
+ if ((unit == METAG_UNIT_A0) || (unit == METAG_UNIT_A1)) {
+ if (reg >= ((sizeof(regs->ax)/N_UNITS/REG_SIZE)))
+ goto bad_reg;
+ } else if ((unit == METAG_UNIT_D0) || (unit == METAG_UNIT_D1)) {
+ if (reg >= ((sizeof(regs->dx)/N_UNITS/REG_SIZE)))
+ goto bad_reg;
+ }
+
+ switch(unit) {
+ case METAG_UNIT_A1:
+ return regs->ax[reg][1];
+ case METAG_UNIT_D0:
+ return regs->dx[reg][0];
+ case METAG_UNIT_D1:
+ return regs->dx[reg][1];
+ case METAG_UNIT_A0:
+ return regs->ax[reg][0];
+ /* We really shouldn't be here. */
+ default:
+ assert(unit != unit);
+ abort();
+ }
+ return 0;
+
+bad_reg:
+ fprintf(stderr,
+ "Reading from register %d of unit %d is not implemented.",
+ reg, unit);
+ return 0;
+
+}
+
+static int
+metag_next_pcs(struct process *proc, uint32_t pc, uint32_t *newpc)
+{
+ uint32_t inst;
+ int nr = 0, imm, reg_val;
+ unsigned int unit = 0, reg;
+ struct user_gp_regs regs;
+ struct iovec iov;
+
+ inst = ptrace(PTRACE_PEEKTEXT, proc->pid, pc, 0);
+
+ if (inst == 0xa0fffffe) { /* NOP (Special branch instruction) */
+ newpc[nr++] = pc + 4;
+ } else if ((inst & 0xff000000) == 0xa0000000) {
+ /* Matching 0xA 0x0 for opcode for B #S19 or B<cc> #S19.
+ *
+ * Potential Targets:
+ * - pc + #S19 * METAG_INSN_SIZE if R=1 or <CC> true
+ * - pc + 4 */
+ imm = ((inst << 8) >> 13) * METAG_INSN_SIZE;
+ newpc[nr++] = pc + imm;
+ newpc[nr++] = pc + 4;
+ } else if ((inst & 0xff000000) == 0xac000000) {
+ /* Matching 0xA 0xC for opcode.
+ * JUMP BBx.r,#X16 or CALL BBx.r,#X16
+ *
+ * pc = reg + #x16 (aligned) */
+ imm = (inst >> 3) & 0xffff;
+ reg = (inst >> 19) & 0x1f;
+ unit = metag_bu_map[inst & 0x3];
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ if (ptrace(PTRACE_GETREGSET, proc->pid,
+ NT_PRSTATUS, (long)&iov))
+ goto ptrace_fail;
+
+ reg_val = get_regval_from_unit(unit, reg, ®s);
+ newpc[nr++] = (reg_val + imm) & -METAG_INSN_SIZE;
+ } else if ((inst & 0xff000000) == 0xab000000) {
+ /* Matching 0xA 0xB for opcode.
+ *
+ * CALLR BBx.r,#S19 */
+ imm = ((inst << 8) >> 13) * METAG_INSN_SIZE;
+ newpc[nr++] = pc + imm;
+ } else if ((inst & 0xff0001e0) == 0xa30000a0) {
+ /*
+ * Matching 0xA 0x3 for opcode and then
+ * Ud (bit 8-5) = 0x5 = METAG_UNIT_PC
+ *
+ * Potential MOV PC,.. or SWAP<cc> PC,.. or SWAP<cc> ..,PC
+ */
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ if (ptrace(PTRACE_GETREGSET, proc->pid,
+ NT_PRSTATUS, (long)&iov))
+ goto ptrace_fail;
+
+ /*
+ * Maybe PC is the source register for a SWAP?
+ * bit9 = 1 and bit13-10(Us) == METAG_UNIT_PC
+ */
+ if (((inst >> 9 ) & 0x1) &&
+ (((inst >> 10) & 0xf) == METAG_UNIT_PC)) {
+ /* PC will get its value from the
+ * destination register. */
+ reg = (inst >> 14) & 0x1f;
+ unit = (inst >> 5) & 0xf;
+ } else { /* PC is the destination register.
+ * Find the source register. */
+ reg = (inst >> 19) & 0x1f;
+ unit = (inst >> 10) & 0xf;
+ }
+
+ switch(unit) {
+ case METAG_UNIT_D0:
+ case METAG_UNIT_D1:
+ case METAG_UNIT_A0:
+ case METAG_UNIT_A1:
+ reg_val = get_regval_from_unit(unit, reg, ®s);
+ break;
+ case METAG_UNIT_PC:
+ reg_val = regs.pc;
+ break;
+ default:
+ goto unhandled;
+ }
+ newpc[nr++] = reg_val;
+ /* In case it is a conditional instruction. */
+ newpc[nr++] = pc + 4;
+ } else if ((inst & 0xff00001f) == 0xc600000a){
+ /* Matching 0xC 0x{4,6} for opcode
+ * and UD == 0x5 == METAG_UNIT_PC
+ *
+ * GETD PC, [A0.r + #S6] or
+ * GETD PC, [A0.r + A0.r] */
+ unit = metag_bu_map[(inst >> 5) & 0x3];
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ reg = (inst >> 14) & 0x1f;
+ imm = (inst << 18) >> 5; /* sign-extend it */
+ if (ptrace(PTRACE_GETREGSET, proc->pid,
+ NT_PRSTATUS, (long)&iov))
+ goto ptrace_fail;
+ reg_val = get_regval_from_unit(unit, reg, ®s) + imm;
+ /* See where reg_val actually points to. */
+ newpc[nr++] = ptrace(PTRACE_PEEKTEXT, proc->pid, reg_val, 0);
+ } else if (((inst & 0xfe0001e0) == 0x840000a0) || /* ADDcc R, A, R */
+ ((inst & 0xfe00003f) == 0x8600002a) || /* ADD R, A, #X8 */
+ ((inst & 0xfe0001e0) == 0x8c0000a0) || /* SUBcc R, A, R */
+ ((inst & 0xfe00003f) == 0x8e00002a) || /* SUB R, A, #X8 */
+ ((inst & 0xf40001e0) == 0x040000a0) || /* ADDcc R, D, D */
+ ((inst & 0xfe00003f) == 0x0600002a) || /* ADD R, D, #X8 */
+ ((inst & 0xf40001e0) == 0x140000a0) || /* SUBcc R, D, D */
+ ((inst & 0xf600003f) == 0x1600002a)) { /* SUB R, D, #X8 */
+
+ /* bits4-1(Ud) == METAG_UNIT_PC */
+
+ int src1, src2, pc_src1 = 0, pc_src2 = 0, is_aunit = 0;
+ int umask = 0, optype = 0;
+
+ /* Look for O2R bit */
+ if ((((inst >> 24) & 0x6) == 0x4) && (inst & 0x1))
+ goto unhandled;
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ if (ptrace(PTRACE_GETREGSET, proc->pid,
+ NT_PRSTATUS, (long)&iov))
+ goto ptrace_fail;
+
+ /* Figure out unit for source registers based on the opcode. */
+ switch((inst >> 28) & 0xf) {
+ case 0: /* ADD<cc> Rx.r, Dx.r, De.r|#X8 */
+ case 1: /* SUB<cc> Rx.r, Dx.r, De.r|#X8 */
+ unit = METAG_UNIT_D0 + ((inst >> 24) & 0x1);
+ is_aunit = 0;
+ umask = 0x1f;
+ optype = (inst >> 28) & 0x1;
+ break;
+ case 8:
+ unit = METAG_UNIT_A0 + ((inst >> 24) & 0x1);
+ is_aunit = 1;
+ umask = 0xf;
+ optype = (inst >> 27) & 0x1;
+ break;
+ }
+
+ /* Get pc bits (if any). */
+ if (is_aunit) {
+ pc_src1 = (inst >> 18) & 0x1;
+ pc_src2 = (inst >> 13) & 0x1;
+ }
+
+ /* Determine ADD|SUB format. Immediate or register ? */
+ if ((inst >> 25) & 0x1) { /* ADD|SUB cc PC, X, #imm8 */
+ src2 = (inst >> 6) & 0xff; /* so we can share code. */
+ reg = (inst >> 14) & umask;
+ if (pc_src1) /* This can only be true for AU ops. */
+ src1 = regs.pc;
+ else /* This covers both AU an DU ops. */
+ src1 = get_regval_from_unit(unit, reg, ®s);
+ } else { /* ADD|SUB cc PC, X, X */
+ if (pc_src1)
+ src1 = regs.pc;
+ else
+ src1 = get_regval_from_unit(unit, (inst >> 14)
+ & umask, ®s);
+ if (pc_src2)
+ src2 = regs.pc;
+ else
+ src2 = get_regval_from_unit(unit, (inst >> 9)
+ & umask, ®s);
+ }
+
+ /* Construct the new PC. */
+ if (optype)
+ /* SUB */
+ newpc[nr++] = src1 - src2;
+ else /* ADD */
+ newpc[nr++] = src1 + src2;
+ /* Conditional instruction so PC may not change. */
+ newpc[nr++] = pc + 4;
+ } else {
+ newpc[nr++] = pc + 4;
+ }
+
+ if (nr <= 0 || nr > 2)
+ goto fail;
+ if (nr == 2 && newpc[1] == 0)
+ goto fail;
+
+ return nr;
+
+ptrace_fail:
+ fprintf(stderr, "Failed to read the registers pid=%d @ pc=0x%08x\n",
+ proc->pid, pc);
+ return 0;
+unhandled:
+ fprintf(stderr, "Unhandled instruction: pc=0x%08x, inst=0x%08x\n",
+ pc, inst);
+ return 0;
+fail:
+ fprintf(stderr, "nr=%d pc=0x%08x\n", nr, pc);
+ fprintf(stderr, "newpc=0x%08x 0x%08x\n", newpc[0], newpc[1]);
+ return 0;
+
+}
+
+enum sw_singlestep_status
+arch_sw_singlestep(struct process *proc, struct breakpoint *bp,
+ int (*add_cb)(arch_addr_t, struct sw_singlestep_data *),
+ struct sw_singlestep_data *add_cb_data)
+{
+ arch_addr_t pc = get_instruction_pointer(proc);
+ uint32_t newpcs[2];
+ int nr;
+
+ nr = metag_next_pcs(proc, (uint32_t)pc, newpcs);
+
+ while (nr-- > 0) {
+ arch_addr_t baddr = (arch_addr_t) newpcs[nr];
+ if (dict_find(proc->leader->breakpoints, &baddr) != NULL) {
+ fprintf(stderr, "skip %p %p\n", baddr, add_cb_data);
+ continue;
+ }
+
+ if (add_cb(baddr, add_cb_data) < 0)
+ return SWS_FAIL;
+ }
+
+ ptrace(PTRACE_SYSCALL, proc->pid, 0, 0);
+ return SWS_OK;
+}
+
+long
+gimme_arg(enum tof type, struct process *proc, int arg_num,
+ struct arg_type_info *info)
+{
+ long ret;
+ struct user_gp_regs regs;
+ struct iovec iov;
+
+ /* get GP registers */
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ if (ptrace(PTRACE_GETREGSET, proc->pid, NT_PRSTATUS, (long)&iov))
+ return 0;
+
+ debug(2, "type %d arg %d arg",type, arg_num);
+ if (type == LT_TOF_FUNCTION || type == LT_TOF_SYSCALL) {
+ if (arg_num < 6) {
+ /* Args go backwards starting from D1Ar1 (D1.3) */
+ ret = ((unsigned long *)®s.dx[3][1])[-arg_num];
+ debug(2,"ret = %#lx",ret);
+ return ret;
+ } else {
+ return 0;
+ }
+ }
+ if (arg_num >= 0) {
+ fprintf(stderr,"args on return?");
+ }
+ if (type == LT_TOF_FUNCTIONR || type == LT_TOF_SYSCALLR) {
+ return regs.dx[0][0]; /* D0Re0 (D0.0) */
+ }
+
+ fprintf(stderr, "gimme_arg called with wrong arguments\n");
+
+ return 0;
+}