Merge pull request #124556 from bergkvist/bergkvist/make-c-wrapper

Generate tiny compiled binary for wrapping executables
main
Robert Hensing 3 years ago committed by GitHub
commit 9fb7d91888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      doc/stdenv/stdenv.chapter.md
  2. 6
      maintainers/maintainer-list.nix
  3. 384
      pkgs/build-support/setup-hooks/make-binary-wrapper.sh
  4. 2
      pkgs/test/default.nix
  5. 21
      pkgs/test/make-binary-wrapper/add-flags.c
  6. 2
      pkgs/test/make-binary-wrapper/add-flags.cmdline
  7. 6
      pkgs/test/make-binary-wrapper/add-flags.env
  8. 7
      pkgs/test/make-binary-wrapper/argv0.c
  9. 1
      pkgs/test/make-binary-wrapper/argv0.cmdline
  10. 2
      pkgs/test/make-binary-wrapper/argv0.env
  11. 7
      pkgs/test/make-binary-wrapper/basic.c
  12. 0
      pkgs/test/make-binary-wrapper/basic.cmdline
  13. 2
      pkgs/test/make-binary-wrapper/basic.env
  14. 11
      pkgs/test/make-binary-wrapper/chdir.c
  15. 1
      pkgs/test/make-binary-wrapper/chdir.cmdline
  16. 2
      pkgs/test/make-binary-wrapper/chdir.env
  17. 53
      pkgs/test/make-binary-wrapper/combination.c
  18. 6
      pkgs/test/make-binary-wrapper/combination.cmdline
  19. 8
      pkgs/test/make-binary-wrapper/combination.env
  20. 54
      pkgs/test/make-binary-wrapper/default.nix
  21. 14
      pkgs/test/make-binary-wrapper/env.c
  22. 4
      pkgs/test/make-binary-wrapper/env.cmdline
  23. 6
      pkgs/test/make-binary-wrapper/env.env
  24. 22
      pkgs/test/make-binary-wrapper/envcheck.c
  25. 6
      pkgs/test/make-binary-wrapper/inherit-argv0.c
  26. 1
      pkgs/test/make-binary-wrapper/inherit-argv0.cmdline
  27. 2
      pkgs/test/make-binary-wrapper/inherit-argv0.env
  28. 14
      pkgs/test/make-binary-wrapper/invalid-env.c
  29. 2
      pkgs/test/make-binary-wrapper/invalid-env.cmdline
  30. 26
      pkgs/test/make-binary-wrapper/prefix.c
  31. 2
      pkgs/test/make-binary-wrapper/prefix.cmdline
  32. 3
      pkgs/test/make-binary-wrapper/prefix.env
  33. 26
      pkgs/test/make-binary-wrapper/suffix.c
  34. 2
      pkgs/test/make-binary-wrapper/suffix.cmdline
  35. 3
      pkgs/test/make-binary-wrapper/suffix.env
  36. 15
      pkgs/top-level/all-packages.nix

