To fix LD, replace sh with perl, which allows omitting spaces:
LD='perl -e exec"c99",@ARGV,"-lm" --'
The "Unterminated quoted string" wording of that error suggests a sh implementation derived from the Almquist shell such as FreeBSD's, NetBSD's, busybox' or dash found as sh on most Debian-derived systems, while bash (still used for sh on most other GNU systems and macOS) has "unexpected EOF while looking for matching". The behavior is similar, so quoting the Bash man page:
Parameter Expansion
[...]
Quote Removal
After the preceding expansions, all unquoted occurrences of the characters \, ', and " that did not result from one of the above expansions are removed.
Your ${LD} was "one of the above expansions", namely parameter expansion. So, when the expansion as shown by echo ${LD} produced sh -c 'c99 "$@" -lm', none of the remaining single or double quotes worked.
To illustrate this, I replaced the sh -c part, and ran the command LD="perl -E \$,=x;say@ARGV 'c99 \"\$@\" -lm'"; ${LD}, which printed 'c99x"$@"x-lm'. The remaining quotes are shown to be unprocessed, and the "x"s means the outer Bash shell was previously invoking the inner Dash shell with 5 arguments, instead of how you wanted to suppress word splitting using quotes to keep it 3 arguments.
You said you wanted to "defer changing it", so I used Perl as a workaround. It would be better to change it to an actual Bash function. So your
LD="sh -c 'c99 \"\$@\" -lm'"
${LD} $ld_opts $ldflags_common $ldflags "$@" -o $bin
should become:
my_ld() {
c99 "$@" -lm
}
my_ld $ld_opts $ldflags_common $ldflags "$@" -o $bin
LD=/path/to/your/wrapper-script. A function may also work but may not always be in scope (i.e. they're only available in the shell they're defined in. and child shells if exported).$ld_optsshould probably be an array and referenced as${ld_opts[@]}.