]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb, gdbarch: Enable inferior calls for shadow stack support.
authorChristina Schimpe <christina.schimpe@intel.com>
Thu, 11 Apr 2024 09:53:35 +0000 (05:53 -0400)
committerChristina Schimpe <christina.schimpe@intel.com>
Fri, 29 Aug 2025 17:02:10 +0000 (17:02 +0000)
Inferior calls in GDB reset the current PC to the beginning of the function
that is called.  As no call instruction is executed the new return address
needs to be pushed to the shadow stack and the shadow stack pointer needs
to be updated.

This commit adds a new gdbarch method to push an address on the shadow
stack.  The method is used to adapt the function 'call_function_by_hand_dummy'
for inferior call shadow stack support.

Reviewed-By: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Approved-By: Luis Machado <luis.machado@arm.com>
Approved-By: Andrew Burgess <aburgess@redhat.com>
gdb/gdbarch-gen.c
gdb/gdbarch-gen.h
gdb/gdbarch_components.py
gdb/infcall.c

index 3019cc581a76dc7d4e998a34e910c721a694c9d2..a77f9b7d26599c832dc9d6110f7a4ef7250c30b0 100644 (file)
@@ -261,6 +261,7 @@ struct gdbarch
   gdbarch_read_core_file_mappings_ftype *read_core_file_mappings = default_read_core_file_mappings;
   gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes;
   gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context;
+  gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -533,6 +534,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of read_core_file_mappings, invalid_p == 0.  */
   /* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0.  */
   /* Skip verify of core_parse_exec_context, invalid_p == 0.  */
+  /* Skip verify of shadow_stack_push, has predicate.  */
   if (!log.empty ())
     internal_error (_("verify_gdbarch: the following are invalid ...%s"),
                    log.c_str ());
@@ -1401,6 +1403,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
              "gdbarch_dump: core_parse_exec_context = <%s>\n",
              host_address_to_string (gdbarch->core_parse_exec_context));
+  gdb_printf (file,
+             "gdbarch_dump: gdbarch_shadow_stack_push_p() = %d\n",
+             gdbarch_shadow_stack_push_p (gdbarch));
+  gdb_printf (file,
+             "gdbarch_dump: shadow_stack_push = <%s>\n",
+             host_address_to_string (gdbarch->shadow_stack_push));
   if (gdbarch->dump_tdep != NULL)
     gdbarch->dump_tdep (gdbarch, file);
 }
@@ -5529,3 +5537,27 @@ set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch,
 {
   gdbarch->core_parse_exec_context = core_parse_exec_context;
 }
+
+bool
+gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->shadow_stack_push != NULL;
+}
+
+void
+gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->shadow_stack_push != NULL);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_push called\n");
+  gdbarch->shadow_stack_push (gdbarch, new_addr, regcache);
+}
+
+void
+set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
+                              gdbarch_shadow_stack_push_ftype shadow_stack_push)
+{
+  gdbarch->shadow_stack_push = shadow_stack_push;
+}
index f1044ae2f2adab784147cc8321b09b1b7c452803..701e9f099b1eb7848ac270f7b77c0fd04b5a55eb 100644 (file)
@@ -1789,3 +1789,17 @@ extern void set_gdbarch_use_target_description_from_corefile_notes (struct gdbar
 typedef core_file_exec_context (gdbarch_core_parse_exec_context_ftype) (struct gdbarch *gdbarch, bfd *cbfd);
 extern core_file_exec_context gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, bfd *cbfd);
 extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarch_core_parse_exec_context_ftype *core_parse_exec_context);
+
+/* Some targets support special hardware-assisted control-flow protection
+   technologies.  For example, the Intel Control-Flow Enforcement Technology
+   (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
+   To enable shadow stack support for inferior calls the shadow_stack_push
+   gdbarch hook has to be provided.
+
+   Push NEW_ADDR to the shadow stack and update the shadow stack pointer. */
+
+extern bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_shadow_stack_push_ftype) (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
+extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
+extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stack_push_ftype *shadow_stack_push);
index c4cf87e3c2dda4a28fde830f2740f8a509dc4abe..3ae79e44c1e357c07b364656512f7cf959d6749b 100644 (file)
@@ -2831,3 +2831,19 @@ which all assume current_inferior() is the one to read from.
     predefault="default_core_parse_exec_context",
     invalid=False,
 )
+
+Method(
+    comment="""
+Some targets support special hardware-assisted control-flow protection
+technologies.  For example, the Intel Control-Flow Enforcement Technology
+(Intel CET) on x86 provides a shadow stack and indirect branch tracking.
+To enable shadow stack support for inferior calls the shadow_stack_push
+gdbarch hook has to be provided.
+
+Push NEW_ADDR to the shadow stack and update the shadow stack pointer.
+""",
+    type="void",
+    name="shadow_stack_push",
+    params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
+    predicate=True,
+)
index 2b5936d1621ef4bd05d7e7dfc9f0f1c3b2329928..db6d677436799fa3bb1eeb00c310934bc69599c4 100644 (file)
@@ -1448,10 +1448,16 @@ call_function_by_hand_dummy (struct value *function,
   /* Create the dummy stack frame.  Pass in the call dummy address as,
      presumably, the ABI code knows where, in the call dummy, the
      return address should be pointed.  */
-  sp = gdbarch_push_dummy_call (gdbarch, function,
-                               get_thread_regcache (inferior_thread ()),
-                               bp_addr, args.size (), args.data (),
-                               sp, return_method, struct_addr);
+  regcache *regcache = get_thread_regcache (inferior_thread ());
+  sp = gdbarch_push_dummy_call (gdbarch, function, regcache, bp_addr,
+                               args.size (), args.data (), sp,
+                               return_method, struct_addr);
+
+  /* Push the return address of the inferior (bp_addr) to the shadow stack
+     and update the shadow stack pointer.  As we don't execute a call
+     instruction to call the function we need to handle this manually.  */
+  if (gdbarch_shadow_stack_push_p (gdbarch))
+    gdbarch_shadow_stack_push (gdbarch, bp_addr, regcache);
 
   /* Set up a frame ID for the dummy frame so we can pass it to
      set_momentary_breakpoint.  We need to give the breakpoint a frame