/*
* GRUB -- GRand Unified Bootloader
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <wait.h>
#include <inttypes.h>
static void
do_spawn_file_actions(const char *prog, posix_spawn_file_actions_t *file_actionsp,
int fd0, int dup_src, int dup_dest);
/*
* Wrap a gcc invocation used to compile a .S file and transform it into
* a pipeline of gcc preprocessing + gas assembly
*/
int
main(int argc, char **argv, char **envp)
{
unsigned long allocsz = (argc + 3) * sizeof (char *);
int i, j, ret;
const char *compiler_path, *assembler_path;
char **compiler_argv, **assembler_argv, *output_file = NULL;
char *debug_mode = NULL;
int output_mode = 0;
int pipefds[2];
int added_dash_E = 0;
pid_t cpid = 0, apid = 0;
posix_spawn_file_actions_t asm_file_actions, cc_file_actions;
if (argc < 3) {
fprintf(stderr, "Syntax:\n%s compiler_path "
"assembler_path {Compiler command line}\n", argv[0]);
exit(1);
}
/* argv[1] is the path to the compiler. */
/* argv[2] is the path to the assembler */
compiler_path = argv[1];
assembler_path = argv[2];
argv = &argv[3];
argc -= 3;
if (allocsz >= SIZE_MAX) {
fprintf(stderr, "Too many arguments passed to %s.\n", argv[0]);
return (1);
}
compiler_argv = malloc((size_t)allocsz); /* Parfait_ALLOW integer-overflow */
j = 0;
compiler_argv[j++] = strdup(compiler_path);
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "-c") == 0) {
added_dash_E = 1;
compiler_argv[j++] = strdup("-E");
} else if (strcmp(argv[i], "-o") == 0) {
/* Look ahead to next arg */
if ((i + 1) == argc || output_file != NULL) {
fprintf(stderr, "Invalid command line.\n");
exit(1);
}
output_file = argv[i + 1];
i++;
} else {
compiler_argv[j++] = strdup(argv[i]);
if (strcmp(argv[i], "-m32") == 0)
output_mode = 32;
else if (strcmp(argv[i], "-m64") == 0)
output_mode = 64;
else if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "-gstabs") == 0)
debug_mode = "--gdwarf2";
else if (strcmp(argv[i], "-gstabs") == 0)
debug_mode = "-gstabs";
else if (strcmp(argv[i], "-gstabs+") == 0)
debug_mode = "-gstabs+";
}
}
if (added_dash_E == 0)
compiler_argv[j++] = strdup("-E");
compiler_argv[j] = 0;
j = 0;
assembler_argv = malloc(10 * sizeof(char *));
assembler_argv[j++] = strdup(assembler_path);
if (output_mode > 0)
assembler_argv[j++] = strdup((output_mode == 32) ? "--32" : "--64");
if (debug_mode != NULL)
assembler_argv[j++] = strdup(debug_mode);
if (output_file == NULL) {
fprintf(stderr, "Unknown output file.\n");
exit(1);
}
assembler_argv[j++] = strdup("--traditional-format");
assembler_argv[j++] = strdup("-o");
assembler_argv[j++] = strdup(output_file);
assembler_argv[j] = NULL;
/* Create the stdout -> stdin pipe */
if (pipe(pipefds) < 0) {
fprintf(stderr, "Error creating pipe(): %s", strerror(errno));
exit(1);
}
do_spawn_file_actions(assembler_path, &asm_file_actions, pipefds[0], pipefds[1], 0);
do_spawn_file_actions(compiler_path, &cc_file_actions, pipefds[1], pipefds[0], 1);
/* spawn the assembler with the new command line */
if (posix_spawnp(&apid, assembler_path, &asm_file_actions, NULL,
assembler_argv, envp) < 0) {
fprintf(stderr, "posix_spawnp() failed: %s\n", strerror(errno));
exit(1);
}
/* spawn the compiler: */
ret = (posix_spawnp(&cpid, compiler_path, &cc_file_actions, NULL,
compiler_argv, envp) < 0) ? 1 : 0;
/* Close pipe fds */
(void) close(pipefds[0]);
(void) close(pipefds[1]);
/* We MUST wait for the processes to complete! */
if (ret == 0) {
waitid(P_PID, cpid, NULL, WEXITED);
waitid(P_PID, apid, NULL, WEXITED);
}
return (ret);
}
static void
do_spawn_file_actions(const char *prog, posix_spawn_file_actions_t *file_actionsp,
int fd0, int dup_src, int dup_dest)
{
if (posix_spawn_file_actions_init(file_actionsp) < 0) {
fprintf(stderr, "Error initting file actions for %s\n", prog);
exit(1);
}
if (posix_spawn_file_actions_adddup2(file_actionsp, dup_src, dup_dest) < 0) {
fprintf(stderr, "Error setting dup2 file action for fd%d -> fd%d for %s\n", dup_src, dup_dest, prog);
exit(1);
}
if (posix_spawn_file_actions_addclose(file_actionsp, fd0) < 0) {
fprintf(stderr, "Error setting close file action for fd%d for %s\n", fd0, prog);
exit(1);
}
if (posix_spawn_file_actions_addclose(file_actionsp, dup_src) < 0) {
fprintf(stderr, "Error setting close file action for fd%d for %s\n", dup_src, prog);
exit(1);
}
}