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 }