diff --git a/gdb/NEWS b/gdb/NEWS
index 4cf91053c95..7c2b0cf30e5 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,15 @@
 
 *** Changes since GDB 17
 
+* Source-tracking breakpoints
+
+  GDB now supports source-tracking breakpoints, which automatically adjust
+  their location when source code changes between rebuilds. When enabled,
+  file and line breakpoints capture the surrounding source code context and
+  use it to adjust the breakpoint line if the source is modified.
+
+  Source tracking can be enabled with 'set breakpoint source-tracking enabled on'.
+
 * Support for .gdb_index sections with version less than 7 has been
   removed.
 
@@ -80,6 +89,12 @@ unset local-environment
   Analogs of the existing "environment" commands that affect GDB's own
   environment.  The local environment is used by "shell", "pipe", and
   other commands that launch a subprocess other than an inferior.
+set breakpoint source-tracking enabled [on|off]
+show breakpoint source-tracking enabled
+  Enable or disable source-tracking for file and line breakpoints.
+  When enabled, breakpoints capture surrounding source code and
+  automatically adjust their location when the source changes between
+  recompilations.
 
 save history FILENAME
   Save the command history to the given file.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index d7be1b44229..a6c2bcae0f3 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -69,6 +69,7 @@
 #include "cli/cli-style.h"
 #include "cli/cli-decode.h"
 #include "break-cond-parse.h"
+#include "source-cache.h"
 
 /* readline defines this.  */
 #undef savestring
@@ -574,6 +575,10 @@ show_automatic_hardware_breakpoints (struct ui_file *file, int from_tty,
    processing user input.  */
 static bool always_inserted_mode = false;
 
+/* Control whether file & line breakpoints are created as source-tracking
+   breakpoints.  */
+static bool source_tracking_breakpoints = false;
+
 static void
 show_always_inserted_mode (struct ui_file *file, int from_tty,
 		     struct cmd_list_element *c, const char *value)
@@ -593,6 +598,179 @@ show_debug_breakpoint (struct ui_file *file, int from_tty,
   gdb_printf (file, _("Breakpoint location debugging is %s.\n"), value);
 }
 
+/* Return true if the breakpoint source is being tracked.  */
+
+static bool
+breakpoint_source_is_tracked (const breakpoint_source *src)
+{
+  if (src == nullptr)
+    return false;
+  return src->source_lines.size () > 0 && src->bp_line > 0;
+}
+
+/* Calculate the starting line number for captured source.  */
+
+static int
+breakpoint_source_get_start_line (const breakpoint_source *src)
+{
+  if (!breakpoint_source_is_tracked (src))
+    return 0;
+  return src->bp_line - src->bp_line_stored;
+}
+
+/* Return true if SPEC is suitable for source tracking, otherwise false.  A
+   location spec is suitable for tracking if it is an explicit location
+   spec, and the line offset is an absolute line number.  We also don't
+   allow for SPEC to be function or label based.  Most of these
+   restrictions could be lifted, but this would likely require additional
+   work to support these changes, especially when updating the location
+   spec.  */
+
+static bool
+breakpoint_locspec_suitable_for_tracking (const location_spec *spec)
+{
+  if (spec->type () != EXPLICIT_LOCATION_SPEC)
+    return false;
+
+  const explicit_location_spec *explicit_loc
+    = as_explicit_location_spec (spec);
+
+  if (explicit_loc->function_name.get () != nullptr
+      || explicit_loc->label_name.get () != nullptr
+      || explicit_loc->source_filename.get () == nullptr)
+    return false;
+
+  if (explicit_loc->line_offset.sign != LINE_OFFSET_NONE)
+    return false;
+
+  return explicit_loc->line_offset.offset > 0;
+}
+
+/* Print captured source lines to stdout, marking the breakpoint line with '>'.  */
+
+static void
+breakpoint_source_print (const breakpoint_source *src)
+{
+  if (!breakpoint_source_is_tracked (src))
+    return;
+
+  int start_line = breakpoint_source_get_start_line (src);
+  for (int j = 0; j < (int) src->source_lines.size (); j++)
+    {
+      int line_num = start_line + j;
+      char prefix;
+      if (j == src->bp_line_stored)
+	prefix = '>';
+      else
+	prefix = ' ';
+      gdb_printf ("%c %ps %s", prefix,
+		  styled_string (line_number_style.style (),
+				 pulongest (line_num)),
+		  src->source_lines[j].c_str ());
+      if (src->source_lines[j].empty ()
+	  || src->source_lines[j].back () != '\n')
+	gdb_putc ('\n');
+    }
+}
+
+/* Implement the "maintenance info source-tracking-context" command.  */
+
+static void
+maintenance_info_source_tracking_context (const char *args, int from_tty)
+{
+  if (args == NULL || *args == '\0')
+    error (_("Breakpoint number required."));
+
+  /* Parse the breakpoint number.  */
+  const char *end = args;
+  int num = get_number_trailer (&end, 0);
+
+  if (num <= 0)
+    error (_("Invalid breakpoint number '%s'."), args);
+
+  /* Find the breakpoint.  */
+  breakpoint *b = nullptr;
+  for (breakpoint &bp : all_breakpoints ())
+    {
+      if (bp.number == num)
+	{
+	  b = &bp;
+	  break;
+	}
+    }
+
+  if (b == nullptr)
+    error (_("No breakpoint number %d."), num);
+
+  /* Check if source tracking is enabled for this breakpoint.  */
+  if (!breakpoint_source_is_tracked (b->bp_source.get ()))
+    {
+      gdb_printf (_("Breakpoint %d does not have source tracking enabled.\n"), num);
+      return;
+    }
+
+  /* Print the source tracking information.  */
+  breakpoint_source_print (b->bp_source.get ());
+}
+
+/* Capture source lines around a breakpoint location for source tracking.
+   Returns a breakpoint_source structure with the captured lines, or an
+   empty structure if capture fails.  Does not print the lines.  */
+
+static breakpoint_source
+breakpoint_source_capture (gdb::array_view<const symtab_and_line> sals,
+			   int num_of_lines)
+{
+  /* Check if we have a valid symtab - if not, we can't capture source lines.
+     The symtab can be missing if the executable wasn't compiled with
+     debugging symbols.  */
+  if (sals.empty () || sals[0].symtab == nullptr || sals[0].line <= 0)
+    return {};
+
+  breakpoint_source result;
+  result.bp_line = sals[0].line;
+
+  /* Calculate the starting line, centering around the breakpoint line.
+     Avoid going before line 1.  */
+  int lines_to_capture = num_of_lines;
+  int start_line = sals[0].line - (lines_to_capture / 2);
+  if (start_line < 1)
+    start_line = 1;
+
+  /* Get line offsets to check file bounds.  */
+  const std::vector<off_t> *offsets;
+  if (!g_source_cache.get_line_charpos (sals[0].symtab, &offsets))
+      return {};
+
+  /* Adjust number of lines if we'd run past the end of the file.  */
+  if (start_line + lines_to_capture > (int) offsets->size ())
+    lines_to_capture = (int) offsets->size () - start_line;
+
+  /* Get the BFD from the symtab.  */
+  if (sals[0].symtab->compunit()->objfile())
+    result.source_bfd = sals[0].symtab->compunit()->objfile()->obfd;
+
+  /* Capture the source lines.  */
+  auto restore_styling = make_scoped_restore (&source_styling, false);
+  for (int j = 0; j < lines_to_capture; j++)
+    {
+      std::string line;
+      if (!g_source_cache.get_source_lines (sals[0].symtab, start_line + j,
+					    start_line + j, &line))
+	{
+	  /* Failed to read source - return empty structure so this
+	     breakpoint won't be tracked.  */
+	  warning (_("Failed to capture source lines for source tracking."));
+	  return {};
+	}
+      result.source_lines.push_back (line);
+      if (start_line + j == sals[0].line)
+	result.bp_line_stored = j;
+    }
+
+  return result;
+}
+
 /* See breakpoint.h.  */
 
 int
@@ -838,6 +1016,8 @@ static int tracepoint_count;
 
 static struct cmd_list_element *breakpoint_set_cmdlist;
 static struct cmd_list_element *breakpoint_show_cmdlist;
+static struct cmd_list_element *source_tracking_bp_set_cmdlist;
+static struct cmd_list_element *source_tracking_bp_show_cmdlist;
 struct cmd_list_element *save_cmdlist;
 
 /* Return whether a breakpoint is an active enabled breakpoint.  */
@@ -6821,6 +7001,16 @@ print_one_breakpoint_location (struct breakpoint *b,
       uiout->text ("\n");
     }
 
+  if (!part_of_multiple && breakpoint_source_is_tracked (b->bp_source.get ()))
+    {
+      uiout->text ("\tsource-tracking enabled (tracking ");
+      uiout->field_signed ("tracked-lines",
+			   b->bp_source->source_lines.size ());
+      uiout->text (" lines around line ");
+      uiout->field_signed ("original-line", b->bp_source->bp_line);
+      uiout->text (")\n");
+    }
+
   if (!part_of_multiple)
     {
       if (b->hit_count)
@@ -8930,7 +9120,35 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
 				enabled, flags,
 				display_canonical);
 
+  /* Only capture source lines for file:line breakpoints when source
+     tracking is enabled.  We check explicit_line to ensure the user
+     explicitly specified a line number (e.g., "break file.c:23" or
+     "break 23"), as opposed to "break function_name" or temporary
+     breakpoints set by commands like "start".
+
+     We also only track single-location breakpoints.  Multi-location
+     breakpoints (e.g., breakpoints on inline functions that are inlined
+     in multiple places) are too complex to track reliably as each location
+     may have moved differently.  */
+  if (source_tracking_breakpoints && sals.size () == 1
+      && sals[0].explicit_line
+      && breakpoint_locspec_suitable_for_tracking (b->locspec.get ()))
+    {
+      /* Capture source if we have valid symtab and line info.
+	 This works for both "b file:line" and "b line" formats.
+	 We capture BREAKPOINT_SRC_CTX_LINES lines to provide
+	 context around the breakpoint location.  */
+      b->bp_source = std::make_unique<breakpoint_source>
+	(breakpoint_source_capture (sals, BREAKPOINT_SRC_CTX_LINES));
+    }
+
+  bool warn_not_tracked = (source_tracking_breakpoints && sals.size () == 1
+			   && sals[0].explicit_line
+			   && !breakpoint_source_is_tracked (b->bp_source.get ()));
   install_breakpoint (internal, std::move (b), 0);
+  if (warn_not_tracked)
+    warning (_("Source file not available; breakpoint will not be "
+	       "source-tracked."));
 }
 
 /* Add SALS.nelts breakpoints to the breakpoint table.  For each
@@ -9213,7 +9431,6 @@ breakpoint_ops_for_location_spec (const location_spec *locspec,
 }
 
 /* See breakpoint.h.  */
