diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 1622ad6..c72c440 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -60,6 +60,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
 #include <sys/statfs.h>
 #include <utime.h>
 #include <sys/sysinfo.h>
+#include <sys/signalfd.h>
 //#include <sys/user.h>
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
@@ -294,6 +295,15 @@ static bitmask_transtbl fcntl_flags_tbl[] = {
   { 0, 0, 0, 0 }
 };
 
+struct target_fd_ops {
+    abi_long (*read) (int, void *, size_t);
+};
+
+typedef struct target_fd_ops target_fd_ops_t;
+
+static int target_fd_max;
+static target_fd_ops_t **target_fd_ops;
+
 static int sys_getcwd1(char *buf, size_t size)
 {
   if (getcwd(buf, size) == NULL) {
@@ -4878,6 +4888,7 @@ void syscall_init(void)
     const argtype *arg_type;
     int size;
     int i;
+    struct rlimit rlim;
 
 #define STRUCT(name, ...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def);
 #define STRUCT_SPECIAL(name) thunk_register_struct_direct(STRUCT_ ## name, #name, &struct_ ## name ## _def);
@@ -4885,6 +4896,14 @@ void syscall_init(void)
 #undef STRUCT
 #undef STRUCT_SPECIAL
 
+    /* allocate an array to store fd ops */
+
+    if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
+        target_fd_max = rlim.rlim_cur;
+        target_fd_ops = g_malloc0(target_fd_max * sizeof(target_fd_ops_t *));
+    }
+
+
     /* Build target_to_host_errno_table[] table from
      * host_to_target_errno_table[]. */
     for (i = 0; i < ERRNO_TABLE_SIZE; i++) {
@@ -5512,6 +5531,51 @@ static target_timer_t get_timer_id(abi_long arg)
     return timerid;
 }
 
+/* signalfd siginfo conversion */
+
+static void
+host_to_target_signalfd_siginfo(struct signalfd_siginfo *tinfo,
+                                const struct signalfd_siginfo *info)
+{
+    int sig = host_to_target_signal(info->ssi_signo);
+    tinfo->ssi_signo = tswap32(sig);
+    tinfo->ssi_errno = 0; /* unused */
+    tinfo->ssi_code = tswap32(info->ssi_code);
+    tinfo->ssi_pid =  tswap32(info->ssi_pid);
+    tinfo->ssi_uid =  tswap32(info->ssi_uid);
+    tinfo->ssi_fd =  tswap32(info->ssi_fd);
+    tinfo->ssi_tid =  tswap32(info->ssi_tid);
+    tinfo->ssi_band =  tswap32(info->ssi_band);
+    tinfo->ssi_overrun =  tswap32(info->ssi_overrun);
+    tinfo->ssi_trapno =  tswap32(info->ssi_trapno);
+    tinfo->ssi_status =  tswap32(info->ssi_status);
+    tinfo->ssi_int =  tswap32(info->ssi_int);
+    tinfo->ssi_ptr =  tswap64(info->ssi_ptr);
+    tinfo->ssi_utime =  tswap64(info->ssi_utime);
+    tinfo->ssi_stime =  tswap64(info->ssi_stime);
+    tinfo->ssi_addr =  tswap64(info->ssi_addr);
+}
+
+static abi_long target_signalfd_read(int fd, void *buf, size_t count)
+{
+    int ret;
+    int i;
+
+    ret = get_errno(read(fd, buf, count));
+    if (ret < 0) {
+        return ret;
+    }
+
+    for (i = 0; i < ret; i += sizeof(struct signalfd_siginfo)) {
+        host_to_target_signalfd_siginfo(buf + i, buf + i);
+    }
+    return ret;
+}
+
+static target_fd_ops_t target_signalfd_ops = {
+    .read = target_signalfd_read,
+};
+
 /* do_syscall() should always have a single exit point at the end so
    that actions, such as logging of syscall results, can be performed.
    All errnos that do_syscall() returns must be -TARGET_<errcode>. */
@@ -5571,7 +5635,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         else {
             if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
                 goto efault;
-            ret = get_errno(read(arg1, p, arg3));
+            if (arg1 < target_fd_max &&
+                target_fd_ops[arg1] &&
+                target_fd_ops[arg1]->read) {
+                ret =  target_fd_ops[arg1]->read(arg1, p, arg3);
+            } else {
+                ret = get_errno(read(arg1, p, arg3));
+            }
             unlock_user(p, arg2, ret);
         }
         break;
@@ -5598,6 +5668,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         unlock_user(p, arg2, 0);
         break;
     case TARGET_NR_close:
+        if (arg1 < target_fd_max &&
+            target_fd_ops[arg1]) {
+            target_fd_ops[arg1] = NULL;
+        }
         ret = get_errno(close(arg1));
         break;
     case TARGET_NR_brk:
@@ -9451,6 +9525,26 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         break;
 #endif
 #endif
+#if defined(TARGET_NR_signalfd4)
+    case TARGET_NR_signalfd4:
+        {
+            target_sigset_t *target_mask;
+            sigset_t mask;
+            if (!lock_user_struct(VERIFY_READ, target_mask, arg2, 1)) {
+                goto efault;
+            }
+
+            target_to_host_sigset(&mask, target_mask);
+
+            ret = get_errno(signalfd(arg1, &mask, arg4));
+            if (ret >= 0) {
+                target_fd_ops[ret] = &target_signalfd_ops;
+            }
+
+            unlock_user_struct(target_mask, arg2, 0);
+        }
+        break;
+#endif
 #if defined(CONFIG_EPOLL)
 #if defined(TARGET_NR_epoll_create)
     case TARGET_NR_epoll_create:
@@ -9743,6 +9837,27 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
     }
 #endif
 
+#if defined(TARGET_NR_signalfd)
+    case TARGET_NR_signalfd:
+        {
+            target_sigset_t *target_mask;
+            sigset_t mask;
+            if (!lock_user_struct(VERIFY_READ, target_mask, arg2, 1)) {
+                goto efault;
+            }
+
+            target_to_host_sigset(&mask, target_mask);
+
+            ret = get_errno(signalfd(arg1, &mask, arg3));
+            if (ret >= 0) {
+                target_fd_ops[ret] = &target_signalfd_ops;
+            }
+
+            unlock_user_struct(target_mask, arg2, 0);
+        }
+        break;
+#endif
+
 #if defined(TARGET_NR_timerfd_create) && defined(CONFIG_TIMERFD)
     case TARGET_NR_timerfd_create:
         ret = get_errno(timerfd_create(arg1,
