bcsave.lua 18 KB

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