-
 int
 create_breakpoint (struct gdbarch *gdbarch,
 		   location_spec *locspec,
@@ -13186,6 +13403,157 @@ code_breakpoint::location_spec_to_sals (location_spec *locspec,
   return sals;
 }
 
+/* Match BREAKPOINT_SRC_CTX_LINES lines of the initially stored source in a
+   BREAKPOINT_SRC_CTX_LINES * BREAKPOINT_SRC_SEARCH_MULTIPLIER lines current
+   source window.
+   Returns new breakpoint line on success or -1 on failure.  */
+static int
+sliding_window_match (breakpoint_source *bp_source,
+		      breakpoint_source *tmp_source)
+{
+  int bp_stored = bp_source->bp_line_stored;
+  int tmp_size = (int) tmp_source->source_lines.size ();
+  int bp_size = (int) bp_source->source_lines.size ();
+
+  for (int i = 0; i < tmp_size; i++)
+    {
+      /* Look for the initial breakpoint line in a stored window.  */
+      if (bp_source->source_lines[bp_stored] == tmp_source->source_lines[i])
+	{
+	  /* Check if the stored lines before and after the breakpoint line
+	     also match, to reduce false positives.  */
+	  if (i > 0 && bp_stored > 0
+	      && (i + 1) < tmp_size
+	      && (bp_stored + 1) < bp_size)
+	    {
+	      if ((bp_source->source_lines[bp_stored - 1] == tmp_source->source_lines[i - 1])
+		  && (bp_source->source_lines[bp_stored + 1] == tmp_source->source_lines[i + 1]))
+		{
+		  return tmp_source->bp_line + i - tmp_source->bp_line_stored;
+		}
+	    }
+	  else if (i == 0 && (i + 1) < tmp_size
+		   && (bp_stored + 1) < bp_size)
+	    {
+	      if (bp_source->source_lines[bp_stored + 1] == tmp_source->source_lines[i + 1])
+		{
+		  return tmp_source->bp_line + i - tmp_source->bp_line_stored;
+		}
+	    }
+	  else if ((i + 1) == tmp_size && i > 0
+		   && bp_stored > 0)
+	    {
+	      if (bp_source->source_lines[bp_stored - 1] == tmp_source->source_lines[i - 1])
+		{
+		  return tmp_source->bp_line + i - tmp_source->bp_line_stored;
+		}
+	    }
+	}
+    }
+
+  return -1;
+}
+
+/* See breakpoint.h.  */
+
+void
+code_breakpoint::adjust_bp_for_source_tracking
+  (program_space *filter_pspace,
+   std::vector<symtab_and_line> &expanded)
+{
+  if (expanded.empty () || expanded[0].symtab == nullptr
+      || !breakpoint_source_is_tracked (bp_source.get ()))
+    return;
+
+  struct compunit_symtab *cust = expanded[0].symtab->compunit ();
+  if (cust == nullptr || cust->objfile () == nullptr)
+    return;
+
+  bfd *current_bfd = cust->objfile ()->obfd.get ();
+  if (bp_source->source_bfd.get () == current_bfd)
+    return;
+
+  /* BFD changed — executable was reloaded.  */
+  if (expanded.size () != 1)
+    {
+      warning (_("Breakpoint %d now has multiple locations after reload, "
+		 "disabling source tracking."), number);
+      bp_source.reset ();
+      return;
+    }
+
+  /* If this fails then the location spec has changed since the
+     breakpoint's source tracking was initially setup.  */
+  gdb_assert (breakpoint_locspec_suitable_for_tracking (locspec.get ()));
+
+  int bp_stored = bp_source->bp_line_stored;
+  std::string line;
+  auto restore_styling = make_scoped_restore (&source_styling, false);
+  if (!g_source_cache.get_source_lines (expanded[0].symtab,
+					expanded[0].line,
+					expanded[0].line, &line))
+    {
+      /* Source is unreadable after reload — drop tracking.  */
+      bp_source.reset ();
+      return;
+    }
+
+  if (line == bp_source->source_lines[bp_stored])
+    {
+      /* Line unchanged — just refresh the capture with the new BFD.  */
+      bp_source = std::make_unique<breakpoint_source>
+	(breakpoint_source_capture (expanded, BREAKPOINT_SRC_CTX_LINES));
+      return;
+    }
+
+  breakpoint_source tmp_source
+    = breakpoint_source_capture (expanded,
+				 BREAKPOINT_SRC_CTX_LINES
+				 * BREAKPOINT_SRC_SEARCH_MULTIPLIER);
+  int new_bp_line = sliding_window_match (bp_source.get (), &tmp_source);
+  if (new_bp_line == -1)
+    {
+      warning (_("Breakpoint %d source code not found "
+		 "after reload, keeping original location."), number);
+      bp_source.reset ();
+      return;
+    }
+
+  location_spec *spec = locspec.get ();
+  std::string bp_string (spec->to_string ());
+  auto pos = bp_string.rfind (':');
+  if (pos == std::string::npos)
+    {
+      warning (_("unable to update location spec for breakpoint %d, "
+		 "disabling source tracking."), number);
+      bp_source.reset ();
+      return;
+    }
+
+  bp_string = bp_string.substr (0, pos + 1);
+  bp_string.append (std::to_string (new_bp_line));
+
+  /* set_string only updates the cached display string, not the parsed
+     spec_string that location_spec_to_sals actually uses.  Replace the
+     location spec entirely by re-parsing the updated string so the new
+     line number takes effect.  */
+  const char *new_spec_str = bp_string.c_str ();
+  locspec = string_to_location_spec (&new_spec_str, current_language);
+  spec = locspec.get ();
+
+  int found;
+  expanded = location_spec_to_sals (spec, filter_pspace, &found);
+  if (found && new_bp_line != bp_source->bp_line)
+    {
+      gdb_printf (_("Breakpoint %d adjusted from line %d to line %d.\n"),
+		  number, bp_source->bp_line, new_bp_line);
+      notify_breakpoint_modified (this);
+    }
+
+  bp_source = std::make_unique<breakpoint_source>
+    (breakpoint_source_capture (expanded, BREAKPOINT_SRC_CTX_LINES));
+}
+
 /* The default re_set method, for typical hardware or software
    breakpoints.  Reevaluate the breakpoint and recreate its
    locations.  */
@@ -13216,6 +13584,9 @@ code_breakpoint::re_set_default (struct program_space *filter_pspace)
 
       if (locspec_range_end != nullptr)
 	{
+	  /* Ranged breakpoints are not currently tracked.  */
+	  gdb_assert (!breakpoint_source_is_tracked (bp_source.get ()));
+
 	  std::vector<symtab_and_line> sals_end
 	    = location_spec_to_sals (locspec_range_end.get (),
 				     filter_pspace, &found);
@@ -13224,6 +13595,8 @@ code_breakpoint::re_set_default (struct program_space *filter_pspace)
 	}
     }
 
