xref: /aosp_15_r20/external/toolchain-utils/compiler_wrapper/libc_exec.go (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1*760c253cSXin Li// Copyright 2019 The ChromiumOS Authors
2*760c253cSXin Li// Use of this source code is governed by a BSD-style license that can be
3*760c253cSXin Li// found in the LICENSE file.
4*760c253cSXin Li
5*760c253cSXin Li//go:build libc_exec
6*760c253cSXin Li// +build libc_exec
7*760c253cSXin Li
8*760c253cSXin Lipackage main
9*760c253cSXin Li
10*760c253cSXin Li// #include <errno.h>
11*760c253cSXin Li// #include <stdio.h>
12*760c253cSXin Li// #include <stdlib.h>
13*760c253cSXin Li// #include <string.h>
14*760c253cSXin Li// #include <unistd.h>
15*760c253cSXin Li// #include <sys/types.h>
16*760c253cSXin Li// #include <sys/wait.h>
17*760c253cSXin Li//
18*760c253cSXin Li// int libc_exec(const char *pathname, char *const argv[], char *const envp[]) {
19*760c253cSXin Li//	// Since fork() brings us to one thread, we can only use async-signal-safe funcs below.
20*760c253cSXin Li//	pid_t pid = fork();
21*760c253cSXin Li//	if (pid == 0) {
22*760c253cSXin Li//		// crbug.com/1166017: we're (very rarely) getting ERESTARTSYS on some builders.
23*760c253cSXin Li//		// Documentation indicates that this is a bug in the kernel. Work around it by
24*760c253cSXin Li//		// retrying. 25 is an arbitrary retry number that Should Be Enough For Anyone(TM).
25*760c253cSXin Li//		int i = 0;
26*760c253cSXin Li//		for (; i < 25; i++) {
27*760c253cSXin Li//			execve(pathname, argv, envp);
28*760c253cSXin Li//			if (errno != 512) {
29*760c253cSXin Li//				break;
30*760c253cSXin Li//			}
31*760c253cSXin Li//			// Sleep a bit. Not sure if this helps, but if the condition we're seeing is
32*760c253cSXin Li//			// transient, it *hopefully* should. nanosleep isn't async-signal safe, so
33*760c253cSXin Li//			// we have to live with sleep()
34*760c253cSXin Li//			sleep(1);
35*760c253cSXin Li//		}
36*760c253cSXin Li//		fprintf(stderr, "exec failed (errno: %d)\n", errno);
37*760c253cSXin Li//		_exit(1);
38*760c253cSXin Li//	}
39*760c253cSXin Li//	if (pid < 0) {
40*760c253cSXin Li//		return errno;
41*760c253cSXin Li//	}
42*760c253cSXin Li//
43*760c253cSXin Li//	int wstatus;
44*760c253cSXin Li//	pid_t waited = waitpid(pid, &wstatus, 0);
45*760c253cSXin Li//	if (waited == -1) {
46*760c253cSXin Li//		return errno;
47*760c253cSXin Li//	}
48*760c253cSXin Li//	exit(WEXITSTATUS(wstatus));
49*760c253cSXin Li//}
50*760c253cSXin Liimport "C"
51*760c253cSXin Liimport (
52*760c253cSXin Li	"os/exec"
53*760c253cSXin Li	"unsafe"
54*760c253cSXin Li)
55*760c253cSXin Li
56*760c253cSXin Li// Replacement for syscall.Execve that uses the libc.
57*760c253cSXin Li// This allows tools that rely on intercepting syscalls via
58*760c253cSXin Li// LD_PRELOAD to work properly (e.g. gentoo sandbox).
59*760c253cSXin Li// Note that this changes the go binary to be a dynamically linked one.
60*760c253cSXin Li// See crbug.com/1000863 for details.
61*760c253cSXin Li// To use this version of exec, please add '-tags libc_exec' when building Go binary.
62*760c253cSXin Li// Without the tags, libc_exec.go will not be used.
63*760c253cSXin Li
64*760c253cSXin Lifunc execCmd(env env, cmd *command) error {
65*760c253cSXin Li	freeList := []unsafe.Pointer{}
66*760c253cSXin Li	defer func() {
67*760c253cSXin Li		for _, ptr := range freeList {
68*760c253cSXin Li			C.free(ptr)
69*760c253cSXin Li		}
70*760c253cSXin Li	}()
71*760c253cSXin Li
72*760c253cSXin Li	goStrToC := func(goStr string) *C.char {
73*760c253cSXin Li		cstr := C.CString(goStr)
74*760c253cSXin Li		freeList = append(freeList, unsafe.Pointer(cstr))
75*760c253cSXin Li		return cstr
76*760c253cSXin Li	}
77*760c253cSXin Li
78*760c253cSXin Li	goSliceToC := func(goSlice []string) **C.char {
79*760c253cSXin Li		// len(goSlice)+1 as the c array needs to be null terminated.
80*760c253cSXin Li		cArray := C.malloc(C.size_t(len(goSlice)+1) * C.size_t(unsafe.Sizeof(uintptr(0))))
81*760c253cSXin Li		freeList = append(freeList, cArray)
82*760c253cSXin Li
83*760c253cSXin Li		// Convert the C array to a Go Array so we can index it.
84*760c253cSXin Li		// Note: Storing pointers to the c heap in go pointer types is ok
85*760c253cSXin Li		// (see https://golang.org/cmd/cgo/).
86*760c253cSXin Li		cArrayForIndex := (*[1<<30 - 1]*C.char)(cArray)
87*760c253cSXin Li		for i, str := range goSlice {
88*760c253cSXin Li			cArrayForIndex[i] = goStrToC(str)
89*760c253cSXin Li		}
90*760c253cSXin Li		cArrayForIndex[len(goSlice)] = nil
91*760c253cSXin Li
92*760c253cSXin Li		return (**C.char)(cArray)
93*760c253cSXin Li	}
94*760c253cSXin Li
95*760c253cSXin Li	execCmd := exec.Command(cmd.Path, cmd.Args...)
96*760c253cSXin Li	mergedEnv := mergeEnvValues(env.environ(), cmd.EnvUpdates)
97*760c253cSXin Li	if errno := C.libc_exec(goStrToC(execCmd.Path), goSliceToC(execCmd.Args), goSliceToC(mergedEnv)); errno != 0 {
98*760c253cSXin Li		return newErrorwithSourceLocf("exec error: %d", errno)
99*760c253cSXin Li	}
100*760c253cSXin Li
101*760c253cSXin Li	return nil
102*760c253cSXin Li}
103