diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c
index 0c1ec53025..eb72be5dd0 100644
--- a/linux-user/arm/signal.c
+++ b/linux-user/arm/signal.c
@@ -102,13 +102,13 @@ struct sigframe_v1
 {
     struct target_sigcontext sc;
     abi_ulong extramask[TARGET_NSIG_WORDS-1];
-    abi_ulong retcode;
+    abi_ulong retcode[4];
 };
 
 struct sigframe_v2
 {
     struct target_ucontext_v2 uc;
-    abi_ulong retcode;
+    abi_ulong retcode[4];
 };
 
 struct rt_sigframe_v1
@@ -117,14 +117,14 @@ struct rt_sigframe_v1
     abi_ulong puc;
     struct target_siginfo info;
     struct target_ucontext_v1 uc;
-    abi_ulong retcode;
+    abi_ulong retcode[4];
 };
 
 struct rt_sigframe_v2
 {
     struct target_siginfo info;
     struct target_ucontext_v2 uc;
-    abi_ulong retcode;
+    abi_ulong retcode[4];
 };
 
 #define TARGET_CONFIG_CPU_32 1
@@ -147,6 +147,21 @@ static const abi_ulong retcodes[4] = {
         SWI_SYS_RT_SIGRETURN,   SWI_THUMB_RT_SIGRETURN
 };
 
+/*
+ * Stub needed to make sure the FD register (r9) contains the right
+ * value.
+ */
+static const unsigned long sigreturn_fdpic_codes[3] = {
+    0xe59fc004, /* ldr r12, [pc, #4] to read function descriptor */
+    0xe59c9004, /* ldr r9, [r12, #4] to setup GOT */
+    0xe59cf000  /* ldr pc, [r12] to jump into restorer */
+};
+
+static const unsigned long sigreturn_fdpic_thumb_codes[3] = {
+    0xc008f8df, /* ldr r12, [pc, #8] to read function descriptor */
+    0x9004f8dc, /* ldr r9, [r12, #4] to setup GOT */
+    0xf000f8dc  /* ldr pc, [r12] to jump into restorer */
+};
 
 static inline int valid_user_regs(CPUARMState *regs)
 {
@@ -200,13 +215,33 @@ get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize)
     return (sp - framesize) & ~7;
 }
 
-static void
+static int
 setup_return(CPUARMState *env, struct target_sigaction *ka,
              abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr)
 {
-    abi_ulong handler = ka->_sa_handler;
+    abi_ulong handler = 0;
+    abi_ulong handler_fdpic_GOT = 0;
     abi_ulong retcode;
-    int thumb = handler & 1;
+
+    int thumb;
+    int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info);
+
+    if (is_fdpic) {
+        /* In FDPIC mode, ka->_sa_handler points to a function
+         * descriptor (FD). The first word contains the address of the
+         * handler. The second word contains the value of the PIC
+         * register (r9).  */
+        abi_ulong funcdesc_ptr = ka->_sa_handler;
+        if (get_user_ual(handler, funcdesc_ptr)
+            || get_user_ual(handler_fdpic_GOT, funcdesc_ptr + 4)) {
+            return 1;
+        }
+    } else {
+        handler = ka->_sa_handler;
+    }
+
+    thumb = handler & 1;
+
     uint32_t cpsr = cpsr_read(env);
 
     cpsr &= ~CPSR_IT;
