as.c (8168B)
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 "\n" 41 "DESCRIPTION\n" 42 " Reads a single text source written in a GAS subset (AT&T syntax " 43 "on\n" 44 " x86, standard mnemonics on aarch64/riscv64) and writes a " 45 "relocatable\n" 46 " object via the same back-end the C compiler emits to. INPUT.S is " 47 "run\n" 48 " through the C preprocessor first; INPUT.s is assembled directly.\n" 49 " Exactly one positional input is required; -o is required.\n" 50 "\n" 51 "OPTIONS\n" 52 " -o PATH Output object path (required)\n" 53 " -g Emit DWARF debug info from .file/.loc " 54 "directives\n" 55 " -I DIR, -IDIR Add a user include directory for INPUT.S\n" 56 " -isystem DIR Add a system include directory for INPUT.S\n" 57 " -D NAME[=VALUE] Define a preprocessor macro for INPUT.S\n" 58 " -U NAME Undefine a preprocessor macro for INPUT.S\n" 59 " -target TRIPLE Cross-assemble target. See `kit cc --help` " 60 "for " 61 "the\n" 62 " full list of recognized arches and OSes. " 63 "Default:\n" 64 " the host target.\n" 65 " -h, --help Show this help and exit\n" 66 "\n" 67 "EXIT CODES\n" 68 " 0 success 1 assemble error 2 bad " 69 "usage\n"))); 70 } 71 72 /* Returns 0 on success; non-zero on bad args (already reported). */ 73 static int as_parse(int argc, char** argv, AsOptions* o, DriverEnv* env, 74 DriverCflags* cf, DriverTargetFeatures* tf) { 75 int i; 76 77 o->target = driver_host_target(); 78 79 for (i = 1; i < argc; ++i) { 80 const char* a = argv[i]; 81 int r; 82 83 r = driver_cflags_try_consume(cf, env, AS_TOOL, argc, argv, &i); 84 if (r < 0) return 1; 85 if (r > 0) continue; 86 87 if (driver_streq(a, "-g")) { 88 o->debug_info = 1; 89 continue; 90 } 91 92 if (driver_streq(a, "-o")) { 93 if (++i >= argc) { 94 driver_errf(AS_TOOL, "-o requires an argument"); 95 return 1; 96 } 97 o->output_path = argv[i]; 98 continue; 99 } 100 101 if (driver_streq(a, "-target")) { 102 if (++i >= argc) { 103 driver_errf(AS_TOOL, "-target requires an argument"); 104 return 1; 105 } 106 if (driver_target_from_triple(argv[i], &o->target) != 0) { 107 driver_errf(AS_TOOL, "unrecognized target: %.*s", 108 KIT_SLICE_ARG(kit_slice_cstr(argv[i]))); 109 return 1; 110 } 111 continue; 112 } 113 114 { 115 int tr = 116 driver_target_features_try_consume(tf, env, AS_TOOL, argc, argv, &i); 117 if (tr < 0) return 1; 118 if (tr > 0) continue; 119 } 120 121 if (a[0] == '-' && a[1] != '\0') { 122 driver_errf(AS_TOOL, "unknown flag: %.*s", 123 KIT_SLICE_ARG(kit_slice_cstr(a))); 124 return 1; 125 } 126 127 if (o->source) { 128 driver_errf(AS_TOOL, "multiple inputs not supported"); 129 return 1; 130 } 131 o->source = a; 132 } 133 134 if (!o->source) { 135 driver_errf(AS_TOOL, "no input file"); 136 as_usage(); 137 return 1; 138 } 139 if (!o->output_path) { 140 driver_errf(AS_TOOL, "-o is required"); 141 return 1; 142 } 143 return 0; 144 } 145 146 int driver_as(int argc, char** argv) { 147 DriverEnv env; 148 AsOptions o = {0}; 149 DriverCflags cf = {0}; 150 DriverTargetFeatures tf = {0}; 151 KitContext ctx; 152 KitPreprocessOptions pp; 153 KitTarget* target = NULL; 154 KitCompiler* compiler = NULL; 155 KitWriter* writer = NULL; 156 KitWriter* pp_writer = NULL; 157 KitFileData src = {0}; 158 KitSlice input; 159 KitSlice asm_input; 160 KitAsmCompileOptions copts; 161 KitAsmCompileOptions zero = {0}; 162 const uint8_t* pp_data; 163 size_t pp_len; 164 int rc = 1; 165 int loaded = 0; 166 167 if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) { 168 driver_help_as(); 169 return 0; 170 } 171 172 driver_env_init(&env); 173 174 if (driver_cflags_init(&cf, &env, argc) != 0 || 175 driver_target_features_init(&tf, &env, argc) != 0) { 176 driver_errf(AS_TOOL, "out of memory"); 177 driver_target_features_fini(&tf, &env); 178 driver_cflags_fini(&cf, &env); 179 driver_env_fini(&env); 180 return 2; 181 } 182 183 if (as_parse(argc, argv, &o, &env, &cf, &tf) != 0) { 184 driver_target_features_fini(&tf, &env); 185 driver_cflags_fini(&cf, &env); 186 driver_env_fini(&env); 187 return 2; 188 } 189 driver_cflags_fill_pp(&cf, &pp); 190 191 ctx = driver_env_to_context(&env); 192 193 if (ctx.file_io->read_all(ctx.file_io->user, o.source, &src) != KIT_OK) { 194 driver_errf(AS_TOOL, "failed to read: %.*s", 195 KIT_SLICE_ARG(kit_slice_cstr(o.source))); 196 goto out; 197 } 198 loaded = 1; 199 200 if (ctx.file_io->open_writer(ctx.file_io->user, o.output_path, &writer) != 201 KIT_OK) { 202 driver_errf(AS_TOOL, "failed to open output: %.*s", 203 KIT_SLICE_ARG(kit_slice_cstr(o.output_path))); 204 goto out; 205 } 206 207 if (driver_target_new(&ctx, o.target, &tf, AS_TOOL, &target) != KIT_OK || 208 driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { 209 driver_errf(AS_TOOL, "failed to initialize compiler"); 210 goto out; 211 } 212 213 copts = zero; 214 copts.code.debug_info = o.debug_info; 215 216 input.data = src.data; 217 input.len = src.size; 218 asm_input = input; 219 220 if (driver_has_suffix(o.source, ".S")) { 221 if (kit_writer_mem(ctx.heap, &pp_writer) != KIT_OK) { 222 driver_errf(AS_TOOL, "out of memory"); 223 goto out; 224 } 225 if (kit_cpp_preprocess(compiler, &pp, kit_slice_cstr(o.source), &input, 226 pp_writer) != KIT_OK) 227 goto out; 228 if (kit_writer_status(pp_writer) != KIT_OK) { 229 driver_errf(AS_TOOL, "failed to preprocess: %.*s", 230 KIT_SLICE_ARG(kit_slice_cstr(o.source))); 231 goto out; 232 } 233 pp_data = kit_writer_mem_bytes(pp_writer, &pp_len); 234 asm_input.data = pp_data; 235 asm_input.len = pp_len; 236 } 237 238 { 239 KitCompileSessionOptions sopts; 240 KitCompileSession* session = NULL; 241 KitSourceInput sin; 242 KitObjBuilder* ob = NULL; 243 KitStatus st; 244 memset(&sopts, 0, sizeof(sopts)); 245 sopts.lang = KIT_LANG_ASM; 246 sopts.compile.code = copts.code; 247 sopts.compile.diagnostics = copts.diagnostics; 248 sopts.compile.language_options = &copts; 249 memset(&sin, 0, sizeof(sin)); 250 sin.name = kit_slice_cstr(o.source); 251 sin.bytes = asm_input; 252 sin.lang = KIT_LANG_ASM; 253 st = kit_compile_session_new(compiler, &sopts, &session); 254 if (st == KIT_OK) st = kit_compile_session_compile(session, &sin, &ob); 255 if (st == KIT_OK) st = kit_obj_builder_emit(ob, writer); 256 kit_obj_builder_free(ob); 257 kit_compile_session_free(session); 258 rc = st == KIT_OK ? 0 : 1; 259 } 260 261 out: 262 if (compiler) driver_compiler_free(compiler); 263 kit_target_free(target); 264 if (pp_writer) kit_writer_close(pp_writer); 265 if (writer) kit_writer_close(writer); 266 if (loaded) ctx.file_io->release(ctx.file_io->user, &src); 267 driver_target_features_fini(&tf, &env); 268 driver_cflags_fini(&cf, &env); 269 driver_env_fini(&env); 270 return rc; 271 }