const char bcsave_lua[] =
"----------------------------------------------------------------------------\n"
"-- LuaJIT module to save/list bytecode.\n"
"--\n"
"-- Copyright (C) 2005-2017 Mike Pall. All rights reserved.\n"
"-- Released under the MIT license. See Copyright Notice in luajit.h\n"
"----------------------------------------------------------------------------\n"
"--\n"
"-- This module saves or lists the bytecode for an input file.\n"
"-- It's run by the -b command line option.\n"
"--\n"
"------------------------------------------------------------------------------\n"
"\n"
"local jit = require(\"jit\")\n"
"assert(jit.version_num == 20100, \"LuaJIT core/library version mismatch\")\n"
"local bit = require(\"bit\")\n"
"\n"
"-- Symbol name prefix for LuaJIT bytecode.\n"
"local LJBC_PREFIX = \"luaJIT_BC_\"\n"
"\n"
"------------------------------------------------------------------------------\n"
"\n"
"local function usage()\n"
"  io.stderr:write[[\n"
"Save LuaJIT bytecode: luajit -b[options] input output\n"
"  -l        Only list bytecode.\n"
"  -s        Strip debug info (default).\n"
"  -g        Keep debug info.\n"
"  -n name   Set module name (default: auto-detect from input name).\n"
"  -t type   Set output file type (default: auto-detect from output name).\n"
"  -a arch   Override architecture for object files (default: native).\n"
"  -o os     Override OS for object files (default: native).\n"
"  -e chunk  Use chunk string as input.\n"
"  --        Stop handling options.\n"
"  -         Use stdin as input and/or stdout as output.\n"
"\n"
"File types: c h obj o raw (default)\n"
"]]\n"
"  os.exit(1)\n"
"end\n"
"\n"
"local function check(ok, ...)\n"
"  if ok then return ok, ... end\n"
"  io.stderr:write(\"luajit: \", ...)\n"
"  io.stderr:write(\"\\n\")\n"
"  os.exit(1)\n"
"end\n"
"\n"
"local function readfile(input)\n"
"  if type(input) == \"function\" then return input end\n"
"  if input == \"-\" then input = nil end\n"
"  return check(loadfile(input))\n"
"end\n"
"\n"
"local function savefile(name, mode)\n"
"  if name == \"-\" then return io.stdout end\n"
"  return check(io.open(name, mode))\n"
"end\n"
"\n"
"------------------------------------------------------------------------------\n"
"\n"
"local map_type = {\n"
"  raw = \"raw\", c = \"c\", h = \"h\", o = \"obj\", obj = \"obj\",\n"
"}\n"
"\n"
"local map_arch = {\n"
"  x86 = true, x64 = true, arm = true, arm64 = true, arm64be = true,\n"
"  ppc = true, mips = true, mipsel = true,\n"
"}\n"
"\n"
"local map_os = {\n"
"  linux = true, windows = true, osx = true, freebsd = true, netbsd = true,\n"
"  openbsd = true, dragonfly = true, solaris = true,\n"
"}\n"
"\n"
"local function checkarg(str, map, err)\n"
"  str = string.lower(str)\n"
"  local s = check(map[str], \"unknown \", err)\n"
"  return s == true and str or s\n"
"end\n"
"\n"
"local function detecttype(str)\n"
"  local ext = string.match(string.lower(str), \"%.(%a+)$\")\n"
"  return map_type[ext] or \"raw\"\n"
"end\n"
"\n"
"local function checkmodname(str)\n"
"  check(string.match(str, \"^[%w_.%-]+$\"), \"bad module name\")\n"
"  return string.gsub(str, \"[%.%-]\", \"_\")\n"
"end\n"
"\n"
"local function detectmodname(str)\n"
"  if type(str) == \"string\" then\n"
"    local tail = string.match(str, \"[^/\\\\]+$\")\n"
"    if tail then str = tail end\n"
"    local head = string.match(str, \"^(.*)%.[^.]*$\")\n"
"    if head then str = head end\n"
"    str = string.match(str, \"^[%w_.%-]+\")\n"
"  else\n"
"    str = nil\n"
"  end\n"
"  check(str, \"cannot derive module name, use -n name\")\n"
"  return string.gsub(str, \"[%.%-]\", \"_\")\n"
"end\n"
"\n"
"------------------------------------------------------------------------------\n"
"\n"
"local function bcsave_tail(fp, output, s)\n"
"  local ok, err = fp:write(s)\n"
"  if ok and output ~= \"-\" then ok, err = fp:close() end\n"
"  check(ok, \"cannot write \", output, \": \", err)\n"
"end\n"
"\n"
"local function bcsave_raw(output, s)\n"
"  local fp = savefile(output, \"wb\")\n"
"  bcsave_tail(fp, output, s)\n"
"end\n"
"\n"
"local function bcsave_c(ctx, output, s)\n"
"  local fp = savefile(output, \"w\")\n"
"  if ctx.type == \"c\" then\n"
"    fp:write(string.format([[\n"
"#ifdef _cplusplus\n"
"extern \"C\"\n"
"#endif\n"
"#ifdef _WIN32\n"
"__declspec(dllexport)\n"
"#endif\n"
"const unsigned char %s%s[] = {\n"
"]], LJBC_PREFIX, ctx.modname))\n"
"  else\n"
"    fp:write(string.format([[\n"
"#define %s%s_SIZE %d\n"
"static const unsigned char %s%s[] = {\n"
"]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))\n"
"  end\n"
"  local t, n, m = {}, 0, 0\n"
"  for i=1,#s do\n"
"    local b = tostring(string.byte(s, i))\n"
"    m = m + #b + 1\n"
"    if m > 78 then\n"
"      fp:write(table.concat(t, \",\", 1, n), \",\\n\")\n"
"      n, m = 0, #b + 1\n"
"    end\n"
"    n = n + 1\n"
"    t[n] = b\n"
"  end\n"
"  bcsave_tail(fp, output, table.concat(t, \",\", 1, n)..\"\\n};\\n\")\n"
"end\n"
"\n"
"local function bcsave_elfobj(ctx, output, s, ffi)\n"
"  ffi.cdef[[\n"
"typedef struct {\n"
"  uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];\n"
"  uint16_t type, machine;\n"
"  uint32_t version;\n"
"  uint32_t entry, phofs, shofs;\n"
"  uint32_t flags;\n"
"  uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;\n"
"} ELF32header;\n"
"typedef struct {\n"
"  uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];\n"
"  uint16_t type, machine;\n"
"  uint32_t version;\n"
"  uint64_t entry, phofs, shofs;\n"
"  uint32_t flags;\n"
"  uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;\n"
"} ELF64header;\n"
"typedef struct {\n"
"  uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;\n"
"} ELF32sectheader;\n"
"typedef struct {\n"
"  uint32_t name, type;\n"
"  uint64_t flags, addr, ofs, size;\n"
"  uint32_t link, info;\n"
"  uint64_t align, entsize;\n"
"} ELF64sectheader;\n"
"typedef struct {\n"
"  uint32_t name, value, size;\n"
"  uint8_t info, other;\n"
"  uint16_t sectidx;\n"
"} ELF32symbol;\n"
"typedef struct {\n"
"  uint32_t name;\n"
"  uint8_t info, other;\n"
"  uint16_t sectidx;\n"
"  uint64_t value, size;\n"
"} ELF64symbol;\n"
"typedef struct {\n"
"  ELF32header hdr;\n"
"  ELF32sectheader sect[6];\n"
"  ELF32symbol sym[2];\n"
"  uint8_t space[4096];\n"
"} ELF32obj;\n"
"typedef struct {\n"
"  ELF64header hdr;\n"
"  ELF64sectheader sect[6];\n"
"  ELF64symbol sym[2];\n"
"  uint8_t space[4096];\n"
"} ELF64obj;\n"
"]]\n"
"  local symname = LJBC_PREFIX..ctx.modname\n"
"  local is64, isbe = false, false\n"
"  if ctx.arch == \"x64\" or ctx.arch == \"arm64\" or ctx.arch == \"arm64be\" then\n"
"    is64 = true\n"
"  elseif ctx.arch == \"ppc\" or ctx.arch == \"mips\" then\n"
"    isbe = true\n"
"  end\n"
"\n"
"  -- Handle different host/target endianess.\n"
"  local function f32(x) return x end\n"
"  local f16, fofs = f32, f32\n"
"  if ffi.abi(\"be\") ~= isbe then\n"
"    f32 = bit.bswap\n"
"    function f16(x) return bit.rshift(bit.bswap(x), 16) end\n"
"    if is64 then\n"
"      local two32 = ffi.cast(\"int64_t\", 2^32)\n"
"      function fofs(x) return bit.bswap(x)*two32 end\n"
"    else\n"
"      fofs = f32\n"
"    end\n"
"  end\n"
"\n"
"  -- Create ELF object and fill in header.\n"
"  local o = ffi.new(is64 and \"ELF64obj\" or \"ELF32obj\")\n"
"  local hdr = o.hdr\n"
"  if ctx.os == \"bsd\" or ctx.os == \"other\" then -- Determine native hdr.eosabi.\n"
"    local bf = assert(io.open(\"/bin/ls\", \"rb\"))\n"
"    local bs = bf:read(9)\n"
"    bf:close()\n"
"    ffi.copy(o, bs, 9)\n"
"    check(hdr.emagic[0] == 127, \"no support for writing native object files\")\n"
"  else\n"
"    hdr.emagic = \"\\127ELF\"\n"
"    hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0\n"
"  end\n"
"  hdr.eclass = is64 and 2 or 1\n"
"  hdr.eendian = isbe and 2 or 1\n"
"  hdr.eversion = 1\n"
"  hdr.type = f16(1)\n"
"  hdr.machine = f16(({ x86=3, x64=62, arm=40, arm64=183, arm64be=183, ppc=20, mips=8, mipsel=8 })[ctx.arch])\n"
"  if ctx.arch == \"mips\" or ctx.arch == \"mipsel\" then\n"
"    hdr.flags = f32(0x50001006)\n"
"  end\n"
"  hdr.version = f32(1)\n"
"  hdr.shofs = fofs(ffi.offsetof(o, \"sect\"))\n"
"  hdr.ehsize = f16(ffi.sizeof(hdr))\n"
"  hdr.shentsize = f16(ffi.sizeof(o.sect[0]))\n"
"  hdr.shnum = f16(6)\n"
"  hdr.shstridx = f16(2)\n"
"\n"
"  -- Fill in sections and symbols.\n"
"  local sofs, ofs = ffi.offsetof(o, \"space\"), 1\n"
"  for i,name in ipairs{\n"
"      \".symtab\", \".shstrtab\", \".strtab\", \".rodata\", \".note.GNU-stack\",\n"
"    } do\n"
"    local sect = o.sect[i]\n"
"    sect.align = fofs(1)\n"
"    sect.name = f32(ofs)\n"
"    ffi.copy(o.space+ofs, name)\n"
"    ofs = ofs + #name+1\n"
"  end\n"
"  o.sect[1].type = f32(2) -- .symtab\n"
"  o.sect[1].link = f32(3)\n"
"  o.sect[1].info = f32(1)\n"
"  o.sect[1].align = fofs(8)\n"
"  o.sect[1].ofs = fofs(ffi.offsetof(o, \"sym\"))\n"
"  o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))\n"
"  o.sect[1].size = fofs(ffi.sizeof(o.sym))\n"
"  o.sym[1].name = f32(1)\n"
"  o.sym[1].sectidx = f16(4)\n"
"  o.sym[1].size = fofs(#s)\n"
"  o.sym[1].info = 17\n"
"  o.sect[2].type = f32(3) -- .shstrtab\n"
"  o.sect[2].ofs = fofs(sofs)\n"
"  o.sect[2].size = fofs(ofs)\n"
"  o.sect[3].type = f32(3) -- .strtab\n"
"  o.sect[3].ofs = fofs(sofs + ofs)\n"
"  o.sect[3].size = fofs(#symname+1)\n"
"  ffi.copy(o.space+ofs+1, symname)\n"
"  ofs = ofs + #symname + 2\n"
"  o.sect[4].type = f32(1) -- .rodata\n"
"  o.sect[4].flags = fofs(2)\n"
"  o.sect[4].ofs = fofs(sofs + ofs)\n"
"  o.sect[4].size = fofs(#s)\n"
"  o.sect[5].type = f32(1) -- .note.GNU-stack\n"
"  o.sect[5].ofs = fofs(sofs + ofs + #s)\n"
"\n"
"  -- Write ELF object file.\n"
"  local fp = savefile(output, \"wb\")\n"
"  fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))\n"
"  bcsave_tail(fp, output, s)\n"
"end\n"
"\n"
"local function bcsave_peobj(ctx, output, s, ffi)\n"
"  ffi.cdef[[\n"
"typedef struct {\n"
"  uint16_t arch, nsects;\n"
"  uint32_t time, symtabofs, nsyms;\n"
"  uint16_t opthdrsz, flags;\n"
"} PEheader;\n"
"typedef struct {\n"
"  char name[8];\n"
"  uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;\n"
"  uint16_t nreloc, nline;\n"
"  uint32_t flags;\n"
"} PEsection;\n"
"typedef struct __attribute((packed)) {\n"
"  union {\n"
"    char name[8];\n"
"    uint32_t nameref[2];\n"
"  };\n"
"  uint32_t value;\n"
"  int16_t sect;\n"
"  uint16_t type;\n"
"  uint8_t scl, naux;\n"
"} PEsym;\n"
"typedef struct __attribute((packed)) {\n"
"  uint32_t size;\n"
"  uint16_t nreloc, nline;\n"
"  uint32_t cksum;\n"
"  uint16_t assoc;\n"
"  uint8_t comdatsel, unused[3];\n"
"} PEsymaux;\n"
"typedef struct {\n"
"  PEheader hdr;\n"
"  PEsection sect[2];\n"
"  // Must be an even number of symbol structs.\n"
"  PEsym sym0;\n"
"  PEsymaux sym0aux;\n"
"  PEsym sym1;\n"
"  PEsymaux sym1aux;\n"
"  PEsym sym2;\n"
"  PEsym sym3;\n"
"  uint32_t strtabsize;\n"
"  uint8_t space[4096];\n"
"} PEobj;\n"
"]]\n"
"  local symname = LJBC_PREFIX..ctx.modname\n"
"  local is64 = false\n"
"  if ctx.arch == \"x86\" then\n"
"    symname = \"_\"..symname\n"
"  elseif ctx.arch == \"x64\" then\n"
"    is64 = true\n"
"  end\n"
"  local symexport = \"   /EXPORT:\"..symname..\",DATA \"\n"
"\n"
"  -- The file format is always little-endian. Swap if the host is big-endian.\n"
"  local function f32(x) return x end\n"
"  local f16 = f32\n"
"  if ffi.abi(\"be\") then\n"
"    f32 = bit.bswap\n"
"    function f16(x) return bit.rshift(bit.bswap(x), 16) end\n"
"  end\n"
"\n"
"  -- Create PE object and fill in header.\n"
"  local o = ffi.new(\"PEobj\")\n"
"  local hdr = o.hdr\n"
"  hdr.arch = f16(({ x86=0x14c, x64=0x8664, arm=0x1c0, ppc=0x1f2, mips=0x366, mipsel=0x366 })[ctx.arch])\n"
"  hdr.nsects = f16(2)\n"
"  hdr.symtabofs = f32(ffi.offsetof(o, \"sym0\"))\n"
"  hdr.nsyms = f32(6)\n"
"\n"
"  -- Fill in sections and symbols.\n"
"  o.sect[0].name = \".drectve\"\n"
"  o.sect[0].size = f32(#symexport)\n"
"  o.sect[0].flags = f32(0x00100a00)\n"
"  o.sym0.sect = f16(1)\n"
"  o.sym0.scl = 3\n"
"  o.sym0.name = \".drectve\"\n"
"  o.sym0.naux = 1\n"
"  o.sym0aux.size = f32(#symexport)\n"
"  o.sect[1].name = \".rdata\"\n"
"  o.sect[1].size = f32(#s)\n"
"  o.sect[1].flags = f32(0x40300040)\n"
"  o.sym1.sect = f16(2)\n"
"  o.sym1.scl = 3\n"
"  o.sym1.name = \".rdata\"\n"
"  o.sym1.naux = 1\n"
"  o.sym1aux.size = f32(#s)\n"
"  o.sym2.sect = f16(2)\n"
"  o.sym2.scl = 2\n"
"  o.sym2.nameref[1] = f32(4)\n"
"  o.sym3.sect = f16(-1)\n"
"  o.sym3.scl = 2\n"
"  o.sym3.value = f32(1)\n"
"  o.sym3.name = \"@feat.00\" -- Mark as SafeSEH compliant.\n"
"  ffi.copy(o.space, symname)\n"
"  local ofs = #symname + 1\n"
"  o.strtabsize = f32(ofs + 4)\n"
"  o.sect[0].ofs = f32(ffi.offsetof(o, \"space\") + ofs)\n"
"  ffi.copy(o.space + ofs, symexport)\n"
"  ofs = ofs + #symexport\n"
"  o.sect[1].ofs = f32(ffi.offsetof(o, \"space\") + ofs)\n"
"\n"
"  -- Write PE object file.\n"
"  local fp = savefile(output, \"wb\")\n"
"  fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))\n"
"  bcsave_tail(fp, output, s)\n"
"end\n"
"\n"
"local function bcsave_machobj(ctx, output, s, ffi)\n"
"  ffi.cdef[[\n"
"typedef struct\n"
"{\n"
"  uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;\n"
"} mach_header;\n"
"typedef struct\n"
"{\n"
"  mach_header; uint32_t reserved;\n"
"} mach_header_64;\n"
"typedef struct {\n"
"  uint32_t cmd, cmdsize;\n"
"  char segname[16];\n"
"  uint32_t vmaddr, vmsize, fileoff, filesize;\n"
"  uint32_t maxprot, initprot, nsects, flags;\n"
"} mach_segment_command;\n"
"typedef struct {\n"
"  uint32_t cmd, cmdsize;\n"
"  char segname[16];\n"
"  uint64_t vmaddr, vmsize, fileoff, filesize;\n"
"  uint32_t maxprot, initprot, nsects, flags;\n"
"} mach_segment_command_64;\n"
"typedef struct {\n"
"  char sectname[16], segname[16];\n"
"  uint32_t addr, size;\n"
"  uint32_t offset, align, reloff, nreloc, flags;\n"
"  uint32_t reserved1, reserved2;\n"
"} mach_section;\n"
"typedef struct {\n"
"  char sectname[16], segname[16];\n"
"  uint64_t addr, size;\n"
"  uint32_t offset, align, reloff, nreloc, flags;\n"
"  uint32_t reserved1, reserved2, reserved3;\n"
"} mach_section_64;\n"
"typedef struct {\n"
"  uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;\n"
"} mach_symtab_command;\n"
"typedef struct {\n"
"  int32_t strx;\n"
"  uint8_t type, sect;\n"
"  int16_t desc;\n"
"  uint32_t value;\n"
"} mach_nlist;\n"
"typedef struct {\n"
"  uint32_t strx;\n"
"  uint8_t type, sect;\n"
"  uint16_t desc;\n"
"  uint64_t value;\n"
"} mach_nlist_64;\n"
"typedef struct\n"
"{\n"
"  uint32_t magic, nfat_arch;\n"
"} mach_fat_header;\n"
"typedef struct\n"
"{\n"
"  uint32_t cputype, cpusubtype, offset, size, align;\n"
"} mach_fat_arch;\n"
"typedef struct {\n"
"  struct {\n"
"    mach_header hdr;\n"
"    mach_segment_command seg;\n"
"    mach_section sec;\n"
"    mach_symtab_command sym;\n"
"  } arch[1];\n"
"  mach_nlist sym_entry;\n"
"  uint8_t space[4096];\n"
"} mach_obj;\n"
"typedef struct {\n"
"  struct {\n"
"    mach_header_64 hdr;\n"
"    mach_segment_command_64 seg;\n"
"    mach_section_64 sec;\n"
"    mach_symtab_command sym;\n"
"  } arch[1];\n"
"  mach_nlist_64 sym_entry;\n"
"  uint8_t space[4096];\n"
"} mach_obj_64;\n"
"typedef struct {\n"
"  mach_fat_header fat;\n"
"  mach_fat_arch fat_arch[2];\n"
"  struct {\n"
"    mach_header hdr;\n"
"    mach_segment_command seg;\n"
"    mach_section sec;\n"
"    mach_symtab_command sym;\n"
"  } arch[2];\n"
"  mach_nlist sym_entry;\n"
"  uint8_t space[4096];\n"
"} mach_fat_obj;\n"
"]]\n"
"  local symname = '_'..LJBC_PREFIX..ctx.modname\n"
"  local isfat, is64, align, mobj = false, false, 4, \"mach_obj\"\n"
"  if ctx.arch == \"x64\" then\n"
"    is64, align, mobj = true, 8, \"mach_obj_64\"\n"
"  elseif ctx.arch == \"arm\" then\n"
"    isfat, mobj = true, \"mach_fat_obj\"\n"
"  elseif ctx.arch == \"arm64\" then\n"
"    is64, align, isfat, mobj = true, 8, true, \"mach_fat_obj\"\n"
"  else\n"
"    check(ctx.arch == \"x86\", \"unsupported architecture for OSX\")\n"
"  end\n"
"  local function aligned(v, a) return bit.band(v+a-1, -a) end\n"
"  local be32 = bit.bswap -- Mach-O FAT is BE, supported archs are LE.\n"
"\n"
"  -- Create Mach-O object and fill in header.\n"
"  local o = ffi.new(mobj)\n"
"  local mach_size = aligned(ffi.offsetof(o, \"space\")+#symname+2, align)\n"
"  local cputype = ({ x86={7}, x64={0x01000007}, arm={7,12}, arm64={0x01000007,0x0100000c} })[ctx.arch]\n"
"  local cpusubtype = ({ x86={3}, x64={3}, arm={3,9}, arm64={3,0} })[ctx.arch]\n"
"  if isfat then\n"
"    o.fat.magic = be32(0xcafebabe)\n"
"    o.fat.nfat_arch = be32(#cpusubtype)\n"
"  end\n"
"\n"
"  -- Fill in sections and symbols.\n"
"  for i=0,#cpusubtype-1 do\n"
"    local ofs = 0\n"
"    if isfat then\n"
"      local a = o.fat_arch[i]\n"
"      a.cputype = be32(cputype[i+1])\n"
"      a.cpusubtype = be32(cpusubtype[i+1])\n"
"      -- Subsequent slices overlap each other to share data.\n"
"      ofs = ffi.offsetof(o, \"arch\") + i*ffi.sizeof(o.arch[0])\n"
"      a.offset = be32(ofs)\n"
"      a.size = be32(mach_size-ofs+#s)\n"
"    end\n"
"    local a = o.arch[i]\n"
"    a.hdr.magic = is64 and 0xfeedfacf or 0xfeedface\n"
"    a.hdr.cputype = cputype[i+1]\n"
"    a.hdr.cpusubtype = cpusubtype[i+1]\n"
"    a.hdr.filetype = 1\n"
"    a.hdr.ncmds = 2\n"
"    a.hdr.sizeofcmds = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)+ffi.sizeof(a.sym)\n"
"    a.seg.cmd = is64 and 0x19 or 0x1\n"
"    a.seg.cmdsize = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)\n"
"    a.seg.vmsize = #s\n"
"    a.seg.fileoff = mach_size-ofs\n"
"    a.seg.filesize = #s\n"
"    a.seg.maxprot = 1\n"
"    a.seg.initprot = 1\n"
"    a.seg.nsects = 1\n"
"    ffi.copy(a.sec.sectname, \"__data\")\n"
"    ffi.copy(a.sec.segname, \"__DATA\")\n"
"    a.sec.size = #s\n"
"    a.sec.offset = mach_size-ofs\n"
"    a.sym.cmd = 2\n"
"    a.sym.cmdsize = ffi.sizeof(a.sym)\n"
"    a.sym.symoff = ffi.offsetof(o, \"sym_entry\")-ofs\n"
"    a.sym.nsyms = 1\n"
"    a.sym.stroff = ffi.offsetof(o, \"sym_entry\")+ffi.sizeof(o.sym_entry)-ofs\n"
"    a.sym.strsize = aligned(#symname+2, align)\n"
"  end\n"
"  o.sym_entry.type = 0xf\n"
"  o.sym_entry.sect = 1\n"
"  o.sym_entry.strx = 1\n"
"  ffi.copy(o.space+1, symname)\n"
"\n"
"  -- Write Macho-O object file.\n"
"  local fp = savefile(output, \"wb\")\n"
"  fp:write(ffi.string(o, mach_size))\n"
"  bcsave_tail(fp, output, s)\n"
"end\n"
"\n"
"local function bcsave_obj(ctx, output, s)\n"
"  local ok, ffi = pcall(require, \"ffi\")\n"
"  check(ok, \"FFI library required to write this file type\")\n"
"  if ctx.os == \"windows\" then\n"
"    return bcsave_peobj(ctx, output, s, ffi)\n"
"  elseif ctx.os == \"osx\" then\n"
"    return bcsave_machobj(ctx, output, s, ffi)\n"
"  else\n"
"    return bcsave_elfobj(ctx, output, s, ffi)\n"
"  end\n"
"end\n"
"\n"
"------------------------------------------------------------------------------\n"
"\n"
"local function bclist(input, output)\n"
"  local f = readfile(input)\n"
"  require(\"jit.bc\").dump(f, savefile(output, \"w\"), true)\n"
"end\n"
"\n"
"local function bcsave(ctx, input, output)\n"
"  local f = readfile(input)\n"
"  local s = string.dump(f, ctx.strip)\n"
"  local t = ctx.type\n"
"  if not t then\n"
"    t = detecttype(output)\n"
"    ctx.type = t\n"
"  end\n"
"  if t == \"raw\" then\n"
"    bcsave_raw(output, s)\n"
"  else\n"
"    if not ctx.modname then ctx.modname = detectmodname(input) end\n"
"    if t == \"obj\" then\n"
"      bcsave_obj(ctx, output, s)\n"
"    else\n"
"      bcsave_c(ctx, output, s)\n"
"    end\n"
"  end\n"
"end\n"
"\n"
"local function docmd(...)\n"
"  local arg = {...}\n"
"  local n = 1\n"
"  local list = false\n"
"  local ctx = {\n"
"    strip = true, arch = jit.arch, os = string.lower(jit.os),\n"
"    type = false, modname = false,\n"
"  }\n"
"  while n <= #arg do\n"
"    local a = arg[n]\n"
"    if type(a) == \"string\" and string.sub(a, 1, 1) == \"-\" and a ~= \"-\" then\n"
"      table.remove(arg, n)\n"
"      if a == \"--\" then break end\n"
"      for m=2,#a do\n"
"\tlocal opt = string.sub(a, m, m)\n"
"\tif opt == \"l\" then\n"
"\t  list = true\n"
"\telseif opt == \"s\" then\n"
"\t  ctx.strip = true\n"
"\telseif opt == \"g\" then\n"
"\t  ctx.strip = false\n"
"\telse\n"
"\t  if arg[n] == nil or m ~= #a then usage() end\n"
"\t  if opt == \"e\" then\n"
"\t    if n ~= 1 then usage() end\n"
"\t    arg[1] = check(loadstring(arg[1]))\n"
"\t  elseif opt == \"n\" then\n"
"\t    ctx.modname = checkmodname(table.remove(arg, n))\n"
"\t  elseif opt == \"t\" then\n"
"\t    ctx.type = checkarg(table.remove(arg, n), map_type, \"file type\")\n"
"\t  elseif opt == \"a\" then\n"
"\t    ctx.arch = checkarg(table.remove(arg, n), map_arch, \"architecture\")\n"
"\t  elseif opt == \"o\" then\n"
"\t    ctx.os = checkarg(table.remove(arg, n), map_os, \"OS name\")\n"
"\t  else\n"
"\t    usage()\n"
"\t  end\n"
"\tend\n"
"      end\n"
"    else\n"
"      n = n + 1\n"
"    end\n"
"  end\n"
"  if list then\n"
"    if #arg == 0 or #arg > 2 then usage() end\n"
"    bclist(arg[1], arg[2] or \"-\")\n"
"  else\n"
"    if #arg ~= 2 then usage() end\n"
"    bcsave(ctx, arg[1], arg[2])\n"
"  end\n"
"end\n"
"\n"
"------------------------------------------------------------------------------\n"
"\n"
"-- Public module functions.\n"
"return {\n"
"  start = docmd -- Process -b command line option.\n"
"}\n"
"\n"
""
;
