bcsave.lua 17 KB

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