diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2012-12-12 17:24:39 -0500 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2012-12-13 11:34:24 -0500 |
commit | cb67e161bc947ab467657dda38168c2b2266f5bc (patch) | |
tree | 289e3905765484e8736a62f1a8a289d833e0960e /arch/tile | |
parent | 17a263540cebd4c615755300f34c695b15378a58 (diff) |
arch/tile: provide PT_FLAGS_COMPAT value in pt_regs
This flag is set for ptrace GETREGS or PEEKUSER for processes
that are COMPAT, i.e. 32-bit. This allows things like strace
to easily discover what personality to use, for example.
Acked-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Diffstat (limited to 'arch/tile')
-rw-r--r-- | arch/tile/include/uapi/asm/ptrace.h | 6 | ||||
-rw-r--r-- | arch/tile/kernel/ptrace.c | 57 |
2 files changed, 46 insertions, 17 deletions
diff --git a/arch/tile/include/uapi/asm/ptrace.h b/arch/tile/include/uapi/asm/ptrace.h index c717d0fec72..0d2208803b2 100644 --- a/arch/tile/include/uapi/asm/ptrace.h +++ b/arch/tile/include/uapi/asm/ptrace.h | |||
@@ -84,5 +84,11 @@ struct pt_regs { | |||
84 | #define PTRACE_O_TRACEMIGRATE 0x00010000 | 84 | #define PTRACE_O_TRACEMIGRATE 0x00010000 |
85 | #define PTRACE_EVENT_MIGRATE 16 | 85 | #define PTRACE_EVENT_MIGRATE 16 |
86 | 86 | ||
87 | /* | ||
88 | * Flag bits in pt_regs.flags that are part of the ptrace API. | ||
89 | * We start our numbering higher up to avoid confusion with the | ||
90 | * non-ABI kernel-internal values that use the low 16 bits. | ||
91 | */ | ||
92 | #define PT_FLAGS_COMPAT 0x10000 /* process is an -m32 compat process */ | ||
87 | 93 | ||
88 | #endif /* _UAPI_ASM_TILE_PTRACE_H */ | 94 | #endif /* _UAPI_ASM_TILE_PTRACE_H */ |
diff --git a/arch/tile/kernel/ptrace.c b/arch/tile/kernel/ptrace.c index e92e40527d6..64ba102c596 100644 --- a/arch/tile/kernel/ptrace.c +++ b/arch/tile/kernel/ptrace.c | |||
@@ -45,6 +45,41 @@ void ptrace_disable(struct task_struct *child) | |||
45 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 45 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
46 | } | 46 | } |
47 | 47 | ||
48 | /* | ||
49 | * Get registers from task and ready the result for userspace. | ||
50 | * Note that we localize the API issues to getregs() and putregs() at | ||
51 | * some cost in performance, e.g. we need a full pt_regs copy for | ||
52 | * PEEKUSR, and two copies for POKEUSR. But in general we expect | ||
53 | * GETREGS/PUTREGS to be the API of choice anyway. | ||
54 | */ | ||
55 | static char *getregs(struct task_struct *child, struct pt_regs *uregs) | ||
56 | { | ||
57 | *uregs = *task_pt_regs(child); | ||
58 | |||
59 | /* Set up flags ABI bits. */ | ||
60 | uregs->flags = 0; | ||
61 | #ifdef CONFIG_COMPAT | ||
62 | if (task_thread_info(child)->status & TS_COMPAT) | ||
63 | uregs->flags |= PT_FLAGS_COMPAT; | ||
64 | #endif | ||
65 | |||
66 | return (char *)uregs; | ||
67 | } | ||
68 | |||
69 | /* Put registers back to task. */ | ||
70 | static void putregs(struct task_struct *child, struct pt_regs *uregs) | ||
71 | { | ||
72 | struct pt_regs *regs = task_pt_regs(child); | ||
73 | |||
74 | /* Don't allow overwriting the kernel-internal flags word. */ | ||
75 | uregs->flags = regs->flags; | ||
76 | |||
77 | /* Only allow setting the ICS bit in the ex1 word. */ | ||
78 | uregs->ex1 = PL_ICS_EX1(USER_PL, EX1_ICS(uregs->ex1)); | ||
79 | |||
80 | *regs = *uregs; | ||
81 | } | ||
82 | |||
48 | long arch_ptrace(struct task_struct *child, long request, | 83 | long arch_ptrace(struct task_struct *child, long request, |
49 | unsigned long addr, unsigned long data) | 84 | unsigned long addr, unsigned long data) |
50 | { | 85 | { |
@@ -53,14 +88,13 @@ long arch_ptrace(struct task_struct *child, long request, | |||
53 | long ret = -EIO; | 88 | long ret = -EIO; |
54 | char *childreg; | 89 | char *childreg; |
55 | struct pt_regs copyregs; | 90 | struct pt_regs copyregs; |
56 | int ex1_offset; | ||
57 | 91 | ||
58 | switch (request) { | 92 | switch (request) { |
59 | 93 | ||
60 | case PTRACE_PEEKUSR: /* Read register from pt_regs. */ | 94 | case PTRACE_PEEKUSR: /* Read register from pt_regs. */ |
61 | if (addr >= PTREGS_SIZE) | 95 | if (addr >= PTREGS_SIZE) |
62 | break; | 96 | break; |
63 | childreg = (char *)task_pt_regs(child) + addr; | 97 | childreg = getregs(child, ©regs) + addr; |
64 | #ifdef CONFIG_COMPAT | 98 | #ifdef CONFIG_COMPAT |
65 | if (is_compat_task()) { | 99 | if (is_compat_task()) { |
66 | if (addr & (sizeof(compat_long_t)-1)) | 100 | if (addr & (sizeof(compat_long_t)-1)) |
@@ -79,17 +113,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
79 | case PTRACE_POKEUSR: /* Write register in pt_regs. */ | 113 | case PTRACE_POKEUSR: /* Write register in pt_regs. */ |
80 | if (addr >= PTREGS_SIZE) | 114 | if (addr >= PTREGS_SIZE) |
81 | break; | 115 | break; |
82 | childreg = (char *)task_pt_regs(child) + addr; | 116 | childreg = getregs(child, ©regs) + addr; |
83 | |||
84 | /* Guard against overwrites of the privilege level. */ | ||
85 | ex1_offset = PTREGS_OFFSET_EX1; | ||
86 | #if defined(CONFIG_COMPAT) && defined(__BIG_ENDIAN) | ||
87 | if (is_compat_task()) /* point at low word */ | ||
88 | ex1_offset += sizeof(compat_long_t); | ||
89 | #endif | ||
90 | if (addr == ex1_offset) | ||
91 | data = PL_ICS_EX1(USER_PL, EX1_ICS(data)); | ||
92 | |||
93 | #ifdef CONFIG_COMPAT | 117 | #ifdef CONFIG_COMPAT |
94 | if (is_compat_task()) { | 118 | if (is_compat_task()) { |
95 | if (addr & (sizeof(compat_long_t)-1)) | 119 | if (addr & (sizeof(compat_long_t)-1)) |
@@ -102,11 +126,12 @@ long arch_ptrace(struct task_struct *child, long request, | |||
102 | break; | 126 | break; |
103 | *(long *)childreg = data; | 127 | *(long *)childreg = data; |
104 | } | 128 | } |
129 | putregs(child, ©regs); | ||
105 | ret = 0; | 130 | ret = 0; |
106 | break; | 131 | break; |
107 | 132 | ||
108 | case PTRACE_GETREGS: /* Get all registers from the child. */ | 133 | case PTRACE_GETREGS: /* Get all registers from the child. */ |
109 | if (copy_to_user(datap, task_pt_regs(child), | 134 | if (copy_to_user(datap, getregs(child, ©regs), |
110 | sizeof(struct pt_regs)) == 0) { | 135 | sizeof(struct pt_regs)) == 0) { |
111 | ret = 0; | 136 | ret = 0; |
112 | } | 137 | } |
@@ -115,9 +140,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
115 | case PTRACE_SETREGS: /* Set all registers in the child. */ | 140 | case PTRACE_SETREGS: /* Set all registers in the child. */ |
116 | if (copy_from_user(©regs, datap, | 141 | if (copy_from_user(©regs, datap, |
117 | sizeof(struct pt_regs)) == 0) { | 142 | sizeof(struct pt_regs)) == 0) { |
118 | copyregs.ex1 = | 143 | putregs(child, ©regs); |
119 | PL_ICS_EX1(USER_PL, EX1_ICS(copyregs.ex1)); | ||
120 | *task_pt_regs(child) = copyregs; | ||
121 | ret = 0; | 144 | ret = 0; |
122 | } | 145 | } |
123 | break; | 146 | break; |