comparison third_party/luajit/src/jit/bcsave.lua @ 186:8cf4ec5e2191 hg-web

Fixed merge conflict.
author MrJuneJune <me@mrjunejune.com>
date Fri, 23 Jan 2026 22:38:59 -0800
parents 94705b5986b3
children
comparison
equal deleted inserted replaced
176:fed99fc04e12 186:8cf4ec5e2191
1 ----------------------------------------------------------------------------
2 -- LuaJIT module to save/list bytecode.
3 --
4 -- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
5 -- Released under the MIT license. See Copyright Notice in luajit.h
6 ----------------------------------------------------------------------------
7 --
8 -- This module saves or lists the bytecode for an input file.
9 -- It's run by the -b command line option.
10 --
11 ------------------------------------------------------------------------------
12
13 local jit = require("jit")
14 assert(jit.version_num == 20199, "LuaJIT core/library version mismatch")
15 local bit = require("bit")
16
17 -- Symbol name prefix for LuaJIT bytecode.
18 local LJBC_PREFIX = "luaJIT_BC_"
19
20 local type, assert = type, assert
21 local format = string.format
22 local tremove, tconcat = table.remove, table.concat
23
24 ------------------------------------------------------------------------------
25
26 local function usage()
27 io.stderr:write[[
28 Save LuaJIT bytecode: luajit -b[options] input output
29 -l Only list bytecode.
30 -s Strip debug info (default).
31 -g Keep debug info.
32 -n name Set module name (default: auto-detect from input name).
33 -t type Set output file type (default: auto-detect from output name).
34 -a arch Override architecture for object files (default: native).
35 -o os Override OS for object files (default: native).
36 -F name Override filename (default: input filename).
37 -e chunk Use chunk string as input.
38 -- Stop handling options.
39 - Use stdin as input and/or stdout as output.
40
41 File types: c h obj o raw (default)
42 ]]
43 os.exit(1)
44 end
45
46 local function check(ok, ...)
47 if ok then return ok, ... end
48 io.stderr:write("luajit: ", ...)
49 io.stderr:write("\n")
50 os.exit(1)
51 end
52
53 local function readfile(ctx, input)
54 if type(input) == "function" then return input end
55 if ctx.filename then
56 local data
57 if input == "-" then
58 data = io.stdin:read("*a")
59 else
60 local fp = assert(io.open(input, "rb"))
61 data = assert(fp:read("*a"))
62 assert(fp:close())
63 end
64 return check(load(data, ctx.filename))
65 else
66 if input == "-" then input = nil end
67 return check(loadfile(input))
68 end
69 end
70
71 local function savefile(name, mode)
72 if name == "-" then return io.stdout end
73 return check(io.open(name, mode))
74 end
75
76 local function set_stdout_binary(ffi)
77 ffi.cdef[[int _setmode(int fd, int mode);]]
78 ffi.C._setmode(1, 0x8000)
79 end
80
81 ------------------------------------------------------------------------------
82
83 local map_type = {
84 raw = "raw", c = "c", h = "h", o = "obj", obj = "obj",
85 }
86
87 local map_arch = {
88 x86 = { e = "le", b = 32, m = 3, p = 0x14c, },
89 x64 = { e = "le", b = 64, m = 62, p = 0x8664, },
90 arm = { e = "le", b = 32, m = 40, p = 0x1c0, },
91 arm64 = { e = "le", b = 64, m = 183, p = 0xaa64, },
92 arm64be = { e = "be", b = 64, m = 183, },
93 ppc = { e = "be", b = 32, m = 20, },
94 mips = { e = "be", b = 32, m = 8, f = 0x50001006, },
95 mipsel = { e = "le", b = 32, m = 8, f = 0x50001006, },
96 mips64 = { e = "be", b = 64, m = 8, f = 0x80000007, },
97 mips64el = { e = "le", b = 64, m = 8, f = 0x80000007, },
98 mips64r6 = { e = "be", b = 64, m = 8, f = 0xa0000407, },
99 mips64r6el = { e = "le", b = 64, m = 8, f = 0xa0000407, },
100 }
101
102 local map_os = {
103 linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
104 openbsd = true, dragonfly = true, solaris = true,
105 }
106
107 local function checkarg(str, map, err)
108 str = str:lower()
109 local s = check(map[str], "unknown ", err)
110 return type(s) == "string" and s or str
111 end
112
113 local function detecttype(str)
114 local ext = str:lower():match("%.(%a+)$")
115 return map_type[ext] or "raw"
116 end
117
118 local function checkmodname(str)
119 check(str:match("^[%w_.%-]+$"), "bad module name")
120 return str:gsub("[%.%-]", "_")
121 end
122
123 local function detectmodname(str)
124 if type(str) == "string" then
125 local tail = str:match("[^/\\]+$")
126 if tail then str = tail end
127 local head = str:match("^(.*)%.[^.]*$")
128 if head then str = head end
129 str = str:match("^[%w_.%-]+")
130 else
131 str = nil
132 end
133 check(str, "cannot derive module name, use -n name")
134 return str:gsub("[%.%-]", "_")
135 end
136
137 ------------------------------------------------------------------------------
138
139 local function bcsave_tail(fp, output, s)
140 local ok, err = fp:write(s)
141 if ok and output ~= "-" then ok, err = fp:close() end
142 check(ok, "cannot write ", output, ": ", err)
143 end
144
145 local function bcsave_raw(output, s)
146 if output == "-" and jit.os == "Windows" then
147 local ok, ffi = pcall(require, "ffi")
148 check(ok, "FFI library required to write binary file to stdout")
149 set_stdout_binary(ffi)
150 end
151 local fp = savefile(output, "wb")
152 bcsave_tail(fp, output, s)
153 end
154
155 local function bcsave_c(ctx, output, s)
156 local fp = savefile(output, "w")
157 if ctx.type == "c" then
158 fp:write(format([[
159 #ifdef __cplusplus
160 extern "C"
161 #endif
162 #ifdef _WIN32
163 __declspec(dllexport)
164 #endif
165 const unsigned char %s%s[] = {
166 ]], LJBC_PREFIX, ctx.modname))
167 else
168 fp:write(format([[
169 #define %s%s_SIZE %d
170 static const unsigned char %s%s[] = {
171 ]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))
172 end
173 local t, n, m = {}, 0, 0
174 for i=1,#s do
175 local b = tostring(string.byte(s, i))
176 m = m + #b + 1
177 if m > 78 then
178 fp:write(tconcat(t, ",", 1, n), ",\n")
179 n, m = 0, #b + 1
180 end
181 n = n + 1
182 t[n] = b
183 end
184 bcsave_tail(fp, output, tconcat(t, ",", 1, n).."\n};\n")
185 end
186
187 local function bcsave_elfobj(ctx, output, s, ffi)
188 ffi.cdef[[
189 typedef struct {
190 uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
191 uint16_t type, machine;
192 uint32_t version;
193 uint32_t entry, phofs, shofs;
194 uint32_t flags;
195 uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
196 } ELF32header;
197 typedef struct {
198 uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
199 uint16_t type, machine;
200 uint32_t version;
201 uint64_t entry, phofs, shofs;
202 uint32_t flags;
203 uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
204 } ELF64header;
205 typedef struct {
206 uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
207 } ELF32sectheader;
208 typedef struct {
209 uint32_t name, type;
210 uint64_t flags, addr, ofs, size;
211 uint32_t link, info;
212 uint64_t align, entsize;
213 } ELF64sectheader;
214 typedef struct {
215 uint32_t name, value, size;
216 uint8_t info, other;
217 uint16_t sectidx;
218 } ELF32symbol;
219 typedef struct {
220 uint32_t name;
221 uint8_t info, other;
222 uint16_t sectidx;
223 uint64_t value, size;
224 } ELF64symbol;
225 typedef struct {
226 ELF32header hdr;
227 ELF32sectheader sect[6];
228 ELF32symbol sym[2];
229 uint8_t space[4096];
230 } ELF32obj;
231 typedef struct {
232 ELF64header hdr;
233 ELF64sectheader sect[6];
234 ELF64symbol sym[2];
235 uint8_t space[4096];
236 } ELF64obj;
237 ]]
238 local symname = LJBC_PREFIX..ctx.modname
239 local ai = assert(map_arch[ctx.arch])
240 local is64, isbe = ai.b == 64, ai.e == "be"
241
242 -- Handle different host/target endianess.
243 local function f32(x) return x end
244 local f16, fofs = f32, f32
245 if ffi.abi("be") ~= isbe then
246 f32 = bit.bswap
247 function f16(x) return bit.rshift(bit.bswap(x), 16) end
248 if is64 then
249 local two32 = ffi.cast("int64_t", 2^32)
250 function fofs(x) return bit.bswap(x)*two32 end
251 else
252 fofs = f32
253 end
254 end
255
256 -- Create ELF object and fill in header.
257 local o = ffi.new(is64 and "ELF64obj" or "ELF32obj")
258 local hdr = o.hdr
259 if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi.
260 local bf = assert(io.open("/bin/ls", "rb"))
261 local bs = bf:read(9)
262 bf:close()
263 ffi.copy(o, bs, 9)
264 check(hdr.emagic[0] == 127, "no support for writing native object files")
265 else
266 hdr.emagic = "\127ELF"
267 hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0
268 end
269 hdr.eclass = is64 and 2 or 1
270 hdr.eendian = isbe and 2 or 1
271 hdr.eversion = 1
272 hdr.type = f16(1)
273 hdr.machine = f16(ai.m)
274 hdr.flags = f32(ai.f or 0)
275 hdr.version = f32(1)
276 hdr.shofs = fofs(ffi.offsetof(o, "sect"))
277 hdr.ehsize = f16(ffi.sizeof(hdr))
278 hdr.shentsize = f16(ffi.sizeof(o.sect[0]))
279 hdr.shnum = f16(6)
280 hdr.shstridx = f16(2)
281
282 -- Fill in sections and symbols.
283 local sofs, ofs = ffi.offsetof(o, "space"), 1
284 for i,name in ipairs{
285 ".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack",
286 } do
287 local sect = o.sect[i]
288 sect.align = fofs(1)
289 sect.name = f32(ofs)
290 ffi.copy(o.space+ofs, name)
291 ofs = ofs + #name+1
292 end
293 o.sect[1].type = f32(2) -- .symtab
294 o.sect[1].link = f32(3)
295 o.sect[1].info = f32(1)
296 o.sect[1].align = fofs(8)
297 o.sect[1].ofs = fofs(ffi.offsetof(o, "sym"))
298 o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))
299 o.sect[1].size = fofs(ffi.sizeof(o.sym))
300 o.sym[1].name = f32(1)
301 o.sym[1].sectidx = f16(4)
302 o.sym[1].size = fofs(#s)
303 o.sym[1].info = 17
304 o.sect[2].type = f32(3) -- .shstrtab
305 o.sect[2].ofs = fofs(sofs)
306 o.sect[2].size = fofs(ofs)
307 o.sect[3].type = f32(3) -- .strtab
308 o.sect[3].ofs = fofs(sofs + ofs)
309 o.sect[3].size = fofs(#symname+2)
310 ffi.copy(o.space+ofs+1, symname)
311 ofs = ofs + #symname + 2
312 o.sect[4].type = f32(1) -- .rodata
313 o.sect[4].flags = fofs(2)
314 o.sect[4].ofs = fofs(sofs + ofs)
315 o.sect[4].size = fofs(#s)
316 o.sect[5].type = f32(1) -- .note.GNU-stack
317 o.sect[5].ofs = fofs(sofs + ofs + #s)
318
319 -- Write ELF object file.
320 local fp = savefile(output, "wb")
321 fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
322 bcsave_tail(fp, output, s)
323 end
324
325 local function bcsave_peobj(ctx, output, s, ffi)
326 ffi.cdef[[
327 typedef struct {
328 uint16_t arch, nsects;
329 uint32_t time, symtabofs, nsyms;
330 uint16_t opthdrsz, flags;
331 } PEheader;
332 typedef struct {
333 char name[8];
334 uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
335 uint16_t nreloc, nline;
336 uint32_t flags;
337 } PEsection;
338 typedef struct __attribute((packed)) {
339 union {
340 char name[8];
341 uint32_t nameref[2];
342 };
343 uint32_t value;
344 int16_t sect;
345 uint16_t type;
346 uint8_t scl, naux;
347 } PEsym;
348 typedef struct __attribute((packed)) {
349 uint32_t size;
350 uint16_t nreloc, nline;
351 uint32_t cksum;
352 uint16_t assoc;
353 uint8_t comdatsel, unused[3];
354 } PEsymaux;
355 typedef struct {
356 PEheader hdr;
357 PEsection sect[2];
358 // Must be an even number of symbol structs.
359 PEsym sym0;
360 PEsymaux sym0aux;
361 PEsym sym1;
362 PEsymaux sym1aux;
363 PEsym sym2;
364 PEsym sym3;
365 uint32_t strtabsize;
366 uint8_t space[4096];
367 } PEobj;
368 ]]
369 local symname = LJBC_PREFIX..ctx.modname
370 local ai = assert(map_arch[ctx.arch])
371 local is64 = ai.b == 64
372 local symexport = " /EXPORT:"..symname..",DATA "
373
374 -- The file format is always little-endian. Swap if the host is big-endian.
375 local function f32(x) return x end
376 local f16 = f32
377 if ffi.abi("be") then
378 f32 = bit.bswap
379 function f16(x) return bit.rshift(bit.bswap(x), 16) end
380 end
381
382 -- Create PE object and fill in header.
383 local o = ffi.new("PEobj")
384 local hdr = o.hdr
385 hdr.arch = f16(assert(ai.p))
386 hdr.nsects = f16(2)
387 hdr.symtabofs = f32(ffi.offsetof(o, "sym0"))
388 hdr.nsyms = f32(6)
389
390 -- Fill in sections and symbols.
391 o.sect[0].name = ".drectve"
392 o.sect[0].size = f32(#symexport)
393 o.sect[0].flags = f32(0x00100a00)
394 o.sym0.sect = f16(1)
395 o.sym0.scl = 3
396 o.sym0.name = ".drectve"
397 o.sym0.naux = 1
398 o.sym0aux.size = f32(#symexport)
399 o.sect[1].name = ".rdata"
400 o.sect[1].size = f32(#s)
401 o.sect[1].flags = f32(0x40300040)
402 o.sym1.sect = f16(2)
403 o.sym1.scl = 3
404 o.sym1.name = ".rdata"
405 o.sym1.naux = 1
406 o.sym1aux.size = f32(#s)
407 o.sym2.sect = f16(2)
408 o.sym2.scl = 2
409 o.sym2.nameref[1] = f32(4)
410 o.sym3.sect = f16(-1)
411 o.sym3.scl = 2
412 o.sym3.value = f32(1)
413 o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant.
414 ffi.copy(o.space, symname)
415 local ofs = #symname + 1
416 o.strtabsize = f32(ofs + 4)
417 o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs)
418 ffi.copy(o.space + ofs, symexport)
419 ofs = ofs + #symexport
420 o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs)
421
422 -- Write PE object file.
423 local fp = savefile(output, "wb")
424 fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
425 bcsave_tail(fp, output, s)
426 end
427
428 local function bcsave_machobj(ctx, output, s, ffi)
429 ffi.cdef[[
430 typedef struct
431 {
432 uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;
433 } mach_header;
434 typedef struct
435 {
436 mach_header; uint32_t reserved;
437 } mach_header_64;
438 typedef struct {
439 uint32_t cmd, cmdsize;
440 char segname[16];
441 uint32_t vmaddr, vmsize, fileoff, filesize;
442 uint32_t maxprot, initprot, nsects, flags;
443 } mach_segment_command;
444 typedef struct {
445 uint32_t cmd, cmdsize;
446 char segname[16];
447 uint64_t vmaddr, vmsize, fileoff, filesize;
448 uint32_t maxprot, initprot, nsects, flags;
449 } mach_segment_command_64;
450 typedef struct {
451 char sectname[16], segname[16];
452 uint32_t addr, size;
453 uint32_t offset, align, reloff, nreloc, flags;
454 uint32_t reserved1, reserved2;
455 } mach_section;
456 typedef struct {
457 char sectname[16], segname[16];
458 uint64_t addr, size;
459 uint32_t offset, align, reloff, nreloc, flags;
460 uint32_t reserved1, reserved2, reserved3;
461 } mach_section_64;
462 typedef struct {
463 uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;
464 } mach_symtab_command;
465 typedef struct {
466 int32_t strx;
467 uint8_t type, sect;
468 int16_t desc;
469 uint32_t value;
470 } mach_nlist;
471 typedef struct {
472 int32_t strx;
473 uint8_t type, sect;
474 uint16_t desc;
475 uint64_t value;
476 } mach_nlist_64;
477 typedef struct
478 {
479 int32_t magic, nfat_arch;
480 } mach_fat_header;
481 typedef struct
482 {
483 int32_t cputype, cpusubtype, offset, size, align;
484 } mach_fat_arch;
485 typedef struct {
486 struct {
487 mach_header hdr;
488 mach_segment_command seg;
489 mach_section sec;
490 mach_symtab_command sym;
491 } arch[1];
492 mach_nlist sym_entry;
493 uint8_t space[4096];
494 } mach_obj;
495 typedef struct {
496 struct {
497 mach_header_64 hdr;
498 mach_segment_command_64 seg;
499 mach_section_64 sec;
500 mach_symtab_command sym;
501 } arch[1];
502 mach_nlist_64 sym_entry;
503 uint8_t space[4096];
504 } mach_obj_64;
505 typedef struct {
506 mach_fat_header fat;
507 mach_fat_arch fat_arch[2];
508 struct {
509 mach_header hdr;
510 mach_segment_command seg;
511 mach_section sec;
512 mach_symtab_command sym;
513 } arch[2];
514 mach_nlist sym_entry;
515 uint8_t space[4096];
516 } mach_fat_obj;
517 typedef struct {
518 mach_fat_header fat;
519 mach_fat_arch fat_arch[2];
520 struct {
521 mach_header_64 hdr;
522 mach_segment_command_64 seg;
523 mach_section_64 sec;
524 mach_symtab_command sym;
525 } arch[2];
526 mach_nlist_64 sym_entry;
527 uint8_t space[4096];
528 } mach_fat_obj_64;
529 ]]
530 local symname = '_'..LJBC_PREFIX..ctx.modname
531 local isfat, is64, align, mobj = false, false, 4, "mach_obj"
532 if ctx.arch == "x64" then
533 is64, align, mobj = true, 8, "mach_obj_64"
534 elseif ctx.arch == "arm" then
535 isfat, mobj = true, "mach_fat_obj"
536 elseif ctx.arch == "arm64" then
537 is64, align, isfat, mobj = true, 8, true, "mach_fat_obj_64"
538 else
539 check(ctx.arch == "x86", "unsupported architecture for OSX")
540 end
541 local function aligned(v, a) return bit.band(v+a-1, -a) end
542 local be32 = bit.bswap -- Mach-O FAT is BE, supported archs are LE.
543
544 -- Create Mach-O object and fill in header.
545 local o = ffi.new(mobj)
546 local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, align)
547 local cputype = ({ x86={7}, x64={0x01000007}, arm={7,12}, arm64={0x01000007,0x0100000c} })[ctx.arch]
548 local cpusubtype = ({ x86={3}, x64={3}, arm={3,9}, arm64={3,0} })[ctx.arch]
549 if isfat then
550 o.fat.magic = be32(0xcafebabe)
551 o.fat.nfat_arch = be32(#cpusubtype)
552 end
553
554 -- Fill in sections and symbols.
555 for i=0,#cpusubtype-1 do
556 local ofs = 0
557 if isfat then
558 local a = o.fat_arch[i]
559 a.cputype = be32(cputype[i+1])
560 a.cpusubtype = be32(cpusubtype[i+1])
561 -- Subsequent slices overlap each other to share data.
562 ofs = ffi.offsetof(o, "arch") + i*ffi.sizeof(o.arch[0])
563 a.offset = be32(ofs)
564 a.size = be32(mach_size-ofs+#s)
565 end
566 local a = o.arch[i]
567 a.hdr.magic = is64 and 0xfeedfacf or 0xfeedface
568 a.hdr.cputype = cputype[i+1]
569 a.hdr.cpusubtype = cpusubtype[i+1]
570 a.hdr.filetype = 1
571 a.hdr.ncmds = 2
572 a.hdr.sizeofcmds = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)+ffi.sizeof(a.sym)
573 a.seg.cmd = is64 and 0x19 or 0x1
574 a.seg.cmdsize = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)
575 a.seg.vmsize = #s
576 a.seg.fileoff = mach_size-ofs
577 a.seg.filesize = #s
578 a.seg.maxprot = 1
579 a.seg.initprot = 1
580 a.seg.nsects = 1
581 ffi.copy(a.sec.sectname, "__data")
582 ffi.copy(a.sec.segname, "__DATA")
583 a.sec.size = #s
584 a.sec.offset = mach_size-ofs
585 a.sym.cmd = 2
586 a.sym.cmdsize = ffi.sizeof(a.sym)
587 a.sym.symoff = ffi.offsetof(o, "sym_entry")-ofs
588 a.sym.nsyms = 1
589 a.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)-ofs
590 a.sym.strsize = aligned(#symname+2, align)
591 end
592 o.sym_entry.type = 0xf
593 o.sym_entry.sect = 1
594 o.sym_entry.strx = 1
595 ffi.copy(o.space+1, symname)
596
597 -- Write Macho-O object file.
598 local fp = savefile(output, "wb")
599 fp:write(ffi.string(o, mach_size))
600 bcsave_tail(fp, output, s)
601 end
602
603 local function bcsave_obj(ctx, output, s)
604 local ok, ffi = pcall(require, "ffi")
605 check(ok, "FFI library required to write this file type")
606 if output == "-" and jit.os == "Windows" then
607 set_stdout_binary(ffi)
608 end
609 if ctx.os == "windows" then
610 return bcsave_peobj(ctx, output, s, ffi)
611 elseif ctx.os == "osx" then
612 return bcsave_machobj(ctx, output, s, ffi)
613 else
614 return bcsave_elfobj(ctx, output, s, ffi)
615 end
616 end
617
618 ------------------------------------------------------------------------------
619
620 local function bclist(ctx, input, output)
621 local f = readfile(ctx, input)
622 require("jit.bc").dump(f, savefile(output, "w"), true)
623 end
624
625 local function bcsave(ctx, input, output)
626 local f = readfile(ctx, input)
627 local s = string.dump(f, ctx.strip)
628 local t = ctx.type
629 if not t then
630 t = detecttype(output)
631 ctx.type = t
632 end
633 if t == "raw" then
634 bcsave_raw(output, s)
635 else
636 if not ctx.modname then ctx.modname = detectmodname(input) end
637 if t == "obj" then
638 bcsave_obj(ctx, output, s)
639 else
640 bcsave_c(ctx, output, s)
641 end
642 end
643 end
644
645 local function docmd(...)
646 local arg = {...}
647 local n = 1
648 local list = false
649 local ctx = {
650 strip = true, arch = jit.arch, os = jit.os:lower(),
651 type = false, modname = false,
652 }
653 while n <= #arg do
654 local a = arg[n]
655 if type(a) == "string" and a:sub(1, 1) == "-" and a ~= "-" then
656 tremove(arg, n)
657 if a == "--" then break end
658 for m=2,#a do
659 local opt = a:sub(m, m)
660 if opt == "l" then
661 list = true
662 elseif opt == "s" then
663 ctx.strip = true
664 elseif opt == "g" then
665 ctx.strip = false
666 else
667 if arg[n] == nil or m ~= #a then usage() end
668 if opt == "e" then
669 if n ~= 1 then usage() end
670 arg[1] = check(loadstring(arg[1]))
671 elseif opt == "n" then
672 ctx.modname = checkmodname(tremove(arg, n))
673 elseif opt == "t" then
674 ctx.type = checkarg(tremove(arg, n), map_type, "file type")
675 elseif opt == "a" then
676 ctx.arch = checkarg(tremove(arg, n), map_arch, "architecture")
677 elseif opt == "o" then
678 ctx.os = checkarg(tremove(arg, n), map_os, "OS name")
679 elseif opt == "F" then
680 ctx.filename = "@"..tremove(arg, n)
681 else
682 usage()
683 end
684 end
685 end
686 else
687 n = n + 1
688 end
689 end
690 if list then
691 if #arg == 0 or #arg > 2 then usage() end
692 bclist(ctx, arg[1], arg[2] or "-")
693 else
694 if #arg ~= 2 then usage() end
695 bcsave(ctx, arg[1], arg[2])
696 end
697 end
698
699 ------------------------------------------------------------------------------
700
701 -- Public module functions.
702 return {
703 start = docmd -- Process -b command line option.
704 }
705