[gdb/testsuite] Handle ptrace operation not permitted in can_spawn_for_attach

Message ID 20240416194439.21455-1-tdevries@suse.de
State Dropped
Headers
Series [gdb/testsuite] Handle ptrace operation not permitted in can_spawn_for_attach |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm fail Testing failed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 fail Testing failed

Commit Message

Tom de Vries April 16, 2024, 7:44 p.m. UTC
  When running the testsuite on a system with kernel.yama.ptrace_scope set to 1,
we run into attach failures.

Fix this by recognizing "ptrace: Operation not permitted" in
can_spawn_for_attach.

Tested on aarch64-linux.
---
 gdb/testsuite/gdb.base/break-interp.exp       |  4 ++
 gdb/testsuite/gdb.base/dprintf-detach.exp     |  2 +-
 .../run-control-while-bg-execution.exp        | 10 ++-
 .../gdb.multi/attach-while-running.exp        |  2 +-
 .../gdb.threads/attach-into-signal.exp        |  3 +-
 .../gdb.threads/attach-slow-waitpid.exp       |  3 +-
 gdb/testsuite/gdb.threads/attach-stopped.exp  |  3 +-
 .../gdb.threads/check-libthread-db.exp        | 42 +++++------
 gdb/testsuite/lib/gdb.exp                     | 71 +++++++++++++++----
 9 files changed, 100 insertions(+), 40 deletions(-)


base-commit: 6732c57eeea61d72ad1046fae1dd0d00920150e1
  

Comments

Tom de Vries April 17, 2024, 9:37 a.m. UTC | #1
On 4/16/24 21:44, Tom de Vries wrote:
> When running the testsuite on a system with kernel.yama.ptrace_scope set to 1,
> we run into attach failures.
> 
> Fix this by recognizing "ptrace: Operation not permitted" in
> can_spawn_for_attach.
> 
> Tested on aarch64-linux.

AFAIK testing on aarch64-linux went fine, but on x86_64-linux I ran into 
some problems (also reported by linaro CI), so I need to revise this 
approach.  I'll work on a v2.

Thanks,
- Tom

