summaryrefslogtreecommitdiff
path: root/sysdeps/linux-gnu/arm/trace.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/linux-gnu/arm/trace.c')
-rw-r--r--sysdeps/linux-gnu/arm/trace.c659
1 files changed, 612 insertions, 47 deletions
diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c
index c528cfb..5e51e91 100644
--- a/sysdeps/linux-gnu/arm/trace.c
+++ b/sysdeps/linux-gnu/arm/trace.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 1998,2004,2008,2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
*
@@ -29,10 +29,13 @@
#include <sys/ptrace.h>
#include <asm/ptrace.h>
-#include "proc.h"
+#include "bits.h"
#include "common.h"
+#include "proc.h"
#include "output.h"
#include "ptrace.h"
+#include "regs.h"
+#include "type.h"
#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
@@ -42,13 +45,9 @@
# define PTRACE_POKEUSER PTRACE_POKEUSR
#endif
-#define off_r0 ((void *)0)
-#define off_r7 ((void *)28)
-#define off_ip ((void *)48)
-#define off_pc ((void *)60)
-
void
-get_arch_dep(Process *proc) {
+get_arch_dep(struct process *proc)
+{
proc_archdep *a;
if (!proc->arch_ptr)
@@ -63,21 +62,28 @@ get_arch_dep(Process *proc) {
* -1 on error.
*/
int
-syscall_p(Process *proc, int status, int *sysnum) {
+syscall_p(struct process *proc, int status, int *sysnum)
+{
if (WIFSTOPPED(status)
&& WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
- /* get the user's pc (plus 8) */
- unsigned pc = ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0);
+ uint32_t pc, ip;
+ if (arm_get_register(proc, ARM_REG_PC, &pc) < 0
+ || arm_get_register(proc, ARM_REG_IP, &ip) < 0)
+ return -1;
+
pc = pc - 4;
+
/* fetch the SWI instruction */
unsigned insn = ptrace(PTRACE_PEEKTEXT, proc->pid,
(void *)pc, 0);
- int ip = ptrace(PTRACE_PEEKUSER, proc->pid, off_ip, 0);
if (insn == 0xef000000 || insn == 0x0f000000
|| (insn & 0xffff0000) == 0xdf000000) {
/* EABI syscall */
- *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, off_r7, 0);
+ uint32_t r7;
+ if (arm_get_register(proc, ARM_REG_R7, &r7) < 0)
+ return -1;
+ *sysnum = r7;
} else if ((insn & 0xfff00000) == 0xef900000) {
/* old ABI syscall */
*sysnum = insn & 0xfffff;
@@ -103,46 +109,605 @@ syscall_p(Process *proc, int status, int *sysnum) {
return 0;
}
-long
-gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+static arch_addr_t
+arm_branch_dest(const arch_addr_t pc, const uint32_t insn)
{
- proc_archdep *a = (proc_archdep *) proc->arch_ptr;
+ /* Bits 0-23 are signed immediate value. */
+ return pc + ((((insn & 0xffffff) ^ 0x800000) - 0x800000) << 2) + 8;
+}
- if (arg_num == -1) { /* return value */
- return ptrace(PTRACE_PEEKUSER, proc->pid, off_r0, 0);
- }
+/* Addresses for calling Thumb functions have the bit 0 set.
+ Here are some macros to test, set, or clear bit 0 of addresses. */
+/* XXX double cast */
+#define IS_THUMB_ADDR(addr) ((uintptr_t)(addr) & 1)
+#define MAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) | 1))
+#define UNMAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) & ~1))
- /* deal with the ARM calling conventions */
- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
- if (arg_num < 4) {
- if (a->valid && type == LT_TOF_FUNCTION)
- return a->regs.uregs[arg_num];
- if (a->valid && type == LT_TOF_FUNCTIONR)
- return a->func_arg[arg_num];
- return ptrace(PTRACE_PEEKUSER, proc->pid,
- (void *)(4 * arg_num), 0);
- } else {
- return ptrace(PTRACE_PEEKDATA, proc->pid,
- proc->stack_pointer + 4 * (arg_num - 4),
- 0);
+enum {
+ COND_ALWAYS = 0xe,
+ COND_NV = 0xf,
+ FLAG_C = 0x20000000,
+};
+
+static int
+arm_get_next_pcs(struct process *proc,
+ const arch_addr_t pc, arch_addr_t next_pcs[2])
+{
+ uint32_t this_instr;
+ uint32_t status;
+ if (proc_read_32(proc, pc, &this_instr) < 0
+ || arm_get_register(proc, ARM_REG_CPSR, &status) < 0)
+ return -1;
+
+ /* In theory, we sometimes don't even need to add any
+ * breakpoints at all. If the conditional bits of the
+ * instruction indicate that it should not be taken, then we
+ * can just skip it altogether without bothering. We could
+ * also emulate the instruction under the breakpoint.
+ *
+ * Here, we make it as simple as possible (though We Accept
+ * Patches). */
+ int nr = 0;
+
+ /* ARM can branch either relatively by using a branch
+ * instruction, or absolutely, by doing arbitrary arithmetic
+ * with PC as the destination. */
+ const unsigned cond = BITS(this_instr, 28, 31);
+ const unsigned opcode = BITS(this_instr, 24, 27);
+
+ if (cond == COND_NV)
+ switch (opcode) {
+ arch_addr_t addr;
+ case 0xa:
+ case 0xb:
+ /* Branch with Link and change to Thumb. */
+ /* XXX double cast. */
+ addr = (arch_addr_t)
+ ((uint32_t)arm_branch_dest(pc, this_instr)
+ | (((this_instr >> 24) & 0x1) << 1));
+ next_pcs[nr++] = MAKE_THUMB_ADDR(addr);
+ break;
}
- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) {
- if (arg_num < 5) {
- if (a->valid && type == LT_TOF_SYSCALL)
- return a->regs.uregs[arg_num];
- if (a->valid && type == LT_TOF_SYSCALLR)
- return a->sysc_arg[arg_num];
- return ptrace(PTRACE_PEEKUSER, proc->pid,
- (void *)(4 * arg_num), 0);
- } else {
- return ptrace(PTRACE_PEEKDATA, proc->pid,
- proc->stack_pointer + 4 * (arg_num - 5),
- 0);
+ else
+ switch (opcode) {
+ uint32_t operand1, operand2, result = 0;
+ case 0x0:
+ case 0x1: /* data processing */
+ case 0x2:
+ case 0x3:
+ if (BITS(this_instr, 12, 15) != ARM_REG_PC)
+ break;
+
+ if (BITS(this_instr, 22, 25) == 0
+ && BITS(this_instr, 4, 7) == 9) { /* multiply */
+ invalid:
+ fprintf(stderr,
+ "Invalid update to pc in instruction.\n");
+ break;
+ }
+
+ /* BX <reg>, BLX <reg> */
+ if (BITS(this_instr, 4, 27) == 0x12fff1
+ || BITS(this_instr, 4, 27) == 0x12fff3) {
+ enum arm_register reg = BITS(this_instr, 0, 3);
+ /* XXX double cast: no need to go
+ * through tmp. */
+ uint32_t tmp;
+ if (arm_get_register_offpc(proc, reg, &tmp) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)tmp;
+ return 0;
+ }
+
+ /* Multiply into PC. */
+ if (arm_get_register_offpc
+ (proc, BITS(this_instr, 16, 19), &operand1) < 0)
+ return -1;
+
+ int c = (status & FLAG_C) ? 1 : 0;
+ if (BIT(this_instr, 25)) {
+ uint32_t immval = BITS(this_instr, 0, 7);
+ uint32_t rotate = 2 * BITS(this_instr, 8, 11);
+ operand2 = (((immval >> rotate)
+ | (immval << (32 - rotate)))
+ & 0xffffffff);
+ } else {
+ /* operand 2 is a shifted register. */
+ if (arm_get_shifted_register
+ (proc, this_instr, c, pc, &operand2) < 0)
+ return -1;
+ }
+
+ switch (BITS(this_instr, 21, 24)) {
+ case 0x0: /*and */
+ result = operand1 & operand2;
+ break;
+
+ case 0x1: /*eor */
+ result = operand1 ^ operand2;
+ break;
+
+ case 0x2: /*sub */
+ result = operand1 - operand2;
+ break;
+
+ case 0x3: /*rsb */
+ result = operand2 - operand1;
+ break;
+
+ case 0x4: /*add */
+ result = operand1 + operand2;
+ break;
+
+ case 0x5: /*adc */
+ result = operand1 + operand2 + c;
+ break;
+
+ case 0x6: /*sbc */
+ result = operand1 - operand2 + c;
+ break;
+
+ case 0x7: /*rsc */
+ result = operand2 - operand1 + c;
+ break;
+
+ case 0x8:
+ case 0x9:
+ case 0xa:
+ case 0xb: /* tst, teq, cmp, cmn */
+ /* Only take the default branch. */
+ result = 0;
+ break;
+
+ case 0xc: /*orr */
+ result = operand1 | operand2;
+ break;
+
+ case 0xd: /*mov */
+ /* Always step into a function. */
+ result = operand2;
+ break;
+
+ case 0xe: /*bic */
+ result = operand1 & ~operand2;
+ break;
+
+ case 0xf: /*mvn */
+ result = ~operand2;
+ break;
+ }
+
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)result;
+ break;
+
+ case 0x4:
+ case 0x5: /* data transfer */
+ case 0x6:
+ case 0x7:
+ /* Ignore if insn isn't load or Rn not PC. */
+ if (!BIT(this_instr, 20)
+ || BITS(this_instr, 12, 15) != ARM_REG_PC)
+ break;
+
+ if (BIT(this_instr, 22))
+ goto invalid;
+
+ /* byte write to PC */
+ uint32_t base;
+ if (arm_get_register_offpc
+ (proc, BITS(this_instr, 16, 19), &base) < 0)
+ return -1;
+
+ if (BIT(this_instr, 24)) {
+ /* pre-indexed */
+ int c = (status & FLAG_C) ? 1 : 0;
+ uint32_t offset;
+ if (BIT(this_instr, 25)) {
+ if (arm_get_shifted_register
+ (proc, this_instr, c,
+ pc, &offset) < 0)
+ return -1;
+ } else {
+ offset = BITS(this_instr, 0, 11);
+ }
+
+ if (BIT(this_instr, 23))
+ base += offset;
+ else
+ base -= offset;
+ }
+
+ /* XXX two double casts. */
+ uint32_t next;
+ if (proc_read_32(proc, (arch_addr_t)base, &next) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)next;
+ break;
+
+ case 0x8:
+ case 0x9: /* block transfer */
+ if (!BIT(this_instr, 20))
+ break;
+ /* LDM */
+ if (BIT(this_instr, 15)) {
+ /* Loading pc. */
+ int offset = 0;
+ enum arm_register rn = BITS(this_instr, 16, 19);
+ uint32_t rn_val;
+ if (arm_get_register(proc, rn, &rn_val) < 0)
+ return -1;
+
+ int pre = BIT(this_instr, 24);
+ if (BIT(this_instr, 23)) {
+ /* Bit U = up. */
+ unsigned reglist
+ = BITS(this_instr, 0, 14);
+ offset = bitcount(reglist) * 4;
+ if (pre)
+ offset += 4;
+ } else if (pre) {
+ offset = -4;
+ }
+
+ /* XXX double cast. */
+ arch_addr_t addr
+ = (arch_addr_t)(rn_val + offset);
+ uint32_t next;
+ if (proc_read_32(proc, addr, &next) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)next;
+ }
+ break;
+
+ case 0xb: /* branch & link */
+ case 0xa: /* branch */
+ next_pcs[nr++] = arm_branch_dest(pc, this_instr);
+ break;
+
+ case 0xc:
+ case 0xd:
+ case 0xe: /* coproc ops */
+ case 0xf: /* SWI */
+ break;
+ }
+
+ /* Otherwise take the next instruction. */
+ if (cond != COND_ALWAYS || nr == 0)
+ next_pcs[nr++] = pc + 4;
+ return 0;
+}
+
+/* Return the size in bytes of the complete Thumb instruction whose
+ * first halfword is INST1. */
+
+static int
+thumb_insn_size (unsigned short inst1)
+{
+ if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+ return 4;
+ else
+ return 2;
+}
+
+static int
+thumb_get_next_pcs(struct process *proc,
+ const arch_addr_t pc, arch_addr_t next_pcs[2])
+{
+ uint16_t inst1;
+ uint32_t status;
+ if (proc_read_16(proc, pc, &inst1) < 0
+ || arm_get_register(proc, ARM_REG_CPSR, &status) < 0)
+ return -1;
+
+ int nr = 0;
+
+ /* We currently ignore Thumb-2 conditional execution support
+ * (the IT instruction). No branches are allowed in IT block,
+ * and it's not legal to jump in the middle of it, so unless
+ * we need to singlestep through large swaths of code, which
+ * we currently don't, we can ignore them. */
+
+ if ((inst1 & 0xff00) == 0xbd00) { /* pop {rlist, pc} */
+ /* Fetch the saved PC from the stack. It's stored
+ * above all of the other registers. */
+ const unsigned offset = bitcount(BITS(inst1, 0, 7)) * 4;
+ uint32_t sp;
+ uint32_t next;
+ /* XXX two double casts */
+ if (arm_get_register(proc, ARM_REG_SP, &sp) < 0
+ || proc_read_32(proc, (arch_addr_t)(sp + offset),
+ &next) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)next;
+ } else if ((inst1 & 0xf000) == 0xd000) { /* conditional branch */
+ const unsigned long cond = BITS(inst1, 8, 11);
+ if (cond != 0x0f) { /* SWI */
+ next_pcs[nr++] = pc + (SBITS(inst1, 0, 7) << 1);
+ if (cond == COND_ALWAYS)
+ return 0;
+ }
+ } else if ((inst1 & 0xf800) == 0xe000) { /* unconditional branch */
+ next_pcs[nr++] = pc + (SBITS(inst1, 0, 10) << 1);
+ } else if (thumb_insn_size(inst1) == 4) { /* 32-bit instruction */
+ unsigned short inst2;
+ if (proc_read_16(proc, pc + 2, &inst2) < 0)
+ return -1;
+
+ if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000) {
+ /* Branches and miscellaneous control instructions. */
+
+ if ((inst2 & 0x1000) != 0
+ || (inst2 & 0xd001) == 0xc000) {
+ /* B, BL, BLX. */
+
+ const int imm1 = SBITS(inst1, 0, 10);
+ const unsigned imm2 = BITS(inst2, 0, 10);
+ const unsigned j1 = BIT(inst2, 13);
+ const unsigned j2 = BIT(inst2, 11);
+
+ int32_t offset
+ = ((imm1 << 12) + (imm2 << 1));
+ offset ^= ((!j2) << 22) | ((!j1) << 23);
+
+ /* XXX double cast */
+ uint32_t next = (uint32_t)(pc + offset);
+ /* For BLX make sure to clear the low bits. */
+ if (BIT(inst2, 12) == 0)
+ next = next & 0xfffffffc;
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)next;
+ return 0;
+ } else if (inst1 == 0xf3de
+ && (inst2 & 0xff00) == 0x3f00) {
+ /* SUBS PC, LR, #imm8. */
+ uint32_t next;
+ if (arm_get_register(proc, ARM_REG_LR,
+ &next) < 0)
+ return -1;
+ next -= inst2 & 0x00ff;
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)next;
+ return 0;
+ } else if ((inst2 & 0xd000) == 0x8000
+ && (inst1 & 0x0380) != 0x0380) {
+ /* Conditional branch. */
+ const int sign = SBITS(inst1, 10, 10);
+ const unsigned imm1 = BITS(inst1, 0, 5);
+ const unsigned imm2 = BITS(inst2, 0, 10);
+ const unsigned j1 = BIT(inst2, 13);
+ const unsigned j2 = BIT(inst2, 11);
+
+ int32_t offset = (sign << 20)
+ + (j2 << 19) + (j1 << 18);
+ offset += (imm1 << 12) + (imm2 << 1);
+ next_pcs[nr++] = pc + offset;
+ if (BITS(inst1, 6, 9) == COND_ALWAYS)
+ return 0;
+ }
+ } else if ((inst1 & 0xfe50) == 0xe810) {
+ int load_pc = 1;
+ int offset;
+ const enum arm_register rn = BITS(inst1, 0, 3);
+
+ if (BIT(inst1, 7) && !BIT(inst1, 8)) {
+ /* LDMIA or POP */
+ if (!BIT(inst2, 15))
+ load_pc = 0;
+ offset = bitcount(inst2) * 4 - 4;
+ } else if (!BIT(inst1, 7) && BIT(inst1, 8)) {
+ /* LDMDB */
+ if (!BIT(inst2, 15))
+ load_pc = 0;
+ offset = -4;
+ } else if (BIT(inst1, 7) && BIT(inst1, 8)) {
+ /* RFEIA */
+ offset = 0;
+ } else if (!BIT(inst1, 7) && !BIT(inst1, 8)) {
+ /* RFEDB */
+ offset = -8;
+ } else {
+ load_pc = 0;
+ }
+
+ if (load_pc) {
+ uint32_t addr;
+ if (arm_get_register(proc, rn, &addr) < 0)
+ return -1;
+ arch_addr_t a = (arch_addr_t)(addr + offset);
+ uint32_t next;
+ if (proc_read_32(proc, a, &next) < 0)
+ return -1;
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)next;
+ }
+ } else if ((inst1 & 0xffef) == 0xea4f
+ && (inst2 & 0xfff0) == 0x0f00) {
+ /* MOV PC or MOVS PC. */
+ const enum arm_register rn = BITS(inst2, 0, 3);
+ uint32_t next;
+ if (arm_get_register(proc, rn, &next) < 0)
+ return -1;
+ /* XXX double cast */
+ next_pcs[nr++] = (arch_addr_t)next;
+ } else if ((inst1 & 0xff70) == 0xf850
+ && (inst2 & 0xf000) == 0xf000) {
+ /* LDR PC. */
+ const enum arm_register rn = BITS(inst1, 0, 3);
+ uint32_t base;
+ if (arm_get_register(proc, rn, &base) < 0)
+ return -1;
+
+ int load_pc = 1;
+ if (rn == ARM_REG_PC) {
+ base = (base + 4) & ~(uint32_t)0x3;
+ if (BIT(inst1, 7))
+ base += BITS(inst2, 0, 11);
+ else
+ base -= BITS(inst2, 0, 11);
+ } else if (BIT(inst1, 7)) {
+ base += BITS(inst2, 0, 11);
+ } else if (BIT(inst2, 11)) {
+ if (BIT(inst2, 10)) {
+ if (BIT(inst2, 9))
+ base += BITS(inst2, 0, 7);
+ else
+ base -= BITS(inst2, 0, 7);
+ }
+ } else if ((inst2 & 0x0fc0) == 0x0000) {
+ const int shift = BITS(inst2, 4, 5);
+ const enum arm_register rm = BITS(inst2, 0, 3);
+ uint32_t v;
+ if (arm_get_register(proc, rm, &v) < 0)
+ return -1;
+ base += v << shift;
+ } else {
+ /* Reserved. */
+ load_pc = 0;
+ }
+
+ if (load_pc) {
+ /* xxx double casts */
+ uint32_t next;
+ if (proc_read_32(proc,
+ (arch_addr_t)base, &next) < 0)
+ return -1;
+ next_pcs[nr++] = (arch_addr_t)next;
+ }
+ } else if ((inst1 & 0xfff0) == 0xe8d0
+ && (inst2 & 0xfff0) == 0xf000) {
+ /* TBB. */
+ const enum arm_register tbl_reg = BITS(inst1, 0, 3);
+ const enum arm_register off_reg = BITS(inst2, 0, 3);
+
+ uint32_t table;
+ if (tbl_reg == ARM_REG_PC)
+ /* Regcache copy of PC isn't right yet. */
+ /* XXX double cast */
+ table = (uint32_t)pc + 4;
+ else if (arm_get_register(proc, tbl_reg, &table) < 0)
+ return -1;
+
+ uint32_t offset;
+ if (arm_get_register(proc, off_reg, &offset) < 0)
+ return -1;
+
+ table += offset;
+ uint8_t length;
+ /* XXX double cast */
+ if (proc_read_8(proc, (arch_addr_t)table, &length) < 0)
+ return -1;
+
+ next_pcs[nr++] = pc + 2 * length;
+
+ } else if ((inst1 & 0xfff0) == 0xe8d0
+ && (inst2 & 0xfff0) == 0xf010) {
+ /* TBH. */
+ const enum arm_register tbl_reg = BITS(inst1, 0, 3);
+ const enum arm_register off_reg = BITS(inst2, 0, 3);
+
+ uint32_t table;
+ if (tbl_reg == ARM_REG_PC)
+ /* Regcache copy of PC isn't right yet. */
+ /* XXX double cast */
+ table = (uint32_t)pc + 4;
+ else if (arm_get_register(proc, tbl_reg, &table) < 0)
+ return -1;
+
+ uint32_t offset;
+ if (arm_get_register(proc, off_reg, &offset) < 0)
+ return -1;
+
+ table += 2 * offset;
+ uint16_t length;
+ /* XXX double cast */
+ if (proc_read_16(proc, (arch_addr_t)table, &length) < 0)
+ return -1;
+
+ next_pcs[nr++] = pc + 2 * length;
}
- } else {
- fprintf(stderr, "gimme_arg called with wrong arguments\n");
- exit(1);
}
+
+ /* Otherwise take the next instruction. */
+ if (nr == 0)
+ next_pcs[nr++] = pc + thumb_insn_size(inst1);
return 0;
}
+
+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)
+{
+ const arch_addr_t pc = get_instruction_pointer(proc);
+
+ uint32_t cpsr;
+ if (arm_get_register(proc, ARM_REG_CPSR, &cpsr) < 0)
+ return SWS_FAIL;
+
+ const unsigned thumb_p = BIT(cpsr, 5);
+ arch_addr_t next_pcs[2] = {};
+ if ((thumb_p ? &thumb_get_next_pcs
+ : &arm_get_next_pcs)(proc, pc, next_pcs) < 0)
+ return SWS_FAIL;
+
+ int i;
+ for (i = 0; i < 2; ++i) {
+ /* XXX double cast. */
+ arch_addr_t target
+ = (arch_addr_t)(((uintptr_t)next_pcs[i]) | thumb_p);
+ if (next_pcs[i] != 0 && add_cb(target, add_cb_data) < 0)
+ return SWS_FAIL;
+ }
+
+ debug(1, "PTRACE_CONT");
+ ptrace(PTRACE_CONT, proc->pid, 0, 0);
+ return SWS_OK;
+}
+
+size_t
+arch_type_sizeof(struct process *proc, struct arg_type_info *info)
+{
+ if (proc == NULL)
+ return (size_t)-2;
+
+ switch (info->type) {
+ case ARGTYPE_VOID:
+ return 0;
+
+ case ARGTYPE_CHAR:
+ return 1;
+
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ return 2;
+
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_POINTER:
+ return 4;
+
+ case ARGTYPE_FLOAT:
+ return 4;
+ case ARGTYPE_DOUBLE:
+ return 8;
+
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT:
+ /* Use default value. */
+ return (size_t)-2;
+
+ default:
+ assert(info->type != info->type);
+ abort();
+ }
+}
+
+size_t
+arch_type_alignof(struct process *proc, struct arg_type_info *info)
+{
+ return arch_type_sizeof(proc, info);
+}