| /* |
| * make sure PEEKUSER matches GETREGS |
| * |
| * Copyright (c) 2008 Analog Devices Inc. |
| * |
| * Licensed under the GPL-2 or later |
| */ |
| |
| #define _GNU_SOURCE |
| |
| #include <errno.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #include <config.h> |
| #include "ptrace.h" |
| |
| #include "test.h" |
| #include "spawn_ptrace_child.h" |
| |
| char *TCID = "ptrace04"; |
| |
| static void cleanup(); |
| |
| #define R(r) { .name = "PT_" #r, .off = PT_##r }, |
| static struct { |
| const char *name; |
| long off; |
| } regs[] = { |
| #ifdef __bfin__ |
| R(ORIG_R0) R(ORIG_P0) |
| R(R0) R(R1) R(R2) R(R3) R(R4) R(R5) R(R6) R(R7) |
| R(P0) R(P1) R(P2) R(P3) R(P4) R(P5) R(FP) R(USP) |
| R(I0) R(I1) R(I2) R(I3) |
| R(M0) R(M1) R(M2) R(M3) |
| R(L0) R(L1) R(L2) R(L3) |
| R(B0) R(B1) R(B2) R(B3) |
| R(A0X) R(A0W) R(A1X) R(A1W) |
| R(LC0) R(LC1) R(LT0) R(LT1) R(LB0) R(LB1) |
| R(ASTAT) |
| R(RETS) R(PC) R(RETX) R(RETN) R(RETE) |
| R(SEQSTAT) R(IPEND) R(SYSCFG) |
| #endif |
| }; |
| |
| int TST_TOTAL = 2; |
| |
| void compare_registers(unsigned char poison) |
| { |
| #ifdef HAVE_STRUCT_PTRACE_REGS |
| ptrace_regs _pt_regs; |
| size_t i; |
| long ret; |
| bool failed = false; |
| |
| memset(&_pt_regs, poison, sizeof(_pt_regs)); |
| errno = 0; |
| ret = ptrace(PTRACE_GETREGS, pid, NULL, &_pt_regs); |
| if (ret && errno) { |
| tst_resm(TFAIL | TERRNO, "PTRACE_GETREGS failed"); |
| } else { |
| |
| for (i = 0; i < ARRAY_SIZE(regs); ++i) { |
| errno = 0; |
| ret = ptrace(PTRACE_PEEKUSER, pid, |
| (void *)regs[i].off, NULL); |
| if (ret && errno) { |
| tst_resm(TFAIL | TERRNO, |
| "PTRACE_PEEKUSER: register %s " |
| "(offset %li) failed", |
| regs[i].name, regs[i].off); |
| failed = true; |
| continue; |
| } |
| |
| long *pt_val = (void *)&_pt_regs + regs[i].off; |
| if (*pt_val != ret) { |
| tst_resm(TFAIL, |
| "register %s (offset %li) did not " |
| "match\n\tGETREGS: 0x%08lx " |
| "PEEKUSER: 0x%08lx", |
| regs[i].name, regs[i].off, *pt_val, |
| ret); |
| failed = true; |
| } |
| |
| } |
| |
| } |
| |
| tst_resm((failed ? TFAIL : TPASS), |
| "PTRACE PEEKUSER/GETREGS (poison 0x%02x)", poison); |
| #else |
| tst_brkm(TCONF, cleanup, "System doesn't have ptrace_regs structure"); |
| #endif |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| const char *msg; |
| |
| if (ARRAY_SIZE(regs) == 0) |
| tst_brkm(TCONF, NULL, "test not supported for your arch (yet)"); |
| |
| if ((msg = parse_opts(argc, argv, NULL, NULL))) |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| |
| make_a_baby(argc, argv); |
| |
| /* first compare register states when execl() syscall starts */ |
| tst_resm(TINFO, "Before exec() in child"); |
| compare_registers(0x00); |
| compare_registers(0xff); |
| |
| /* then compare register states after execl() syscall finishes */ |
| tst_resm(TINFO, "After exec() in child"); |
| errno = 0; |
| if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL) && errno) { |
| tst_brkm(TFAIL, NULL, "PTRACE_SYSCALL failed: %s", |
| strerror(errno)); |
| } |
| compare_registers(0x00); |
| compare_registers(0xff); |
| |
| /* hopefully this worked */ |
| ptrace(PTRACE_KILL, pid, NULL, NULL); |
| |
| tst_exit(); |
| } |
| |
| static void cleanup(void) |
| { |
| } |