1
1
mirror of https://github.com/rui314/mold.git synced 2024-12-25 17:34:02 +03:00
mold/subprocess.cc

264 lines
6.7 KiB
C++
Raw Normal View History

2021-01-09 15:21:45 +03:00
#include "mold.h"
2021-01-15 13:56:52 +03:00
#include <openssl/sha.h>
2021-01-27 09:28:41 +03:00
#include <sys/signal.h>
2021-01-09 15:21:45 +03:00
#include <sys/socket.h>
2021-01-09 16:57:43 +03:00
#include <sys/stat.h>
#include <sys/types.h>
2021-01-09 15:21:45 +03:00
#include <sys/un.h>
2021-01-27 09:20:46 +03:00
#include <sys/wait.h>
2021-01-09 15:21:45 +03:00
#include <unistd.h>
#define DAEMON_TIMEOUT 30
// Exiting from a program with large memory usage is slow --
// it may take a few hundred milliseconds. To hide the latency,
// we fork a child and let it do the actual linking work.
std::function<void()> fork_child() {
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(1);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
if (pid > 0) {
// Parent
close(pipefd[1]);
2021-01-27 09:20:46 +03:00
if (read(pipefd[0], (char[1]){}, 1) == 1)
_exit(0);
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status))
_exit(WEXITSTATUS(status));
if (WIFSIGNALED(status))
2021-01-27 09:28:41 +03:00
raise(WTERMSIG(status));
2021-01-27 09:20:46 +03:00
_exit(1);
2021-01-09 15:21:45 +03:00
}
// Child
close(pipefd[0]);
return [=]() { write(pipefd[1], (char []){1}, 1); };
}
2021-01-15 18:38:38 +03:00
static std::string base64(u8 *data, u64 size) {
2021-01-15 13:48:44 +03:00
static const char chars[] =
2021-01-09 15:30:41 +03:00
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
2021-01-09 15:21:45 +03:00
2021-01-15 18:38:38 +03:00
std::ostringstream out;
2021-01-15 13:48:44 +03:00
auto encode = [&](u32 x) {
2021-01-15 18:38:38 +03:00
out << chars[x & 0b111111]
<< chars[(x >> 6) & 0b111111]
<< chars[(x >> 12) & 0b111111]
<< chars[(x >> 18) & 0b111111];
2021-01-15 13:48:44 +03:00
};
2021-01-24 06:01:43 +03:00
i64 i = 0;
2021-01-15 18:38:38 +03:00
for (; i < size - 3; i += 3)
encode((data[i + 2] << 16) | (data[i + 1] << 8) | data[i]);
if (i == size - 1)
encode(data[i]);
else if (i == size - 2)
encode((data[i + 1] << 8) | data[i]);
return out.str();
2021-01-09 15:21:45 +03:00
}
2021-01-15 18:38:38 +03:00
static std::string compute_sha256(char **argv) {
2021-03-29 19:49:09 +03:00
SHA256_CTX sha;
SHA256_Init(&sha);
2021-01-15 13:56:52 +03:00
2021-01-24 06:01:43 +03:00
for (i64 i = 0; argv[i]; i++)
2021-01-15 13:48:44 +03:00
if (!strcmp(argv[i], "-preload") && !strcmp(argv[i], "--preload"))
2021-03-29 19:49:09 +03:00
SHA256_Update(&sha, argv[i], strlen(argv[i]) + 1);
2021-01-15 13:48:44 +03:00
2021-01-15 18:38:38 +03:00
u8 digest[SHA256_SIZE];
2021-03-29 19:49:09 +03:00
SHA256_Final(digest, &sha);
2021-01-15 18:38:38 +03:00
return base64(digest, SHA256_SIZE);
2021-01-15 13:48:44 +03:00
}
2021-03-29 14:29:57 +03:00
template <typename E>
static void send_fd(Context<E> &ctx, i64 conn, i64 fd) {
2021-01-09 15:21:45 +03:00
struct iovec iov;
char dummy = '1';
iov.iov_base = &dummy;
iov.iov_len = 1;
2021-01-09 17:13:38 +03:00
struct msghdr msg = {};
2021-01-09 15:21:45 +03:00
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = buf;
msg.msg_controllen = CMSG_LEN(sizeof(int));
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*(int *)CMSG_DATA(cmsg) = fd;
if (sendmsg(conn, &msg, 0) == -1)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "sendmsg failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
}
2021-03-29 14:29:57 +03:00
template <typename E>
static i64 recv_fd(Context<E> &ctx, i64 conn) {
2021-01-09 15:21:45 +03:00
struct iovec iov;
char buf[1];
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
2021-01-09 17:13:38 +03:00
struct msghdr msg = {};
2021-01-09 15:21:45 +03:00
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char cmsgbuf[CMSG_SPACE(sizeof(int))];
msg.msg_control = (caddr_t)cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
2021-01-24 06:01:43 +03:00
i64 len = recvmsg(conn, &msg, 0);
2021-01-09 15:21:45 +03:00
if (len <= 0)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "recvmsg failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
struct cmsghdr *cmsg;
cmsg = CMSG_FIRSTHDR(&msg);
return *(int *)CMSG_DATA(cmsg);
}
2021-03-29 14:29:57 +03:00
template <typename E>
bool resume_daemon(Context<E> &ctx, char **argv, i64 *code) {
2021-01-24 06:01:43 +03:00
i64 conn = socket(AF_UNIX, SOCK_STREAM, 0);
2021-01-09 15:21:45 +03:00
if (conn == -1)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "socket failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
2021-01-15 18:38:38 +03:00
std::string path = "/tmp/mold-" + compute_sha256(argv);
2021-01-09 15:21:45 +03:00
2021-01-09 17:13:38 +03:00
struct sockaddr_un name = {};
2021-01-09 15:21:45 +03:00
name.sun_family = AF_UNIX;
memcpy(name.sun_path, path.data(), path.size());
if (connect(conn, (struct sockaddr *)&name, sizeof(name)) != 0) {
close(conn);
return false;
}
2021-03-29 10:48:23 +03:00
send_fd(ctx, conn, STDOUT_FILENO);
send_fd(ctx, conn, STDERR_FILENO);
2021-01-24 06:01:43 +03:00
i64 r = read(conn, (char[1]){}, 1);
2021-01-09 15:21:45 +03:00
*code = (r != 1);
return true;
}
2021-03-29 14:29:57 +03:00
template <typename E>
void daemonize(Context<E> &ctx, char **argv,
2021-03-29 10:48:23 +03:00
std::function<void()> *wait_for_client,
2021-01-09 15:21:45 +03:00
std::function<void()> *on_complete) {
if (daemon(1, 0) == -1)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "daemon failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
2021-01-24 06:01:43 +03:00
i64 sock = socket(AF_UNIX, SOCK_STREAM, 0);
2021-01-09 15:21:45 +03:00
if (sock == -1)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "socket failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
2021-01-15 18:38:38 +03:00
socket_tmpfile = strdup(("/tmp/mold-" + compute_sha256(argv)).c_str());
2021-01-09 15:21:45 +03:00
2021-01-09 17:13:38 +03:00
struct sockaddr_un name = {};
2021-01-09 15:21:45 +03:00
name.sun_family = AF_UNIX;
strcpy(name.sun_path, socket_tmpfile);
2021-01-09 16:57:43 +03:00
u32 orig_mask = umask(0177);
2021-01-09 15:21:45 +03:00
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1) {
if (errno != EADDRINUSE)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "bind failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
unlink(socket_tmpfile);
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "bind failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
}
2021-01-09 16:57:43 +03:00
umask(orig_mask);
2021-01-09 15:21:45 +03:00
if (listen(sock, 0) == -1)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "listen failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
2021-01-24 06:01:43 +03:00
static i64 conn = -1;
2021-01-09 15:21:45 +03:00
2021-03-29 10:48:23 +03:00
*wait_for_client = [=, &ctx]() {
2021-01-09 15:21:45 +03:00
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
struct timeval tv;
tv.tv_sec = DAEMON_TIMEOUT;
tv.tv_usec = 0;
2021-01-24 06:01:43 +03:00
i64 res = select(sock + 1, &rfds, NULL, NULL, &tv);
2021-01-09 15:21:45 +03:00
if (res == -1)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "select failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
if (res == 0) {
std::cout << "timeout\n";
exit(0);
}
conn = accept(sock, NULL, NULL);
if (conn == -1)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "accept failed: " << strerror(errno);
2021-01-09 15:21:45 +03:00
unlink(socket_tmpfile);
2021-01-09 15:33:46 +03:00
2021-03-29 10:48:23 +03:00
dup2(recv_fd(ctx, conn), STDOUT_FILENO);
dup2(recv_fd(ctx, conn), STDERR_FILENO);
2021-01-09 15:21:45 +03:00
};
*on_complete = [=]() { write(conn, (char []){1}, 1); };
}
2021-03-25 10:03:23 +03:00
2021-03-29 14:29:57 +03:00
template <typename E>
static std::string get_self_path(Context<E> &ctx) {
2021-03-25 10:03:23 +03:00
char buf[4096];
i64 n = readlink("/proc/self/exe", buf, sizeof(buf));
if (n == -1)
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "readlink(\"/proc/self/exe\") failed: " << strerror(errno);
2021-03-25 10:03:23 +03:00
if (n == sizeof(buf))
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "readlink: path too long";
2021-03-25 10:03:23 +03:00
return buf;
}
2021-03-29 14:29:57 +03:00
template <typename E>
2021-03-25 10:03:23 +03:00
[[noreturn]]
2021-03-29 14:29:57 +03:00
void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
2021-03-25 10:03:23 +03:00
std::string_view arg1 = argv[1];
assert(arg1 == "-run" || arg1 == "--run");
if (!argv[2])
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "-run: argument missing";
2021-03-25 10:03:23 +03:00
2021-03-29 10:48:23 +03:00
std::string self = get_self_path(ctx);
2021-03-25 10:03:23 +03:00
std::string env = "LD_PRELOAD=" + path_dirname(self) + "/mold-wrapper.so";
putenv(strdup(env.c_str()));
2021-03-26 16:12:07 +03:00
putenv(strdup(("MOLD_REAL_PATH=" + self).c_str()));
2021-03-25 10:03:23 +03:00
execvp(argv[2], argv + 2);
2021-03-29 10:48:23 +03:00
Fatal(ctx) << "execvp failed: " << strerror(errno);
2021-03-25 10:03:23 +03:00
}
2021-03-29 14:29:57 +03:00
2021-04-05 16:19:51 +03:00
#define INSTANTIATE(E) \
template bool resume_daemon(Context<E> &, char **, i64 *); \
template void daemonize(Context<E> &, char **, \
std::function<void()> *, \
std::function<void()> *); \
template void process_run_subcommand(Context<E> &, int, char **)
INSTANTIATE(X86_64);
INSTANTIATE(I386);