> ---
>   gdb/testsuite/gdb.base/break-interp.exp       |  4 ++
>   gdb/testsuite/gdb.base/dprintf-detach.exp     |  2 +-
>   .../run-control-while-bg-execution.exp        | 10 ++-
>   .../gdb.multi/attach-while-running.exp        |  2 +-
>   .../gdb.threads/attach-into-signal.exp        |  3 +-
>   .../gdb.threads/attach-slow-waitpid.exp       |  3 +-
>   gdb/testsuite/gdb.threads/attach-stopped.exp  |  3 +-
>   .../gdb.threads/check-libthread-db.exp        | 42 +++++------
>   gdb/testsuite/lib/gdb.exp                     | 71 +++++++++++++++----
>   9 files changed, 100 insertions(+), 40 deletions(-)
> 
> diff --git a/gdb/testsuite/gdb.base/break-interp.exp b/gdb/testsuite/gdb.base/break-interp.exp
> index addacde552d..d7f84db4770 100644
> --- a/gdb/testsuite/gdb.base/break-interp.exp
> +++ b/gdb/testsuite/gdb.base/break-interp.exp
> @@ -318,6 +318,10 @@ proc test_attach_gdb {file pid displacement prefix} {
>   }
>   
>   proc test_attach {file displacement {relink_args ""}} {
> +    if { ![can_spawn_for_attach] } {
> +	return
> +    }
> +
>       global board_info
>       global exec
>   
> diff --git a/gdb/testsuite/gdb.base/dprintf-detach.exp b/gdb/testsuite/gdb.base/dprintf-detach.exp
> index 550d319a895..b4184d698df 100644
> --- a/gdb/testsuite/gdb.base/dprintf-detach.exp
> +++ b/gdb/testsuite/gdb.base/dprintf-detach.exp
> @@ -21,7 +21,7 @@
>   load_lib gdbserver-support.exp
>   
>   # The test relies on "detach/attach".
> -require !use_gdb_stub
> +require can_spawn_for_attach
>   
>   standard_testfile
>   set escapedbinfile [string_to_regexp ${binfile}]
> diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
> index f1cbd9351d3..a36c4ee3614 100644
> --- a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
> +++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
> @@ -108,8 +108,14 @@ proc do_test { action1 action2 } {
>       }
>   }
>   
> -foreach_with_prefix action1 { kill detach add none } {
> -    foreach_with_prefix action2 { start run attach } {
> +set actions1 { kill detach add none }
> +set actions2 { start run }
> +if { [can_spawn_for_attach] } {
> +    lappend actions2 attach
> +}
> +
> +foreach_with_prefix action1 $actions1 {
> +    foreach_with_prefix action2 $actions2  {
>   	do_test $action1 $action2
>       }
>   }
> diff --git a/gdb/testsuite/gdb.multi/attach-while-running.exp b/gdb/testsuite/gdb.multi/attach-while-running.exp
> index eade8b42a18..ca4fa635467 100644
> --- a/gdb/testsuite/gdb.multi/attach-while-running.exp
> +++ b/gdb/testsuite/gdb.multi/attach-while-running.exp
> @@ -36,7 +36,7 @@
>   
>   standard_testfile
>   
> -require !use_gdb_stub
> +require can_spawn_for_attach
>   
>   if { [build_executable "failed to prepare" ${testfile} ${srcfile}] } {
>       return
> diff --git a/gdb/testsuite/gdb.threads/attach-into-signal.exp b/gdb/testsuite/gdb.threads/attach-into-signal.exp
> index 87e34070548..91da960e09a 100644
> --- a/gdb/testsuite/gdb.threads/attach-into-signal.exp
> +++ b/gdb/testsuite/gdb.threads/attach-into-signal.exp
> @@ -17,7 +17,8 @@
>   # This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
>   
>   # This test only works on Linux
> -require !use_gdb_stub isnative
> +require can_spawn_for_attach
> +require isnative
>   require {!is_remote host}
>   require {istarget *-linux*}
>   
> diff --git a/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp b/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp
> index dc3e62a7b7e..28d70daad8c 100644
> --- a/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp
> +++ b/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp
> @@ -37,7 +37,8 @@
>   # during the attach phase.
>   
>   # This test only works on Linux
> -require !use_gdb_stub isnative
> +require can_spawn_for_attach
> +require isnative
>   require {!is_remote host}
>   require {istarget *-linux*}
>   
> diff --git a/gdb/testsuite/gdb.threads/attach-stopped.exp b/gdb/testsuite/gdb.threads/attach-stopped.exp
> index 78e194c992f..0421ffc3794 100644
> --- a/gdb/testsuite/gdb.threads/attach-stopped.exp
> +++ b/gdb/testsuite/gdb.threads/attach-stopped.exp
> @@ -18,7 +18,8 @@
>   # This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
>   
>   # This test only works on Linux
> -require !use_gdb_stub isnative
> +require can_spawn_for_attach
> +require isnative
>   require {!is_remote host}
>   require {istarget *-linux*}
>   
> diff --git a/gdb/testsuite/gdb.threads/check-libthread-db.exp b/gdb/testsuite/gdb.threads/check-libthread-db.exp
> index 5662eeda077..6976fe6f83b 100644
> --- a/gdb/testsuite/gdb.threads/check-libthread-db.exp
> +++ b/gdb/testsuite/gdb.threads/check-libthread-db.exp
> @@ -102,25 +102,27 @@ with_test_prefix "automated load-time check" {
>       }
>   
>       # Automated load-time check with NPTL fully operational.
> -    with_test_prefix "libpthread.so fully initialized" {
> -	clean_restart ${binfile}
> -
> -	gdb_test_no_output "maint set check-libthread-db 1"
> -	gdb_test_no_output "set debug libthread-db 1"
> -
> -	set test_spawn_id [spawn_wait_for_attach $binfile]
> -	set testpid [spawn_id_get_pid $test_spawn_id]
> -
> -	gdb_test_sequence "attach $testpid" \
> -	    "check debug libthread-db output" {
> -		"\[\r\n\]+Running libthread_db integrity checks:"
> -		"\[\r\n\]+\[ \]+Got thread 0x\[1-9a-f\]\[0-9a-f\]+ => \[0-9\]+ => 0x\[1-9a-f\]\[0-9a-f\]+ ... OK"
> -		"\[\r\n\]+\[ \]+Got thread 0x\[1-9a-f\]\[0-9a-f\]+ => \[0-9\]+ => 0x\[1-9a-f\]\[0-9a-f\]+ ... OK"
> -		"\[\r\n\]+libthread_db integrity checks passed."
> -		"\[\r\n\]+[Thread debugging using libthread_db enabled]"
> -	    }
> -
> -	gdb_exit
> -	kill_wait_spawned_process $test_spawn_id
> +    if { [can_spawn_for_attach] } {
> +	with_test_prefix "libpthread.so fully initialized" {
> +	    clean_restart ${binfile}
> +
> +	    gdb_test_no_output "maint set check-libthread-db 1"
> +	    gdb_test_no_output "set debug libthread-db 1"
> +
> +	    set test_spawn_id [spawn_wait_for_attach $binfile]
> +	    set testpid [spawn_id_get_pid $test_spawn_id]
> +
> +	    gdb_test_sequence "attach $testpid" \
> +		"check debug libthread-db output" {
> +		    "\[\r\n\]+Running libthread_db integrity checks:"
> +		    "\[\r\n\]+\[ \]+Got thread 0x\[1-9a-f\]\[0-9a-f\]+ => \[0-9\]+ => 0x\[1-9a-f\]\[0-9a-f\]+ ... OK"
> +		    "\[\r\n\]+\[ \]+Got thread 0x\[1-9a-f\]\[0-9a-f\]+ => \[0-9\]+ => 0x\[1-9a-f\]\[0-9a-f\]+ ... OK"
> +		    "\[\r\n\]+libthread_db integrity checks passed."
> +		    "\[\r\n\]+[Thread debugging using libthread_db enabled]"
> +		}
> +
> +	    gdb_exit
> +	    kill_wait_spawned_process $test_spawn_id
> +	}
>       }
>   }
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index d48ea37c0cc..566a8296130 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -6131,6 +6131,43 @@ proc gdb_exit { } {
>       catch default_gdb_exit
>   }
>   
> +# Helper function for can_spawn_for_attach.  Try to spawn and attach, and
> +# return 0 only if we cannot attach because it's unsupported.
> +
> +gdb_caching_proc can_spawn_for_attach_1 {} {
> +    # Assume yes.
> +    set res 1
> +
> +    set me "can_spawn_for_attach"
> +    set src { int main (void) { sleep (600); return 0; } }
> +    if {![gdb_simple_compile $me $src executable]} {
> +	return $res
> +    }
> +
> +    set test_spawn_id [spawn_wait_for_attach_1 $obj]
> +    file delete $obj
> +
> +    gdb_start
> +
> +    set test_pid [spawn_id_get_pid $test_spawn_id]
> +    set attaching_re "Attaching to process $test_pid"
> +    gdb_test_multiple "attach $test_pid" "can spawn for attach" {
> +	-re -wrap "$attaching_re\r\n.*ptrace: Operation not permitted\\." {
> +	    # Not permitted.
> +	    set res 0
> +	}
> +	-re -wrap "" {
> +	    # Don't know.
> +	}
> +    }
> +
> +    gdb_exit
> +
> +    kill_wait_spawned_process $test_spawn_id
> +
> +    return $res
> +}
> +
>   # Return true if we can spawn a program on the target and attach to
>   # it.
>   
> @@ -6151,8 +6188,9 @@ proc can_spawn_for_attach { } {
>   	return 0
>       }
>   
> -    # Assume yes.
> -    return 1
> +    gdb_exit
> +
> +    return [can_spawn_for_attach_1]
>   }
>   
>   # Centralize the failure checking of "attach" command.
> @@ -6265,20 +6303,12 @@ proc spawn_id_get_pid { spawn_id } {
>       return $testpid
>   }
>   
> -# Start a set of programs running and then wait for a bit, to be sure
> -# that they can be attached to.  Return a list of processes spawn IDs,
> -# one element for each process spawned.  It's a test error to call
> -# this when [can_spawn_for_attach] is false.
> +# Helper function for spawn_wait_for_attach.  As spawn_wait_for_attach, but
> +# doesn't check for can_spawn_for_attach.
>   
> -proc spawn_wait_for_attach { executable_list } {
> +proc spawn_wait_for_attach_1 { executable_list } {
>       set spawn_id_list {}
>   
> -    if ![can_spawn_for_attach] {
> -	# The caller should have checked can_spawn_for_attach itself
> -	# before getting here.
> -	error "can't spawn for attach with this target/board"
> -    }
> -
>       foreach {executable} $executable_list {
>   	# Note we use Expect's spawn, not Tcl's exec, because with
>   	# spawn we control when to wait for/reap the process.  That
> @@ -6292,6 +6322,21 @@ proc spawn_wait_for_attach { executable_list } {
>       return $spawn_id_list
>   }
>   
> +# Start a set of programs running and then wait for a bit, to be sure
> +# that they can be attached to.  Return a list of processes spawn IDs,
> +# one element for each process spawned.  It's a test error to call
> +# this when [can_spawn_for_attach] is false.
> +
> +proc spawn_wait_for_attach { executable_list } {
> +    if ![can_spawn_for_attach] {
> +	# The caller should have checked can_spawn_for_attach itself
> +	# before getting here.
> +	error "can't spawn for attach with this target/board"
> +    }
> +
> +    return [spawn_wait_for_attach_1 $executable_list]
> +}
> +
>   #
>   # gdb_load_cmd -- load a file into the debugger.
>   #		  ARGS - additional args to load command.
> 
> base-commit: 6732c57eeea61d72ad1046fae1dd0d00920150e1
  

Patch

diff --git a/gdb/testsuite/gdb.base/break-interp.exp b/gdb/testsuite/gdb.base/break-interp.exp
index addacde552d..d7f84db4770 100644
--- a/gdb/testsuite/gdb.base/break-interp.exp
+++ b/gdb/testsuite/gdb.base/break-interp.exp
@@ -318,6 +318,10 @@  proc test_attach_gdb {file pid displacement prefix} {
 }
 
 proc test_attach {file displacement {relink_args ""}} {
+    if { ![can_spawn_for_attach] } {
+	return
+    }
+
     global board_info
     global exec
 
diff --git a/gdb/testsuite/gdb.base/dprintf-detach.exp b/gdb/testsuite/gdb.base/dprintf-detach.exp
index 550d319a895..b4184d698df 100644
--- a/gdb/testsuite/gdb.base/dprintf-detach.exp
+++ b/gdb/testsuite/gdb.base/dprintf-detach.exp
@@ -21,7 +21,7 @@ 
 load_lib gdbserver-support.exp
 
 # The test relies on "detach/attach".
-require !use_gdb_stub
+require can_spawn_for_attach
 
 standard_testfile
 set escapedbinfile [string_to_regexp ${binfile}]
diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
index f1cbd9351d3..a36c4ee3614 100644
--- a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
+++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
@@ -108,8 +108,14 @@  proc do_test { action1 action2 } {
     }
 }
 
-foreach_with_prefix action1 { kill detach add none } {
-    foreach_with_prefix action2 { start run attach } {
+set actions1 { kill detach add none }
+set actions2 { start run }
+if { [can_spawn_for_attach] } {
+    lappend actions2 attach
+}
+
+foreach_with_prefix action1 $actions1 {
+    foreach_with_prefix action2 $actions2  {
 	do_test $action1 $action2
     }
 }
diff --git a/gdb/testsuite/gdb.multi/attach-while-running.exp b/gdb/testsuite/gdb.multi/attach-while-running.exp
index eade8b42a18..ca4fa635467 100644
--- a/gdb/testsuite/gdb.multi/attach-while-running.exp
+++ b/gdb/testsuite/gdb.multi/attach-while-running.exp
@@ -36,7 +36,7 @@ 
 
 standard_testfile
 
-require !use_gdb_stub
+require can_spawn_for_attach
 
 if { [build_executable "failed to prepare" ${testfile} ${srcfile}] } {
     return
diff --git a/gdb/testsuite/gdb.threads/attach-into-signal.exp b/gdb/testsuite/gdb.threads/attach-into-signal.exp
index 87e34070548..91da960e09a 100644
--- a/gdb/testsuite/gdb.threads/attach-into-signal.exp
+++ b/gdb/testsuite/gdb.threads/attach-into-signal.exp
@@ -17,7 +17,8 @@ 
 # This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
 
 # This test only works on Linux
-require !use_gdb_stub isnative
+require can_spawn_for_attach
+require isnative
 require {!is_remote host}
 require {istarget *-linux*}
 
diff --git a/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp b/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp
index dc3e62a7b7e..28d70daad8c 100644
--- a/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp
+++ b/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp
@@ -37,7 +37,8 @@ 
 # during the attach phase.
 
 # This test only works on Linux
-require !use_gdb_stub isnative
+require can_spawn_for_attach
+require isnative
 require {!is_remote host}
 require {istarget *-linux*}
 
diff --git a/gdb/testsuite/gdb.threads/attach-stopped.exp b/gdb/testsuite/gdb.threads/attach-stopped.exp
index 78e194c992f..0421ffc3794 100644
--- a/gdb/testsuite/gdb.threads/attach-stopped.exp
+++ b/gdb/testsuite/gdb.threads/attach-stopped.exp
@@ -18,7 +18,8 @@ 
 # This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
 
 # This test only works on Linux
-require !use_gdb_stub isnative
+require can_spawn_for_attach
+require isnative
 require {!is_remote host}
 require {istarget *-linux*}
 
diff --git a/gdb/testsuite/gdb.threads/check-libthread-db.exp b/gdb/testsuite/gdb.threads/check-libthread-db.exp
index 5662eeda077..6976fe6f83b 100644
--- a/gdb/testsuite/gdb.threads/check-libthread-db.exp
+++ b/gdb/testsuite/gdb.threads/check-libthread-db.exp
@@ -102,25 +102,27 @@  with_test_prefix "automated load-time check" {
     }
 
     # Automated load-time check with NPTL fully operational.
-    with_test_prefix "libpthread.so fully initialized" {
-	clean_restart ${binfile}
-
-	gdb_test_no_output "maint set check-libthread-db 1"
-	gdb_test_no_output "set debug libthread-db 1"
-
-	set test_spawn_id [spawn_wait_for_attach $binfile]
-	set testpid [spawn_id_get_pid $test_spawn_id]
-
-	gdb_test_sequence "attach $testpid" \
-	    "check debug libthread-db output" {
-		"\[\r\n\]+Running libthread_db integrity checks:"
-		"\[\r\n\]+\[ \]+Got thread 0x\[1-9a-f\]\[0-9a-f\]+ => \[0-9\]+ => 0x\[1-9a-f\]\[0-9a-f\]+ ... OK"
-		"\[\r\n\]+\[ \]+Got thread 0x\[1-9a-f\]\[0-9a-f\]+ => \[0-9\]+ => 0x\[1-9a-f\]\[0-9a-f\]+ ... OK"
-		"\[\r\n\]+libthread_db integrity checks passed."
-		"\[\r\n\]+[Thread debugging using libthread_db enabled]"
-	    }
-
-	gdb_exit
-	kill_wait_spawned_process $test_spawn_id
+    if { [can_spawn_for_attach] } {
+	with_test_prefix "libpthread.so fully initialized" {
+	    clean_restart ${binfile}
+
+	    gdb_test_no_output "maint set check-libthread-db 1"
+	    gdb_test_no_output "set debug libthread-db 1"
+
+	    set test_spawn_id [spawn_wait_for_attach $binfile]
+	    set testpid [spawn_id_get_pid $test_spawn_id]
+
+	    gdb_test_sequence "attach $testpid" \
+		"check debug libthread-db output" {
+		    "\[\r\n\]+Running libthread_db integrity checks:"
+		    "\[\r\n\]+\[ \]+Got thread 0x\[1-9a-f\]\[0-9a-f\]+ => \[0-9\]+ => 0x\[1-9a-f\]\[0-9a-f\]+ ... OK"
+		    "\[\r\n\]+\[ \]+Got thread 0x\[1-9a-f\]\[0-9a-f\]+ => \[0-9\]+ => 0x\[1-9a-f\]\[0-9a-f\]+ ... OK"
+		    "\[\r\n\]+libthread_db integrity checks passed."
+		    "\[\r\n\]+[Thread debugging using libthread_db enabled]"
+		}
+
+	    gdb_exit
+	    kill_wait_spawned_process $test_spawn_id
+	}
     }
 }
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index d48ea37c0cc..566a8296130 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -6131,6 +6131,43 @@  proc gdb_exit { } {
     catch default_gdb_exit
 }
 
+# Helper function for can_spawn_for_attach.  Try to spawn and attach, and
+# return 0 only if we cannot attach because it's unsupported.
+
+gdb_caching_proc can_spawn_for_attach_1 {} {
+    # Assume yes.
+    set res 1
+
+    set me "can_spawn_for_attach"
+    set src { int main (void) { sleep (600); return 0; } }
+    if {![gdb_simple_compile $me $src executable]} {
+	return $res
+    }
+
+    set test_spawn_id [spawn_wait_for_attach_1 $obj]
+    file delete $obj
+
+    gdb_start
+
+    set test_pid [spawn_id_get_pid $test_spawn_id]
+    set attaching_re "Attaching to process $test_pid"
+    gdb_test_multiple "attach $test_pid" "can spawn for attach" {
+	-re -wrap "$attaching_re\r\n.*ptrace: Operation not permitted\\." {
+	    # Not permitted.
+	    set res 0
+	}
+	-re -wrap "" {
+	    # Don't know.
+	}
+    }
+
+    gdb_exit
+
+    kill_wait_spawned_process $test_spawn_id
+
+    return $res
+}
+
 # Return true if we can spawn a program on the target and attach to
 # it.
 
@@ -6151,8 +6188,9 @@  proc can_spawn_for_attach { } {
 	return 0
     }
 
-    # Assume yes.
-    return 1
+    gdb_exit
+
+    return [can_spawn_for_attach_1]
 }
 
 # Centralize the failure checking of "attach" command.
@@ -6265,20 +6303,12 @@  proc spawn_id_get_pid { spawn_id } {
     return $testpid
 }
 
-# Start a set of programs running and then wait for a bit, to be sure
-# that they can be attached to.  Return a list of processes spawn IDs,
-# one element for each process spawned.  It's a test error to call
-# this when [can_spawn_for_attach] is false.
+# Helper function for spawn_wait_for_attach.  As spawn_wait_for_attach, but
+# doesn't check for can_spawn_for_attach.
 
-proc spawn_wait_for_attach { executable_list } {
+proc spawn_wait_for_attach_1 { executable_list } {
     set spawn_id_list {}
 
-    if ![can_spawn_for_attach] {
-	# The caller should have checked can_spawn_for_attach itself
-	# before getting here.
-	error "can't spawn for attach with this target/board"
-    }
-
     foreach {executable} $executable_list {
 	# Note we use Expect's spawn, not Tcl's exec, because with
 	# spawn we control when to wait for/reap the process.  That
@@ -6292,6 +6322,21 @@  proc spawn_wait_for_attach { executable_list } {
     return $spawn_id_list
 }
 
+# Start a set of programs running and then wait for a bit, to be sure
+# that they can be attached to.  Return a list of processes spawn IDs,
+# one element for each process spawned.  It's a test error to call
+# this when [can_spawn_for_attach] is false.
+
+proc spawn_wait_for_attach { executable_list } {
+    if ![can_spawn_for_attach] {
+	# The caller should have checked can_spawn_for_attach itself
+	# before getting here.
+	error "can't spawn for attach with this target/board"
+    }
+
+    return [spawn_wait_for_attach_1 $executable_list]
+}
+
 #
 # gdb_load_cmd -- load a file into the debugger.
 #		  ARGS - additional args to load command.