+  adjust_bp_for_source_tracking (filter_pspace, expanded);
+
   /* Update the locations for this breakpoint.  For thread-specific
      breakpoints this will remove any old locations that are for the wrong
      program space -- this can happen if the user changes the thread of a
@@ -15007,6 +15380,15 @@ Convenience variable \"$bpnum\" contains the number of the last\n\
 breakpoint set."),
 	   &maintenanceinfolist);
 
+  add_cmd ("source-tracking-context", class_maintenance,
+           maintenance_info_source_tracking_context, _("\
+  Print source tracking context for a breakpoint.\n\
+  Usage: maintenance info source-tracking-context BPNUM\n\
+  \n\
+  Displays the captured source code lines used to track\n\
+  and automatically adjust the breakpoint when source code changes."),
+           &maintenanceinfolist);
+
   add_basic_prefix_cmd ("catch", class_breakpoint, _("\
 Set catchpoints to catch events."),
 			&catch_cmdlist,
@@ -15325,6 +15707,27 @@ Usage: agent-printf \"format string\", ARG1, ARG2, ARG3, ..., ARGN\n\
 This supports most C printf format specifications, like %s, %d, etc.\n\
 This is useful for formatted output in user-defined commands."));
 
+  add_setshow_prefix_cmd ("source-tracking", class_breakpoint,
+			  _("\
+Source tracking breakpoint specific settings."),
+			  _("\
+Source tracking breakpoint specific settings."),
+			  &source_tracking_bp_set_cmdlist,
+			  &source_tracking_bp_show_cmdlist,
+			  &breakpoint_set_cmdlist, &breakpoint_show_cmdlist);
+
+  add_setshow_boolean_cmd ("enabled", class_breakpoint,
+			   &source_tracking_breakpoints, _("\
+Set whether file and line breakpoints use source tracking."), _("\
+Show whether file and line breakpoints use source tracking."), _("\
+When on, breakpoints set with file:line syntax will track the source\n\
+location and automatically adjust when the source changes and the\n\
+inferior is restarted."),
+			   NULL,
+			   NULL,
+			   &source_tracking_bp_set_cmdlist,
+			   &source_tracking_bp_show_cmdlist);
+
   automatic_hardware_breakpoints = true;
 
   gdb::observers::about_to_proceed.attach (breakpoint_about_to_proceed,
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 3e84f1c99ab..f8308be48c7 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -27,6 +27,7 @@
 #include "probe.h"
 #include "location.h"
 #include <vector>
+#include <memory>
 #include "gdbsupport/array-view.h"
 #include "gdbsupport/filtered-iterator.h"
 #include "gdbsupport/iterator-range.h"
@@ -34,6 +35,7 @@
 #include "gdbsupport/safe-iterator.h"
 #include "cli/cli-script.h"
 #include "target/waitstatus.h"
+#include "gdb_bfd.h"
 
 struct block;
 struct gdbpy_breakpoint_object;
@@ -82,6 +84,17 @@ enum remove_bp_reason
    architecture.  */
 
 #define	BREAKPOINT_MAX	16
