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