aboutsummaryrefslogtreecommitdiff
path: root/arch/um/os-Linux
diff options
context:
space:
mode:
Diffstat (limited to 'arch/um/os-Linux')
-rw-r--r--arch/um/os-Linux/Makefile12
-rw-r--r--arch/um/os-Linux/aio.c15
-rw-r--r--arch/um/os-Linux/drivers/etap.h2
-rw-r--r--arch/um/os-Linux/drivers/ethertap_kern.c2
-rw-r--r--arch/um/os-Linux/drivers/ethertap_user.c8
-rw-r--r--arch/um/os-Linux/drivers/tuntap.h2
-rw-r--r--arch/um/os-Linux/drivers/tuntap_kern.c2
-rw-r--r--arch/um/os-Linux/drivers/tuntap_user.c6
-rw-r--r--arch/um/os-Linux/elf_aux.c14
-rw-r--r--arch/um/os-Linux/execvp.c4
-rw-r--r--arch/um/os-Linux/file.c19
-rw-r--r--arch/um/os-Linux/helper.c12
-rw-r--r--arch/um/os-Linux/internal.h1
-rw-r--r--arch/um/os-Linux/irq.c9
-rw-r--r--arch/um/os-Linux/main.c21
-rw-r--r--arch/um/os-Linux/mem.c246
-rw-r--r--arch/um/os-Linux/process.c94
-rw-r--r--arch/um/os-Linux/registers.c11
-rw-r--r--arch/um/os-Linux/sigio.c15
-rw-r--r--arch/um/os-Linux/signal.c100
-rw-r--r--arch/um/os-Linux/skas/mem.c34
-rw-r--r--arch/um/os-Linux/skas/process.c76
-rw-r--r--arch/um/os-Linux/start_up.c26
-rw-r--r--arch/um/os-Linux/sys-i386/Makefile10
-rw-r--r--arch/um/os-Linux/sys-i386/registers.c91
-rw-r--r--arch/um/os-Linux/sys-i386/signal.c13
-rw-r--r--arch/um/os-Linux/sys-i386/task_size.c139
-rw-r--r--arch/um/os-Linux/sys-i386/tls.c36
-rw-r--r--arch/um/os-Linux/sys-x86_64/Makefile10
-rw-r--r--arch/um/os-Linux/sys-x86_64/prctl.c12
-rw-r--r--arch/um/os-Linux/sys-x86_64/registers.c52
-rw-r--r--arch/um/os-Linux/sys-x86_64/signal.c16
-rw-r--r--arch/um/os-Linux/sys-x86_64/task_size.c5
-rw-r--r--arch/um/os-Linux/time.c14
-rw-r--r--arch/um/os-Linux/tls.c35
-rw-r--r--arch/um/os-Linux/tty.c6
-rw-r--r--arch/um/os-Linux/uaccess.c32
-rw-r--r--arch/um/os-Linux/umid.c6
-rw-r--r--arch/um/os-Linux/user_syms.c11
-rw-r--r--arch/um/os-Linux/util.c42
40 files changed, 411 insertions, 850 deletions
diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile
index d66f0388f09..08ff5094fcd 100644
--- a/arch/um/os-Linux/Makefile
+++ b/arch/um/os-Linux/Makefile
@@ -3,15 +3,15 @@
# Licensed under the GPL
#
-obj-y = aio.o elf_aux.o execvp.o file.o helper.o irq.o main.o mem.o process.o \
- registers.o sigio.o signal.o start_up.o time.o tty.o uaccess.o \
- umid.o tls.o user_syms.o util.o drivers/ sys-$(SUBARCH)/ skas/
+obj-y = aio.o execvp.o file.o helper.o irq.o main.o mem.o process.o \
+ registers.o sigio.o signal.o start_up.o time.o tty.o \
+ umid.o user_syms.o util.o drivers/ skas/
+
+obj-$(CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA) += elf_aux.o
USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \
main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \
- tty.o tls.o uaccess.o umid.o util.o
-
-CFLAGS_user_syms.o += -DSUBARCH_$(SUBARCH)
+ tty.o umid.o util.o
HAVE_AIO_ABI := $(shell [ -r /usr/include/linux/aio_abi.h ] && \
echo -DHAVE_AIO_ABI )
diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c
index 57e3d46c989..014eb35fd13 100644
--- a/arch/um/os-Linux/aio.c
+++ b/arch/um/os-Linux/aio.c
@@ -9,12 +9,10 @@
#include <errno.h>
#include <sys/time.h>
#include <asm/unistd.h>
-#include "aio.h"
-#include "init.h"
-#include "kern_constants.h"
-#include "kern_util.h"
-#include "os.h"
-#include "user.h"
+#include <aio.h>
+#include <init.h>
+#include <kern_util.h>
+#include <os.h>
struct aio_thread_req {
enum aio_type type;
@@ -106,8 +104,7 @@ static int aio_thread(void *arg)
struct io_event event;
int err, n, reply_fd;
- signal(SIGWINCH, SIG_IGN);
-
+ os_fix_helper_signals();
while (1) {
n = io_getevents(ctx, 1, 1, &event, NULL);
if (n < 0) {
@@ -175,7 +172,7 @@ static int not_aio_thread(void *arg)
struct aio_thread_reply reply;
int err;
- signal(SIGWINCH, SIG_IGN);
+ os_fix_helper_signals();
while (1) {
err = read(aio_req_fd_r, &req, sizeof(req));
if (err != sizeof(req)) {
diff --git a/arch/um/os-Linux/drivers/etap.h b/arch/um/os-Linux/drivers/etap.h
index ddffd41c3f3..54183a679fd 100644
--- a/arch/um/os-Linux/drivers/etap.h
+++ b/arch/um/os-Linux/drivers/etap.h
@@ -6,7 +6,7 @@
#ifndef __DRIVERS_ETAP_H
#define __DRIVERS_ETAP_H
-#include "net_user.h"
+#include <net_user.h>
struct ethertap_data {
char *dev_name;
diff --git a/arch/um/os-Linux/drivers/ethertap_kern.c b/arch/um/os-Linux/drivers/ethertap_kern.c
index 7f6f9a71aae..f424600a583 100644
--- a/arch/um/os-Linux/drivers/ethertap_kern.c
+++ b/arch/um/os-Linux/drivers/ethertap_kern.c
@@ -9,7 +9,7 @@
#include <linux/init.h>
#include <linux/netdevice.h>
#include "etap.h"
-#include "net_kern.h"
+#include <net_kern.h>
struct ethertap_init {
char *dev_name;
diff --git a/arch/um/os-Linux/drivers/ethertap_user.c b/arch/um/os-Linux/drivers/ethertap_user.c
index cc72cb2c1af..b39b6696ac5 100644
--- a/arch/um/os-Linux/drivers/ethertap_user.c
+++ b/arch/um/os-Linux/drivers/ethertap_user.c
@@ -13,11 +13,9 @@
#include <sys/socket.h>
#include <sys/wait.h>
#include "etap.h"
-#include "kern_constants.h"
-#include "os.h"
-#include "net_user.h"
-#include "um_malloc.h"
-#include "user.h"
+#include <os.h>
+#include <net_user.h>
+#include <um_malloc.h>
#define MAX_PACKET ETH_MAX_PACKET
diff --git a/arch/um/os-Linux/drivers/tuntap.h b/arch/um/os-Linux/drivers/tuntap.h
index f17c31586c8..7367354ac8d 100644
--- a/arch/um/os-Linux/drivers/tuntap.h
+++ b/arch/um/os-Linux/drivers/tuntap.h
@@ -6,7 +6,7 @@
#ifndef __UM_TUNTAP_H
#define __UM_TUNTAP_H
-#include "net_user.h"
+#include <net_user.h>
struct tuntap_data {
char *dev_name;
diff --git a/arch/um/os-Linux/drivers/tuntap_kern.c b/arch/um/os-Linux/drivers/tuntap_kern.c
index 4048800e469..d9d56e5810f 100644
--- a/arch/um/os-Linux/drivers/tuntap_kern.c
+++ b/arch/um/os-Linux/drivers/tuntap_kern.c
@@ -7,7 +7,7 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <asm/errno.h>
-#include "net_kern.h"
+#include <net_kern.h>
#include "tuntap.h"
struct tuntap_init {
diff --git a/arch/um/os-Linux/drivers/tuntap_user.c b/arch/um/os-Linux/drivers/tuntap_user.c
index 2448be03fd7..14126d9176a 100644
--- a/arch/um/os-Linux/drivers/tuntap_user.c
+++ b/arch/um/os-Linux/drivers/tuntap_user.c
@@ -13,11 +13,9 @@
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/uio.h>
-#include "kern_constants.h"
-#include "kern_util.h"
-#include "os.h"
+#include <kern_util.h>
+#include <os.h>
#include "tuntap.h"
-#include "user.h"
static int tuntap_user_init(void *data, void *dev)
{
diff --git a/arch/um/os-Linux/elf_aux.c b/arch/um/os-Linux/elf_aux.c
index 608784d4ec5..1a365ddc4d0 100644
--- a/arch/um/os-Linux/elf_aux.c
+++ b/arch/um/os-Linux/elf_aux.c
@@ -9,21 +9,15 @@
*/
#include <elf.h>
#include <stddef.h>
-#include "init.h"
-#include "elf_user.h"
-#include "mem_user.h"
-#include <kern_constants.h>
+#include <init.h>
+#include <elf_user.h>
+#include <mem_user.h>
-/* Use the one from the kernel - the host may miss it, if having old headers. */
-#if UM_ELF_CLASS == UM_ELFCLASS32
typedef Elf32_auxv_t elf_auxv_t;
-#else
-typedef Elf64_auxv_t elf_auxv_t;
-#endif
/* These are initialized very early in boot and never changed */
char * elf_aux_platform;
-long elf_aux_hwcap;
+extern long elf_aux_hwcap;
unsigned long vsyscall_ehdr;
unsigned long vsyscall_end;
unsigned long __kernel_vsyscall;
diff --git a/arch/um/os-Linux/execvp.c b/arch/um/os-Linux/execvp.c
index 66e583a4031..8fb25ca07c4 100644
--- a/arch/um/os-Linux/execvp.c
+++ b/arch/um/os-Linux/execvp.c
@@ -27,12 +27,12 @@
#include <limits.h>
#ifndef TEST
-#include "um_malloc.h"
+#include <um_malloc.h>
#else
#include <stdio.h>
#define um_kmalloc malloc
#endif
-#include "os.h"
+#include <os.h>
/* Execute FILE, searching in the `PATH' environment variable if it contains
no slashes, with arguments ARGV and environment from `environ'. */
diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c
index 140e587bc0a..08d90fba952 100644
--- a/arch/um/os-Linux/file.c
+++ b/arch/um/os-Linux/file.c
@@ -13,9 +13,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
-#include "kern_constants.h"
-#include "os.h"
-#include "user.h"
+#include <os.h>
static void copy_stat(struct uml_stat *dst, const struct stat64 *src)
{
@@ -239,6 +237,12 @@ void os_close_file(int fd)
{
close(fd);
}
+int os_fsync_file(int fd)
+{
+ if (fsync(fd) < 0)
+ return -errno;
+ return 0;
+}
int os_seek_file(int fd, unsigned long long offset)
{
@@ -268,6 +272,15 @@ int os_write_file(int fd, const void *buf, int len)
return n;
}
+int os_sync_file(int fd)
+{
+ int n = fsync(fd);
+
+ if (n < 0)
+ return -errno;
+ return n;
+}
+
int os_file_size(const char *file, unsigned long long *size_out)
{
struct uml_stat buf;
diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c
index b6b1096152a..e3ee4a51ef6 100644
--- a/arch/um/os-Linux/helper.c
+++ b/arch/um/os-Linux/helper.c
@@ -10,11 +10,9 @@
#include <linux/limits.h>
#include <sys/socket.h>
#include <sys/wait.h>
-#include "kern_constants.h"
-#include "kern_util.h"
-#include "os.h"
-#include "um_malloc.h"
-#include "user.h"
+#include <kern_util.h>
+#include <os.h>
+#include <um_malloc.h>
struct helper_data {
void (*pre_exec)(void*);
@@ -28,14 +26,14 @@ static int helper_child(void *arg)
{
struct helper_data *data = arg;
char **argv = data->argv;
- int err;
+ int err, ret;
if (data->pre_exec != NULL)
(*data->pre_exec)(data->pre_data);
err = execvp_noalloc(data->buf, argv[0], argv);
/* If the exec succeeds, we don't get here */
- write(data->fd, &err, sizeof(err));
+ CATCH_EINTR(ret = write(data->fd, &err, sizeof(err)));
return 0;
}
diff --git a/arch/um/os-Linux/internal.h b/arch/um/os-Linux/internal.h
new file mode 100644
index 00000000000..0dc2c9f135f
--- /dev/null
+++ b/arch/um/os-Linux/internal.h
@@ -0,0 +1 @@
+void alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc);
diff --git a/arch/um/os-Linux/irq.c b/arch/um/os-Linux/irq.c
index 0348b975e81..b9afb74b79a 100644
--- a/arch/um/os-Linux/irq.c
+++ b/arch/um/os-Linux/irq.c
@@ -8,12 +8,9 @@
#include <poll.h>
#include <signal.h>
#include <string.h>
-#include "irq_user.h"
-#include "kern_constants.h"
-#include "os.h"
-#include "process.h"
-#include "um_malloc.h"
-#include "user.h"
+#include <irq_user.h>
+#include <os.h>
+#include <um_malloc.h>
/*
* Locked by irq_lock in arch/um/kernel/irq.c. Changed by os_create_pollfd
diff --git a/arch/um/os-Linux/main.c b/arch/um/os-Linux/main.c
index eee69b9f52c..df9191acd92 100644
--- a/arch/um/os-Linux/main.c
+++ b/arch/um/os-Linux/main.c
@@ -10,17 +10,18 @@
#include <signal.h>
#include <string.h>
#include <sys/resource.h>
-#include "as-layout.h"
-#include "init.h"
-#include "kern_constants.h"
-#include "kern_util.h"
-#include "os.h"
-#include "um_malloc.h"
+#include <as-layout.h>
+#include <init.h>
+#include <kern_util.h>
+#include <os.h>
+#include <um_malloc.h>
#define PGD_BOUND (4 * 1024 * 1024)
#define STACKSIZE (8 * 1024 * 1024)
#define THREAD_NAME_LEN (256)
+long elf_aux_hwcap;
+
static void set_stklim(void)
{
struct rlimit lim;
@@ -78,7 +79,7 @@ static void install_fatal_handler(int sig)
}
}
-#define UML_LIB_PATH ":/usr/lib/uml"
+#define UML_LIB_PATH ":" OS_LIB_PATH "/uml"
static void setup_env_path(void)
{
@@ -122,6 +123,8 @@ int __init main(int argc, char **argv, char **envp)
setup_env_path();
+ setsid();
+
new_argv = malloc((argc + 1) * sizeof(char *));
if (new_argv == NULL) {
perror("Mallocing argv");
@@ -142,11 +145,13 @@ int __init main(int argc, char **argv, char **envp)
*/
install_fatal_handler(SIGINT);
install_fatal_handler(SIGTERM);
- install_fatal_handler(SIGHUP);
+#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
scan_elf_aux(envp);
+#endif
do_uml_initcalls();
+ change_sig(SIGPIPE, 0);
ret = linux_main(argc, argv);
/*
diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c
index e696144d2be..897e9ad0c10 100644
--- a/arch/um/os-Linux/mem.c
+++ b/arch/um/os-Linux/mem.c
@@ -12,205 +12,133 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
-#include <sys/param.h>
-#include "init.h"
-#include "kern_constants.h"
-#include "os.h"
-#include "user.h"
+#include <sys/vfs.h>
+#include <linux/magic.h>
+#include <init.h>
+#include <os.h>
-/* Modified by which_tmpdir, which is called during early boot */
-static char *default_tmpdir = "/tmp";
-
-/*
- * Modified when creating the physical memory file and when checking
- * the tmp filesystem for usability, both happening during early boot.
- */
+/* Set by make_tempfile() during early boot. */
static char *tempdir = NULL;
-static void __init find_tempdir(void)
+/* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */
+static int __init check_tmpfs(const char *dir)
{
- const char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL };
- int i;
- char *dir = NULL;
-
- if (tempdir != NULL)
- /* We've already been called */
- return;
- for (i = 0; dirs[i]; i++) {
- dir = getenv(dirs[i]);
- if ((dir != NULL) && (*dir != '\0'))
- break;
+ struct statfs st;
+
+ printf("Checking if %s is on tmpfs...", dir);
+ if (statfs(dir, &st) < 0) {
+ printf("%s\n", strerror(errno));
+ } else if (st.f_type != TMPFS_MAGIC) {
+ printf("no\n");
+ } else {
+ printf("OK\n");
+ return 0;
}
- if ((dir == NULL) || (*dir == '\0'))
- dir = default_tmpdir;
-
- tempdir = malloc(strlen(dir) + 2);
- if (tempdir == NULL) {
- fprintf(stderr, "Failed to malloc tempdir, "
- "errno = %d\n", errno);
- return;
- }
- strcpy(tempdir, dir);
- strcat(tempdir, "/");
+ return -1;
}
/*
- * This will return 1, with the first character in buf being the
- * character following the next instance of c in the file. This will
- * read the file as needed. If there's an error, -errno is returned;
- * if the end of the file is reached, 0 is returned.
+ * Choose the tempdir to use. We want something on tmpfs so that our memory is
+ * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the
+ * environment, we use that even if it's not on tmpfs, but we warn the user.
+ * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found
+ * then we fall back to /tmp.
*/
-static int next(int fd, char *buf, size_t size, char c)
+static char * __init choose_tempdir(void)
{
- ssize_t n;
- size_t len;
- char *ptr;
-
- while ((ptr = strchr(buf, c)) == NULL) {
- n = read(fd, buf, size - 1);
- if (n == 0)
- return 0;
- else if (n < 0)
- return -errno;
-
- buf[n] = '\0';
+ static const char * const vars[] = {
+ "TMPDIR",
+ "TMP",
+ "TEMP",
+ NULL
+ };
+ static const char fallback_dir[] = "/tmp";
+ static const char * const tmpfs_dirs[] = {
+ "/dev/shm",
+ fallback_dir,
+ NULL
+ };
+ int i;
+ const char *dir;
+
+ printf("Checking environment variables for a tempdir...");
+ for (i = 0; vars[i]; i++) {
+ dir = getenv(vars[i]);
+ if ((dir != NULL) && (*dir != '\0')) {
+ printf("%s\n", dir);
+ if (check_tmpfs(dir) >= 0)
+ goto done;
+ else
+ goto warn;
+ }
}
+ printf("none found\n");
- ptr++;
- len = strlen(ptr);
- memmove(buf, ptr, len + 1);
-
- /*
- * Refill the buffer so that if there's a partial string that we care
- * about, it will be completed, and we can recognize it.
- */
- n = read(fd, &buf[len], size - len - 1);
- if (n < 0)
- return -errno;
+ for (i = 0; tmpfs_dirs[i]; i++) {
+ dir = tmpfs_dirs[i];
+ if (check_tmpfs(dir) >= 0)
+ goto done;
+ }
- buf[len + n] = '\0';
- return 1;
+ dir = fallback_dir;
+warn:
+ printf("Warning: tempdir %s is not on tmpfs\n", dir);
+done:
+ /* Make a copy since getenv results may not remain valid forever. */
+ return strdup(dir);
}
-/* which_tmpdir is called only during early boot */
-static int checked_tmpdir = 0;
-
/*
- * Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner
- * way to do this than to parse /proc/mounts. statfs will return the
- * same filesystem magic number and fs id for both /dev and /dev/shm
- * when they are both tmpfs, so you can't tell if they are different
- * filesystems. Also, there seems to be no other way of finding the
- * mount point of a filesystem from within it.
- *
- * If a /dev/shm tmpfs entry is found, then we switch to using it.
- * Otherwise, we stay with the default /tmp.
+ * Create an unlinked tempfile in a suitable tempdir. template must be the
+ * basename part of the template with a leading '/'.
*/
-static void which_tmpdir(void)
-{
- int fd, found;
- char buf[128] = { '\0' };
-
- if (checked_tmpdir)
- return;
-
- checked_tmpdir = 1;
-
- printf("Checking for tmpfs mount on /dev/shm...");
-
- fd = open("/proc/mounts", O_RDONLY);
- if (fd < 0) {
- printf("failed to open /proc/mounts, errno = %d\n", errno);
- return;
- }
-
- while (1) {
- found = next(fd, buf, ARRAY_SIZE(buf), ' ');
- if (found != 1)
- break;
-
- if (!strncmp(buf, "/dev/shm", strlen("/dev/shm")))
- goto found;
-
- found = next(fd, buf, ARRAY_SIZE(buf), '\n');
- if (found != 1)
- break;
- }
-
-err:
- if (found == 0)
- printf("nothing mounted on /dev/shm\n");
- else if (found < 0)
- printf("read returned errno %d\n", -found);
-
-out:
- close(fd);
-
- return;
-
-found:
- found = next(fd, buf, ARRAY_SIZE(buf), ' ');
- if (found != 1)
- goto err;
-
- if (strncmp(buf, "tmpfs", strlen("tmpfs"))) {
- printf("not tmpfs\n");
- goto out;
- }
-
- printf("OK\n");
- default_tmpdir = "/dev/shm";
- goto out;
-}
-
-static int __init make_tempfile(const char *template, char **out_tempname,
- int do_unlink)
+static int __init make_tempfile(const char *template)
{
char *tempname;
int fd;
- which_tmpdir();
- tempname = malloc(MAXPATHLEN);
- if (tempname == NULL)
- return -1;
+ if (tempdir == NULL) {
+ tempdir = choose_tempdir();
+ if (tempdir == NULL) {
+ fprintf(stderr, "Failed to choose tempdir: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ }
- find_tempdir();
- if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN))
+ tempname = malloc(strlen(tempdir) + strlen(template) + 1);
+ if (tempname == NULL)
return -1;
- if (template[0] != '/')
- strcpy(tempname, tempdir);
- else
- tempname[0] = '\0';
- strncat(tempname, template, MAXPATHLEN-1-strlen(tempname));
+ strcpy(tempname, tempdir);
+ strcat(tempname, template);
fd = mkstemp(tempname);
if (fd < 0) {
fprintf(stderr, "open - cannot create %s: %s\n", tempname,
strerror(errno));
goto out;
}
- if (do_unlink && (unlink(tempname) < 0)) {
+ if (unlink(tempname) < 0) {
perror("unlink");
- goto out;
+ goto close;
}
- if (out_tempname) {
- *out_tempname = tempname;
- } else
- free(tempname);
+ free(tempname);
return fd;
+close:
+ close(fd);
out:
free(tempname);
return -1;
}
-#define TEMPNAME_TEMPLATE "vm_file-XXXXXX"
+#define TEMPNAME_TEMPLATE "/vm_file-XXXXXX"
static int __init create_tmp_file(unsigned long long len)
{
int fd, err;
char zero;
- fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1);
+ fd = make_tempfile(TEMPNAME_TEMPLATE);
if (fd < 0)
exit(1);
@@ -254,7 +182,6 @@ int __init create_mem_file(unsigned long long len)
return fd;
}
-
void __init check_tmpexec(void)
{
void *addr;
@@ -262,14 +189,13 @@ void __init check_tmpexec(void)
addr = mmap(NULL, UM_KERN_PAGE_SIZE,
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0);
- printf("Checking PROT_EXEC mmap in %s...",tempdir);
- fflush(stdout);
+ printf("Checking PROT_EXEC mmap in %s...", tempdir);
if (addr == MAP_FAILED) {
err = errno;
- perror("failed");
+ printf("%s\n", strerror(err));
close(fd);
if (err == EPERM)
- printf("%s must be not mounted noexec\n",tempdir);
+ printf("%s must be not mounted noexec\n", tempdir);
exit(1);
}
printf("OK\n");
diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c
index e0477c3ee89..33496fe2bb5 100644
--- a/arch/um/os-Linux/process.c
+++ b/arch/um/os-Linux/process.c
@@ -4,6 +4,7 @@
*/
#include <stdio.h>
+#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
@@ -12,13 +13,10 @@
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <asm/unistd.h>
-#include "init.h"
-#include "kern_constants.h"
-#include "longjmp.h"
-#include "os.h"
-#include "process.h"
-#include "skas_ptrace.h"
-#include "user.h"
+#include <init.h>
+#include <longjmp.h>
+#include <os.h>
+#include <skas_ptrace.h>
#define ARBITRARY_ADDR -1
#define FAILURE_PID -1
@@ -235,35 +233,65 @@ out:
return ok;
}
-void init_new_thread_signals(void)
+static int os_page_mincore(void *addr)
{
- set_handler(SIGSEGV, (__sighandler_t) sig_handler, SA_ONSTACK,
- SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
- set_handler(SIGTRAP, (__sighandler_t) sig_handler, SA_ONSTACK,
- SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
- set_handler(SIGFPE, (__sighandler_t) sig_handler, SA_ONSTACK,
- SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
- set_handler(SIGILL, (__sighandler_t) sig_handler, SA_ONSTACK,
- SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
- set_handler(SIGBUS, (__sighandler_t) sig_handler, SA_ONSTACK,
- SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
- signal(SIGHUP, SIG_IGN);
+ char vec[2];
+ int ret;
- set_handler(SIGIO, (__sighandler_t) sig_handler,
- SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGALRM,
- SIGVTALRM, -1);
- signal(SIGWINCH, SIG_IGN);
+ ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
+ if (ret < 0) {
+ if (errno == ENOMEM || errno == EINVAL)
+ return 0;
+ else
+ return -errno;
+ }
+
+ return vec[0] & 1;
}
-int run_kernel_thread(int (*fn)(void *), void *arg, jmp_buf **jmp_ptr)
+int os_mincore(void *addr, unsigned long len)
{
- jmp_buf buf;
- int n;
-
- *jmp_ptr = &buf;
- n = UML_SETJMP(&buf);
- if (n != 0)
- return n;
- (*fn)(arg);
- return 0;
+ char *vec;
+ int ret, i;
+
+ if (len <= UM_KERN_PAGE_SIZE)
+ return os_page_mincore(addr);
+
+ vec = calloc(1, (len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE);
+ if (!vec)
+ return -ENOMEM;
+
+ ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
+ if (ret < 0) {
+ if (errno == ENOMEM || errno == EINVAL)
+ ret = 0;
+ else
+ ret = -errno;
+
+ goto out;
+ }
+
+ for (i = 0; i < ((len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); i++) {
+ if (!(vec[i] & 1)) {
+ ret = 0;
+ goto out;
+ }
+ }
+
+ ret = 1;
+out:
+ free(vec);
+ return ret;
+}
+
+void init_new_thread_signals(void)
+{
+ set_handler(SIGSEGV);
+ set_handler(SIGTRAP);
+ set_handler(SIGFPE);
+ set_handler(SIGILL);
+ set_handler(SIGBUS);
+ signal(SIGHUP, SIG_IGN);
+ set_handler(SIGIO);
+ signal(SIGWINCH, SIG_IGN);
}
diff --git a/arch/um/os-Linux/registers.c b/arch/um/os-Linux/registers.c
index 830fe6a1518..2ff8d4fe83c 100644
--- a/arch/um/os-Linux/registers.c
+++ b/arch/um/os-Linux/registers.c
@@ -7,7 +7,9 @@
#include <errno.h>
#include <string.h>
#include <sys/ptrace.h>
-#include "sysdep/ptrace.h"
+#include <sysdep/ptrace.h>
+#include <sysdep/ptrace_user.h>
+#include <registers.h>
int save_registers(int pid, struct uml_pt_regs *regs)
{
@@ -32,6 +34,7 @@ int restore_registers(int pid, struct uml_pt_regs *regs)
/* This is set once at boot time and not changed thereafter */
static unsigned long exec_regs[MAX_REG_NR];
+static unsigned long exec_fp_regs[FP_SIZE];
int init_registers(int pid)
{
@@ -42,10 +45,14 @@ int init_registers(int pid)
return -errno;
arch_init_registers(pid);
+ get_fp_registers(pid, exec_fp_regs);
return 0;
}
-void get_safe_registers(unsigned long *regs)
+void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
{
memcpy(regs, exec_regs, sizeof(exec_regs));
+
+ if (fp_regs)
+ memcpy(fp_regs, exec_fp_regs, sizeof(exec_fp_regs));
}
diff --git a/arch/um/os-Linux/sigio.c b/arch/um/os-Linux/sigio.c
index 63d299df152..46e762f926e 100644
--- a/arch/um/os-Linux/sigio.c
+++ b/arch/um/os-Linux/sigio.c
@@ -11,14 +11,11 @@
#include <sched.h>
#include <signal.h>
#include <string.h>
-#include "kern_constants.h"
-#include "kern_util.h"
-#include "init.h"
-#include "os.h"
-#include "process.h"
-#include "sigio.h"
-#include "um_malloc.h"
-#include "user.h"
+#include <kern_util.h>
+#include <init.h>
+#include <os.h>
+#include <sigio.h>
+#include <um_malloc.h>
/*
* Protected by sigio_lock(), also used by sigio_cleanup, which is an
@@ -58,7 +55,7 @@ static int write_sigio_thread(void *unused)
int i, n, respond_fd;
char c;
- signal(SIGWINCH, SIG_IGN);
+ os_fix_helper_signals();
fds = &current_poll;
while (1) {
n = poll(fds->poll, fds->used, -1);
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
index 6ae180703a6..7b605e4dfff 100644
--- a/arch/um/os-Linux/signal.c
+++ b/arch/um/os-Linux/signal.c
@@ -9,18 +9,13 @@
#include <errno.h>
#include <signal.h>
#include <strings.h>
-#include "as-layout.h"
-#include "kern_util.h"
-#include "os.h"
-#include "process.h"
-#include "sysdep/barrier.h"
-#include "sysdep/sigcontext.h"
-#include "user.h"
-
-/* Copied from linux/compiler-gcc.h since we can't include it directly */
-#define barrier() __asm__ __volatile__("": : :"memory")
-
-void (*sig_info[NSIG])(int, struct uml_pt_regs *) = {
+#include <as-layout.h>
+#include <kern_util.h>
+#include <os.h>
+#include <sysdep/mcontext.h>
+#include "internal.h"
+
+void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = {
[SIGTRAP] = relay_signal,
[SIGFPE] = relay_signal,
[SIGILL] = relay_signal,
@@ -30,7 +25,7 @@ void (*sig_info[NSIG])(int, struct uml_pt_regs *) = {
[SIGIO] = sigio_handler,
[SIGVTALRM] = timer_handler };
-static void sig_handler_common(int sig, struct sigcontext *sc)
+static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc)
{
struct uml_pt_regs r;
int save_errno = errno;
@@ -38,15 +33,15 @@ static void sig_handler_common(int sig, struct sigcontext *sc)
r.is_user = 0;
if (sig == SIGSEGV) {
/* For segfaults, we want the data from the sigcontext. */
- copy_sc(&r, sc);
- GET_FAULTINFO_FROM_SC(r.faultinfo, sc);
+ get_regs_from_mc(&r, mc);
+ GET_FAULTINFO_FROM_MC(r.faultinfo, mc);
}
/* enable signals if sig isn't IRQ signal */
if ((sig != SIGIO) && (sig != SIGWINCH) && (sig != SIGVTALRM))
unblock_signals();
- (*sig_info[sig])(sig, &r);
+ (*sig_info[sig])(sig, si, &r);
errno = save_errno;
}
@@ -66,7 +61,7 @@ static void sig_handler_common(int sig, struct sigcontext *sc)
static int signals_enabled;
static unsigned int signals_pending;
-void sig_handler(int sig, struct sigcontext *sc)
+void sig_handler(int sig, struct siginfo *si, mcontext_t *mc)
{
int enabled;
@@ -78,23 +73,23 @@ void sig_handler(int sig, struct sigcontext *sc)
block_signals();
- sig_handler_common(sig, sc);
+ sig_handler_common(sig, si, mc);
set_signals(enabled);
}
-static void real_alarm_handler(struct sigcontext *sc)
+static void real_alarm_handler(mcontext_t *mc)
{
struct uml_pt_regs regs;
- if (sc != NULL)
- copy_sc(&regs, sc);
+ if (mc != NULL)
+ get_regs_from_mc(&regs, mc);
regs.is_user = 0;
unblock_signals();
- timer_handler(SIGVTALRM, &regs);
+ timer_handler(SIGVTALRM, NULL, &regs);
}
-void alarm_handler(int sig, struct sigcontext *sc)
+void alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc)
{
int enabled;
@@ -106,14 +101,13 @@ void alarm_handler(int sig, struct sigcontext *sc)
block_signals();
- real_alarm_handler(sc);
+ real_alarm_handler(mc);
set_signals(enabled);
}
void timer_init(void)
{
- set_handler(SIGVTALRM, (__sighandler_t) alarm_handler,
- SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, -1);
+ set_handler(SIGVTALRM);
}
void set_sigstack(void *sig_stack, int size)
@@ -126,10 +120,23 @@ void set_sigstack(void *sig_stack, int size)
panic("enabling signal stack failed, errno = %d\n", errno);
}
-static void (*handlers[_NSIG])(int sig, struct sigcontext *sc);
+static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = {
+ [SIGSEGV] = sig_handler,
+ [SIGBUS] = sig_handler,
+ [SIGILL] = sig_handler,
+ [SIGFPE] = sig_handler,
+ [SIGTRAP] = sig_handler,
+
+ [SIGIO] = sig_handler,
+ [SIGWINCH] = sig_handler,
+ [SIGVTALRM] = alarm_handler
+};
+
-void handle_signal(int sig, struct sigcontext *sc)
+static void hard_handler(int sig, siginfo_t *si, void *p)
{
+ struct ucontext *uc = p;
+ mcontext_t *mc = &uc->uc_mcontext;
unsigned long pending = 1UL << sig;
do {
@@ -155,7 +162,7 @@ void handle_signal(int sig, struct sigcontext *sc)
while ((sig = ffs(pending)) != 0){
sig--;
pending &= ~(1 << sig);
- (*handlers[sig])(sig, sc);
+ (*handlers[sig])(sig, (struct siginfo *)si, mc);
}
/*
@@ -169,28 +176,26 @@ void handle_signal(int sig, struct sigcontext *sc)
} while (pending);
}
-extern void hard_handler(int sig);
-
-void set_handler(int sig, void (*handler)(int), int flags, ...)
+void set_handler(int sig)
{
struct sigaction action;
- va_list ap;
+ int flags = SA_SIGINFO | SA_ONSTACK;
sigset_t sig_mask;
- int mask;
- handlers[sig] = (void (*)(int, struct sigcontext *)) handler;
- action.sa_handler = hard_handler;
+ action.sa_sigaction = hard_handler;
+ /* block irq ones */
sigemptyset(&action.sa_mask);
-
- va_start(ap, flags);
- while ((mask = va_arg(ap, int)) != -1)
- sigaddset(&action.sa_mask, mask);
- va_end(ap);
+ sigaddset(&action.sa_mask, SIGVTALRM);
+ sigaddset(&action.sa_mask, SIGIO);
+ sigaddset(&action.sa_mask, SIGWINCH);
if (sig == SIGSEGV)
flags |= SA_NODEFER;
+ if (sigismember(&action.sa_mask, sig))
+ flags |= SA_RESTART; /* if it's an irq signal */
+
action.sa_flags = flags;
action.sa_restorer = NULL;
if (sigaction(sig, &action, NULL) < 0)
@@ -269,9 +274,12 @@ void unblock_signals(void)
* Deal with SIGIO first because the alarm handler might
* schedule, leaving the pending SIGIO stranded until we come
* back here.
+ *
+ * SIGIO's handler doesn't use siginfo or mcontext,
+ * so they can be NULL.
*/
if (save_pending & SIGIO_MASK)
- sig_handler_common(SIGIO, NULL);
+ sig_handler_common(SIGIO, NULL, NULL);
if (save_pending & SIGVTALRM_MASK)
real_alarm_handler(NULL);
@@ -296,3 +304,11 @@ int set_signals(int enable)
return ret;
}
+
+int os_is_signal_stack(void)
+{
+ stack_t ss;
+ sigaltstack(NULL, &ss);
+
+ return ss.ss_flags & SS_ONSTACK;
+}
diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c
index d261f170d12..689b18db798 100644
--- a/arch/um/os-Linux/skas/mem.c
+++ b/arch/um/os-Linux/skas/mem.c
@@ -8,18 +8,16 @@
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
-#include "init.h"
-#include "kern_constants.h"
-#include "as-layout.h"
-#include "mm_id.h"
-#include "os.h"
-#include "proc_mm.h"
-#include "ptrace_user.h"
-#include "registers.h"
-#include "skas.h"
-#include "user.h"
-#include "sysdep/ptrace.h"
-#include "sysdep/stub.h"
+#include <init.h>
+#include <as-layout.h>
+#include <mm_id.h>
+#include <os.h>
+#include <proc_mm.h>
+#include <ptrace_user.h>
+#include <registers.h>
+#include <skas.h>
+#include <sysdep/ptrace.h>
+#include <sysdep/stub.h>
extern unsigned long batch_syscall_stub, __syscall_stub_start;
@@ -39,7 +37,7 @@ static unsigned long syscall_regs[MAX_REG_NR];
static int __init init_syscall_regs(void)
{
- get_safe_registers(syscall_regs);
+ get_safe_registers(syscall_regs, NULL);
syscall_regs[REGS_IP_INDEX] = STUB_CODE +
((unsigned long) &batch_syscall_stub -
(unsigned long) &__syscall_stub_start);
@@ -50,10 +48,6 @@ __initcall(init_syscall_regs);
extern int proc_mm;
-int single_count = 0;
-int multi_count = 0;
-int multi_op_count = 0;
-
static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr)
{
int n, i;
@@ -66,8 +60,6 @@ static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr)
/* FIXME: Need to look up userspace_pid by cpu */
pid = userspace_pid[0];
- multi_count++;
-
n = ptrace_setregs(pid, syscall_regs);
if (n < 0) {
printk(UM_KERN_ERR "Registers - \n");
@@ -128,9 +120,6 @@ long run_syscall_stub(struct mm_id * mm_idp, int syscall,
{
unsigned long *stack = check_init_stack(mm_idp, *addr);
- if (done && *addr == NULL)
- single_count++;
-
*stack += sizeof(long);
stack += *stack / sizeof(long);
@@ -143,7 +132,6 @@ long run_syscall_stub(struct mm_id * mm_idp, int syscall,
*stack++ = args[5];
*stack++ = expected;
*stack = 0;
- multi_op_count++;
if (!done && ((((unsigned long) stack) & ~UM_KERN_PAGE_MASK) <
UM_KERN_PAGE_SIZE - 10 * sizeof(long))) {
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index d6e0a2234b8..908579f2b0a 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -9,31 +9,23 @@
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
-#include <sys/ptrace.h>
#include <sys/wait.h>
#include <asm/unistd.h>
-#include "as-layout.h"
-#include "chan_user.h"
-#include "kern_constants.h"
-#include "kern_util.h"
-#include "mem.h"
-#include "os.h"
-#include "process.h"
-#include "proc_mm.h"
-#include "ptrace_user.h"
-#include "registers.h"
-#include "skas.h"
-#include "skas_ptrace.h"
-#include "user.h"
-#include "sysdep/stub.h"
+#include <as-layout.h>
+#include <init.h>
+#include <kern_util.h>
+#include <mem.h>
+#include <os.h>
+#include <proc_mm.h>
+#include <ptrace_user.h>
+#include <registers.h>
+#include <skas.h>
+#include <skas_ptrace.h>
+#include <sysdep/stub.h>
int is_skas_winch(int pid, int fd, void *data)
{
- if (pid != getpgrp())
- return 0;
-
- register_winch_irq(-1, fd, -1, data, 0);
- return 1;
+ return pid == getpgrp();
}
static int ptrace_dump_regs(int pid)
@@ -169,7 +161,7 @@ static void handle_trap(int pid, struct uml_pt_regs *regs,
if (!local_using_sysemu)
{
- err = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
+ err = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
__NR_getpid);
if (err < 0) {
printk(UM_KERN_ERR "handle_trap - nullifying syscall "
@@ -257,8 +249,8 @@ static int userspace_tramp(void *stack)
set_sigstack((void *) STUB_DATA, UM_KERN_PAGE_SIZE);
sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_ONSTACK | SA_NODEFER;
- sa.sa_handler = (void *) v;
+ sa.sa_flags = SA_ONSTACK | SA_NODEFER | SA_SIGINFO;
+ sa.sa_sigaction = (void *) v;
sa.sa_restorer = NULL;
if (sigaction(SIGSEGV, &sa, NULL) < 0) {
printk(UM_KERN_ERR "userspace_tramp - setting SIGSEGV "
@@ -354,6 +346,10 @@ void userspace(struct uml_pt_regs *regs)
int err, status, op, pid = userspace_pid[0];
/* To prevent races if using_sysemu changes under us.*/
int local_using_sysemu;
+ siginfo_t si;
+
+ /* Handle any immediate reschedules or signals */
+ interrupt_end();
if (getitimer(ITIMER_VIRTUAL, &timer))
printk(UM_KERN_ERR "Failed to get itimer, errno = %d\n", errno);
@@ -373,6 +369,9 @@ void userspace(struct uml_pt_regs *regs)
if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp))
fatal_sigsegv();
+ if (put_fp_registers(pid, regs->fp))
+ fatal_sigsegv();
+
/* Now we set local_using_sysemu to be used for one loop */
local_using_sysemu = get_using_sysemu();
@@ -399,17 +398,27 @@ void userspace(struct uml_pt_regs *regs)
fatal_sigsegv();
}
+ if (get_fp_registers(pid, regs->fp)) {
+ printk(UM_KERN_ERR "userspace - get_fp_registers failed, "
+ "errno = %d\n", errno);
+ fatal_sigsegv();
+ }
+
UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */
if (WIFSTOPPED(status)) {
int sig = WSTOPSIG(status);
+
+ ptrace(PTRACE_GETSIGINFO, pid, 0, (struct siginfo *)&si);
+
switch (sig) {
case SIGSEGV:
if (PTRACE_FULL_FAULTINFO ||
!ptrace_faultinfo) {
get_skas_faultinfo(pid,
&regs->faultinfo);
- (*sig_info[SIGSEGV])(SIGSEGV, regs);
+ (*sig_info[SIGSEGV])(SIGSEGV, (struct siginfo *)&si,
+ regs);
}
else handle_segv(pid, regs);
break;
@@ -417,14 +426,14 @@ void userspace(struct uml_pt_regs *regs)
handle_trap(pid, regs, local_using_sysemu);
break;
case SIGTRAP:
- relay_signal(SIGTRAP, regs);
+ relay_signal(SIGTRAP, (struct siginfo *)&si, regs);
break;
case SIGVTALRM:
now = os_nsecs();
if (now < nsecs)
break;
block_signals();
- (*sig_info[sig])(sig, regs);
+ (*sig_info[sig])(sig, (struct siginfo *)&si, regs);
unblock_signals();
nsecs = timer.it_value.tv_sec *
UM_NSEC_PER_SEC +
@@ -438,7 +447,7 @@ void userspace(struct uml_pt_regs *regs)
case SIGFPE:
case SIGWINCH:
block_signals();
- (*sig_info[sig])(sig, regs);
+ (*sig_info[sig])(sig, (struct siginfo *)&si, regs);
unblock_signals();
break;
default:
@@ -457,10 +466,11 @@ void userspace(struct uml_pt_regs *regs)
}
static unsigned long thread_regs[MAX_REG_NR];
+static unsigned long thread_fp_regs[FP_SIZE];
static int __init init_thread_regs(void)
{
- get_safe_registers(thread_regs);
+ get_safe_registers(thread_regs, thread_fp_regs);
/* Set parent's instruction pointer to start of clone-stub */
thread_regs[REGS_IP_INDEX] = STUB_CODE +
(unsigned long) stub_clone_handler -
@@ -503,6 +513,13 @@ int copy_context_skas0(unsigned long new_stack, int pid)
return err;
}
+ err = put_fp_registers(pid, thread_fp_regs);
+ if (err < 0) {
+ printk(UM_KERN_ERR "copy_context_skas0 : put_fp_registers "
+ "failed, pid = %d, err = %d\n", pid, err);
+ return err;
+ }
+
/* set a well known return code for detection of child write failure */
child_data->err = 12345678;
@@ -644,8 +661,7 @@ int start_idle_thread(void *stack, jmp_buf *switch_buf)
{
int n;
- set_handler(SIGWINCH, (__sighandler_t) sig_handler,
- SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGVTALRM, -1);
+ set_handler(SIGWINCH);
/*
* Can't use UML_SETJMP or UML_LONGJMP here because they save
diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c
index 02ee9adff54..337518c5042 100644
--- a/arch/um/os-Linux/start_up.c
+++ b/arch/um/os-Linux/start_up.c
@@ -13,18 +13,18 @@
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
-#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#include <asm/unistd.h>
-#include "init.h"
-#include "kern_constants.h"
-#include "os.h"
-#include "mem_user.h"
-#include "ptrace_user.h"
-#include "registers.h"
-#include "skas.h"
-#include "skas_ptrace.h"
+#include <init.h>
+#include <os.h>
+#include <mem_user.h>
+#include <ptrace_user.h>
+#include <registers.h>
+#include <skas.h>
+#include <skas_ptrace.h>
static void ptrace_child(void)
{
@@ -225,7 +225,7 @@ static void __init check_sysemu(void)
goto fail;
}
- n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
+ n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
if (n < 0) {
non_fatal("check_sysemu : failed to modify system call "
"return");
@@ -261,7 +261,7 @@ static void __init check_sysemu(void)
"doesn't singlestep");
goto fail;
}
- n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET,
+ n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET,
os_getpid());
if (n < 0)
fatal_perror("check_sysemu : failed to modify "
@@ -317,10 +317,10 @@ static void __init check_ptrace(void)
fatal("check_ptrace : expected (SIGTRAP|0x80), "
"got status = %d", status);
- syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET,
+ syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET,
0);
if (syscall == __NR_getpid) {
- n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
+ n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
__NR_getppid);
if (n < 0)
fatal_perror("check_ptrace : failed to modify "
diff --git a/arch/um/os-Linux/sys-i386/Makefile b/arch/um/os-Linux/sys-i386/Makefile
deleted file mode 100644
index b4bc6ac4f30..00000000000
--- a/arch/um/os-Linux/sys-i386/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
-# Licensed under the GPL
-#
-
-obj-y = registers.o signal.o task_size.o tls.o
-
-USER_OBJS := $(obj-y)
-
-include arch/um/scripts/Makefile.rules
diff --git a/arch/um/os-Linux/sys-i386/registers.c b/arch/um/os-Linux/sys-i386/registers.c
deleted file mode 100644
index 229f7a53d8d..00000000000
--- a/arch/um/os-Linux/sys-i386/registers.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2004 PathScale, Inc
- * Copyright (C) 2004 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
- */
-
-#include <errno.h>
-#include <sys/ptrace.h>
-#include <sys/user.h>
-#include "kern_constants.h"
-#include "longjmp.h"
-#include "user.h"
-#include "sysdep/ptrace_user.h"
-
-int save_fp_registers(int pid, unsigned long *fp_regs)
-{
- if (ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0)
- return -errno;
- return 0;
-}
-
-int restore_fp_registers(int pid, unsigned long *fp_regs)
-{
- if (ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0)
- return -errno;
- return 0;
-}
-
-int save_fpx_registers(int pid, unsigned long *fp_regs)
-{
- if (ptrace(PTRACE_GETFPXREGS, pid, 0, fp_regs) < 0)
- return -errno;
- return 0;
-}
-
-int restore_fpx_registers(int pid, unsigned long *fp_regs)
-{
- if (ptrace(PTRACE_SETFPXREGS, pid, 0, fp_regs) < 0)
- return -errno;
- return 0;
-}
-
-unsigned long get_thread_reg(int reg, jmp_buf *buf)
-{
- switch (reg) {
- case EIP:
- return buf[0]->__eip;
- case UESP:
- return buf[0]->__esp;
- case EBP:
- return buf[0]->__ebp;
- default:
- printk(UM_KERN_ERR "get_thread_regs - unknown register %d\n",
- reg);
- return 0;
- }
-}
-
-int have_fpx_regs = 1;
-
-int get_fp_registers(int pid, unsigned long *regs)
-{
- if (have_fpx_regs)
- return save_fpx_registers(pid, regs);
- else
- return save_fp_registers(pid, regs);
-}
-
-int put_fp_registers(int pid, unsigned long *regs)
-{
- if (have_fpx_regs)
- return restore_fpx_registers(pid, regs);
- else
- return restore_fp_registers(pid, regs);
-}
-
-void arch_init_registers(int pid)
-{
- struct user_fpxregs_struct fpx_regs;
- int err;
-
- err = ptrace(PTRACE_GETFPXREGS, pid, 0, &fpx_regs);
- if (!err)
- return;
-
- if (errno != EIO)
- panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d",
- errno);
-
- have_fpx_regs = 0;
-}
diff --git a/arch/um/os-Linux/sys-i386/signal.c b/arch/um/os-Linux/sys-i386/signal.c
deleted file mode 100644
index f311609f93d..00000000000
--- a/arch/um/os-Linux/sys-i386/signal.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
- */
-
-#include <signal.h>
-
-extern void handle_signal(int sig, struct sigcontext *sc);
-
-void hard_handler(int sig)
-{
- handle_signal(sig, (struct sigcontext *) (&sig + 1));
-}
diff --git a/arch/um/os-Linux/sys-i386/task_size.c b/arch/um/os-Linux/sys-i386/task_size.c
deleted file mode 100644
index be04c1e183b..00000000000
--- a/arch/um/os-Linux/sys-i386/task_size.c
+++ /dev/null
@@ -1,139 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <sys/mman.h>
-#include "longjmp.h"
-#include "kern_constants.h"
-
-static jmp_buf buf;
-
-static void segfault(int sig)
-{
- longjmp(buf, 1);
-}
-
-static int page_ok(unsigned long page)
-{
- unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT);
- unsigned long n = ~0UL;
- void *mapped = NULL;
- int ok = 0;
-
- /*
- * First see if the page is readable. If it is, it may still
- * be a VDSO, so we go on to see if it's writable. If not
- * then try mapping memory there. If that fails, then we're
- * still in the kernel area. As a sanity check, we'll fail if
- * the mmap succeeds, but gives us an address different from
- * what we wanted.
- */
- if (setjmp(buf) == 0)
- n = *address;
- else {
- mapped = mmap(address, UM_KERN_PAGE_SIZE,
- PROT_READ | PROT_WRITE,
- MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (mapped == MAP_FAILED)
- return 0;
- if (mapped != address)
- goto out;
- }
-
- /*
- * Now, is it writeable? If so, then we're in user address
- * space. If not, then try mprotecting it and try the write
- * again.
- */
- if (setjmp(buf) == 0) {
- *address = n;
- ok = 1;
- goto out;
- } else if (mprotect(address, UM_KERN_PAGE_SIZE,
- PROT_READ | PROT_WRITE) != 0)
- goto out;
-
- if (setjmp(buf) == 0) {
- *address = n;
- ok = 1;
- }
-
- out:
- if (mapped != NULL)
- munmap(mapped, UM_KERN_PAGE_SIZE);
- return ok;
-}
-
-unsigned long os_get_top_address(void)
-{
- struct sigaction sa, old;
- unsigned long bottom = 0;
- /*
- * A 32-bit UML on a 64-bit host gets confused about the VDSO at
- * 0xffffe000. It is mapped, is readable, can be reprotected writeable
- * and written. However, exec discovers later that it can't be
- * unmapped. So, just set the highest address to be checked to just
- * below it. This might waste some address space on 4G/4G 32-bit
- * hosts, but shouldn't hurt otherwise.
- */
- unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT;
- unsigned long test, original;
-
- printf("Locating the bottom of the address space ... ");
- fflush(stdout);
-
- /*
- * We're going to be longjmping out of the signal handler, so
- * SA_DEFER needs to be set.
- */
- sa.sa_handler = segfault;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_NODEFER;
- if (sigaction(SIGSEGV, &sa, &old)) {
- perror("os_get_top_address");
- exit(1);
- }
-
- /* Manually scan the address space, bottom-up, until we find
- * the first valid page (or run out of them).
- */
- for (bottom = 0; bottom < top; bottom++) {
- if (page_ok(bottom))
- break;
- }
-
- /* If we've got this far, we ran out of pages. */
- if (bottom == top) {
- fprintf(stderr, "Unable to determine bottom of address "
- "space.\n");
- exit(1);
- }
-
- printf("0x%x\n", bottom << UM_KERN_PAGE_SHIFT);
- printf("Locating the top of the address space ... ");
- fflush(stdout);
-
- original = bottom;
-
- /* This could happen with a 4G/4G split */
- if (page_ok(top))
- goto out;
-
- do {
- test = bottom + (top - bottom) / 2;
- if (page_ok(test))
- bottom = test;
- else
- top = test;
- } while (top - bottom > 1);
-
-out:
- /* Restore the old SIGSEGV handling */
- if (sigaction(SIGSEGV, &old, NULL)) {
- perror("os_get_top_address");
- exit(1);
- }
- top <<= UM_KERN_PAGE_SHIFT;
- printf("0x%x\n", top);
-
- return top;
-}
diff --git a/arch/um/os-Linux/sys-i386/tls.c b/arch/um/os-Linux/sys-i386/tls.c
deleted file mode 100644
index 32ed41ec1a3..00000000000
--- a/arch/um/os-Linux/sys-i386/tls.c
+++ /dev/null
@@ -1,36 +0,0 @@
-#include <errno.h>
-#include <linux/unistd.h>
-
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#include "sysdep/tls.h"
-#include "user.h"
-
-/* Checks whether host supports TLS, and sets *tls_min according to the value
- * valid on the host.
- * i386 host have it == 6; x86_64 host have it == 12, for i386 emulation. */
-void check_host_supports_tls(int *supports_tls, int *tls_min) {
- /* Values for x86 and x86_64.*/
- int val[] = {GDT_ENTRY_TLS_MIN_I386, GDT_ENTRY_TLS_MIN_X86_64};
- int i;
-
- for (i = 0; i < ARRAY_SIZE(val); i++) {
- user_desc_t info;
- info.entry_number = val[i];
-
- if (syscall(__NR_get_thread_area, &info) == 0) {
- *tls_min = val[i];
- *supports_tls = 1;
- return;
- } else {
- if (errno == EINVAL)
- continue;
- else if (errno == ENOSYS)
- *supports_tls = 0;
- return;
- }
- }
-
- *supports_tls = 0;
-}
diff --git a/arch/um/os-Linux/sys-x86_64/Makefile b/arch/um/os-Linux/sys-x86_64/Makefile
deleted file mode 100644
index a44a47f8f57..00000000000
--- a/arch/um/os-Linux/sys-x86_64/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
-# Licensed under the GPL
-#
-
-obj-y = registers.o prctl.o signal.o task_size.o
-
-USER_OBJS := $(obj-y)
-
-include arch/um/scripts/Makefile.rules
diff --git a/arch/um/os-Linux/sys-x86_64/prctl.c b/arch/um/os-Linux/sys-x86_64/prctl.c
deleted file mode 100644
index 9d34eddb517..00000000000
--- a/arch/um/os-Linux/sys-x86_64/prctl.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright (C) 2007 Jeff Dike (jdike@{addtoit.com,linux.intel.com})
- * Licensed under the GPL
- */
-
-#include <sys/ptrace.h>
-#include <linux/ptrace.h>
-
-int os_arch_prctl(int pid, int code, unsigned long *addr)
-{
- return ptrace(PTRACE_ARCH_PRCTL, pid, (unsigned long) addr, code);
-}
diff --git a/arch/um/os-Linux/sys-x86_64/registers.c b/arch/um/os-Linux/sys-x86_64/registers.c
deleted file mode 100644
index 594d97ad02b..00000000000
--- a/arch/um/os-Linux/sys-x86_64/registers.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2006 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
- */
-
-#include <errno.h>
-#include <sys/ptrace.h>
-#define __FRAME_OFFSETS
-#include <asm/ptrace.h>
-#include "kern_constants.h"
-#include "longjmp.h"
-#include "user.h"
-
-int save_fp_registers(int pid, unsigned long *fp_regs)
-{
- if (ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0)
- return -errno;
- return 0;
-}
-
-int restore_fp_registers(int pid, unsigned long *fp_regs)
-{
- if (ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0)
- return -errno;
- return 0;
-}
-
-unsigned long get_thread_reg(int reg, jmp_buf *buf)
-{
- switch (reg) {
- case RIP:
- return buf[0]->__rip;
- case RSP:
- return buf[0]->__rsp;
- case RBP:
- return buf[0]->__rbp;
- default:
- printk(UM_KERN_ERR "get_thread_regs - unknown register %d\n",
- reg);
- return 0;
- }
-}
-
-int get_fp_registers(int pid, unsigned long *regs)
-{
- return save_fp_registers(pid, regs);
-}
-
-int put_fp_registers(int pid, unsigned long *regs)
-{
- return restore_fp_registers(pid, regs);
-}
diff --git a/arch/um/os-Linux/sys-x86_64/signal.c b/arch/um/os-Linux/sys-x86_64/signal.c
deleted file mode 100644
index 82a388822cd..00000000000
--- a/arch/um/os-Linux/sys-x86_64/signal.c
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
- */
-
-#include <signal.h>
-
-extern void handle_signal(int sig, struct sigcontext *sc);
-
-void hard_handler(int sig)
-{
- struct ucontext *uc;
- asm("movq %%rdx, %0" : "=r" (uc));
-
- handle_signal(sig, (struct sigcontext *) &uc->uc_mcontext);
-}
diff --git a/arch/um/os-Linux/sys-x86_64/task_size.c b/arch/um/os-Linux/sys-x86_64/task_size.c
deleted file mode 100644
index 26a0dd1f349..00000000000
--- a/arch/um/os-Linux/sys-x86_64/task_size.c
+++ /dev/null
@@ -1,5 +0,0 @@
-unsigned long os_get_top_address(unsigned long shift)
-{
- /* The old value of CONFIG_TOP_ADDR */
- return 0x7fc0000000;
-}
diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c
index 6e3359d6a83..e9824d5dd7d 100644
--- a/arch/um/os-Linux/time.c
+++ b/arch/um/os-Linux/time.c
@@ -8,11 +8,9 @@
#include <signal.h>
#include <time.h>
#include <sys/time.h>
-#include "kern_constants.h"
-#include "kern_util.h"
-#include "os.h"
-#include "process.h"
-#include "user.h"
+#include <kern_util.h>
+#include <os.h>
+#include "internal.h"
int set_interval(void)
{
@@ -81,7 +79,7 @@ long long os_nsecs(void)
return timeval_to_ns(&tv);
}
-#ifdef UML_CONFIG_NO_HZ
+#ifdef UML_CONFIG_NO_HZ_COMMON
static int after_sleep_interval(struct timespec *ts)
{
return 0;
@@ -89,7 +87,7 @@ static int after_sleep_interval(struct timespec *ts)
static void deliver_alarm(void)
{
- alarm_handler(SIGVTALRM, NULL);
+ alarm_handler(SIGVTALRM, NULL, NULL);
}
static unsigned long long sleep_time(unsigned long long nsecs)
@@ -116,7 +114,7 @@ static void deliver_alarm(void)
skew += this_tick - last_tick;
while (skew >= one_tick) {
- alarm_handler(SIGVTALRM, NULL);
+ alarm_handler(SIGVTALRM, NULL, NULL);
skew -= one_tick;
}
diff --git a/arch/um/os-Linux/tls.c b/arch/um/os-Linux/tls.c
deleted file mode 100644
index 73277801ef1..00000000000
--- a/arch/um/os-Linux/tls.c
+++ /dev/null
@@ -1,35 +0,0 @@
-#include <errno.h>
-#include <sys/ptrace.h>
-#include "sysdep/tls.h"
-
-/* TLS support - we basically rely on the host's one.*/
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-
-#ifndef PTRACE_SET_THREAD_AREA
-#define PTRACE_SET_THREAD_AREA 26
-#endif
-
-int os_set_thread_area(user_desc_t *info, int pid)
-{
- int ret;
-
- ret = ptrace(PTRACE_SET_THREAD_AREA, pid, info->entry_number,
- (unsigned long) info);
- if (ret < 0)
- ret = -errno;
- return ret;
-}
-
-int os_get_thread_area(user_desc_t *info, int pid)
-{
- int ret;
-
- ret = ptrace(PTRACE_GET_THREAD_AREA, pid, info->entry_number,
- (unsigned long) info);
- if (ret < 0)
- ret = -errno;
- return ret;
-}
diff --git a/arch/um/os-Linux/tty.c b/arch/um/os-Linux/tty.c
index b09ff66a77e..721d8afa329 100644
--- a/arch/um/os-Linux/tty.c
+++ b/arch/um/os-Linux/tty.c
@@ -7,10 +7,8 @@
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
-#include "kern_constants.h"
-#include "kern_util.h"
-#include "os.h"
-#include "user.h"
+#include <kern_util.h>
+#include <os.h>
struct grantpt_info {
int fd;
diff --git a/arch/um/os-Linux/uaccess.c b/arch/um/os-Linux/uaccess.c
deleted file mode 100644
index 087ed74ffca..00000000000
--- a/arch/um/os-Linux/uaccess.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
- * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
- */
-
-#include <stddef.h>
-#include "longjmp.h"
-
-unsigned long __do_user_copy(void *to, const void *from, int n,
- void **fault_addr, jmp_buf **fault_catcher,
- void (*op)(void *to, const void *from,
- int n), int *faulted_out)
-{
- unsigned long *faddrp = (unsigned long *) fault_addr, ret;
-
- jmp_buf jbuf;
- *fault_catcher = &jbuf;
- if (UML_SETJMP(&jbuf) == 0) {
- (*op)(to, from, n);
- ret = 0;
- *faulted_out = 0;
- }
- else {
- ret = *faddrp;
- *faulted_out = 1;
- }
- *fault_addr = NULL;
- *fault_catcher = NULL;
- return ret;
-}
-
diff --git a/arch/um/os-Linux/umid.c b/arch/um/os-Linux/umid.c
index a27defb8188..c1dc89261f6 100644
--- a/arch/um/os-Linux/umid.c
+++ b/arch/um/os-Linux/umid.c
@@ -12,10 +12,8 @@
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
-#include "init.h"
-#include "kern_constants.h"
-#include "os.h"
-#include "user.h"
+#include <init.h>
+#include <os.h>
#define UML_DIR "~/.uml/"
diff --git a/arch/um/os-Linux/user_syms.c b/arch/um/os-Linux/user_syms.c
index 05f5ea8e83d..db4a034aeee 100644
--- a/arch/um/os-Linux/user_syms.c
+++ b/arch/um/os-Linux/user_syms.c
@@ -1,5 +1,5 @@
-#include "linux/types.h"
-#include "linux/module.h"
+#include <linux/types.h>
+#include <linux/module.h>
/* Some of this are builtin function (some are not but could in the future),
* so I *must* declare good prototypes for them and then EXPORT them.
@@ -45,7 +45,7 @@ EXPORT_SYMBOL(readdir64);
extern void truncate64(void) __attribute__((weak));
EXPORT_SYMBOL(truncate64);
-#ifdef SUBARCH_i386
+#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
EXPORT_SYMBOL(vsyscall_ehdr);
EXPORT_SYMBOL(vsyscall_end);
#endif
@@ -113,3 +113,8 @@ EXPORT_SYMBOL(__stack_smash_handler);
extern long __guard __attribute__((weak));
EXPORT_SYMBOL(__guard);
+
+#ifdef _FORTIFY_SOURCE
+extern int __sprintf_chk(char *str, int flag, size_t strlen, const char *format);
+EXPORT_SYMBOL(__sprintf_chk);
+#endif
diff --git a/arch/um/os-Linux/util.c b/arch/um/os-Linux/util.c
index 6ea77979531..faee55ef6d2 100644
--- a/arch/um/os-Linux/util.c
+++ b/arch/um/os-Linux/util.c
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
@@ -12,9 +13,7 @@
#include <wait.h>
#include <sys/mman.h>
#include <sys/utsname.h>
-#include "kern_constants.h"
-#include "os.h"
-#include "user.h"
+#include <os.h>
void stack_protections(unsigned long address)
{
@@ -75,6 +74,36 @@ void setup_hostinfo(char *buf, int len)
host.release, host.version, host.machine);
}
+/*
+ * We cannot use glibc's abort(). It makes use of tgkill() which
+ * has no effect within UML's kernel threads.
+ * After that glibc would execute an invalid instruction to kill
+ * the calling process and UML crashes with SIGSEGV.
+ */
+static inline void __attribute__ ((noreturn)) uml_abort(void)
+{
+ sigset_t sig;
+
+ fflush(NULL);
+
+ if (!sigemptyset(&sig) && !sigaddset(&sig, SIGABRT))
+ sigprocmask(SIG_UNBLOCK, &sig, 0);
+
+ for (;;)
+ if (kill(getpid(), SIGABRT) < 0)
+ exit(127);
+}
+
+/*
+ * UML helper threads must not handle SIGWINCH/INT/TERM
+ */
+void os_fix_helper_signals(void)
+{
+ signal(SIGWINCH, SIG_IGN);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+}
+
void os_dump_core(void)
{
int pid;
@@ -116,5 +145,10 @@ void os_dump_core(void)
while ((pid = waitpid(-1, NULL, WNOHANG | __WALL)) > 0)
os_kill_ptraced_process(pid, 0);
- abort();
+ uml_abort();
+}
+
+void um_early_printk(const char *s, unsigned int n)
+{
+ printf("%.*s", n, s);
}