diff mbox

[i386] : Implement attribute ((naked))

Message ID CAFULd4bsPgS9mhT4nXAqk2N9Atv-FNhfdHf_jggf_GM0pdGZSg@mail.gmail.com
State New
Headers show

Commit Message

Uros Bizjak July 30, 2017, 8:14 p.m. UTC
Hello!

attribute ((naked)) generates function body without function frame,
and as shown in PR 25967 [1], users are looking for this feature also
for x86 targets. Recently, Daniel introduced a testcase that would
benefit from this attribute.

Attached patch also adds a trap instruction (ud2) at the end of the
naked function, where epilogue would be. If there is a path through
the function that would reach epilogue, trap insn triggers, as shown
in added naked-3.c test.

As documented (many times) in the documentation, the only correct
usage is with classic "asm" statement. The documentation is perhaps
too pessimistic; with some caution, quite complex c code can be mixed
with asm prologue and epilogue. YMMV, the warning is there.

2017-07-30  Uros Bizjak  <ubizjak@gmail.com>

    PR target/25967
    * config/i386/i386.c (ix86_function_naked): New function.
    (ix86_can_use_return_insn_p): Return false for naked functions.
    (ix86_expand_prologue): Skip prologue for naked functions.
    (ix86_expand_epilogue): Skip epilogue for naked functions
    and emit trap instruction.
    (ix86_warn_func_return): New function.
    (ix86_attribute_table): Add "naked" attribute specification.
    (TARGET_WARN_FUNC_RETURN): Define.
    * doc/extend.texi (x86 Function Attributes) <naked>: Document it.

testsuite/ChangeLog:

2017-07-30  Uros Bizjak  <ubizjak@gmail.com>

    PR target/25967
    * gcc.target/i386/naked-1.c: New test.
    * gcc.target/i386/naked-2.c: Ditto.
    * gcc.target/i386/naked-3.c: Ditto.
    * gcc.target/x86_64/abi/ms-sysv/ms-sysv.c: Remove
    do_test_body0 stub function, use attribute "naked" instead.
    * gcc.dg/pr44290-1.c: Use naked_functions effective target.
    * gcc.dg/pr44290-2.c: Ditto.

Patch was bootstrapped and regression tested on x86_64-linux-gnu {,-m32}.

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25967

Uros.
diff mbox

Patch

Index: config/i386/i386.c
===================================================================
--- config/i386/i386.c	(revision 250721)
+++ config/i386/i386.c	(working copy)
@@ -8748,6 +8748,15 @@  ix86_function_ms_hook_prologue (const_tree fn)
   return false;
 }
 
+static bool
+ix86_function_naked (const_tree fn)
+{
+  if (fn && lookup_attribute ("naked", DECL_ATTRIBUTES (fn)))
+    return true;
+
+  return false;
+}
+
 /* Write the extra assembler code needed to declare a function properly.  */
 
 void
@@ -12249,6 +12258,9 @@  ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
+  if (ix86_function_naked (current_function_decl))
+    return false;
+
   /* Don't use `ret' instruction in interrupt handler.  */
   if (! reload_completed
       || frame_pointer_needed
@@ -14327,6 +14339,9 @@  ix86_expand_prologue (void)
   bool sse_registers_saved;
   rtx static_chain = NULL_RTX;
 
+  if (ix86_function_naked (current_function_decl))
+    return;
+
   ix86_finalize_stack_realign_flags ();
 
   /* DRAP should not coexist with stack_realign_fp */
@@ -15184,6 +15199,13 @@  ix86_expand_epilogue (int style)
   bool using_drap;
   bool restore_stub_is_tail = false;
 
+  if (ix86_function_naked (current_function_decl))
+    {
+      /* The program should not reach this point.  */
+      emit_insn (gen_trap ());
+      return;
+    }
+
   ix86_finalize_stack_realign_flags ();
   frame = m->frame;
 
@@ -31652,6 +31674,14 @@  ix86_trampoline_init (rtx m_tramp, tree fndecl, rt
 		     LCT_NORMAL, VOIDmode, 1, XEXP (m_tramp, 0), Pmode);
 #endif
 }
+
+static bool
+ix86_warn_func_return (tree decl)
+{
+  /* Naked functions are implemented entirely in assembly, including the
+     return sequence, so suppress warnings about this.  */
+  return !ix86_function_naked (decl);
+}
 
 /* The following file contains several enumerations and data structures
    built from the definitions in i386-builtin-types.def.  */
@@ -46486,6 +46516,8 @@  static const struct attribute_spec ix86_attribute_
     ix86_handle_interrupt_attribute, false },
   { "no_caller_saved_registers", 0, 0, false, true, true,
     ix86_handle_no_caller_saved_registers_attribute, false },
+  { "naked", 0, 0, true, false, false,
+    ix86_handle_fndecl_attribute, false },
 
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
@@ -52722,6 +52754,9 @@  ix86_run_selftests (void)
 #undef TARGET_RETURN_POPS_ARGS
 #define TARGET_RETURN_POPS_ARGS ix86_return_pops_args
 
+#undef TARGET_WARN_FUNC_RETURN
+#define TARGET_WARN_FUNC_RETURN ix86_warn_func_return
+
 #undef TARGET_LEGITIMATE_COMBINED_INSN
 #define TARGET_LEGITIMATE_COMBINED_INSN ix86_legitimate_combined_insn
 
