From 2b5a2d4a8e8e41a283767088443845913c3ab340 Mon Sep 17 00:00:00 2001 From: Tobias Bergkvist Date: Tue, 7 Dec 2021 01:50:38 +0100 Subject: [PATCH 1/3] Switch to embedding input arguments instead of generated C code in binary. --- .../setup-hooks/make-binary-wrapper.sh | 95 ++++++++++++++++--- 1 file changed, 84 insertions(+), 11 deletions(-) diff --git a/pkgs/build-support/setup-hooks/make-binary-wrapper.sh b/pkgs/build-support/setup-hooks/make-binary-wrapper.sh index fa02d59ef46..5080d0065aa 100644 --- a/pkgs/build-support/setup-hooks/make-binary-wrapper.sh +++ b/pkgs/build-support/setup-hooks/make-binary-wrapper.sh @@ -51,14 +51,14 @@ wrapProgramBinary() { makeBinaryWrapper "$hidden" "$prog" --inherit-argv0 "${@:2}" } -# Generate source code for the wrapper in such a way that the wrapper source code +# 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 makeBinaryWrapper makeDocumentedCWrapper() { local src docs src=$(makeCWrapper "$@") - docs=$(documentationString "$src") + docs=$(docstring "$@") printf '%s\n\n' "$src" printf '%s\n' "$docs" } @@ -66,7 +66,7 @@ makeDocumentedCWrapper() { # makeCWrapper EXECUTABLE ARGS # ARGS: same as makeBinaryWrapper makeCWrapper() { - local argv0 inherit_argv0 n params cmd main flagsBefore flags executable params length + 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=("$@") @@ -238,14 +238,6 @@ unsetEnv() { assertValidEnvName "$1" } -# Put the entire source code into const char* SOURCE_CODE to make it readable after compilation. -# documentationString SOURCE_CODE -documentationString() { - local docs - docs=$(escapeStringLiteral $'\n----------\n// This binary wrapper was compiled from the following generated C-code:\n'"$1"$'\n----------\n') - printf '%s' "const char * SOURCE_CODE = \"$docs\";" -} - # Makes it safe to insert STRING within quotes in a C String Literal. # escapeStringLiteral STRING escapeStringLiteral() { @@ -295,3 +287,84 @@ void set_env_suffix(char *env, char *sep, char *suffix) { } " } + +# 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 +} From 7cf1aa102a513ad7df9c6e6c0e57254b2f44efaf Mon Sep 17 00:00:00 2001 From: Tobias Bergkvist Date: Tue, 7 Dec 2021 17:42:54 +0100 Subject: [PATCH 2/3] Separate out indentation responsibility to indent4 in makeCWrapper using awk. Generated code no longer needs to worry about its own indent level in the output. --- .../setup-hooks/make-binary-wrapper.sh | 66 ++++++++++--------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/pkgs/build-support/setup-hooks/make-binary-wrapper.sh b/pkgs/build-support/setup-hooks/make-binary-wrapper.sh index 5080d0065aa..ec60f9ba722 100644 --- a/pkgs/build-support/setup-hooks/make-binary-wrapper.sh +++ b/pkgs/build-support/setup-hooks/make-binary-wrapper.sh @@ -76,82 +76,82 @@ makeCWrapper() { case $p in --set) cmd=$(setEnv "${params[n + 1]}" "${params[n + 2]}") - main="$main $cmd"$'\n' + main="$main$cmd"$'\n' n=$((n + 2)) - [ $n -ge "$length" ] && main="$main #error makeCWrapper: $p takes 2 arguments"$'\n' + [ $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' + 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' + [ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 2 arguments"$'\n' ;; --unset) cmd=$(unsetEnv "${params[n + 1]}") - main="$main $cmd"$'\n' + 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' + [ $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' + 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' + [ $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' + 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' + [ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 3 arguments"$'\n' ;; --chdir) cmd=$(changeDir "${params[n + 1]}") - main="$main $cmd"$'\n' + 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' + [ $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' + [ $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' + [ $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: Uknown argument ${p}"$'\n' + main="$main#error makeCWrapper: Uknown 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 "$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 " @@ -162,8 +162,8 @@ makeCWrapper() { [ -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' "$main" - printf '%s\n' "}" + printf '\n%s' "$(indent4 "$main")" + printf '\n%s\n' "}" } addFlags() { @@ -172,17 +172,17 @@ addFlags() { flags=("$@") for ((n = 0; n < ${#flags[*]}; n += 1)); do flag=$(escapeStringLiteral "${flags[$n]}") - result="$result ${var}[$((n+1))] = \"$flag\";"$'\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\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;" + 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 @@ -249,10 +249,16 @@ escapeStringLiteral() { 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.";; + *=*) 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 } From 32d566e1b9d9ff55ec5b013d5420419649780e0d Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Wed, 8 Dec 2021 18:59:38 +0200 Subject: [PATCH 3/3] wrapProgramBinary -> binaryWrapProgram --- doc/stdenv/stdenv.chapter.md | 4 ++-- pkgs/build-support/setup-hooks/make-binary-wrapper.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/stdenv/stdenv.chapter.md b/doc/stdenv/stdenv.chapter.md index 91e59bbce48..bd9d9e1e368 100644 --- a/doc/stdenv/stdenv.chapter.md +++ b/doc/stdenv/stdenv.chapter.md @@ -796,7 +796,7 @@ A setup-hook very similar to `makeWrapper`, only it creates a tiny _compiled_ wr Compiled wrappers generated by `makeBinaryWrapper` can be inspected with `less ` - by scrolling past the binary data you should be able to see the C code that generated the executable and there see the environment variables that were injected into the wrapper. -Similarly to `wrapProgram`, the `makeBinaryWrapper` setup-hook provides a `wrapProgramBinary` with similar command line arguments. +Similarly to `wrapProgram`, the `makeBinaryWrapper` setup-hook provides a `binaryWrapProgram` with similar command line arguments. ### `substitute` \ \ \ {#fun-substitute} @@ -875,7 +875,7 @@ Convenience function for `makeWrapper` that replaces `<\executable\>` with a wra 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. -### `wrapProgramBinary` \ \ {#fun-wrapProgramBinary} +### `binaryWrapProgram` \ \ {#fun-binaryWrapProgram} Convenience function for `makeBinaryWrapper` that replaces `<\executable\>` with a wrapper that executes the original program. It takes all the same arguments as `makeBinaryWrapper`, except for `--argv0`. diff --git a/pkgs/build-support/setup-hooks/make-binary-wrapper.sh b/pkgs/build-support/setup-hooks/make-binary-wrapper.sh index ec60f9ba722..d4d4163c4b7 100644 --- a/pkgs/build-support/setup-hooks/make-binary-wrapper.sh +++ b/pkgs/build-support/setup-hooks/make-binary-wrapper.sh @@ -34,8 +34,8 @@ makeBinaryWrapper() { makeDocumentedCWrapper "$1" "${@:3}" | cc -Os -x c -o "$2" - } -# Syntax: wrapProgramBinary -wrapProgramBinary() { +# Syntax: binaryWrapProgram +binaryWrapProgram() { local prog="$1" local hidden