| /* |
| * Copyright (c) 2002 Andi Kleen <ak@suse.de> |
| * Copyright (c) 2002 Michal Ludvig <mludvig@suse.cz> |
| * Copyright (c) 2002 Roland McGrath <roland@redhat.com> |
| * Copyright (c) 2008-2013 Denys Vlasenko <vda.linux@googlemail.com> |
| * Copyright (c) 2012 H.J. Lu <hongjiu.lu@intel.com> |
| * Copyright (c) 2010-2015 Dmitry V. Levin <ldv@altlinux.org> |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* Return codes: 1 - ok, 0 - ignore, other - error. */ |
| static int |
| arch_get_scno(struct tcb *tcp) |
| { |
| long scno = 0; |
| unsigned int currpers; |
| |
| #ifndef __X32_SYSCALL_BIT |
| # define __X32_SYSCALL_BIT 0x40000000 |
| #endif |
| |
| #if 1 |
| /* |
| * GETREGSET of NT_PRSTATUS tells us regset size, |
| * which unambiguously detects i386. |
| * |
| * Linux kernel distinguishes x86-64 and x32 processes |
| * solely by looking at __X32_SYSCALL_BIT: |
| * arch/x86/include/asm/compat.h::is_x32_task(): |
| * if (task_pt_regs(current)->orig_ax & __X32_SYSCALL_BIT) |
| * return true; |
| */ |
| if (x86_io.iov_len == sizeof(i386_regs)) { |
| scno = i386_regs.orig_eax; |
| currpers = 1; |
| } else { |
| scno = x86_64_regs.orig_rax; |
| currpers = 0; |
| if (scno & __X32_SYSCALL_BIT) { |
| /* |
| * Syscall number -1 requires special treatment: |
| * it might be a side effect of SECCOMP_RET_ERRNO |
| * filtering that sets orig_rax to -1 |
| * in some versions of linux kernel. |
| * If that is the case, then |
| * __X32_SYSCALL_BIT logic does not apply. |
| */ |
| if ((long long) x86_64_regs.orig_rax != -1) { |
| scno -= __X32_SYSCALL_BIT; |
| currpers = 2; |
| } else { |
| # ifdef X32 |
| currpers = 2; |
| # endif |
| } |
| } |
| } |
| |
| #elif 0 |
| /* |
| * cs = 0x33 for long mode (native 64 bit and x32) |
| * cs = 0x23 for compatibility mode (32 bit) |
| * ds = 0x2b for x32 mode (x86-64 in 32 bit) |
| */ |
| scno = x86_64_regs.orig_rax; |
| switch (x86_64_regs.cs) { |
| case 0x23: currpers = 1; break; |
| case 0x33: |
| if (x86_64_regs.ds == 0x2b) { |
| currpers = 2; |
| scno &= ~__X32_SYSCALL_BIT; |
| } else |
| currpers = 0; |
| break; |
| default: |
| error_msg("Unknown value CS=0x%08X while " |
| "detecting personality of process PID=%d", |
| (int)x86_64_regs.cs, tcp->pid); |
| currpers = current_personality; |
| break; |
| } |
| #elif 0 |
| /* |
| * This version analyzes the opcode of a syscall instruction. |
| * (int 0x80 on i386 vs. syscall on x86-64) |
| * It works, but is too complicated, and strictly speaking, unreliable. |
| */ |
| unsigned long call, rip = x86_64_regs.rip; |
| /* sizeof(syscall) == sizeof(int 0x80) == 2 */ |
| rip -= 2; |
| errno = 0; |
| call = ptrace(PTRACE_PEEKTEXT, tcp->pid, (char *)rip, (char *)0); |
| if (errno) |
| perror_msg("ptrace_peektext failed"); |
| switch (call & 0xffff) { |
| /* x86-64: syscall = 0x0f 0x05 */ |
| case 0x050f: currpers = 0; break; |
| /* i386: int 0x80 = 0xcd 0x80 */ |
| case 0x80cd: currpers = 1; break; |
| default: |
| currpers = current_personality; |
| error_msg("Unknown syscall opcode (0x%04X) while " |
| "detecting personality of process PID=%d", |
| (int)call, tcp->pid); |
| break; |
| } |
| #endif |
| |
| #ifdef X32 |
| /* |
| * If we are built for a x32 system, then personality 0 is x32 |
| * (not x86_64), and stracing of x86_64 apps is not supported. |
| * Stracing of i386 apps is still supported. |
| */ |
| if (currpers == 0) { |
| error_msg("syscall_%lu(...) in unsupported " |
| "64-bit mode of process PID=%d", scno, tcp->pid); |
| return 0; |
| } |
| currpers &= ~2; /* map 2,1 to 0,1 */ |
| #endif /* X32 */ |
| |
| update_personality(tcp, currpers); |
| tcp->scno = scno; |
| return 1; |
| } |