compiler_wrapper: Use syscall.exec on platforms other than Chrome OS

In crbug.com/1000863, it's reported that golang exec don't play well on
Chrome OS, portage sandbox to be exact. That's when we start to use
libc's exec. However, the wrapper is also used on non-Chrome OS
platforms, such as Android, and linking against libc has no benefit,
and might be the root cause of a recent bug.

This patch adds code to selectively use Golang's or libc's exec,
depending on the platform.

BUG=chromium:1000863
BUG=b:144783188
TEST=Build the wrapper locally with Chrome OS and Android configurations

Change-Id: Ifd6fa8223205536450b65f728060d7c5556d7619
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/2051669
Tested-by: Tiancong Wang <tcwang@google.com>
Commit-Queue: Tiancong Wang <tcwang@google.com>
Reviewed-by: George Burgess <gbiv@chromium.org>
diff --git a/compiler_wrapper/build.py b/compiler_wrapper/build.py
index 4257abf..037b940 100755
--- a/compiler_wrapper/build.py
+++ b/compiler_wrapper/build.py
@@ -39,10 +39,16 @@
       '-X',
       'main.Version=' + version,
   ]
+
+  # If the wrapper is intended for Chrome OS, we need to use libc's exec.
+  extra_args = []
+  if 'cros' in args.config:
+    extra_args = ['-tags', 'libc_exec']
+
   return [
       'go', 'build', '-o',
       os.path.abspath(args.output_file), '-ldflags', ' '.join(ldFlags)
-  ]
+  ] + extra_args
 
 
 def read_version(build_dir):
diff --git a/compiler_wrapper/env.go b/compiler_wrapper/env.go
index 4d83b17..56b3a91 100644
--- a/compiler_wrapper/env.go
+++ b/compiler_wrapper/env.go
@@ -71,7 +71,7 @@
 }
 
 func (env *processEnv) exec(cmd *command) error {
-	return libcExec(env, cmd)
+	return execCmd(env, cmd)
 }
 
 func (env *processEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
diff --git a/compiler_wrapper/go_exec.go b/compiler_wrapper/go_exec.go
new file mode 100644
index 0000000..2f2e5ad
--- /dev/null
+++ b/compiler_wrapper/go_exec.go
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// +build !libc_exec
+
+package main
+
+import (
+	"os/exec"
+	"syscall"
+)
+
+// Implement exec for users that don't need to dynamically link with glibc
+// See b/144783188 and libc_exec.go.
+
+func execCmd(env env, cmd *command) error {
+	execCmd := exec.Command(cmd.Path, cmd.Args...)
+	mergedEnv := mergeEnvValues(env.environ(), cmd.EnvUpdates)
+
+	ret := syscall.Exec(execCmd.Path, execCmd.Args, mergedEnv)
+	return newErrorwithSourceLocf("exec error: %v", ret)
+}
diff --git a/compiler_wrapper/libc_exec.go b/compiler_wrapper/libc_exec.go
index 268221d..f8db9d8 100644
--- a/compiler_wrapper/libc_exec.go
+++ b/compiler_wrapper/libc_exec.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// +build libc_exec
+
 package main
 
 // #include <errno.h>
@@ -26,7 +28,10 @@
 // LD_PRELOAD to work properly (e.g. gentoo sandbox).
 // Note that this changes the go binary to be a dynamically linked one.
 // See crbug.com/1000863 for details.
-func libcExec(env env, cmd *command) error {
+// To use this version of exec, please add '-tags libc_exec' when building Go binary.
+// Without the tags, libc_exec.go will be used.
+
+func execCmd(env env, cmd *command) error {
 	freeList := []unsafe.Pointer{}
 	defer func() {
 		for _, ptr := range freeList {