+
+/* Number of source lines to capture around a breakpoint for source tracking.
+   This context is used to match and relocate breakpoints when the executable
+   is reloaded.  The window is centered on the breakpoint line, capturing
+   lines both before and after it.  */
+#define BREAKPOINT_SRC_CTX_LINES 3
+
+/* Multiplier for the search window when looking for relocated breakpoints.
+   We search in (BREAKPOINT_SRC_CTX_LINES * BREAKPOINT_SRC_SEARCH_MULTIPLIER)
+   lines to find code that may have moved.  */
+#define BREAKPOINT_SRC_SEARCH_MULTIPLIER 4
 
 
 /* Type of breakpoint.  */
@@ -614,6 +627,28 @@ extern bool target_exact_watchpoints;
 using bp_location_list = intrusive_list<bp_location>;
 using bp_location_iterator = bp_location_list::iterator;
 using bp_location_range = iterator_range<bp_location_iterator>;
+/* Captured source code around a breakpoint location, used for
+   source-tracking breakpoints.  When source tracking is enabled,
+   this structure stores the original source lines around a breakpoint
+   so the breakpoint can be automatically adjusted if the source code
+   changes when the executable is reloaded.  */
+struct breakpoint_source
+{
+  /* The captured source lines as strings.  The number of captured lines
+     is source_lines.size().  */
+  std::vector<std::string> source_lines;
+
+  /* The original line number where the breakpoint was set
+     in the source file.  */
+  int bp_line = 0;
+
+  /* Index into source_lines vector indicating which line
+     contains the breakpoint (0-based).  */
+  int bp_line_stored = 0;
+
+  /* BFD when source was captured.  */
+  gdb_bfd_ref_ptr source_bfd;
+};
 
 /* Note that the ->silent field is not currently used by any commands
    (though the code is in there if it was to be, and set_raw_breakpoint
@@ -863,6 +898,11 @@ struct breakpoint : public intrusive_list_node<breakpoint>
      find the end of the range.  */
   location_spec_up locspec_range_end;
 
+  /* Captured source code around the breakpoint location, used to
+     track source around the breakpoint to automatically adjust the breakpoint
+     when source code changes between recompilations.  */
+  std::unique_ptr<breakpoint_source> bp_source;
+
   /* Architecture we used to set the breakpoint.  */
   struct gdbarch *gdbarch;
   /* Language we used to set the breakpoint.  */
@@ -983,6 +1023,14 @@ struct code_breakpoint : public breakpoint
   /* Helper method that does the basic work of re_set.  */
   void re_set_default (program_space *pspace);
 
+  /* Helper method for re_set_default.  Checks if the executable was
+     reloaded and if so, attempts to adjust the breakpoint location
+     using source tracking.  EXPANDED may be updated if the location
+     is adjusted.  */
+  void adjust_bp_for_source_tracking
+    (program_space *filter_pspace,
+     std::vector<symtab_and_line> &expanded);
+
   /* Find the SaL locations corresponding to the given LOCATION.
      On return, FOUND will be 1 if any SaL was found, zero otherwise.  */
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 16cd01aad4d..d85ff72c0de 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -4659,6 +4659,17 @@ program.
 On some systems, you can set breakpoints in shared libraries before
 the executable is run.
 
+@cindex source-tracking breakpoints
+@value{GDBN} supports @dfn{source-tracking breakpoints}, which
+automatically adjust their location when source code changes between
+recompilations.  When enabled with @code{set breakpoint source-tracking
+enabled on}, breakpoints set by file and line number capture the
+surrounding source code lines.  If the source file is modified and the
+executable is rebuilt, @value{GDBN} attempts to relocate the breakpoint
+by searching for the captured source code.
+Note that breakpoints set by function name or address are
+not affected by source tracking.  @xref{Set Breaks}.
+
 @cindex watchpoints
 @cindex data breakpoints
 @cindex memory tracing
@@ -41980,6 +41991,13 @@ Shared library events.
 
 @end table
 
+@kindex maint info source-tracking-context
+@item maint info source-tracking-context @var{num}
+For source tracking breakpoints (@pxref{Set Breaks}), print the
+tracked source code context for breakpoint @var{num}.  If breakpoint
+@var{num} is not source tracked, or @var{num} is not a valid
+breakpoint number, then the command gives an error.
+
 @kindex maint info btrace
 @item maint info btrace
 Pint information about raw branch tracing data.
@@ -42848,6 +42866,19 @@ Control whether to show all non zero areas within a 1k block starting
 at thread local base, when using the @samp{info w32 thread-information-block}
 command.
 
+@kindex set breakpoint source-tracking enabled
+@kindex show breakpoint source-tracking enabled
+@item set breakpoint source-tracking enabled @r{[}on@r{|}off@r{]}
+@itemx show breakpoint source-tracking enabled
+Control whether to enable source-tracking for breakpoints set by file and
+line number.  Use @code{on} to enable, @code{off} to disable.  When enabled,
+@value{GDBN} captures the surrounding source code lines when creating a
+file:line breakpoint.  When the executable is reloaded, @value{GDBN}
+attempts to relocate the breakpoint by searching for the captured source
+code, allowing breakpoints to automatically adjust their location when source
+files are modified between compilations.  The default is @code{off}.
+Breakpoints set by function name or address are not affected by this setting.
+
 @kindex maint set target-async
 @kindex maint show target-async
 @item maint set target-async