Index: doc/extend.texi
===================================================================
--- doc/extend.texi	(revision 250721)
+++ doc/extend.texi	(working copy)
@@ -5370,6 +5370,17 @@  this function attribute to make GCC generate the `
 prologue used in Win32 API functions in Microsoft Windows XP Service Pack 2
 and newer.
 
+@item naked
+@cindex @code{naked} function attribute, x86
+This attribute allows the compiler to construct the
+requisite function declaration, while allowing the body of the
+function to be assembly code. The specified function will not have
+prologue/epilogue sequences generated by the compiler. Only basic
+@code{asm} statements can safely be included in naked functions
+(@pxref{Basic Asm}). While using extended @code{asm} or a mixture of
+basic @code{asm} and C code may appear to work, they cannot be
+depended upon to work reliably and are not supported.
+
 @item regparm (@var{number})
 @cindex @code{regparm} function attribute, x86
 @cindex functions that are passed arguments in registers on x86-32
Index: testsuite/gcc.dg/pr44290-1.c
===================================================================
--- testsuite/gcc.dg/pr44290-1.c	(revision 250721)
+++ testsuite/gcc.dg/pr44290-1.c	(working copy)
@@ -1,4 +1,5 @@ 
-/* { dg-do compile { target arm*-*-* avr-*-* mcore-*-* rx-*-* spu-*-* } } */
+/* { dg-do compile } */
+/* { dg-require-effective-target naked_functions } */
 /* { dg-options "-O2 -fdump-tree-optimized" } */
 
 static void __attribute__((naked))
Index: testsuite/gcc.dg/pr44290-2.c
===================================================================
--- testsuite/gcc.dg/pr44290-2.c	(revision 250721)
+++ testsuite/gcc.dg/pr44290-2.c	(working copy)
@@ -1,4 +1,5 @@ 
-/* { dg-do compile { target arm*-*-* avr-*-* mcore-*-* rx-*-* spu-*-* } } */
+/* { dg-do compile } */
+/* { dg-require-effective-target naked_functions } */
 /* { dg-options "-O2 -fdump-tree-optimized" } */
 
 static unsigned long __attribute__((naked))
Index: testsuite/gcc.target/i386/naked-1.c
===================================================================
--- testsuite/gcc.target/i386/naked-1.c	(nonexistent)
+++ testsuite/gcc.target/i386/naked-1.c	(working copy)
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+/* Verify that __attribute__((naked)) produces a naked function 
+   that does not use ret to return but traps at the end.  */
+void
+__attribute__((naked))
+foo (void)
+{
+  __asm__ ("# naked");
+}
+/* { dg-final { scan-assembler "# naked" } } */
+/* { dg-final { scan-assembler "ud2" } } */
+/* { dg-final { scan-assembler-not "ret" } } */
Index: testsuite/gcc.target/i386/naked-2.c
===================================================================
--- testsuite/gcc.target/i386/naked-2.c	(nonexistent)
+++ testsuite/gcc.target/i386/naked-2.c	(working copy)
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+/* Verify that __attribute__((naked)) produces a naked function 
+   that does not construct a frame.  */
+void
+__attribute__((naked))
+foo (void)
+{
+  __asm__ ("# naked");
+}
+/* { dg-final { scan-assembler "# naked" } } */
+/* { dg-final { scan-assembler-not "push" } } */
+/* { dg-final { scan-assembler-not "pop" } } */
Index: testsuite/gcc.target/i386/naked-3.c
===================================================================
--- testsuite/gcc.target/i386/naked-3.c	(nonexistent)
+++ testsuite/gcc.target/i386/naked-3.c	(working copy)
@@ -0,0 +1,39 @@ 
+/* { dg-do run { target *-*-linux* *-*-gnu* } } */
+/* { dg-options "-O2" } */
+
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+
+int data;
+
+/* Verify that naked function traps at the end.  */
+
+void
+__attribute__((naked, noinline, noclone))
+naked (void)
+{
+  if (data == 0x12345678)
+    return;
+  asm ("ret");
+}
+
+void handler (int i)
+{
+  exit (0);
+}
+
+int main ()
+{
+  struct sigaction s;
+
+  sigemptyset (&s.sa_mask);
+  s.sa_handler = handler;
+  s.sa_flags = 0;
+  sigaction (SIGILL, &s, NULL);
+
+  data = 0x12345678;
+  naked ();
+
+  abort ();
+}
Index: testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c
===================================================================
--- testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c	(revision 250721)
+++ testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c	(working copy)
@@ -169,15 +169,9 @@  static const char *argv0;
 
 #define TEST_DATA_OFFSET(f)	((int)__builtin_offsetof(struct test_data, f))
 
-void __attribute__((used))
-do_test_body0 (void)
-{
-  __asm__ ("\n"
-	"	.globl " ASMNAME(do_test_body) "\n"
-#ifdef __ELF__
-	"	.type " ASMNAME(do_test_body) ",@function\n"
-#endif
-	ASMNAME(do_test_body) ":\n"
+void __attribute__((naked))
+do_test_body (void)
+{__asm__ (
 	"	# rax, r10 and r11 are usable here.\n"
 	"\n"
 	"	# Save registers.\n"
@@ -212,9 +206,6 @@  static const char *argv0;
 	"	call	" ASMNAME(mem_to_regs) "\n"
 	"\n"
 	"	retq\n"
-#ifdef __ELF__
-	"	.size " ASMNAME(do_test_body) ",.-" ASMNAME(do_test_body) "\n"
-#endif
 	::
 	"i"(TEST_DATA_OFFSET(regdata[REG_SET_SAVE])),
 	"i"(TEST_DATA_OFFSET(regdata[REG_SET_INPUT])),