Bug 27745 - When re-running after an exec, the exec'd file gets executed with the exec'r args
Summary: When re-running after an exec, the exec'd file gets executed with the exec'r ...
Status: NEW
Alias: None
Product: gdb
Classification: Unclassified
Component: gdb (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-04-16 19:40 UTC by Simon Marchi
Modified: 2021-05-13 19:29 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Simon Marchi 2021-04-16 19:40:08 UTC
With these two programs:

execer.c
---8<---
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv) {
    printf("I am execer and my argv[1] is: %s\n", argv[1]);
    execl("./execee", "./execee", "arg-for-execee", NULL);
    return 0;
}
--->8---

execee.c
---8<---
#include <stdio.h>

int main(int argc, char **argv) {
    printf("I am execee and my argv[1] is: %s\n", argv[1]);
    return 0;
}
--->8---

Let's try to run in GDB:

$ ./gdb -q -nx --data-directory=data-directory --args ./execer args-for-execer
Reading symbols from ./execer...
(gdb) r
Starting program: /home/simark/build/binutils-gdb/gdb/execer args-for-execer
I am execer and my argv[1] is: args-for-execer
process 1479927 is executing new program: /home/simark/build/binutils-gdb/gdb/execee
I am execee and my argv[1] is: arg-for-execee
[Inferior 1 (process 1479927) exited normally]
(gdb) 

So far so good, the arguments match the program.  Let's try to run again:

(gdb) r
Starting program: /home/simark/build/binutils-gdb/gdb/execee args-for-execer
I am execee and my argv[1] is: args-for-execer
[Inferior 1 (process 1479957) exited normally]
(gdb) 

Woops, we have executed execee with the arguments for execer.  I think this behavior is not very useful.

My takeaway is:

- When re-running after an exec, GDB executes the last known program, execee in this case.  It can be fine in some use cases, where you just want to debug the execee again and again.  But it should find out the arguments with which the execee was executed, save them in the inferior, and re-use them when you "run".
- In other use cases, the user could want to start debugging from scratch, that means re-running execer.  GDB could remember the latest user-specified executable for the inferior.  There would then be a setting to decide which executable to run (the latest user-specified executable, execer, or the latest seen executable, execee)

It is possible to remember the original executable by using "follow-exec-mode == new", but IMO that's not very practical, the user needs to switch back to inferior 1 to restart.  And if they restart multiple times, the new inferiors just pile up.  It would make more sense to be able to re-run from the start (execer) using a single inferior.
Comment 1 Tom Tromey 2021-04-17 16:42:37 UTC
> It would make more sense to be able to re-run from the start (execer) using a single inferior.