diff --git a/gdb/testsuite/gdb.base/adjust_breakpoint-2.cpp b/gdb/testsuite/gdb.base/adjust_breakpoint-2.cpp
new file mode 100644
index 00000000000..0fb29c4ddac
--- /dev/null
+++ b/gdb/testsuite/gdb.base/adjust_breakpoint-2.cpp
@@ -0,0 +1,39 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+foo (void)
+{
+  return 0;
+}
+
+int
+foo (int var)
+{
+  int i = 8;
+  var += 10;
+  var += i;
+
+  return var;
+}
+
+int
+main (void)
+{
+  foo ();
+  return foo (2);
+}
diff --git a/gdb/testsuite/gdb.base/adjust_breakpoint-3.cpp b/gdb/testsuite/gdb.base/adjust_breakpoint-3.cpp
new file mode 100644
index 00000000000..cca12c5e170
--- /dev/null
+++ b/gdb/testsuite/gdb.base/adjust_breakpoint-3.cpp
@@ -0,0 +1,41 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+int
+foo () {
+  return 0;
+}
+
+int
+foo (int var)
+{
+  int j = 0;
+  int i = 8;
+
+  var += 10;
+  var += i;
+
+  return var;
+}
+
+int
+main (void)
+{
+  foo ();
+  return foo (2);
+}
diff --git a/gdb/testsuite/gdb.base/adjust_breakpoint-4.cpp b/gdb/testsuite/gdb.base/adjust_breakpoint-4.cpp
new file mode 100644
index 00000000000..371f5553bb4
--- /dev/null
+++ b/gdb/testsuite/gdb.base/adjust_breakpoint-4.cpp
@@ -0,0 +1,37 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 1992-2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+foo () {
+  return 0;
+}
+
+int
+foo (int var)
+{
+  int i = 8;
+
+
+  return var;
+}
+
+int
+main (void)
+{
+  foo ();
+  return foo (2);
+}
diff --git a/gdb/testsuite/gdb.base/adjust_breakpoint-missing-source.exp b/gdb/testsuite/gdb.base/adjust_breakpoint-missing-source.exp
new file mode 100644
index 00000000000..8965a7e30e6
--- /dev/null
+++ b/gdb/testsuite/gdb.base/adjust_breakpoint-missing-source.exp
@@ -0,0 +1,55 @@
+# Copyright 2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test that source-tracking breakpoints handle missing source files correctly.
+# When source tracking is enabled but the source file cannot be found, GDB
+# should create a non-tracking breakpoint and not crash or fail.
+
+standard_testfile adjust_breakpoint.cpp
+set build_srcfile xxx-${testfile}.cpp
+
+set new_source_file [standard_output_file ${build_srcfile}]
+remote_exec build "cp ${srcdir}/${subdir}/adjust_breakpoint.cpp $new_source_file"
+if { [prepare_for_testing "failed to prepare" $testfile $new_source_file] } {
+    return -1
+}
+
+# Enable source tracking
+gdb_test_no_output "set breakpoint source-tracking enabled on" \
+    "enable source tracking breakpoints"
+
+# Now remove the source file before setting the breakpoint
+set bp_line [gdb_get_line_number "return 0;" $new_source_file]
+remote_exec build "rm -f $new_source_file"
+
+# Verify the source is not available
+gdb_test "list" ".*No such file or directory.*" \
+    "verify source file is not available"
+
+# Try to set a breakpoint - should succeed but not be tracked
+gdb_test "break ${build_srcfile}:${bp_line}" "Breakpoint.*at.*" \
+    "create breakpoint when source file missing"
+
+# Check that the breakpoint was created but is not tracked
+# (should not show "source-tracking enabled" message)
+set test "breakpoint not tracked when source missing"
+gdb_test_multiple "info breakpoints" $test {
+    -re "source-tracking enabled.*$gdb_prompt $" {
+        fail $test
+    }
+    -re "breakpoint.*${build_srcfile}:${bp_line}.*$gdb_prompt $" {
+        pass $test
+    }
+}
diff --git a/gdb/testsuite/gdb.base/adjust_breakpoint.cpp b/gdb/testsuite/gdb.base/adjust_breakpoint.cpp
new file mode 100644
index 00000000000..1cedcb05ddc
--- /dev/null
+++ b/gdb/testsuite/gdb.base/adjust_breakpoint.cpp
@@ -0,0 +1,40 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 1992-2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+foo (void)
+{
+  return 0;
+}
+
+int
+foo (int var)
+{
+  int i = 8;
+
+  var += 10;
+  var += i;
+
+  return var;
+}
+
+int
+main (void)
+{
+  foo ();
+  return foo (2);
+}
diff --git a/gdb/testsuite/gdb.base/adjust_breakpoint.exp b/gdb/testsuite/gdb.base/adjust_breakpoint.exp
new file mode 100644
index 00000000000..9f31380d5fc
--- /dev/null
+++ b/gdb/testsuite/gdb.base/adjust_breakpoint.exp
@@ -0,0 +1,118 @@
+# Copyright 2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# 1) Set the breakpoint to a certain line in $srcfile. Replace the $srcfile
+# with tmp-$srcfile which is exactly the same except one line is missing,
+# which changes the line where the breakpoint was initially set and moves
+# the breakpoint one line backwards.
+# Check if GDB adjusted the line correctly.
+#
+# 2) Do all the same but move the breakpoint a few lines forward by adding an
+# additional line to the tmp2-$srcfile.
+
+standard_testfile .cpp -2.cpp -3.cpp -4.cpp
+set build_srcfile ${testfile}-xxx.cpp
+
+set new_source_file [standard_output_file ${build_srcfile}]
+remote_exec build "cp ${srcdir}/${subdir}/${srcfile} $new_source_file"
+if { [prepare_for_testing "failed to prepare" $testfile $new_source_file] } {
+    return
+}
+
+# Enable source tracking for breakpoints
+gdb_test_no_output "set breakpoint source-tracking enabled on" \
+    "enable source tracking breakpoints"
+
+# part 1) move the breakpoint backward
+set lineno [gdb_get_line_number "var += i;" $new_source_file]
+gdb_breakpoint ${build_srcfile}:$lineno
+
+# Sleep to ensure timestamp changes when we rebuild
+sleep 1
+remote_exec build "cp ${srcdir}/${subdir}/${srcfile2} $new_source_file"
+if {[build_executable "failed to prepare" $testfile $new_source_file] == -1} {
+    return
+}
+
+set lineno [expr {$lineno - 1}]
+gdb_test "r" "Breakpoint 1,.*$build_srcfile:$lineno\r\n$lineno\t.*" \
+    "run stops at adjusted breakpoint location"
+gdb_test "info breakpoints"\
+    "breakpoint.*keep.*y.*$hex.*$build_srcfile:$lineno.*already hit 1 time"\
+    "info breakpoints show the breakpoint was adjusted one line backward"
+
+# part 2) move the breakpoint forward
+clean_restart ${testfile}
+gdb_test_no_output "set breakpoint source-tracking enabled on" \
+    "enable source tracking breakpoints for part 2"
+gdb_breakpoint ${build_srcfile}:$lineno
+
+# Sleep to ensure timestamp changes when we rebuild
+sleep 1
+remote_exec build "cp ${srcdir}/${subdir}/${srcfile3} $new_source_file"
+if {[build_executable "failed to prepare" $testfile $new_source_file] == -1} {
+    return
+}
+
+set lineno [expr {$lineno + 2}]
+gdb_test "r" "Breakpoint 1,.*$build_srcfile:$lineno\r\n$lineno\t.*" \
+    "run for the second time stops at adjusted breakpoint location"
+gdb_test "info breakpoints"\
+    "breakpoint.*keep.*y.*$hex.*$build_srcfile:$lineno.*already hit 1 time"\
+    "info breakpoints show the breakpoint was adjusted forward"
+
+# part 3) the breakpoint line disappears
+clean_restart ${testfile}
+gdb_test_no_output "set breakpoint source-tracking enabled on" \
+    "enable source tracking breakpoints for part 3"
+set lineno [gdb_get_line_number "var += 10;" $new_source_file]
+gdb_breakpoint ${build_srcfile}:$lineno
+
+# Sleep to ensure timestamp changes when we rebuild
+sleep 1
+remote_exec build "cp ${srcdir}/${subdir}/${srcfile4} $new_source_file"
+if {[build_executable "failed to prepare" $testfile $new_source_file] == -1} {
+    return
+}
+
+# When the original line is removed and cannot be found in the search window,
+# the breakpoint stays at the symbol-resolved location. Line 10 becomes blank
+# in tmp3, so GDB resolves it to line 11 (return var;) or stays at line 10.
+# We test that it doesn't move beyond the reasonable range.
+set lineno_re "(?:$lineno|[expr {$lineno + 1}])"
+gdb_test "r" "Breakpoint 1,.*$build_srcfile:$lineno_re\r\n$lineno_re\t.*" \
+    "run for the third time stops near original location"
+gdb_test "info breakpoints"\
+    "breakpoint.*keep.*y.*$hex.*$build_srcfile:$lineno_re.*already hit 1 time"\
+    "the breakpoint stays near original location when line disappears"
+
+# part 4) source tracking disabled - breakpoint should not adjust
+clean_restart ${testfile}
+# Don't enable source tracking - test that breakpoints don't adjust without it
+set lineno [gdb_get_line_number "return var;" $new_source_file]
+gdb_breakpoint ${build_srcfile}:$lineno
+
+# Sleep to ensure timestamp changes when we rebuild
+sleep 1
+remote_exec build "cp ${srcdir}/${subdir}/${srcfile2} $new_source_file"
+if {[build_executable "failed to prepare" $testfile $new_source_file] == -1} {
+    return
+}
+
+gdb_test "r" "Breakpoint 1,.*$build_srcfile:$lineno\r\n$lineno\t.*" \
+    "run for the fourth time stops at unadjusted location"
+gdb_test "info breakpoints"\
+    "breakpoint.*keep.*y.*$hex.*$build_srcfile:$lineno.*already hit 1 time"\
+    "breakpoint not adjusted when tracking disabled"
diff --git a/gdb/testsuite/gdb.base/source-tracking-inline-1.c b/gdb/testsuite/gdb.base/source-tracking-inline-1.c
new file mode 100644
index 00000000000..cbc1ee993de
--- /dev/null
+++ b/gdb/testsuite/gdb.base/source-tracking-inline-1.c
@@ -0,0 +1,50 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+volatile int global_var = 0;
+
+static void __attribute__ ((__always_inline__))
+inline_func (void)
+{
+  ++global_var;		/* Breakpoint here.  */
+}
+
+int __attribute__ ((noinline, noclone))
+foo (int x)
+{
+  inline_func ();
+  return x + global_var;
+}
+
+int __attribute__ ((noinline, noclone))
+bar (int x)
+{
+  inline_func ();
+  return x - global_var;
+}
+
+int
+main (void)
+{
+  ++global_var;
+
+  int ans = foo (42) + bar (10);
+
+  ++global_var;
+
+  return ans - global_var;
+}
diff --git a/gdb/testsuite/gdb.base/source-tracking-inline-2.c b/gdb/testsuite/gdb.base/source-tracking-inline-2.c
new file mode 100644
index 00000000000..724bb78845a
--- /dev/null
+++ b/gdb/testsuite/gdb.base/source-tracking-inline-2.c
@@ -0,0 +1,49 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+volatile int global_var = 0;
+
+static void __attribute__ ((__always_inline__))
+inline_func (void)
+{
+  ++global_var;		/* Breakpoint here.  */
+}
+
+int __attribute__ ((noinline, noclone))
+foo (int x)
+{
+  inline_func ();
+  return x + global_var;
+}
+
+int __attribute__ ((noinline, noclone))
+bar (int x)
+{
+  inline_func ();
+  return x - global_var;
+}
+
+int
+main (void)
+{
+  ++global_var;
+
+  int ans = foo (42) + bar (10);
+
+  ++global_var;
+
+  return ans - global_var;
+}
diff --git a/gdb/testsuite/gdb.base/source-tracking-inline.c b/gdb/testsuite/gdb.base/source-tracking-inline.c
new file mode 100644
index 00000000000..cbc1ee993de
--- /dev/null
+++ b/gdb/testsuite/gdb.base/source-tracking-inline.c
@@ -0,0 +1,50 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2026 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+volatile int global_var = 0;
+
+static void __attribute__ ((__always_inline__))
+inline_func (void)
+{
+  ++global_var;		/* Breakpoint here.  */
+}
+
+int __attribute__ ((noinline, noclone))
+foo (int x)
+{
+  inline_func ();
+  return x + global_var;
+}
+
+int __attribute__ ((noinline, noclone))
+bar (int x)
+{
+  inline_func ();
+  return x - global_var;
+}
+
+int
+main (void)
+{
+  ++global_var;
+
+  int ans = foo (42) + bar (10);
+
+  ++global_var;
+
+  return ans - global_var;
+}
diff --git a/gdb/testsuite/gdb.base/source-tracking-inline.exp b/gdb/testsuite/gdb.base/source-tracking-inline.exp
new file mode 100644
index 00000000000..6c1b8022d29
--- /dev/null
+++ b/gdb/testsuite/gdb.base/source-tracking-inline.exp
@@ -0,0 +1,77 @@
+# Copyright 2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+standard_testfile .c -1.c -2.c
+
+set build_srcfile [standard_output_file $srcfile]
+
+remote_exec build "cp $srcdir/$subdir/$srcfile2 $build_srcfile"
+if { [prepare_for_testing "failed to prepare" $testfile $build_srcfile {debug nowarnings}] } {
+    return
+}
+
+if {![runto_main]} {
+    return
+}
+
+gdb_test_no_output "set breakpoint source-tracking enabled on" \
+    "enable source tracking breakpoints"
+
+set lineno [gdb_get_line_number "Breakpoint here."]
+
+# The breakpoint is on an inline function called from two places,
+# so it resolves to 2 locations.
+gdb_test "break $srcfile:$lineno" \
+    "^Breakpoint $decimal at $hex: $srcfile:$lineno\\. \\(2 locations\\)"
+
+# Check that we are tracking the expected number of lines.
+#
+# With the multi-location fix, this breakpoint should NOT be tracked
+# because it has 2 locations (inline function called from two places).
+# The output should show <MULTIPLE> but NOT "source-tracking enabled".
+set test "multi-location breakpoint not tracked"
+gdb_test_multiple "info breakpoints" $test {
+    -re "source-tracking enabled.*$gdb_prompt $" {
+	fail "$test (tracking incorrectly enabled)"
+    }
+    -re "<MULTIPLE>.*$gdb_prompt $" {
+	pass $test
+    }
+}
+
+sleep 1
+remote_exec build "cp $srcdir/$subdir/$srcfile3 $build_srcfile"
+if { [build_executable "failed to build" $testfile $build_srcfile {debug nowarnings}] } {
+    return
+}
+
+# Reload the executable.  'start' internally uses 'tbreak main', which
+# is not a file:line breakpoint, so it will not be source-tracked.
+gdb_test "with confirm off -- start"
+
+# After reload, the breakpoint should still NOT be tracked because it
+# still has 2 locations.  This verifies we don't try to track
+# multi-location breakpoints even after reload.
+set test "multi-location breakpoint still not tracked after reload"
+gdb_test_multiple "info breakpoints" $test {
+    -re "source-tracking enabled.*$gdb_prompt $" {
+	fail "$test (tracking incorrectly enabled)"
+    }
+    -re "<MULTIPLE>.*$gdb_prompt $" {
+	pass $test
+    }
+}
+
+gdb_test "continue"