@@ -217,7 +252,28 @@ setup_return(CPUARMState *env, struct target_sigaction *ka,
     }
 
     if (ka->sa_flags & TARGET_SA_RESTORER) {
-        retcode = ka->sa_restorer;
+        if (is_fdpic) {
+            /* For FDPIC we ensure that the restorer is called with a
+             * correct r9 value.  For that we need to write code on
+             * the stack that sets r9 and jumps back to restorer
+             * value.
+             */
+            if (thumb) {
+                __put_user(sigreturn_fdpic_thumb_codes[0], rc);
+                __put_user(sigreturn_fdpic_thumb_codes[1], rc + 1);
+                __put_user(sigreturn_fdpic_thumb_codes[2], rc + 2);
+                __put_user((abi_ulong)ka->sa_restorer, rc + 3);
+            } else {
+                __put_user(sigreturn_fdpic_codes[0], rc);
+                __put_user(sigreturn_fdpic_codes[1], rc + 1);
+                __put_user(sigreturn_fdpic_codes[2], rc + 2);
+                __put_user((abi_ulong)ka->sa_restorer, rc + 3);
+            }
+
+            retcode = rc_addr + thumb;
+        } else {
+            retcode = ka->sa_restorer;
+        }
     } else {
         unsigned int idx = thumb;
 
@@ -231,10 +287,15 @@ setup_return(CPUARMState *env, struct target_sigaction *ka,
     }
 
     env->regs[0] = usig;
+    if (is_fdpic) {
+        env->regs[9] = handler_fdpic_GOT;
+    }
     env->regs[13] = frame_addr;
     env->regs[14] = retcode;
     env->regs[15] = handler & (thumb ? ~1 : ~3);
     cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr);
+
+    return 0;
 }
 
 static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env)
@@ -327,12 +388,15 @@ static void setup_frame_v1(int usig, struct target_sigaction *ka,
         __put_user(set->sig[i], &frame->extramask[i - 1]);
     }
 
-    setup_return(regs, ka, &frame->retcode, frame_addr, usig,
-                 frame_addr + offsetof(struct sigframe_v1, retcode));
+    if (setup_return(regs, ka, frame->retcode, frame_addr, usig,
+                     frame_addr + offsetof(struct sigframe_v1, retcode))) {
+        goto sigsegv;
+    }
 
     unlock_user_struct(frame, frame_addr, 1);
     return;
 sigsegv:
+    unlock_user_struct(frame, frame_addr, 1);
     force_sigsegv(usig);
 }
 
@@ -349,12 +413,15 @@ static void setup_frame_v2(int usig, struct target_sigaction *ka,
 
     setup_sigframe_v2(&frame->uc, set, regs);
 
-    setup_return(regs, ka, &frame->retcode, frame_addr, usig,
-                 frame_addr + offsetof(struct sigframe_v2, retcode));
+    if (setup_return(regs, ka, frame->retcode, frame_addr, usig,
+                     frame_addr + offsetof(struct sigframe_v2, retcode))) {
+        goto sigsegv;
+    }
 
     unlock_user_struct(frame, frame_addr, 1);
     return;
 sigsegv:
+    unlock_user_struct(frame, frame_addr, 1);
     force_sigsegv(usig);
 }
 
@@ -404,8 +471,10 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka,
         __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
     }
 
-    setup_return(env, ka, &frame->retcode, frame_addr, usig,
-                 frame_addr + offsetof(struct rt_sigframe_v1, retcode));
+    if (setup_return(env, ka, frame->retcode, frame_addr, usig,
+                     frame_addr + offsetof(struct rt_sigframe_v1, retcode))) {
+        goto sigsegv;
+    }
 
     env->regs[1] = info_addr;
     env->regs[2] = uc_addr;
@@ -413,6 +482,7 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka,
     unlock_user_struct(frame, frame_addr, 1);
     return;
 sigsegv:
+    unlock_user_struct(frame, frame_addr, 1);
     force_sigsegv(usig);
 }
 
@@ -435,8 +505,10 @@ static void setup_rt_frame_v2(int usig, struct target_sigaction *ka,
 
     setup_sigframe_v2(&frame->uc, set, env);
 
-    setup_return(env, ka, &frame->retcode, frame_addr, usig,
-                 frame_addr + offsetof(struct rt_sigframe_v2, retcode));
+    if (setup_return(env, ka, frame->retcode, frame_addr, usig,
+                     frame_addr + offsetof(struct rt_sigframe_v2, retcode))) {
+        goto sigsegv;
+    }
 
     env->regs[1] = info_addr;
     env->regs[2] = uc_addr;
@@ -444,6 +516,7 @@ static void setup_rt_frame_v2(int usig, struct target_sigaction *ka,
     unlock_user_struct(frame, frame_addr, 1);
     return;
 sigsegv:
+    unlock_user_struct(frame, frame_addr, 1);
     force_sigsegv(usig);
 }
 
