| /* |
| * This file is part of ltrace. |
| * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. |
| * Copyright (C) 1998,2004,2008,2009 Juan Cespedes |
| * Copyright (C) 2006 Ian Wienand |
| * |
| * 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 <string.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <signal.h> |
| #include <sys/ptrace.h> |
| #include <asm/ptrace.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 |
| #endif |
| |
| #if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) |
| # define PTRACE_POKEUSER PTRACE_POKEUSR |
| #endif |
| |
| void |
| get_arch_dep(struct process *proc) |
| { |
| proc_archdep *a; |
| |
| if (!proc->arch_ptr) |
| proc->arch_ptr = (void *)malloc(sizeof(proc_archdep)); |
| a = (proc_archdep *) (proc->arch_ptr); |
| a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0); |
| } |
| |
| /* Returns 0 if not a syscall, |
| * 1 if syscall entry, 2 if syscall exit, |
| * 3 if arch-specific syscall entry, 4 if arch-specific syscall exit, |
| * -1 on error. |
| */ |
| int |
| syscall_p(struct process *proc, int status, int *sysnum) |
| { |
| if (WIFSTOPPED(status) |
| && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { |
| 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); |
| |
| if (insn == 0xef000000 || insn == 0x0f000000 |
| || (insn & 0xffff0000) == 0xdf000000) { |
| /* EABI syscall */ |
| 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; |
| } else { |
| /* TODO: handle swi<cond> variations */ |
| /* one possible reason for getting in here is that we |
| * are coming from a signal handler, so the current |
| * PC does not point to the instruction just after the |
| * "swi" one. */ |
| output_line(proc, "unexpected instruction 0x%x at %p", |
| insn, pc); |
| return 0; |
| } |
| if ((*sysnum & 0xf0000) == 0xf0000) { |
| /* arch-specific syscall */ |
| *sysnum &= ~0xf0000; |
| return ip ? 4 : 3; |
| } |
| /* ARM syscall convention: on syscall entry, ip is zero; |
| * on syscall exit, ip is non-zero */ |
| return ip ? 2 : 1; |
| } |
| return 0; |
| } |
| |
| static arch_addr_t |
| arm_branch_dest(const arch_addr_t pc, const uint32_t insn) |
| { |
| /* Bits 0-23 are signed immediate value. */ |
| return pc + ((((insn & 0xffffff) ^ 0x800000) - 0x800000) << 2) + 8; |
| } |
| |
| /* 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)) |
| |
| 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 |
| 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; |
| } |
| } |
| |
| |
| /* 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); |
| } |