Maybe one idea would be to have a notion of a process tree and
a "primary" inferior in the tree.  Then, inferiors that are created
via fork/exec could be automatically removed.  That is, "run"
would remove the whole tree and restart at the top.
Comment 2 Simon Marchi 2021-04-19 01:09:05 UTC
(In reply to Tom Tromey from comment #1)
> > It would make more sense to be able to re-run from the start (execer) using a single inferior.
> 
> Maybe one idea would be to have a notion of a process tree and
> a "primary" inferior in the tree.  Then, inferiors that are created
> via fork/exec could be automatically removed.  That is, "run"
> would remove the whole tree and restart at the top.

I like that.
Comment 3 Pedro Alves 2021-04-21 14:59:00 UTC
> It is possible to remember the original executable by using "follow-exec-mode == > new", but IMO that's not very practical, the user needs to switch back to 
> inferior 1 to restart.  And if they restart multiple times, the new inferiors 
> just pile up.  It would make more sense to be able to re-run from the start 
> (execer) using a single inferior.

See below.

> Maybe one idea would be to have a notion of a process tree and
> a "primary" inferior in the tree.  Then, inferiors that are created
> via fork/exec could be automatically removed.  That is, "run"
> would remove the whole tree and restart at the top.

That was already sort of the idea behind "follow-exec-mode == new".  Note how GDB marks fork children inferiors as "removable":

	      child_inf->removable = 1;

And then normal_stop removes these inferiors when possible:

  /* Try to get rid of automatically added inferiors that are no
     longer needed.  Keeping those around slows down things linearly.
     Note that this never removes the current inferior.  */
  prune_inferiors ();

This was never fully developed -- I mean, for example, there's no way to force an inferior to be non-removable in the CLI.
Comment 4 Sourceware Commits 2021-05-13 19:29:59 UTC
The master branch has been updated by Simon Marchi <simark@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=2af87c859fe450d4a3a841cf19637a91d53c9486

commit 2af87c859fe450d4a3a841cf19637a91d53c9486
Author: Simon Marchi <simon.marchi@efficios.com>
Date:   Thu May 13 15:27:55 2021 -0400

    gdb: call target_follow_exec when "set follow-exec-mode" is "same"
    
    target_follow_exec is currently only called in the "follow-exec-mode ==
    new" branch of follow_exec, not the "follow-exec-mode == same" branch.
    I think it would make sense to call it regardless of the mode to let
    targets do some necessary handling.
    
    This is needed in the context of rocm-gdb [1], where a target is pushed
    on top of the linux-nat target.  On exec, it needs to do some
    bookkeeping, close some file descriptors / handles that were related to
    the process pre-exec and open some new ones for the process post-exec.
    
    However, by looking at the only in-tree implementation of
    target_ops::follow_exec, remote_target::follow_exec, I found that it
    would be useful for the extended-remote target too, to align its
    behavior with native debugging (although I think that behavior is not
    very user-friendly, see PR 27745 [2]).
    
    Using two programs, one (let's call it "execer") that execs the other
    (let's call it "execee"), with native:
    
        $ ./gdb -q -nx --data-directory=data-directory ./execer
        Reading symbols from ./execer...
        (gdb) r
        Starting program: /home/simark/build/binutils-gdb/gdb/execer
        I am execer
        process 1495622 is executing new program: /home/simark/build/binutils-gdb/gdb/execee
        I am execee
        [Inferior 1 (process 1495622) exited normally]
        (gdb) r
        Starting program: /home/simark/build/binutils-gdb/gdb/execee
        I am execee
        [Inferior 1 (process 1495626) exited normally]
    
    And now with gdbserver (some irrelevant output lines removed for brevity):
    
        $ ./gdbserver --once --multi :1234
        ...
    
        $ ./gdb -q -nx --data-directory=data-directory ./execer -ex "set remote exec-file /home/simark/build/binutils-gdb/gdb/execer" -ex "tar ext :1234"
        Reading symbols from ./execer...
        Remote debugging using :1234
        (gdb) r
        Starting program: /home/simark/build/binutils-gdb/gdb/execer
        process 1495724 is executing new program: /home/simark/build/binutils-gdb/gdb/execee
        [Inferior 1 (process 1495724) exited normally]
        (gdb) r
        `target:/home/simark/build/binutils-gdb/gdb/execee' has disappeared; keeping its symbols.
        Starting program: target:/home/simark/build/binutils-gdb/gdb/execee
        warning: Build ID mismatch between current exec-file target:/home/simark/build/binutils-gdb/gdb/execee
        and automatically determined exec-file target:/home/simark/build/binutils-gdb/gdb/execer
        exec-file-mismatch handling is currently "ask"
        Reading /home/simark/build/binutils-gdb/gdb/execer from remote target...
        Load new symbol table from "target:/home/simark/build/binutils-gdb/gdb/execer"? (y or n)
    
    When handling the exec, GDB updates the exec-file of the inferior to be
    the execee.  This means that a subsequent "run" will run the execee, not
    the original executable (execer).
    
    remote_target::follow_exec is meant to update the "remote exec-file",
    which is the file on the remote system that will be executed if you
    "run" the inferior, to the execee as well.  However, this is not called
    when follow-exec-mode is same, because target_follow_exec is not called
    in this branch.  As a result, GDB thinks the inferior is executing
    execee but the remote side is really executing execer, hence the
    mismatch message.
    
    By calling target_follow_exec in the "same" branch of the follow_exec
    function, we ensure that everybody agrees, and we get the same behavior
    with the extended-remote target as we get with the native target, the
    execee is executed on the second run:
    
        $ ./gdbserver --once --multi :1234
        ...
    
        $ ./gdb -q -nx --data-directory=data-directory ./execer -ex "set remote exec-file /home/simark/build/binutils-gdb/gdb/execer" -ex "tar ext :1234"
        Reading symbols from ./execer...
        Remote debugging using :1234
        (gdb) r
        Starting program: /home/simark/build/binutils-gdb/gdb/execer
        process 1501445 is executing new program: /home/simark/build/binutils-gdb/gdb/execee
        [Inferior 1 (process 1501445) exited normally]
        (gdb) r
        `target:/home/simark/build/binutils-gdb/gdb/execee' has disappeared; keeping its symbols.
        Starting program: target:/home/simark/build/binutils-gdb/gdb/execee
        [Inferior 1 (process 1501447) exited normally]
        (gdb)
    
    This scenario is tested in gdb.base/foll-exec-mode.exp, and in fact this
    patch fixes the test for me when using
    --target_board=native-extended-gdbserver.
    
    gdb/ChangeLog:
    
            * infrun.c (follow_exec): Call target_follow_fork when
            follow-exec-mode is same.
            * target.h (target_follow_fork): Improve doc.
    
    [1] https://github.com/ROCm-Developer-Tools/ROCgdb
    [2] https://sourceware.org/bugzilla/show_bug.cgi?id=27745
    
    Change-Id: I4ee84a875e39bf3f8eaf3e6789a4bfe23a2a430e