@ -796,7 +796,7 @@ The standard environment provides a number of useful functions.
### `makeWrapper` \<executable\> \<wrapperfile\> \<args\> {#fun-makeWrapper}
Constructs a wrapper for a program with various possible arguments. For example:
Constructs a wrapper for a program with various possible arguments. It is defined as part of 2 setup-hooks named `makeWrapper` and `makeBinaryWrapper` that implement the same bash functions. Hence, to use it you have to add `makeWrapper` to your `nativeBuildInputs`. Here's an example usage:
```bash
# adds `FOOBAR=baz` to `$out/bin/foo`’s environment
@ -808,9 +808,11 @@ makeWrapper $out/bin/foo $wrapperfile --set FOOBAR baz
makeWrapper $out/bin/foo $wrapperfile --prefix PATH : ${lib.makeBinPath [ hello git ]}
```
There’s many more kinds of arguments, they are documented in `nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh`.
There’s many more kinds of arguments, they are documented in `nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh` for the `makeWrapper` implementation and in `nixpkgs/pkgs/build-support/setup-hooks/make-binary-wrapper.sh` for the `makeBinaryWrapper` implementation.
`wrapProgram` is a convenience function you probably want to use most of the time.
`wrapProgram` is a convenience function you probably want to use most of the time, implemented by both `makeWrapper` and `makeBinaryWrapper`.
Using the `makeBinaryWrapper` implementation is usually preferred, as it creates a tiny _compiled_ wrapper executable, that can be used as a shebang interpreter. This is needed mostly on Darwin, where shebangs cannot point to scripts, [due to a limitation with the `execve`-syscall](https://stackoverflow.com/questions/67100831/macos-shebang-with-absolute-path-not-working). Compiled wrappers generated by `makeBinaryWrapper` can be inspected with `less <path-to-wrapper>` - by scrolling past the binary data you should be able to see the shell command that generated the executable and there see the environment variables that were injected into the wrapper.
### `substitute` \<infile\> \<outfile\> \<subs\> {#fun-substitute}
@ -885,9 +887,9 @@ someVar=$(stripHash $name)
### `wrapProgram` \<executable\> \<makeWrapperArgs\> {#fun-wrapProgram}
Convenience function for `makeWrapper` that automatically creates a sane wrapper file. It takes all the same arguments as `makeWrapper`, except for `--argv0`.
Convenience function for `makeWrapper` that replaces `<\executable\>` with a wrapper that executes the original program. It takes all the same arguments as `makeWrapper`, except for `--inherit-argv0` (used by the `makeBinaryWrapper` implementation) and `--argv0` (used by both `makeWrapper` and `makeBinaryWrapper` wrapper implementations).
It cannot be applied multiple times, since it will overwrite the wrapper file.
If you will apply it multiple times, it will overwrite the wrapper file and you will end up with double wrapping, which should be avoided.
## Package setup hooks {#ssec-setup-hooks}

@ -1414,6 +1414,12 @@
githubId = 251106;
name = "Daniel Bergey";
};
bergkvist = {
email = "tobias@bergkv.ist";
github = "bergkvist";
githubId = 410028;
name = "Tobias Bergkvist";
};
betaboon = {
email = "betaboon@0x80.ninja";
github = "betaboon";

@ -0,0 +1,384 @@
set -euo pipefail
# Assert that FILE exists and is executable
#
# assertExecutable FILE
assertExecutable() {
local file="$1"
[[ -f "$file" && -x "$file" ]] || \
die "Cannot wrap '$file' because it is not an executable file"
}
# Generate a binary executable wrapper for wrapping an executable.
# The binary is compiled from generated C-code using gcc.
# makeWrapper EXECUTABLE OUT_PATH ARGS
# ARGS:
# --argv0 NAME : set name of executed process to NAME
# (otherwise it’s called …-wrapped)
# --inherit-argv0 : the executable inherits argv0 from the wrapper.
# (use instead of --argv0 '$0')
# --set VAR VAL : add VAR with value VAL to the executable’s
# environment
# --set-default VAR VAL : like --set, but only adds VAR if not already set in
# the environment
# --unset VAR : remove VAR from the environment
# --chdir DIR : change working directory (use instead of --run "cd DIR")
# --add-flags FLAGS : add FLAGS to invocation of executable
# --prefix ENV SEP VAL : suffix/prefix ENV with VAL, separated by SEP
# --suffix
# To troubleshoot a binary wrapper after you compiled it,
# use the `strings` command or open the binary file in a text editor.
makeWrapper() {
assertExecutable "$1"
makeDocumentedCWrapper "$1" "${@:3}" | \
@CC@ \
-Wall -Werror -Wpedantic \
-Os \
-x c \
-o "$2" -
}
# Syntax: wrapProgram <PROGRAM> <MAKE-WRAPPER FLAGS...>
wrapProgram() {
local prog="$1"
local hidden
assertExecutable "$prog"
hidden="$(dirname "$prog")/.$(basename "$prog")"-wrapped
while [ -e "$hidden" ]; do
hidden="${hidden}_"
done
mv "$prog" "$hidden"
# Silence warning about unexpanded $0:
# shellcheck disable=SC2016
makeWrapper "$hidden" "$prog" --inherit-argv0 "${@:2}"
}
# Generate source code for the wrapper in such a way that the wrapper inputs
# will still be readable even after compilation
# makeDocumentedCWrapper EXECUTABLE ARGS
# ARGS: same as makeWrapper
makeDocumentedCWrapper() {
local src docs
src=$(makeCWrapper "$@")
docs=$(docstring "$@")
printf '%s\n\n' "$src"
printf '%s\n' "$docs"
}
# makeCWrapper EXECUTABLE ARGS
# ARGS: same as makeWrapper
makeCWrapper() {
local argv0 inherit_argv0 n params cmd main flagsBefore flags executable length
local uses_prefix uses_suffix uses_assert uses_assert_success uses_stdio uses_asprintf
executable=$(escapeStringLiteral "$1")
params=("$@")
length=${#params[*]}
for ((n = 1; n < length; n += 1)); do
p="${params[n]}"
case $p in
--set)
cmd=$(setEnv "${params[n + 1]}" "${params[n + 2]}")
main="$main$cmd"$'\n'
n=$((n + 2))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 2 arguments"$'\n'
;;
--set-default)
cmd=$(setDefaultEnv "${params[n + 1]}" "${params[n + 2]}")
main="$main$cmd"$'\n'
uses_stdio=1
uses_assert_success=1
n=$((n + 2))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 2 arguments"$'\n'
;;
--unset)
cmd=$(unsetEnv "${params[n + 1]}")
main="$main$cmd"$'\n'
uses_stdio=1
uses_assert_success=1
n=$((n + 1))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
;;
--prefix)
cmd=$(setEnvPrefix "${params[n + 1]}" "${params[n + 2]}" "${params[n + 3]}")
main="$main$cmd"$'\n'
uses_prefix=1
uses_asprintf=1
uses_stdio=1
uses_assert_success=1
uses_assert=1
n=$((n + 3))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 3 arguments"$'\n'
;;
--suffix)
cmd=$(setEnvSuffix "${params[n + 1]}" "${params[n + 2]}" "${params[n + 3]}")
main="$main$cmd"$'\n'
uses_suffix=1
uses_asprintf=1
uses_stdio=1
uses_assert_success=1
uses_assert=1
n=$((n + 3))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 3 arguments"$'\n'
;;
--chdir)
cmd=$(changeDir "${params[n + 1]}")
main="$main$cmd"$'\n'
uses_stdio=1
uses_assert_success=1
n=$((n + 1))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
;;
--add-flags)
flags="${params[n + 1]}"
flagsBefore="$flagsBefore $flags"
uses_assert=1
n=$((n + 1))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
;;
--argv0)
argv0=$(escapeStringLiteral "${params[n + 1]}")
inherit_argv0=
n=$((n + 1))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
;;
--inherit-argv0)
# Whichever comes last of --argv0 and --inherit-argv0 wins
inherit_argv0=1
;;
*) # Using an error macro, we will make sure the compiler gives an understandable error message
main="$main#error makeCWrapper: Unknown argument ${p}"$'\n'
;;
esac
done
# shellcheck disable=SC2086
[ -z "$flagsBefore" ] || main="$main"${main:+$'\n'}$(addFlags $flagsBefore)$'\n'$'\n'
[ -z "$inherit_argv0" ] && main="${main}argv[0] = \"${argv0:-${executable}}\";"$'\n'
main="${main}return execv(\"${executable}\", argv);"$'\n'
[ -z "$uses_asprintf" ] || printf '%s\n' "#define _GNU_SOURCE /* See feature_test_macros(7) */"
printf '%s\n' "#include <unistd.h>"
printf '%s\n' "#include <stdlib.h>"
[ -z "$uses_assert" ] || printf '%s\n' "#include <assert.h>"
[ -z "$uses_stdio" ] || printf '%s\n' "#include <stdio.h>"
[ -z "$uses_assert_success" ] || printf '\n%s\n' "#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)"
[ -z "$uses_prefix" ] || printf '\n%s\n' "$(setEnvPrefixFn)"
[ -z "$uses_suffix" ] || printf '\n%s\n' "$(setEnvSuffixFn)"
printf '\n%s' "int main(int argc, char **argv) {"
printf '\n%s' "$(indent4 "$main")"
printf '\n%s\n' "}"
}
addFlags() {
local result n flag flags var
var="argv_tmp"
flags=("$@")
for ((n = 0; n < ${#flags[*]}; n += 1)); do
flag=$(escapeStringLiteral "${flags[$n]}")
result="$result${var}[$((n+1))] = \"$flag\";"$'\n'
done
printf '%s\n' "char **$var = calloc($((n+1)) + argc, sizeof(*$var));"
printf '%s\n' "assert($var != NULL);"
printf '%s\n' "${var}[0] = argv[0];"
printf '%s' "$result"
printf '%s\n' "for (int i = 1; i < argc; ++i) {"
printf '%s\n' " ${var}[$n + i] = argv[i];"
printf '%s\n' "}"
printf '%s\n' "${var}[$n + argc] = NULL;"
printf '%s\n' "argv = $var;"
}
# chdir DIR
changeDir() {
local dir
dir=$(escapeStringLiteral "$1")
printf '%s' "assert_success(chdir(\"$dir\"));"
}
# prefix ENV SEP VAL
setEnvPrefix() {
local env sep val
env=$(escapeStringLiteral "$1")
sep=$(escapeStringLiteral "$2")
val=$(escapeStringLiteral "$3")
printf '%s' "set_env_prefix(\"$env\", \"$sep\", \"$val\");"
assertValidEnvName "$1"
}
# suffix ENV SEP VAL
setEnvSuffix() {
local env sep val
env=$(escapeStringLiteral "$1")
sep=$(escapeStringLiteral "$2")
val=$(escapeStringLiteral "$3")
printf '%s' "set_env_suffix(\"$env\", \"$sep\", \"$val\");"
assertValidEnvName "$1"
}
# setEnv KEY VALUE
setEnv() {
local key value
key=$(escapeStringLiteral "$1")
value=$(escapeStringLiteral "$2")
printf '%s' "putenv(\"$key=$value\");"
assertValidEnvName "$1"
}
# setDefaultEnv KEY VALUE
setDefaultEnv() {
local key value
key=$(escapeStringLiteral "$1")
value=$(escapeStringLiteral "$2")
printf '%s' "assert_success(setenv(\"$key\", \"$value\", 0));"
assertValidEnvName "$1"
}
# unsetEnv KEY
unsetEnv() {
local key
key=$(escapeStringLiteral "$1")
printf '%s' "assert_success(unsetenv(\"$key\"));"
assertValidEnvName "$1"
}
# Makes it safe to insert STRING within quotes in a C String Literal.
# escapeStringLiteral STRING
escapeStringLiteral() {
local result
result=${1//$'\\'/$'\\\\'}
result=${result//\"/'\"'}
result=${result//$'\n'/"\n"}
result=${result//$'\r'/"\r"}
printf '%s' "$result"
}
# Indents every non-empty line by 4 spaces. To avoid trailing whitespace, we don't indent empty lines
# indent4 TEXT_BLOCK
indent4() {
printf '%s' "$1" | awk '{ if ($0 != "") { print " "$0 } else { print $0 }}'
}
assertValidEnvName() {
case "$1" in
*=*) printf '\n%s\n' "#error Illegal environment variable name \`$1\` (cannot contain \`=\`)";;
"") printf '\n%s\n' "#error Environment variable name can't be empty.";;
esac
}
setEnvPrefixFn() {
printf '%s' "\
void set_env_prefix(char *env, char *sep, char *prefix) {
char *existing = getenv(env);
if (existing) {
char *val;
assert_success(asprintf(&val, \"%s%s%s\", prefix, sep, existing));
assert_success(setenv(env, val, 1));
free(val);
} else {
assert_success(setenv(env, prefix, 1));
}
}
"
}
setEnvSuffixFn() {
printf '%s' "\
void set_env_suffix(char *env, char *sep, char *suffix) {
char *existing = getenv(env);
if (existing) {
char *val;
assert_success(asprintf(&val, \"%s%s%s\", existing, sep, suffix));
assert_success(setenv(env, val, 1));
free(val);
} else {
assert_success(setenv(env, suffix, 1));
}
}
"
}
# Embed a C string which shows up as readable text in the compiled binary wrapper
# documentationString ARGS
docstring() {
printf '%s' "const char * DOCSTRING = \"$(escapeStringLiteral "
# ------------------------------------------------------------------------------------
# The C-code for this binary wrapper has been generated using the following command:
makeCWrapper $(formatArgs "$@")
# (Use \`nix-shell -p makeBinaryWrapper\` to get access to makeCWrapper in your shell)
# ------------------------------------------------------------------------------------
")\";"
}
# formatArgs EXECUTABLE ARGS
formatArgs() {
printf '%s' "$1"
shift
while [ $# -gt 0 ]; do
case "$1" in
--set)
formatArgsLine 2 "$@"
shift 2
;;
--set-default)
formatArgsLine 2 "$@"
shift 2
;;
--unset)
formatArgsLine 1 "$@"
shift 1
;;
--prefix)
formatArgsLine 3 "$@"
shift 3
;;
--suffix)
formatArgsLine 3 "$@"
shift 3
;;
--chdir)
formatArgsLine 1 "$@"
shift 1
;;
--add-flags)
formatArgsLine 1 "$@"
shift 1
;;
--argv0)
formatArgsLine 1 "$@"
shift 1
;;
--inherit-argv0)
formatArgsLine 0 "$@"
;;
esac
shift
done
printf '%s\n' ""
}
# formatArgsLine ARG_COUNT ARGS
formatArgsLine() {
local ARG_COUNT LENGTH
ARG_COUNT=$1
LENGTH=$#
shift
printf '%s' $' \\\n '"$1"
shift
while [ "$ARG_COUNT" -gt $((LENGTH - $# - 2)) ]; do
printf ' %s' "${1@Q}"
shift
done
}

@ -35,6 +35,8 @@ with pkgs;
macOSSierraShared = callPackage ./macos-sierra-shared {};
make-binary-wrapper = callPackage ./make-binary-wrapper { inherit makeBinaryWrapper; };
cross = callPackage ./cross {};
php = recurseIntoAttrs (callPackages ./php {});

@ -0,0 +1,21 @@
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
int main(int argc, char **argv) {
char **argv_tmp = calloc(5 + argc, sizeof(*argv_tmp));
assert(argv_tmp != NULL);
argv_tmp[0] = argv[0];
argv_tmp[1] = "-x";
argv_tmp[2] = "-y";
argv_tmp[3] = "-z";
argv_tmp[4] = "-abc";
for (int i = 1; i < argc; ++i) {
argv_tmp[4 + i] = argv[i];
}
argv_tmp[4 + argc] = NULL;
argv = argv_tmp;
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

@ -0,0 +1,2 @@
--add-flags "-x -y -z" \
--add-flags -abc

@ -0,0 +1,6 @@
CWD=SUBST_CWD
SUBST_ARGV0
-x
-y
-z
-abc

@ -0,0 +1,7 @@
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv) {
argv[0] = "alternative-name";
return execv("/send/me/flags", argv);
}

@ -0,0 +1,2 @@
CWD=SUBST_CWD
alternative-name

@ -0,0 +1,7 @@
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv) {
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

@ -0,0 +1,2 @@
CWD=SUBST_CWD
SUBST_ARGV0

@ -0,0 +1,11 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
int main(int argc, char **argv) {
assert_success(chdir("/tmp/foo"));
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

@ -0,0 +1,2 @@
CWD=/tmp/foo
SUBST_ARGV0

@ -0,0 +1,53 @@
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
void set_env_prefix(char *env, char *sep, char *prefix) {
char *existing = getenv(env);
if (existing) {
char *val;
assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing));
assert_success(setenv(env, val, 1));
free(val);
} else {
assert_success(setenv(env, prefix, 1));
}
}
void set_env_suffix(char *env, char *sep, char *suffix) {
char *existing = getenv(env);
if (existing) {
char *val;
assert_success(asprintf(&val, "%s%s%s", existing, sep, suffix));
assert_success(setenv(env, val, 1));
free(val);
} else {
assert_success(setenv(env, suffix, 1));
}
}
int main(int argc, char **argv) {
assert_success(setenv("MESSAGE", "HELLO", 0));
set_env_prefix("PATH", ":", "/usr/bin/");
set_env_suffix("PATH", ":", "/usr/local/bin/");
putenv("MESSAGE2=WORLD");
char **argv_tmp = calloc(4 + argc, sizeof(*argv_tmp));
assert(argv_tmp != NULL);
argv_tmp[0] = argv[0];
argv_tmp[1] = "-x";
argv_tmp[2] = "-y";
argv_tmp[3] = "-z";
for (int i = 1; i < argc; ++i) {
argv_tmp[3 + i] = argv[i];
}
argv_tmp[3 + argc] = NULL;
argv = argv_tmp;
argv[0] = "my-wrapper";
return execv("/send/me/flags", argv);
}

@ -0,0 +1,6 @@
--argv0 my-wrapper \
--set-default MESSAGE HELLO \
--prefix PATH : /usr/bin/ \
--suffix PATH : /usr/local/bin/ \
--add-flags "-x -y -z" \
--set MESSAGE2 WORLD

@ -0,0 +1,8 @@
MESSAGE=HELLO
PATH=/usr/bin/:/usr/local/bin/
MESSAGE2=WORLD
CWD=SUBST_CWD
my-wrapper
-x
-y
-z

@ -0,0 +1,54 @@
{ lib, coreutils, python3, gcc, writeText, writeScript, runCommand, makeBinaryWrapper }:
let
env = { nativeBuildInputs = [ makeBinaryWrapper ]; };
envCheck = runCommand "envcheck" env ''
${gcc}/bin/cc -Wall -Werror -Wpedantic -o $out ${./envcheck.c}
'';
makeGoldenTest = testname: runCommand "test-wrapper_${testname}" env ''
mkdir -p /tmp/foo
params=$(<"${./.}/${testname}.cmdline")
eval "makeCWrapper /send/me/flags $params" > wrapper.c
diff wrapper.c "${./.}/${testname}.c"
if [ -f "${./.}/${testname}.env" ]; then
eval "makeWrapper ${envCheck} wrapped $params"
env -i ./wrapped > env.txt
sed "s#SUBST_ARGV0#${envCheck}#;s#SUBST_CWD#$PWD#" \
"${./.}/${testname}.env" > golden-env.txt
if ! diff env.txt golden-env.txt; then
echo "env/argv should be:"
cat golden-env.txt
echo "env/argv output is:"
cat env.txt
exit 1
fi
else
# without a golden env, we expect the wrapper compilation to fail
! eval "makeWrapper ${envCheck} wrapped $params" &> error.txt
fi
cp wrapper.c $out
'';
tests = let
names = [
"add-flags"
"argv0"
"basic"
"chdir"
"combination"
"env"
"inherit-argv0"
"invalid-env"
"prefix"
"suffix"
];
f = name: lib.nameValuePair name (makeGoldenTest name);
in builtins.listToAttrs (builtins.map f names);
in writeText "make-binary-wrapper-test" ''
${lib.concatStringsSep "\n" (lib.mapAttrsToList (_: test: ''
"${test.name}" "${test}"
'') tests)}
'' // tests

@ -0,0 +1,14 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
int main(int argc, char **argv) {
putenv("PART1=HELLO");
assert_success(setenv("PART2", "WORLD", 0));
assert_success(unsetenv("SOME_OTHER_VARIABLE"));
putenv("PART3=\"!!\n\"");
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

@ -0,0 +1,4 @@
--set PART1 HELLO \
--set-default PART2 WORLD \
--unset SOME_OTHER_VARIABLE \
--set PART3 $'"!!\n"'

@ -0,0 +1,6 @@
PART1=HELLO
PART2=WORLD
PART3="!!
"
CWD=SUBST_CWD
SUBST_ARGV0

@ -0,0 +1,22 @@
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv, char **envp) {
for (char **env = envp; *env != 0; ++env) {
puts(*env);
}
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd))) {
printf("CWD=%s\n", cwd);
} else {
perror("getcwd() error");
return 1;
}
for (int i=0; i < argc; ++i) {
puts(argv[i]);
}
return 0;
}

