From 0afe159bc9a10c7d5173bace26df3aa3af7843bf Mon Sep 17 00:00:00 2001 In-Reply-To: <1383928417-38009-1-git-send-email-sson@FreeBSD.org> References: <1383928417-38009-1-git-send-email-sson@FreeBSD.org> From: Stacey Son Date: Sun, 6 Oct 2013 22:14:06 -0500 Subject: [PATCH v3 05/19] bsd-user: move arch/OS dependent code out of syscall.c This change moves the system call handler for sysctl(2) and sysarch(2) from syscall.c to the OS and arch dependent directories. This eliminates many of the #ifdef's in syscall.c. These system call handlers are now located in the host os and target arch directories. --- bsd-user/Makefile.objs | 2 +- bsd-user/arm/target_arch_sigtramp.h | 33 ++++ bsd-user/bsdload.c | 170 +++++++++++++----- bsd-user/elfload.c | 9 +- bsd-user/freebsd/os-sys.c | 284 +++++++++++++++++++++++++++++++ bsd-user/freebsd/target_os_stack.h | 157 +++++++++++++++++ bsd-user/i386/target_arch_sigtramp.h | 11 ++ bsd-user/mips/target_arch_sigtramp.h | 23 +++ bsd-user/mips64/target_arch_sigtramp.h | 23 +++ bsd-user/netbsd/os-sys.c | 46 +++++ bsd-user/netbsd/target_os_stack.h | 33 ++++ bsd-user/openbsd/os-sys.c | 46 +++++ bsd-user/openbsd/target_os_stack.h | 33 ++++ bsd-user/qemu.h | 30 +++- bsd-user/sparc/target_arch_sigtramp.h | 11 ++ bsd-user/sparc64/target_arch_sigtramp.h | 11 ++ bsd-user/syscall.c | 210 +++-------------------- bsd-user/x86_64/target_arch_sigtramp.h | 11 ++ 18 files changed, 900 insertions(+), 243 deletions(-) create mode 100644 bsd-user/arm/target_arch_sigtramp.h create mode 100644 bsd-user/freebsd/os-sys.c create mode 100644 bsd-user/freebsd/target_os_stack.h create mode 100644 bsd-user/i386/target_arch_sigtramp.h create mode 100644 bsd-user/mips/target_arch_sigtramp.h create mode 100644 bsd-user/mips64/target_arch_sigtramp.h create mode 100644 bsd-user/netbsd/os-sys.c create mode 100644 bsd-user/netbsd/target_os_stack.h create mode 100644 bsd-user/openbsd/os-sys.c create mode 100644 bsd-user/openbsd/target_os_stack.h create mode 100644 bsd-user/sparc/target_arch_sigtramp.h create mode 100644 bsd-user/sparc64/target_arch_sigtramp.h create mode 100644 bsd-user/x86_64/target_arch_sigtramp.h diff --git a/bsd-user/Makefile.objs b/bsd-user/Makefile.objs index 41e8dce..a4dca8e 100644 --- a/bsd-user/Makefile.objs +++ b/bsd-user/Makefile.objs @@ -1,2 +1,2 @@ obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ - uaccess.o $(TARGET_ABI_DIR)/target_arch_cpu.o + uaccess.o $(HOST_VARIANT_DIR)/os-sys.o $(TARGET_ABI_DIR)/target_arch_cpu.o diff --git a/bsd-user/arm/target_arch_sigtramp.h b/bsd-user/arm/target_arch_sigtramp.h new file mode 100644 index 0000000..98dc313 --- /dev/null +++ b/bsd-user/arm/target_arch_sigtramp.h @@ -0,0 +1,33 @@ + +#ifndef _TARGET_ARCH_SIGTRAMP_H_ +#define _TARGET_ARCH_SIGTRAMP_H_ + +/* Compare to arm/arm/locore.S ENTRY_NP(sigcode) */ +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + int i; + uint32_t sys_exit = TARGET_FREEBSD_NR_exit; + /* + * The code has to load r7 manually rather than using + * "ldr r7, =SYS_return to make sure the size of the + * code is correct. + */ + uint32_t sigtramp_code[] = { + /* 1 */ 0xE1A0000D, /* mov r0, sp */ + /* 2 */ 0xE59F700C, /* ldr r7, [pc, #12] */ + /* 3 */ 0xEF000000 + sys_sigreturn, /* swi (SYS_sigreturn) */ + /* 4 */ 0xE59F7008, /* ldr r7, [pc, #8] */ + /* 5 */ 0xEF000000 + sys_exit, /* swi (SYS_exit)*/ + /* 6 */ 0xEAFFFFFA, /* b . -16 */ + /* 7 */ sys_sigreturn, + /* 8 */ sys_exit + }; + + for (i = 0; i < 8; i++) { + tswap32s(&sigtramp_code[i]); + } + + return memcpy_to_target(offset, sigtramp_code, TARGET_SZSIGCODE); +} +#endif /* _TARGET_ARCH_SIGTRAMP_H_ */ diff --git a/bsd-user/bsdload.c b/bsd-user/bsdload.c index 2abc713..45fdcf8 100644 --- a/bsd-user/bsdload.c +++ b/bsd-user/bsdload.c @@ -1,4 +1,19 @@ -/* Code for loading BSD executables. Mostly linux kernel code. */ +/* + * Load BSD executables. + * + * 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, see . + */ #include #include @@ -26,38 +41,22 @@ abi_long memcpy_to_target(abi_ulong dest, const void *src, return 0; } -static int in_group_p(gid_t g) -{ - /* return TRUE if we're in the specified group, FALSE otherwise */ - int ngroup; - int i; - gid_t grouplist[TARGET_NGROUPS]; - - ngroup = getgroups(TARGET_NGROUPS, grouplist); - for(i = 0; i < ngroup; i++) { - if(grouplist[i] == g) { - return 1; - } - } - return 0; -} - static int count(char ** vec) { int i; - for(i = 0; *vec; i++) { + for (i = 0; *vec; i++) { vec++; } return(i); } -static int prepare_binprm(struct linux_binprm *bprm) +static int prepare_binprm(struct bsd_binprm *bprm) { struct stat st; int mode; - int retval, id_change; + int retval; if(fstat(bprm->fd, &st) < 0) { return(-errno); @@ -73,14 +72,10 @@ static int prepare_binprm(struct linux_binprm *bprm) bprm->e_uid = geteuid(); bprm->e_gid = getegid(); - id_change = 0; /* Set-uid? */ if(mode & S_ISUID) { bprm->e_uid = st.st_uid; - if(bprm->e_uid != geteuid()) { - id_change = 1; - } } /* Set-gid? */ @@ -91,9 +86,6 @@ static int prepare_binprm(struct linux_binprm *bprm) */ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { bprm->e_gid = st.st_gid; - if (!in_group_p(bprm->e_gid)) { - id_change = 1; - } } memset(bprm->buf, 0, sizeof(bprm->buf)); @@ -154,34 +146,116 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, return sp; } +static int is_there(const char *candidate) +{ + struct stat fin; + + /* XXX work around access(2) false positives for superuser */ + if (access(candidate, X_OK) == 0 && stat(candidate, &fin) == 0 && + S_ISREG(fin.st_mode) && (getuid() != 0 || + (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) { + return 1; + } + + return 0; +} + +static int find_in_path(char *path, const char *filename, char *retpath, + size_t rpsize) +{ + const char *d; + int found; + + if (strchr(filename, '/') != NULL) { + if (is_there(filename)) { + if (!realpath(filename, retpath)) { + return -1; + } + return 0; + } else { + return -1; + } + } + + found = 0; + while ((d = strsep(&path, ":")) != NULL) { + if (*d == '\0') { + d = "."; + } + if (snprintf(retpath, rpsize, "%s/%s", d, filename) >= (int)rpsize) { + continue; + } + if (is_there((const char *)retpath)) { + found = 1; + break; + } + } + return found; +} + int loader_exec(const char * filename, char ** argv, char ** envp, - struct target_pt_regs * regs, struct image_info *infop) + struct target_pt_regs *regs, struct image_info *infop, + struct bsd_binprm *bprm) { - struct linux_binprm bprm; - int retval; - int i; + char *p, *path = NULL, fullpath[PATH_MAX]; + const char *execname = NULL; + int retval, i, found; - bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); + bprm->p = TARGET_PAGE_SIZE * MAX_ARG_PAGES; /* -sizeof(unsigned int); */ for (i=0 ; ipage[i] = NULL; + + /* Find target executable in path, if not already an absolute path. */ + p = getenv("PATH"); + if (p != NULL) { + path = g_strdup(p); + if (path == NULL) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + execname = realpath(filename, NULL); + if (execname == NULL) { + execname = g_strdup(filename); + } + found = find_in_path(path, execname, fullpath, sizeof(fullpath)); + /* Absolute path specified but not found? */ + if (found == -1) { + return -1; + } + if (found) { + retval = open(fullpath, O_RDONLY); + bprm->fullpath = g_strdup(fullpath); + } else { + retval = open(execname, O_RDONLY); + bprm->fullpath = NULL; + } + if (execname) { + g_free((void *)execname); + } + g_free(path); + } else { + retval = open(filename, O_RDONLY); + bprm->fullpath = NULL; + } + if (retval < 0) { return retval; - bprm.fd = retval; - bprm.filename = (char *)filename; - bprm.argc = count(argv); - bprm.argv = argv; - bprm.envc = count(envp); - bprm.envp = envp; + } + + bprm->fd = retval; + bprm->filename = (char *)filename; + bprm->argc = count(argv); + bprm->argv = argv; + bprm->envc = count(envp); + bprm->envp = envp; - retval = prepare_binprm(&bprm); + retval = prepare_binprm(bprm); if(retval>=0) { - if (bprm.buf[0] == 0x7f - && bprm.buf[1] == 'E' - && bprm.buf[2] == 'L' - && bprm.buf[3] == 'F') { - retval = load_elf_binary(&bprm,regs,infop); + if (bprm->buf[0] == 0x7f + && bprm->buf[1] == 'E' + && bprm->buf[2] == 'L' + && bprm->buf[3] == 'F') { + retval = load_elf_binary(bprm, regs, infop); } else { fprintf(stderr, "Unknown binary format\n"); return -1; @@ -196,7 +270,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp, /* Something went wrong, return the inode and free the argument pages*/ for (i=0 ; ipage[i]); } return(retval); } diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index ccf72d1..68d0209 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -91,6 +91,9 @@ enum { #define ELIBBAD 80 #endif +abi_ulong target_stksiz; +abi_ulong target_stkbas; + #ifdef TARGET_I386 #define ELF_PLATFORM get_elf_platform() @@ -665,7 +668,7 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page, return p; } -static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, +static abi_ulong setup_arg_pages(abi_ulong p, struct bsd_binprm *bprm, struct image_info *info) { abi_ulong stack_base, size, error; @@ -1147,8 +1150,8 @@ static void load_symbols(struct elfhdr *hdr, int fd) syminfos = s; } -int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, - struct image_info * info) +int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs, + struct image_info *info) { struct elfhdr elf_ex; struct elfhdr interp_elf_ex; diff --git a/bsd-user/freebsd/os-sys.c b/bsd-user/freebsd/os-sys.c new file mode 100644 index 0000000..c8f999f --- /dev/null +++ b/bsd-user/freebsd/os-sys.c @@ -0,0 +1,284 @@ +/* + * FreeBSD sysctl() and sysarch() system call emulation + * + * Copyright (c) 2013 Stacey D. Son + * + * 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, see . + */ + +#include +#include +#include +#include + +#include "qemu.h" + +#include "target_arch_sysarch.h" +#include "target_os_vmparam.h" + +/* + * XXX this uses the undocumented oidfmt interface to find the kind of + * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt() + * (compare to src/sbin/sysctl/sysctl.c) + */ +static int +oidfmt(int *oid, int len, char *fmt, uint32_t *kind) +{ + int qoid[CTL_MAXNAME+2]; + uint8_t buf[BUFSIZ]; + int i; + size_t j; + + qoid[0] = 0; + qoid[1] = 4; + memcpy(qoid + 2, oid, len * sizeof(int)); + + j = sizeof(buf); + i = sysctl(qoid, len + 2, buf, &j, 0, 0); + if (i) { + return i; + } + + if (kind) { + *kind = *(uint32_t *)buf; + } + + if (fmt) { + strcpy(fmt, (char *)(buf + sizeof(uint32_t))); + } + return 0; +} + +/* + * try and convert sysctl return data for the target. + * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT. + */ +static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind) +{ + switch (kind & CTLTYPE) { + case CTLTYPE_INT: + case CTLTYPE_UINT: + *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp); + break; + +#ifdef TARGET_ABI32 + case CTLTYPE_LONG: + case CTLTYPE_ULONG: + *(uint32_t *)holdp = tswap32(*(long *)holdp); + break; +#else + case CTLTYPE_LONG: + *(uint64_t *)holdp = tswap64(*(long *)holdp); + case CTLTYPE_ULONG: + *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp); + break; +#endif +#if !defined(__FreeBSD_version) || __FreeBSD_version < 900031 + case CTLTYPE_QUAD: +#else + case CTLTYPE_U64: + case CTLTYPE_S64: +#endif + *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp); + break; + + case CTLTYPE_STRING: + break; + + default: + /* XXX unhandled */ + return -1; + } + return 0; +} + +/* + * Convert the undocmented name2oid sysctl data for the target. + */ +static inline void sysctl_name2oid(uint32_t *holdp, size_t holdlen) +{ + size_t i; + + for (i = 0; i < holdlen; i++) { + holdp[i] = tswap32(holdp[i]); + } +} + +static inline void sysctl_oidfmt(uint32_t *holdp) +{ + /* byte swap the kind */ + holdp[0] = tswap32(holdp[0]); +} + +abi_long do_freebsd_sysctl(CPUArchState *env, abi_ulong namep, int32_t namelen, + abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen) +{ + abi_long ret; + void *hnamep, *holdp = NULL, *hnewp = NULL; + size_t holdlen; + abi_ulong oldlen = 0; + int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i; + uint32_t kind = 0; + TaskState *ts = (TaskState *)env->opaque; + + if (oldlenp) { + if (get_user_ual(oldlen, oldlenp)) { + return -TARGET_EFAULT; + } + } + hnamep = lock_user(VERIFY_READ, namep, namelen, 1); + if (hnamep == NULL) { + return -TARGET_EFAULT; + } + if (newp) { + hnewp = lock_user(VERIFY_READ, newp, newlen, 1); + if (hnewp == NULL) { + return -TARGET_EFAULT; + } + } + if (oldp) { + holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0); + if (holdp == NULL) { + return -TARGET_EFAULT; + } + } + holdlen = oldlen; + for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++) { + *q++ = tswap32(*p); + } + oidfmt(snamep, namelen, NULL, &kind); + + /* Handle some arch/emulator dependent sysctl()'s here. */ + switch (snamep[0]) { + case CTL_KERN: + switch (snamep[1]) { + case KERN_USRSTACK: +#if TARGET_USRSTACK != 0 + (*(abi_ulong *)holdp) = tswapal(TARGET_USRSTACK); + holdlen = sizeof(abi_ulong); + ret = 0; +#else + ret = -TARGET_ENOENT; +#endif + goto out; + + case KERN_PS_STRINGS: +#if defined(TARGET_PS_STRINGS) + (*(abi_ulong *)holdp) = tswapal(TARGET_PS_STRINGS); + holdlen = sizeof(abi_ulong); + ret = 0; +#else + ret = -TARGET_ENOENT; +#endif + goto out; + + case KERN_PROC: + switch (snamep[2]) { + case KERN_PROC_PATHNAME: + holdlen = strlen(ts->bprm->fullpath) + 1; + if (holdp) { + if (oldlen < holdlen) { + ret = -TARGET_EINVAL; + goto out; + } + strlcpy(holdp, ts->bprm->fullpath, oldlen); + } + ret = 0; + goto out; + + default: + break; + } + break; + + default: + break; + } + break; + + case CTL_HW: + switch (snamep[1]) { + case HW_MACHINE: + strlcpy(holdp, TARGET_HW_MACHINE, oldlen); + ret = 0; + goto out; + + case HW_MACHINE_ARCH: + strlcpy(holdp, TARGET_HW_MACHINE_ARCH, oldlen); + ret = 0; + goto out; + + case 851: /* hw.availpages */ + { + long lvalue; + size_t len = sizeof(lvalue); + + if (sysctlbyname("hw.availpages", &lvalue, &len, NULL, 0) + == -1) { + ret = -1; + } else { + (*(abi_ulong *)holdp) = tswapal((abi_ulong)lvalue); + holdlen = sizeof(abi_ulong); + ret = 0; + } + } + goto out; + + default: + break; + } + default: + break; + } + + ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen)); + if (!ret && (holdp != 0 && holdlen != 0)) { + if (0 == snamep[0] && (3 == snamep[1] || 4 == snamep[1])) { + if (3 == snamep[1]) { + /* Handle the undocumented name2oid special case. */ + sysctl_name2oid(holdp, holdlen); + } else { + /* Handle oidfmt */ + sysctl_oidfmt(holdp); + } + } else { + sysctl_oldcvt(holdp, holdlen, kind); + } + } +#ifdef DEBUG + else { + printf("sysctl(mib[0]=%d, mib[1]=%d, mib[3]=%d...) returned %d\n", + snamep[0], snamep[1], snamep[2], (int)ret); + } +#endif + +out: + if (oldlenp) { + put_user_ual(holdlen, oldlenp); + } + unlock_user(hnamep, namep, 0); + unlock_user(holdp, oldp, holdlen); + if (hnewp) { + unlock_user(hnewp, newp, 0); + } + g_free(snamep); + return ret; +} + +/* sysarch() is architecture dependent. */ +abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2) +{ + + return do_freebsd_arch_sysarch(cpu_env, arg1, arg2); +} diff --git a/bsd-user/freebsd/target_os_stack.h b/bsd-user/freebsd/target_os_stack.h new file mode 100644 index 0000000..c84b69e --- /dev/null +++ b/bsd-user/freebsd/target_os_stack.h @@ -0,0 +1,157 @@ +#ifndef _TARGET_OS_STACK_H_ +#define _TARGET_OS_STACK_H_ + +#include +#include "target_arch_sigtramp.h" + +/* + * The inital FreeBSD stack is as follows: + * (see kern/kern_exec.c exec_copyout_strings() ) + * + * Hi Address -> char **ps_argvstr (struct ps_strings for ps, w, etc.) + * unsigned ps_nargvstr + * char **ps_envstr + * PS_STRINGS -> unsigned ps_nenvstr + * + * machine dependent sigcode (sv_sigcode of size + * sv_szsigcode) + * + * execpath (absolute image path for rtld) + * + * SSP Canary (sizeof(long) * 8) + * + * page sizes array (usually sizeof(u_long) ) + * + * "destp" -> argv, env strings (up to 262144 bytes) + */ +static inline int setup_initial_stack(struct bsd_binprm *bprm, + abi_ulong *ret_addr) +{ + int i; + abi_ulong stack_hi_addr; + size_t execpath_len, stringspace; + abi_ulong destp, argvp, envp, p; + struct target_ps_strings ps_strs; + char canary[sizeof(abi_long) * 8]; + + stack_hi_addr = p = target_stkbas + target_stksiz; + + /* Save some space for ps_strings. */ + p -= sizeof(struct target_ps_strings); + +#ifdef TARGET_SZSIGCODE + /* Add machine depedent sigcode. */ + p -= TARGET_SZSIGCODE; + if (setup_sigtramp(p, (unsigned)offsetof(struct target_sigframe, sf_uc), + TARGET_FREEBSD_NR_sigreturn)) { + errno = EFAULT; + return -1; + } +#endif + if (bprm->fullpath) { + execpath_len = strlen(bprm->fullpath) + 1; + p -= roundup(execpath_len, sizeof(abi_ulong)); + if (memcpy_to_target(p, bprm->fullpath, execpath_len)) { + errno = EFAULT; + return -1; + } + } + /* Add canary for SSP. */ + arc4random_buf(canary, sizeof(canary)); + p -= roundup(sizeof(canary), sizeof(abi_ulong)); + if (memcpy_to_target(p, canary, sizeof(canary))) { + errno = EFAULT; + return -1; + } + /* Add page sizes array. */ + /* p -= sizeof(int); */ + p -= sizeof(abi_ulong); + /* if (put_user_u32(TARGET_PAGE_SIZE, p)) { */ + if (put_user_ual(TARGET_PAGE_SIZE, p)) { + errno = EFAULT; + return -1; + } + /* Calculate the string space needed */ + stringspace = 0; + for (i = 0; i < bprm->argc; ++i) { + stringspace += strlen(bprm->argv[i]) + 1; + } + for (i = 0; i < bprm->envc; ++i) { + stringspace += strlen(bprm->envp[i]) + 1; + } + if (stringspace > TARGET_ARG_MAX) { + errno = ENOMEM; + return -1; + } + + /* Make room for the argv and envp strings */ + /* p = destp = roundup(p - TARGET_SPACE_USRSPACE - (TARGET_ARG_MAX - stringspace), sizeof(abi_ulong)); */ + argvp = p - TARGET_SPACE_USRSPACE; + p = destp = roundup(p - TARGET_SPACE_USRSPACE - TARGET_ARG_MAX, sizeof(abi_ulong)); + + /* + * Add argv strings. Note that the argv[] vectors are added by + * loader_build_argptr() + */ + /* XXX need to make room for auxargs */ + /* argvp = destp - ((bprm->argc + bprm->envc + 2) * sizeof(abi_ulong)); */ + /* envp = argvp + (bprm->argc + 2) * sizeof(abi_ulong); */ + envp = argvp + (bprm->argc + 1) * sizeof(abi_ulong); + ps_strs.ps_argvstr = tswapl(argvp); + ps_strs.ps_nargvstr = tswap32(bprm->argc); + for (i = 0; i < bprm->argc; ++i) { + size_t len = strlen(bprm->argv[i]) + 1; + + if (memcpy_to_target(destp, bprm->argv[i], len)) { + errno = EFAULT; + return -1; + } + if (put_user_ual(destp, argvp)) { + errno = EFAULT; + return -1; + } + argvp += sizeof(abi_ulong); + destp += len; + } + if (put_user_ual(0, argvp)) { + errno = EFAULT; + return -1; + } + /* + * Add env strings. Note that the envp[] vectors are added by + * loader_build_argptr(). + */ + ps_strs.ps_envstr = tswapl(envp); + ps_strs.ps_nenvstr = tswap32(bprm->envc); + for (i = 0; i < bprm->envc; ++i) { + size_t len = strlen(bprm->envp[i]) + 1; + + if (memcpy_to_target(destp, bprm->envp[i], len)) { + errno = EFAULT; + return -1; + } + if (put_user_ual(destp, envp)) { + errno = EFAULT; + return -1; + } + envp += sizeof(abi_ulong); + destp += len; + } + if (put_user_ual(0, envp)) { + errno = EFAULT; + return -1; + } + if (memcpy_to_target(stack_hi_addr - sizeof(ps_strs), &ps_strs, + sizeof(ps_strs))) { + errno = EFAULT; + return -1; + } + + if (ret_addr) { + *ret_addr = p; + } + + return 0; + } + +#endif /* !_TARGET_OS_STACK_H_ */ diff --git a/bsd-user/i386/target_arch_sigtramp.h b/bsd-user/i386/target_arch_sigtramp.h new file mode 100644 index 0000000..f0f36d1 --- /dev/null +++ b/bsd-user/i386/target_arch_sigtramp.h @@ -0,0 +1,11 @@ + +#ifndef _TARGET_ARCH_SIGTRAMP_H_ +#define _TARGET_ARCH_SIGTRAMP_H_ + +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + + return -TARGET_EOPNOTSUPP; +} +#endif /* _TARGET_ARCH_SIGTRAMP_H_ */ diff --git a/bsd-user/mips/target_arch_sigtramp.h b/bsd-user/mips/target_arch_sigtramp.h new file mode 100644 index 0000000..5e3c69a --- /dev/null +++ b/bsd-user/mips/target_arch_sigtramp.h @@ -0,0 +1,23 @@ + +#ifndef _TARGET_ARCH_SIGTRAMP_H_ +#define _TARGET_ARCH_SIGTRAMP_H_ + +/* Compare to mips/mips/locore.S sigcode() */ +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + int i; + uint32_t sigtramp_code[TARGET_SZSIGCODE/TARGET_INSN_SIZE] = { + /* 1 */ 0x67A40000 + sigf_uc, /* daddu $a0, $sp, (sigf_uc) */ + /* 2 */ 0x24020000 + sys_sigreturn, /* li $v0, (sys_sigreturn) */ + /* 3 */ 0x0000000C, /* syscall */ + /* 4 */ 0x0000000D /* break */ + }; + + for (i = 0; i < 4; i++) { + tswap32s(&sigtramp_code[i]); + } + + return memcpy_to_target(offset, sigtramp_code, TARGET_SZSIGCODE); +} +#endif /* _TARGET_ARCH_SIGTRAMP_H_ */ diff --git a/bsd-user/mips64/target_arch_sigtramp.h b/bsd-user/mips64/target_arch_sigtramp.h new file mode 100644 index 0000000..5e3c69a --- /dev/null +++ b/bsd-user/mips64/target_arch_sigtramp.h @@ -0,0 +1,23 @@ + +#ifndef _TARGET_ARCH_SIGTRAMP_H_ +#define _TARGET_ARCH_SIGTRAMP_H_ + +/* Compare to mips/mips/locore.S sigcode() */ +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + int i; + uint32_t sigtramp_code[TARGET_SZSIGCODE/TARGET_INSN_SIZE] = { + /* 1 */ 0x67A40000 + sigf_uc, /* daddu $a0, $sp, (sigf_uc) */ + /* 2 */ 0x24020000 + sys_sigreturn, /* li $v0, (sys_sigreturn) */ + /* 3 */ 0x0000000C, /* syscall */ + /* 4 */ 0x0000000D /* break */ + }; + + for (i = 0; i < 4; i++) { + tswap32s(&sigtramp_code[i]); + } + + return memcpy_to_target(offset, sigtramp_code, TARGET_SZSIGCODE); +} +#endif /* _TARGET_ARCH_SIGTRAMP_H_ */ diff --git a/bsd-user/netbsd/os-sys.c b/bsd-user/netbsd/os-sys.c new file mode 100644 index 0000000..68ea0e1 --- /dev/null +++ b/bsd-user/netbsd/os-sys.c @@ -0,0 +1,46 @@ +/* + * NetBSD sysctl() and sysarch() system call emulation + * + * + * 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, see . + */ + +#include +#include +#include +#include + +#include "qemu.h" + +#include "target_arch_sysarch.h" +#include "target_os_vmparam.h" + + +/* This must be emulated to support FreeBSD target binaries on NetBSD host. */ + +abi_long do_freebsd_sysctl(CPUArchState *env, abi_ulong namep, int32_t namelen, + abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen) +{ + + qemu_log("qemu: Unsupported syscall __sysctl()\n"); + return -TARGET_ENOSYS; +} + +/* sysarch() is architecture dependent. */ +abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2) +{ + + qemu_log("qemu: Unsupported syscall sysarch()\n"); + return -TARGET_ENOSYS; +} diff --git a/bsd-user/netbsd/target_os_stack.h b/bsd-user/netbsd/target_os_stack.h new file mode 100644 index 0000000..1a26c3f --- /dev/null +++ b/bsd-user/netbsd/target_os_stack.h @@ -0,0 +1,33 @@ +#ifndef _TARGET_OS_STACK_H_ +#define _TARGET_OS_STACK_H_ + +#include "target_arch_sigtramp.h" + +static inline int setup_initial_stack(struct bsd_binprm *bprm, abi_ulong *p) +{ + int i; + abi_ulong stack_base; + + stack_base = (target_stkbas + target_stksiz) - + MAX_ARG_PAGES * TARGET_PAGE_SIZE; + if (p) { + *p = stack_base; + } + + for (i = 0; i < MAX_ARG_PAGES; i++) { + if (bprm->page[i]) { + info->rss++; + if (!memcpy_to_target(stack_base, bprm->page[i], + TARGET_PAGE_SIZE)) { + errno = EFAULT; + return -1; + } + g_free(bprm->page[i]); + } + stack_base += TARGET_PAGE_SIZE; + } + + return 0; +} + +#endif /* !_TARGET_OS_STACK_H_ */ diff --git a/bsd-user/openbsd/os-sys.c b/bsd-user/openbsd/os-sys.c new file mode 100644 index 0000000..30df472 --- /dev/null +++ b/bsd-user/openbsd/os-sys.c @@ -0,0 +1,46 @@ +/* + * OpenBSD sysctl() and sysarch() system call emulation + * + * + * 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, see . + */ + +#include +#include +#include +#include + +#include "qemu.h" + +#include "target_arch_sysarch.h" +#include "target_os_vmparam.h" + + +/* This must be emulated to support FreeBSD target binaries on NetBSD host. */ + +abi_long do_freebsd_sysctl(CPUArchState *env, abi_ulong namep, int32_t namelen, + abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen) +{ + + qemu_log("qemu: Unsupported syscall __sysctl()\n"); + return -TARGET_ENOSYS; +} + +/* sysarch() is architecture dependent. */ +abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2) +{ + + qemu_log("qemu: Unsupported syscall sysarch()\n"); + return -TARGET_ENOSYS; +} diff --git a/bsd-user/openbsd/target_os_stack.h b/bsd-user/openbsd/target_os_stack.h new file mode 100644 index 0000000..1a26c3f --- /dev/null +++ b/bsd-user/openbsd/target_os_stack.h @@ -0,0 +1,33 @@ +#ifndef _TARGET_OS_STACK_H_ +#define _TARGET_OS_STACK_H_ + +#include "target_arch_sigtramp.h" + +static inline int setup_initial_stack(struct bsd_binprm *bprm, abi_ulong *p) +{ + int i; + abi_ulong stack_base; + + stack_base = (target_stkbas + target_stksiz) - + MAX_ARG_PAGES * TARGET_PAGE_SIZE; + if (p) { + *p = stack_base; + } + + for (i = 0; i < MAX_ARG_PAGES; i++) { + if (bprm->page[i]) { + info->rss++; + if (!memcpy_to_target(stack_base, bprm->page[i], + TARGET_PAGE_SIZE)) { + errno = EFAULT; + return -1; + } + g_free(bprm->page[i]); + } + stack_base += TARGET_PAGE_SIZE; + } + + return 0; +} + +#endif /* !_TARGET_OS_STACK_H_ */ diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index cb77069..594de5c 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -90,6 +90,7 @@ typedef struct TaskState { struct TaskState *next; int used; /* non zero if used */ struct image_info *info; + struct bsd_binprm *bprm; struct emulated_sigtable sigtab[TARGET_NSIG]; struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ @@ -118,7 +119,7 @@ extern unsigned long mmap_min_addr; * This structure is used to hold the arguments that are * used when loading binaries. */ -struct linux_binprm { +struct bsd_binprm { char buf[128]; void *page[MAX_ARG_PAGES]; abi_ulong p; @@ -127,19 +128,23 @@ struct linux_binprm { int argc, envc; char **argv; char **envp; - char * filename; /* Name of binary */ + char *filename; /* (Given) Name of binary */ + char *fullpath; /* Full path of binary */ + int (*core_dump)(int, const CPUArchState *); }; void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr); -int loader_exec(const char * filename, char ** argv, char ** envp, - struct target_pt_regs * regs, struct image_info *infop); +int loader_exec(const char *filename, char **argv, char **envp, + struct target_pt_regs *regs, struct image_info *infop, + struct bsd_binprm *bprm); -int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, - struct image_info * info); -int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, - struct image_info * info); +int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs, + struct image_info *info); +int load_flt_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs, + struct image_info *info); +int is_target_elf_binary(int fd); abi_long memcpy_to_target(abi_ulong dest, const void *src, unsigned long len); @@ -232,6 +237,15 @@ extern unsigned long target_maxssiz; extern unsigned long target_sgrowsiz; extern char qemu_proc_pathname[]; +/* syscall.c */ +abi_long get_errno(abi_long ret); +int is_error(abi_long ret); + +/* os-sys.c */ +abi_long do_freebsd_sysctl(CPUArchState *env, abi_ulong namep, int32_t namelen, + abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen); +abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2); + /* user access */ #define VERIFY_READ 0 diff --git a/bsd-user/sparc/target_arch_sigtramp.h b/bsd-user/sparc/target_arch_sigtramp.h new file mode 100644 index 0000000..f0f36d1 --- /dev/null +++ b/bsd-user/sparc/target_arch_sigtramp.h @@ -0,0 +1,11 @@ + +#ifndef _TARGET_ARCH_SIGTRAMP_H_ +#define _TARGET_ARCH_SIGTRAMP_H_ + +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + + return -TARGET_EOPNOTSUPP; +} +#endif /* _TARGET_ARCH_SIGTRAMP_H_ */ diff --git a/bsd-user/sparc64/target_arch_sigtramp.h b/bsd-user/sparc64/target_arch_sigtramp.h new file mode 100644 index 0000000..f0f36d1 --- /dev/null +++ b/bsd-user/sparc64/target_arch_sigtramp.h @@ -0,0 +1,11 @@ + +#ifndef _TARGET_ARCH_SIGTRAMP_H_ +#define _TARGET_ARCH_SIGTRAMP_H_ + +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + + return -TARGET_EOPNOTSUPP; +} +#endif /* _TARGET_ARCH_SIGTRAMP_H_ */ diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c index a4d1583..dbc212d 100644 --- a/bsd-user/syscall.c +++ b/bsd-user/syscall.c @@ -2,6 +2,7 @@ * BSD syscalls * * Copyright (c) 2003 - 2008 Fabrice Bellard + * Copyright (c) 2013 Stacey D. Son * * 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 @@ -36,12 +37,17 @@ #include "qemu.h" #include "qemu-common.h" -//#define DEBUG +#define target_to_host_bitmask(x, tbl) (x) + +/* #define DEBUG */ static abi_ulong target_brk; static abi_ulong target_original_brk; -static inline abi_long get_errno(abi_long ret) +/* + * errno conversion. + */ +abi_long get_errno(abi_long ret) { if (ret == -1) /* XXX need to translate host -> target errnos here */ @@ -50,9 +56,7 @@ static inline abi_long get_errno(abi_long ret) return ret; } -#define target_to_host_bitmask(x, tbl) (x) - -static inline int is_error(abi_long ret) +int is_error(abi_long ret) { return (abi_ulong)ret >= (abi_ulong)(-4096); } @@ -96,175 +100,6 @@ static abi_long do_obreak(abi_ulong new_brk) return 0; } -#if defined(TARGET_I386) -static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms) -{ - abi_long ret = 0; - abi_ulong val; - int idx; - - switch(op) { -#ifdef TARGET_ABI32 - case TARGET_FREEBSD_I386_SET_GSBASE: - case TARGET_FREEBSD_I386_SET_FSBASE: - if (op == TARGET_FREEBSD_I386_SET_GSBASE) -#else - case TARGET_FREEBSD_AMD64_SET_GSBASE: - case TARGET_FREEBSD_AMD64_SET_FSBASE: - if (op == TARGET_FREEBSD_AMD64_SET_GSBASE) -#endif - idx = R_GS; - else - idx = R_FS; - if (get_user(val, parms, abi_ulong)) - return -TARGET_EFAULT; - cpu_x86_load_seg(env, idx, 0); - env->segs[idx].base = val; - break; -#ifdef TARGET_ABI32 - case TARGET_FREEBSD_I386_GET_GSBASE: - case TARGET_FREEBSD_I386_GET_FSBASE: - if (op == TARGET_FREEBSD_I386_GET_GSBASE) -#else - case TARGET_FREEBSD_AMD64_GET_GSBASE: - case TARGET_FREEBSD_AMD64_GET_FSBASE: - if (op == TARGET_FREEBSD_AMD64_GET_GSBASE) -#endif - idx = R_GS; - else - idx = R_FS; - val = env->segs[idx].base; - if (put_user(val, parms, abi_ulong)) - return -TARGET_EFAULT; - break; - /* XXX handle the others... */ - default: - ret = -TARGET_EINVAL; - break; - } - return ret; -} -#endif - -#ifdef TARGET_SPARC -static abi_long do_freebsd_sysarch(void *env, int op, abi_ulong parms) -{ - /* XXX handle - * TARGET_FREEBSD_SPARC_UTRAP_INSTALL, - * TARGET_FREEBSD_SPARC_SIGTRAMP_INSTALL - */ - return -TARGET_EINVAL; -} -#endif - -#ifdef __FreeBSD__ -/* - * XXX this uses the undocumented oidfmt interface to find the kind of - * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt() - * (this is mostly copied from src/sbin/sysctl/sysctl.c) - */ -static int -oidfmt(int *oid, int len, char *fmt, uint32_t *kind) -{ - int qoid[CTL_MAXNAME+2]; - uint8_t buf[BUFSIZ]; - int i; - size_t j; - - qoid[0] = 0; - qoid[1] = 4; - memcpy(qoid + 2, oid, len * sizeof(int)); - - j = sizeof(buf); - i = sysctl(qoid, len + 2, buf, &j, 0, 0); - if (i) - return i; - - if (kind) - *kind = *(uint32_t *)buf; - - if (fmt) - strcpy(fmt, (char *)(buf + sizeof(uint32_t))); - return (0); -} - -/* - * try and convert sysctl return data for the target. - * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT. - */ -static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind) -{ - switch (kind & CTLTYPE) { - case CTLTYPE_INT: - case CTLTYPE_UINT: - *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp); - break; -#ifdef TARGET_ABI32 - case CTLTYPE_LONG: - case CTLTYPE_ULONG: - *(uint32_t *)holdp = tswap32(*(long *)holdp); - break; -#else - case CTLTYPE_LONG: - *(uint64_t *)holdp = tswap64(*(long *)holdp); - case CTLTYPE_ULONG: - *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp); - break; -#endif -#ifdef CTLTYPE_U64 - case CTLTYPE_S64: - case CTLTYPE_U64: -#else - case CTLTYPE_QUAD: -#endif - *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp); - break; - case CTLTYPE_STRING: - break; - default: - /* XXX unhandled */ - return -1; - } - return 0; -} - -/* XXX this needs to be emulated on non-FreeBSD hosts... */ -static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp, - abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen) -{ - abi_long ret; - void *hnamep, *holdp, *hnewp = NULL; - size_t holdlen; - abi_ulong oldlen = 0; - int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i; - uint32_t kind = 0; - - if (oldlenp) - get_user_ual(oldlen, oldlenp); - if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1))) - return -TARGET_EFAULT; - if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1))) - return -TARGET_EFAULT; - if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0))) - return -TARGET_EFAULT; - holdlen = oldlen; - for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++) - *q++ = tswap32(*p); - oidfmt(snamep, namelen, NULL, &kind); - /* XXX swap hnewp */ - ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen)); - if (!ret) - sysctl_oldcvt(holdp, holdlen, kind); - put_user_ual(holdlen, oldlenp); - unlock_user(hnamep, namep, 0); - unlock_user(holdp, oldp, holdlen); - if (hnewp) - unlock_user(hnewp, newp, 0); - g_free(snamep); - return ret; -} -#endif - /* FIXME * lock_iovec()/unlock_iovec() have a return code of 0 for success where * other lock functions have a return code of 0 for failure. @@ -387,20 +222,27 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_FREEBSD_NR_break: ret = do_obreak(arg1); break; -#ifdef __FreeBSD__ - case TARGET_FREEBSD_NR___sysctl: - ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6); + + /* + * sys{ctl, arch, call} + */ + case TARGET_FREEBSD_NR___sysctl: /* sysctl(3) */ + ret = do_freebsd_sysctl(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); break; -#endif - case TARGET_FREEBSD_NR_sysarch: + + case TARGET_FREEBSD_NR_sysarch: /* sysarch(2) */ ret = do_freebsd_sysarch(cpu_env, arg1, arg2); break; - case TARGET_FREEBSD_NR_syscall: - case TARGET_FREEBSD_NR___syscall: - ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0); + + case TARGET_FREEBSD_NR_syscall: /* syscall(2) */ + case TARGET_FREEBSD_NR___syscall: /* __syscall(2) */ + ret = do_freebsd_syscall(cpu_env, arg1 & 0xffff, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, 0); break; + default: - ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); + ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8)); break; } fail: @@ -467,6 +309,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NETBSD_NR_mprotect: ret = get_errno(target_mprotect(arg1, arg2, arg3)); break; + case TARGET_NETBSD_NR_syscall: case TARGET_NETBSD_NR___syscall: ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0); @@ -539,6 +382,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_OPENBSD_NR_mprotect: ret = get_errno(target_mprotect(arg1, arg2, arg3)); break; + case TARGET_OPENBSD_NR_syscall: case TARGET_OPENBSD_NR___syscall: ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0); diff --git a/bsd-user/x86_64/target_arch_sigtramp.h b/bsd-user/x86_64/target_arch_sigtramp.h new file mode 100644 index 0000000..f0f36d1 --- /dev/null +++ b/bsd-user/x86_64/target_arch_sigtramp.h @@ -0,0 +1,11 @@ + +#ifndef _TARGET_ARCH_SIGTRAMP_H_ +#define _TARGET_ARCH_SIGTRAMP_H_ + +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + + return -TARGET_EOPNOTSUPP; +} +#endif /* _TARGET_ARCH_SIGTRAMP_H_ */ -- 1.7.8