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