@ -0,0 +1,6 @@
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv) {
return execv("/send/me/flags", argv);
}

@ -0,0 +1,14 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
int main(int argc, char **argv) {
putenv("==TEST1");
#error Illegal environment variable name `=` (cannot contain `=`)
assert_success(setenv("", "TEST2", 0));
#error Environment variable name can't be empty.
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

@ -0,0 +1,2 @@
--set "=" "TEST1" \
--set-default "" "TEST2"

@ -0,0 +1,26 @@
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
void set_env_prefix(char *env, char *sep, char *prefix) {
char *existing = getenv(env);
if (existing) {
char *val;
assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing));
assert_success(setenv(env, val, 1));
free(val);
} else {
assert_success(setenv(env, prefix, 1));
}
}
int main(int argc, char **argv) {
set_env_prefix("PATH", ":", "/usr/bin/");
set_env_prefix("PATH", ":", "/usr/local/bin/");
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

@ -0,0 +1,2 @@
--prefix PATH : /usr/bin/ \
--prefix PATH : /usr/local/bin/

@ -0,0 +1,3 @@
PATH=/usr/local/bin/:/usr/bin/
CWD=SUBST_CWD
SUBST_ARGV0

@ -0,0 +1,26 @@
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
void set_env_suffix(char *env, char *sep, char *suffix) {
char *existing = getenv(env);
if (existing) {
char *val;
assert_success(asprintf(&val, "%s%s%s", existing, sep, suffix));
assert_success(setenv(env, val, 1));
free(val);
} else {
assert_success(setenv(env, suffix, 1));
}
}
int main(int argc, char **argv) {
set_env_suffix("PATH", ":", "/usr/bin/");
set_env_suffix("PATH", ":", "/usr/local/bin/");
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

@ -0,0 +1,2 @@
--suffix PATH : /usr/bin/ \
--suffix PATH : /usr/local/bin/

@ -0,0 +1,3 @@
PATH=/usr/bin/:/usr/local/bin/
CWD=SUBST_CWD
SUBST_ARGV0

@ -688,6 +688,21 @@ with pkgs;
makeWrapper = makeSetupHook { deps = [ dieHook ]; substitutions = { shell = targetPackages.runtimeShell; }; }
../build-support/setup-hooks/make-wrapper.sh;
makeBinaryWrapper = let
f = { cc, sanitizers }: let
san = lib.concatMapStringsSep " " (s: "-fsanitize=${s}") sanitizers;
script = runCommand "make-binary-wrapper.sh" {} ''
substitute ${../build-support/setup-hooks/make-binary-wrapper.sh} $out \
--replace " @CC@ " " ${cc}/bin/cc ${san} "
'';
in
makeSetupHook { deps = [ dieHook ]; } script;
in
lib.makeOverridable f {
cc = stdenv.cc.cc;
sanitizers = [ "undefined" "address" ];
};
makeModulesClosure = { kernel, firmware, rootModules, allowMissing ? false }:
callPackage ../build-support/kernel/modules-closure.nix {
inherit kernel firmware rootModules allowMissing;

Loading…
Cancel
Save