Setting argv[0] for a valgrind child process?

Is there any way to override the argv[0] specified by valgrind when it execve's the child process?

Why?

I'm using Valgrind with a tool that examines its argv[0] to determine the location of its executable in order to find related executables relative to itself. It exec()s a lot of children, most of which are not of any interest and should not be traced by Valgrind.

I can intercept invocations of the commands of interest by populating a directory with wrapper scripts that call the next executable of the same name on the PATH under the control of valgrind. But valgrind always sets argv[0] to the concrete name of the executable it invoked. I want it to pass the name of the wrapper executable instead, so the child command looks in my wrapper directory for related commands to run.

The usual workaround would be to create a symlink to the real executable from the wrapper dir, then invoke the real executable via the symlink. But that won't work here because that's where the wrapper scripts must exist.

Ugly workaround

So far the only solution I see is to re-exec my wrapper script under valgrind, detect that the wrapper script is running under valgrind, and exec the real target program without wrapping when the script detects it's already running under valgrind. That'll work, but it's ugly. It requires that valgrind --trace-children=yes in order to inspect the actual target, which for my use case is undesirable. And it's expensive to have those short-lived valgrind commands run each wrapper script a second time.

Things I tried

I've tried exec -a /path/to/wrapper/command valgrind /path/to/real/command (bash). But valgrind doesn't seem to notice or care that argv[0] isn't valgrind, and does not pass that on to the child process.

Sample wrapper script with hacky workaround

if [ "${RUNNING_UNDER_VALGRIND:-0}" -eq 0 ]; then
    # Find the real executable that's next on the PATH. But don't run it
    # yet; instead put its path in the environment so it's available
    # when we re-exec ourselves under valgrind.
    export NEXT_EXEC="$(type -pafP $mycmd |  awk '{ if (NR == 2) { print; exit; } }')"

    # Re-exec this wrapper under valgrind's control. Valgrind ignores
    # argv[0] so there's no way to directly run NEXT_EXEC under valgrind
    # and set its argv[0] to point to our $0.
    #
    RUNNING_UNDER_VALGRIND=1 exec valgrind --tool=memcheck --trace-children=yes "$0" "$@"
else
    # We're under valgrind, so exec the real executable that's next on the
    # PATH with an argv[0] that points to this wrapper, so it looks here for
    # peer executables when it wants to exec them. We looked up NEXT_EXEC
    # in our previous life and put it in the environment.
    #
    exec -a "$0" "${NEXT_EXEC}" "$@"
fi

Yes that's gross. It'd be possible to make a C executable that did the same thing a bit quicker, but the same issues apply with having to trace children, getting unwanted extra logs, etc.

Edit:

This works, so long as your target program(s) don't care about the executable name itself, only the directory.

NEXT_EXEC="$(type -pafP $mycmd |  awk '{ if (NR == 2) { print; exit; } }')"
if ! [ "${0}.real" -ef "${NEXT_EXEC}" ]; then
    rm -f "${0}.real"
    ln "${NEXT_EXEC}" "${0}.real"
fi
exec valgrind --trace-children=no "${0}.real" "$@"

Edit 2

Beginnings of a valgrind patch to add support for a --with-argv0 argument. When passed, valgrind core will treat the first argument after the executable name as the argv[0] to supply in the target's command line. Normally it puts the executable name there, and treats the client argument list as starting at argv[1].



Read more here: https://stackoverflow.com/questions/67378933/setting-argv0-for-a-valgrind-child-process

Content Attribution

This content was originally published by Craig Ringer at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: