Diff options
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Rename the release script to build-release.sh
Named like build-development.sh, so the two sort together in tools/.
Diffstat (limited to 'tools/build-release.sh')
-rwxr-xr-xtools/build-release.sh94
1 file changed, 94 insertions, 0 deletions
diff --git a/tools/build-release.sh b/tools/build-release.sh
new file mode 100755
index 0000000..ade979c
--- /dev/null
+++ b/tools/build-release.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+# Build cgit for a release with Linux hardening flags, which are ELF and GCC or
+# Clang specific. The flags add a stack protector, fortified libc calls, a
+# position independent executable, and full RELRO. By default Lua is pinned off
+# so the binary needs no Lua at runtime, and running it as
+# ./tools/build-release.sh lua links in the backend behind the "lua:" filter
+# prefix instead, which needs a Lua dev package installed.
+
+set -eu
+
+if test "$#" -gt 1
+then
+ echo "usage: $0 [lua]" >&2
+ exit 2
+fi
+case "${1:-}" in
+"")
+ set -- NO_LUA=1
+ ;;
+lua)
+ set --
+ ;;
+*)
+ echo "$0: unknown argument \"$1\", expected \"lua\" or nothing" >&2
+ exit 2
+ ;;
+esac
+
+# Every make target below is written relative to the repository root.
+cd "$(dirname "$0")/.."
+
+CC=${CC:-cc}
+
+# Not every hardening flag exists on every toolchain, and an unknown one would
+# fail the build rather than be ignored, so the ones that might be missing are
+# compiled before they are used. The argument is left unquoted so a caller can
+# probe several flags at once.
+supports() {
+ printf 'int main(void){return 0;}\n' >"$probe.c"
+ $CC $1 -o "$probe.out" "$probe.c" >/dev/null 2>&1
+}
+
+# The template is spelled out rather than passed with -t, whose handling of a
+# prefix differs between the GNU and BSD versions.
+probe=$(mktemp "${TMPDIR:-/tmp}/cgit-probe.XXXXXX")
+trap 'rm -f "$probe" "$probe.c" "$probe.out"' EXIT
+
+CFLAGS="-O2 -g -Wall -fstack-protector-strong -fPIE -fno-plt"
+
+# Level 3 adds the bounds checks level 2 could not prove, and needs GCC 12 or
+# Clang 15. Fall back rather than lose fortification altogether.
+if supports "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -O2"
+then
+ CFLAGS="$CFLAGS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"
+else
+ CFLAGS="$CFLAGS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2"
+fi
+
+# Without this a large stack frame can step over a guard page rather than
+# touch it.
+if supports "-fstack-clash-protection"
+then
+ CFLAGS="$CFLAGS -fstack-clash-protection"
+fi
+
+# The debug info otherwise records the absolute build directory, so the same
+# sources built in two checkouts would differ.
+if supports "-ffile-prefix-map=/x=."
+then
+ CFLAGS="$CFLAGS -ffile-prefix-map=$PWD=."
+fi
+
+LDFLAGS="-pie -Wl,-z,relro,-z,now -Wl,-z,noexecstack"
+
+# These reach only the cgit objects, so git's own sources are not held to them.
+# -Wformat-security is an error because a non-literal format with no arguments
+# is never intentional. -Wformat-nonliteral would catch the module-link shape,
+# a non-literal that does take arguments, but it also fires on va_list
+# forwarding and on local format constants, so it is left to manual runs when
+# touching anything that formats.
+#
+# make cgit CGIT_EXTRA_CFLAGS=-Wformat-nonliteral
+#
+CGIT_EXTRA_CFLAGS="-Wformat -Wformat-security -Werror=format-security"
+
+# The build starts from clean so the bundled git objects pick up the same
+# flags, and cleanall rather than clean because only cleanall descends into
+# vendor/git. CC goes on the command line because git's Makefile sets it,
+# which would override the one from the environment the flags were probed
+# with.
+make cleanall
+exec make "$@" CC="$CC" CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" \
+ CGIT_EXTRA_CFLAGS="$CGIT_EXTRA_CFLAGS"