kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

as.c (9623B)


      1 #include <kit/compile.h>
      2 #include <kit/core.h>
      3 #include <kit/preprocess.h>
      4 #include <stdint.h>
      5 #include <string.h>
      6 
      7 #include "cflags.h"
      8 #include "driver.h"
      9 
     10 /* `kit as` — standalone assembler. Reads a single text source, writes a
     11  * relocatable object via a KitCompileSession. `.S` inputs are
     12  * preprocessed first via kit_cpp_preprocess; `.s` inputs are not. The
     13  * accepted input is a GAS subset (AT&T syntax on x86). */
     14 
     15 #define AS_TOOL "as"
     16 
     17 typedef struct AsOptions {
     18   int debug_info;          /* -g                       */
     19   const char* output_path; /* -o                       */
     20   const char* source;      /* single positional input  */
     21   KitTargetSpec target;    /* -target TRIPLE; host default */
     22 } AsOptions;
     23 
     24 static void as_usage(void) {
     25   driver_errf(AS_TOOL, "%.*s",
     26               KIT_SLICE_ARG(KIT_SLICE_LIT(
     27                   "usage: kit as [-g] [-target TRIPLE] -o out.o input.{s,S}\n"
     28                   "       kit as --help    for full option reference")));
     29 }
     30 
     31 void driver_help_as(void) {
     32   driver_printf(
     33       "%.*s",
     34       KIT_SLICE_ARG(KIT_SLICE_LIT(
     35           "kit as — standalone assembler\n"
     36           "\n"
     37           "USAGE\n"
     38           "  kit as [options] -o OUT.o INPUT.s\n"
     39           "  kit as [options] -o OUT.o INPUT.S\n"
     40           "  kit as [options] -o OUT.o -       (read raw assembly from stdin)\n"
     41           "\n"
     42           "DESCRIPTION\n"
     43           "  Reads a single text source written in a GAS subset (AT&T syntax "
     44           "on\n"
     45           "  x86, standard mnemonics on aarch64/riscv64) and writes a "
     46           "relocatable\n"
     47           "  object via the same back-end the C compiler emits to. INPUT.S is "
     48           "run\n"
     49           "  through the C preprocessor first; INPUT.s is assembled directly.\n"
     50           "  Exactly one positional input is required; -o is required.\n"
     51           "\n"
     52           "OPTIONS\n"
     53           "  -o PATH           Output object path (required)\n"
     54           "  -g                Emit DWARF debug info from .file/.loc "
     55           "directives\n"
     56           "  -I DIR, -IDIR     Add a user include directory for INPUT.S\n"
     57           "  -isystem DIR      Add a system include directory for INPUT.S\n"
     58           "  -D NAME[=VALUE]   Define a preprocessor macro for INPUT.S\n"
     59           "  -U NAME           Undefine a preprocessor macro for INPUT.S\n"
     60           "  -target TRIPLE    Cross-assemble target (default: host)\n"
     61           "  -h, --help        Show this help and exit\n"
     62           "  --version         Show Kit version and exit\n"
     63           "\n"
     64           "TARGETS AND INPUT LIMITS\n"
     65           "  Canonical assembler examples are aarch64-none-elf,\n"
     66           "  x86_64-linux-gnu, and riscv64-none-elf. The release also accepts\n"
     67           "  the corresponding hosted/freestanding triples documented by\n"
     68           "  `kit cc --help`. A literal `-` reads raw (non-preprocessed)\n"
     69           "  assembly from stdin and uses <stdin> in diagnostics. Use --\n"
     70           "  before a leading-dash path.\n"
     71           "\n"
     72           "EXAMPLES\n"
     73           "  kit as -target aarch64-none-elf -g -o start.o start.s\n"
     74           "  kit as -target x86_64-linux-gnu -DDEBUG=1 -I include \\\n"
     75           "    -o entry.o entry.S\n"
     76           "\n"
     77           "  printf '.text\\n.globl entry\\nentry:\\n  ret\\n' | \\\n"
     78           "    kit as -target x86_64-linux-gnu -o entry.o -\n"
     79           "\n"
     80           "EXIT CODES\n"
     81           "  0   success           1   assemble error           2   bad "
     82           "usage\n")));
     83 }
     84 
     85 /* Returns 0 on success; non-zero on bad args (already reported). */
     86 static int as_parse(int argc, char** argv, AsOptions* o, DriverEnv* env,
     87                     DriverCflags* cf, DriverTargetFeatures* tf) {
     88   int i;
     89   int options = 1;
     90 
     91   o->target = driver_host_target();
     92 
     93   for (i = 1; i < argc; ++i) {
     94     const char* a = argv[i];
     95     int r;
     96 
     97     if (options && driver_streq(a, "--")) {
     98       options = 0;
     99       continue;
    100     }
    101     if (!options || driver_streq(a, "-")) {
    102       if (o->source) {
    103         driver_errf(AS_TOOL, "multiple inputs not supported");
    104         return 1;
    105       }
    106       o->source = a;
    107       continue;
    108     }
    109 
    110     r = driver_cflags_try_consume(cf, env, AS_TOOL, argc, argv, &i);
    111     if (r < 0) return 1;
    112     if (r > 0) continue;
    113 
    114     if (driver_streq(a, "-g")) {
    115       o->debug_info = 1;
    116       continue;
    117     }
    118 
    119     if (driver_streq(a, "-o")) {
    120       if (++i >= argc) {
    121         driver_errf(AS_TOOL, "-o requires an argument");
    122         return 1;
    123       }
    124       o->output_path = argv[i];
    125       continue;
    126     }
    127 
    128     if (driver_streq(a, "-target")) {
    129       if (++i >= argc) {
    130         driver_errf(AS_TOOL, "-target requires an argument");
    131         return 1;
    132       }
    133       if (driver_target_from_triple(argv[i], &o->target) != 0) {
    134         driver_err_unknown_target(AS_TOOL, argv[i]);
    135         return 1;
    136       }
    137       continue;
    138     }
    139 
    140     {
    141       int tr =
    142           driver_target_features_try_consume(tf, env, AS_TOOL, argc, argv, &i);
    143       if (tr < 0) return 1;
    144       if (tr > 0) continue;
    145     }
    146 
    147     if (a[0] == '-' && a[1] != '\0') {
    148       driver_errf(AS_TOOL, "unknown flag: %.*s",
    149                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    150       return 1;
    151     }
    152 
    153     if (o->source) {
    154       driver_errf(AS_TOOL, "multiple inputs not supported");
    155       return 1;
    156     }
    157     o->source = a;
    158   }
    159 
    160   if (!o->source) {
    161     driver_errf(AS_TOOL, "no input file");
    162     as_usage();
    163     return 1;
    164   }
    165   if (!o->output_path) {
    166     driver_errf(AS_TOOL, "-o is required");
    167     return 1;
    168   }
    169   return 0;
    170 }
    171 
    172 int driver_as(int argc, char** argv) {
    173   DriverEnv env;
    174   AsOptions o = {0};
    175   DriverCflags cf = {0};
    176   DriverTargetFeatures tf = {0};
    177   KitContext ctx;
    178   KitPreprocessOptions pp;
    179   KitTarget* target = NULL;
    180   KitCompiler* compiler = NULL;
    181   KitWriter* writer = NULL;
    182   KitWriter* pp_writer = NULL;
    183   KitFileData src = {0};
    184   uint8_t* stdin_data = NULL;
    185   size_t stdin_len = 0;
    186   KitSlice input;
    187   KitSlice asm_input;
    188   KitAsmCompileOptions copts;
    189   KitAsmCompileOptions zero = {0};
    190   const uint8_t* pp_data;
    191   size_t pp_len;
    192   int rc = 1;
    193   int loaded = 0;
    194 
    195   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
    196     driver_help_as();
    197     return 0;
    198   }
    199 
    200   driver_env_init(&env);
    201 
    202   if (driver_cflags_init(&cf, &env, argc) != 0 ||
    203       driver_target_features_init(&tf, &env, argc) != 0) {
    204     driver_errf(AS_TOOL, "out of memory");
    205     driver_target_features_fini(&tf, &env);
    206     driver_cflags_fini(&cf, &env);
    207     driver_env_fini(&env);
    208     return 2;
    209   }
    210 
    211   if (as_parse(argc, argv, &o, &env, &cf, &tf) != 0) {
    212     driver_target_features_fini(&tf, &env);
    213     driver_cflags_fini(&cf, &env);
    214     driver_env_fini(&env);
    215     return 2;
    216   }
    217   driver_cflags_fill_pp(&cf, &pp);
    218 
    219   ctx = driver_env_to_context(&env);
    220 
    221   if (driver_streq(o.source, "-")) {
    222     if (!driver_read_stdin(&env, &stdin_data, &stdin_len)) {
    223       driver_errf(AS_TOOL, "failed to read stdin");
    224       goto out;
    225     }
    226     input.data = stdin_data;
    227     input.len = stdin_len;
    228   } else {
    229     if (ctx.file_io->read_all(ctx.file_io->user, o.source, &src) != KIT_OK) {
    230       driver_errf(AS_TOOL, "failed to read: %.*s",
    231                   KIT_SLICE_ARG(kit_slice_cstr(o.source)));
    232       goto out;
    233     }
    234     loaded = 1;
    235     input.data = src.data;
    236     input.len = src.size;
    237   }
    238 
    239   if (ctx.file_io->open_writer(ctx.file_io->user, o.output_path, &writer) !=
    240       KIT_OK) {
    241     driver_errf(AS_TOOL, "failed to open output: %.*s",
    242                 KIT_SLICE_ARG(kit_slice_cstr(o.output_path)));
    243     goto out;
    244   }
    245 
    246   if (driver_target_new(&ctx, o.target, &tf, AS_TOOL, &target) != KIT_OK ||
    247       driver_compiler_new(target, &ctx, &compiler) != KIT_OK) {
    248     driver_errf(AS_TOOL, "failed to initialize compiler");
    249     goto out;
    250   }
    251 
    252   copts = zero;
    253   copts.code.debug_info = o.debug_info;
    254 
    255   asm_input = input;
    256 
    257   if (driver_has_suffix(o.source, ".S")) {
    258     if (kit_writer_mem(ctx.heap, &pp_writer) != KIT_OK) {
    259       driver_errf(AS_TOOL, "out of memory");
    260       goto out;
    261     }
    262     if (kit_cpp_preprocess(compiler, &pp, kit_slice_cstr(o.source), &input,
    263                            pp_writer) != KIT_OK)
    264       goto out;
    265     if (kit_writer_status(pp_writer) != KIT_OK) {
    266       driver_errf(AS_TOOL, "failed to preprocess: %.*s",
    267                   KIT_SLICE_ARG(kit_slice_cstr(o.source)));
    268       goto out;
    269     }
    270     pp_data = kit_writer_mem_bytes(pp_writer, &pp_len);
    271     asm_input.data = pp_data;
    272     asm_input.len = pp_len;
    273   }
    274 
    275   {
    276     KitCompileSessionOptions sopts;
    277     KitCompileSession* session = NULL;
    278     KitSourceInput sin;
    279     KitObjBuilder* ob = NULL;
    280     KitStatus st;
    281     memset(&sopts, 0, sizeof(sopts));
    282     sopts.lang = KIT_LANG_ASM;
    283     sopts.compile.code = copts.code;
    284     sopts.compile.diagnostics = copts.diagnostics;
    285     sopts.compile.language_options = &copts;
    286     memset(&sin, 0, sizeof(sin));
    287     sin.name =
    288         kit_slice_cstr(driver_streq(o.source, "-") ? "<stdin>" : o.source);
    289     sin.bytes = asm_input;
    290     sin.lang = KIT_LANG_ASM;
    291     st = kit_compile_session_new(compiler, &sopts, &session);
    292     if (st == KIT_OK) st = kit_compile_session_compile(session, &sin, &ob);
    293     if (st == KIT_OK) st = kit_obj_builder_emit(ob, writer);
    294     kit_obj_builder_free(ob);
    295     kit_compile_session_free(session);
    296     rc = st == KIT_OK ? 0 : 1;
    297   }
    298 
    299 out:
    300   if (compiler) driver_compiler_free(compiler);
    301   kit_target_free(target);
    302   if (pp_writer) kit_writer_close(pp_writer);
    303   if (writer) kit_writer_close(writer);
    304   if (loaded) ctx.file_io->release(ctx.file_io->user, &src);
    305   if (stdin_data) driver_free(&env, stdin_data, stdin_len);
    306   driver_target_features_fini(&tf, &env);
    307   driver_cflags_fini(&cf, &env);
    308   driver_env_fini(&env);
    309   return rc;
    310 }