summaryrefslogtreecommitdiff
path: root/lib/bcsave.lua
diff options
context:
space:
mode:
authorMike Pall <mike>2012-06-08 20:54:47 +0200
committerMike Pall <mike>2012-06-08 21:14:33 +0200
commitcccf7638989275e57377e2e25595213caacb7da3 (patch)
tree40aa697533f22adbd3cbef8f36148abb29ec0de1 /lib/bcsave.lua
parent7266e27404caf007e3ff3fc4e204ea6d4c6a4670 (diff)
downloadluajit-cccf7638989275e57377e2e25595213caacb7da3.tar.gz
luajit-cccf7638989275e57377e2e25595213caacb7da3.tar.bz2
luajit-cccf7638989275e57377e2e25595213caacb7da3.zip
Move lib/* to src/jit/*.
Diffstat (limited to 'lib/bcsave.lua')
-rw-r--r--lib/bcsave.lua500
1 files changed, 0 insertions, 500 deletions
diff --git a/lib/bcsave.lua b/lib/bcsave.lua
deleted file mode 100644
index 70cb6925..00000000
--- a/lib/bcsave.lua
+++ /dev/null
@@ -1,500 +0,0 @@
1----------------------------------------------------------------------------
2-- LuaJIT module to save/list bytecode.
3--
4-- Copyright (C) 2005-2012 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
13local jit = require("jit")
14assert(jit.version_num == 20000, "LuaJIT core/library version mismatch")
15
16-- Symbol name prefix for LuaJIT bytecode.
17local LJBC_PREFIX = "luaJIT_BC_"
18
19------------------------------------------------------------------------------
20
21local function usage()
22 io.stderr:write[[
23Save LuaJIT bytecode: luajit -b[options] input output
24 -l Only list bytecode.
25 -s Strip debug info (default).
26 -g Keep debug info.
27 -n name Set module name (default: auto-detect from input name).
28 -t type Set output file type (default: auto-detect from output name).
29 -a arch Override architecture for object files (default: native).
30 -o os Override OS for object files (default: native).
31 -e chunk Use chunk string as input.
32 -- Stop handling options.
33 - Use stdin as input and/or stdout as output.
34
35File types: c h obj o raw (default)
36]]
37 os.exit(1)
38end
39
40local function check(ok, ...)
41 if ok then return ok, ... end
42 io.stderr:write("luajit: ", ...)
43 io.stderr:write("\n")
44 os.exit(1)
45end
46
47local function readfile(input)
48 if type(input) == "function" then return input end
49 if input == "-" then input = nil end
50 return check(loadfile(input))
51end
52
53local function savefile(name, mode)
54 if name == "-" then return io.stdout end
55 return check(io.open(name, mode))
56end
57
58------------------------------------------------------------------------------
59
60local map_type = {
61 raw = "raw", c = "c", h = "h", o = "obj", obj = "obj",
62}
63
64local map_arch = {
65 x86 = true, x64 = true, arm = true, ppc = true, ppcspe = true,
66 mips = true, mipsel = true,
67}
68
69local map_os = {
70 linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
71 openbsd = true, solaris = true,
72}
73
74local function checkarg(str, map, err)
75 str = string.lower(str)
76 local s = check(map[str], "unknown ", err)
77 return s == true and str or s
78end
79
80local function detecttype(str)
81 local ext = string.match(string.lower(str), "%.(%a+)$")
82 return map_type[ext] or "raw"
83end
84
85local function checkmodname(str)
86 check(string.match(str, "^[%w_.%-]+$"), "bad module name")
87 return string.gsub(str, "[%.%-]", "_")
88end
89
90local function detectmodname(str)
91 if type(str) == "string" then
92 local tail = string.match(str, "[^/\\]+$")
93 if tail then str = tail end
94 local head = string.match(str, "^(.*)%.[^.]*$")
95 if head then str = head end
96 str = string.match(str, "^[%w_.%-]+")
97 else
98 str = nil
99 end
100 check(str, "cannot derive module name, use -n name")
101 return string.gsub(str, "[%.%-]", "_")
102end
103
104------------------------------------------------------------------------------
105
106local function bcsave_tail(fp, output, s)
107 local ok, err = fp:write(s)
108 if ok and output ~= "-" then ok, err = fp:close() end
109 check(ok, "cannot write ", output, ": ", err)
110end
111
112local function bcsave_raw(output, s)
113 local fp = savefile(output, "wb")
114 bcsave_tail(fp, output, s)
115end
116
117local function bcsave_c(ctx, output, s)
118 local fp = savefile(output, "w")
119 if ctx.type == "c" then
120 fp:write(string.format([[
121#ifdef _cplusplus
122extern "C"
123#endif
124#ifdef _WIN32
125__declspec(dllexport)
126#endif
127const char %s%s[] = {
128]], LJBC_PREFIX, ctx.modname))
129 else
130 fp:write(string.format([[
131#define %s%s_SIZE %d
132static const char %s%s[] = {
133]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))
134 end
135 local t, n, m = {}, 0, 0
136 for i=1,#s do
137 local b = tostring(string.byte(s, i))
138 m = m + #b + 1
139 if m > 78 then
140 fp:write(table.concat(t, ",", 1, n), ",\n")
141 n, m = 0, #b + 1
142 end
143 n = n + 1
144 t[n] = b
145 end
146 bcsave_tail(fp, output, table.concat(t, ",", 1, n).."\n};\n")
147end
148
149local function bcsave_elfobj(ctx, output, s, ffi)
150 ffi.cdef[[
151typedef struct {
152 uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
153 uint16_t type, machine;
154 uint32_t version;
155 uint32_t entry, phofs, shofs;
156 uint32_t flags;
157 uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
158} ELF32header;
159typedef struct {
160 uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
161 uint16_t type, machine;
162 uint32_t version;
163 uint64_t entry, phofs, shofs;
164 uint32_t flags;
165 uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
166} ELF64header;
167typedef struct {
168 uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
169} ELF32sectheader;
170typedef struct {
171 uint32_t name, type;
172 uint64_t flags, addr, ofs, size;
173 uint32_t link, info;
174 uint64_t align, entsize;
175} ELF64sectheader;
176typedef struct {
177 uint32_t name, value, size;
178 uint8_t info, other;
179 uint16_t sectidx;
180} ELF32symbol;
181typedef struct {
182 uint32_t name;
183 uint8_t info, other;
184 uint16_t sectidx;
185 uint64_t value, size;
186} ELF64symbol;
187typedef struct {
188 ELF32header hdr;
189 ELF32sectheader sect[6];
190 ELF32symbol sym[2];
191 uint8_t space[4096];
192} ELF32obj;
193typedef struct {
194 ELF64header hdr;
195 ELF64sectheader sect[6];
196 ELF64symbol sym[2];
197 uint8_t space[4096];
198} ELF64obj;
199]]
200 local symname = LJBC_PREFIX..ctx.modname
201 local is64, isbe = false, false
202 if ctx.arch == "x64" then
203 is64 = true
204 elseif ctx.arch == "ppc" or ctx.arch == "ppcspe" or ctx.arch == "mips" then
205 isbe = true
206 end
207
208 -- Handle different host/target endianess.
209 local function f32(x) return x end
210 local f16, fofs = f32, f32
211 if ffi.abi("be") ~= isbe then
212 f32 = bit.bswap
213 function f16(x) return bit.rshift(bit.bswap(x), 16) end
214 if is64 then
215 function fofs(x) return bit.bswap(x)*(2ll^32) end
216 else
217 fofs = f32
218 end
219 end
220
221 -- Create ELF object and fill in header.
222 local o = ffi.new(is64 and "ELF64obj" or "ELF32obj")
223 local hdr = o.hdr
224 if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi.
225 local bf = assert(io.open("/bin/ls", "rb"))
226 local bs = bf:read(9)
227 bf:close()
228 ffi.copy(o, bs, 9)
229 check(hdr.emagic[0] == 127, "no support for writing native object files")
230 else
231 hdr.emagic = "\127ELF"
232 hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0
233 end
234 hdr.eclass = is64 and 2 or 1
235 hdr.eendian = isbe and 2 or 1
236 hdr.eversion = 1
237 hdr.type = f16(1)
238 hdr.machine = f16(({ x86=3, x64=62, arm=40, ppc=20, ppcspe=20, mips=8, mipsel=8 })[ctx.arch])
239 if ctx.arch == "mips" or ctx.arch == "mipsel" then
240 hdr.flags = 0x50001006
241 end
242 hdr.version = f32(1)
243 hdr.shofs = fofs(ffi.offsetof(o, "sect"))
244 hdr.ehsize = f16(ffi.sizeof(hdr))
245 hdr.shentsize = f16(ffi.sizeof(o.sect[0]))
246 hdr.shnum = f16(6)
247 hdr.shstridx = f16(2)
248
249 -- Fill in sections and symbols.
250 local sofs, ofs = ffi.offsetof(o, "space"), 1
251 for i,name in ipairs{
252 ".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack",
253 } do
254 local sect = o.sect[i]
255 sect.align = fofs(1)
256 sect.name = f32(ofs)
257 ffi.copy(o.space+ofs, name)
258 ofs = ofs + #name+1
259 end
260 o.sect[1].type = f32(2) -- .symtab
261 o.sect[1].link = f32(3)
262 o.sect[1].info = f32(1)
263 o.sect[1].align = fofs(8)
264 o.sect[1].ofs = fofs(ffi.offsetof(o, "sym"))
265 o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))
266 o.sect[1].size = fofs(ffi.sizeof(o.sym))
267 o.sym[1].name = f32(1)
268 o.sym[1].sectidx = f16(4)
269 o.sym[1].size = fofs(#s)
270 o.sym[1].info = 17
271 o.sect[2].type = f32(3) -- .shstrtab
272 o.sect[2].ofs = fofs(sofs)
273 o.sect[2].size = fofs(ofs)
274 o.sect[3].type = f32(3) -- .strtab
275 o.sect[3].ofs = fofs(sofs + ofs)
276 o.sect[3].size = fofs(#symname+1)
277 ffi.copy(o.space+ofs+1, symname)
278 ofs = ofs + #symname + 2
279 o.sect[4].type = f32(1) -- .rodata
280 o.sect[4].flags = fofs(2)
281 o.sect[4].ofs = fofs(sofs + ofs)
282 o.sect[4].size = fofs(#s)
283 o.sect[5].type = f32(1) -- .note.GNU-stack
284 o.sect[5].ofs = fofs(sofs + ofs + #s)
285
286 -- Write ELF object file.
287 local fp = savefile(output, "wb")
288 fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
289 bcsave_tail(fp, output, s)
290end
291
292local function bcsave_peobj(ctx, output, s, ffi)
293 ffi.cdef[[
294typedef struct {
295 uint16_t arch, nsects;
296 uint32_t time, symtabofs, nsyms;
297 uint16_t opthdrsz, flags;
298} PEheader;
299typedef struct {
300 char name[8];
301 uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
302 uint16_t nreloc, nline;
303 uint32_t flags;
304} PEsection;
305typedef struct __attribute((packed)) {
306 union {
307 char name[8];
308 uint32_t nameref[2];
309 };
310 uint32_t value;
311 int16_t sect;
312 uint16_t type;
313 uint8_t scl, naux;
314} PEsym;
315typedef struct __attribute((packed)) {
316 uint32_t size;
317 uint16_t nreloc, nline;
318 uint32_t cksum;
319 uint16_t assoc;
320 uint8_t comdatsel, unused[3];
321} PEsymaux;
322typedef struct {
323 PEheader hdr;
324 PEsection sect[2];
325 // Must be an even number of symbol structs.
326 PEsym sym0;
327 PEsymaux sym0aux;
328 PEsym sym1;
329 PEsymaux sym1aux;
330 PEsym sym2;
331 PEsym sym3;
332 uint32_t strtabsize;
333 uint8_t space[4096];
334} PEobj;
335]]
336 local symname = LJBC_PREFIX..ctx.modname
337 local is64 = false
338 if ctx.arch == "x86" then
339 symname = "_"..symname
340 elseif ctx.arch == "x64" then
341 is64 = true
342 end
343 local symexport = " /EXPORT:"..symname..",DATA "
344
345 -- The file format is always little-endian. Swap if the host is big-endian.
346 local function f32(x) return x end
347 local f16 = f32
348 if ffi.abi("be") then
349 f32 = bit.bswap
350 function f16(x) return bit.rshift(bit.bswap(x), 16) end
351 end
352
353 -- Create PE object and fill in header.
354 local o = ffi.new("PEobj")
355 local hdr = o.hdr
356 hdr.arch = f16(({ x86=0x14c, x64=0x8664, arm=0x1c0, ppc=0x1f2, mips=0x366, mipsel=0x366 })[ctx.arch])
357 hdr.nsects = f16(2)
358 hdr.symtabofs = f32(ffi.offsetof(o, "sym0"))
359 hdr.nsyms = f32(6)
360
361 -- Fill in sections and symbols.
362 o.sect[0].name = ".drectve"
363 o.sect[0].size = f32(#symexport)
364 o.sect[0].flags = f32(0x00100a00)
365 o.sym0.sect = f16(1)
366 o.sym0.scl = 3
367 o.sym0.name = ".drectve"
368 o.sym0.naux = 1
369 o.sym0aux.size = f32(#symexport)
370 o.sect[1].name = ".rdata"
371 o.sect[1].size = f32(#s)
372 o.sect[1].flags = f32(0x40300040)
373 o.sym1.sect = f16(2)
374 o.sym1.scl = 3
375 o.sym1.name = ".rdata"
376 o.sym1.naux = 1
377 o.sym1aux.size = f32(#s)
378 o.sym2.sect = f16(2)
379 o.sym2.scl = 2
380 o.sym2.nameref[1] = f32(4)
381 o.sym3.sect = f16(-1)
382 o.sym3.scl = 2
383 o.sym3.value = f32(1)
384 o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant.
385 ffi.copy(o.space, symname)
386 local ofs = #symname + 1
387 o.strtabsize = f32(ofs + 4)
388 o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs)
389 ffi.copy(o.space + ofs, symexport)
390 ofs = ofs + #symexport
391 o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs)
392
393 -- Write PE object file.
394 local fp = savefile(output, "wb")
395 fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
396 bcsave_tail(fp, output, s)
397end
398
399local function bcsave_machobj(ctx, output, s, ffi)
400 check(false, "NYI: no support for writing OSX object files")
401end
402
403local function bcsave_obj(ctx, output, s)
404 local ok, ffi = pcall(require, "ffi")
405 check(ok, "FFI library required to write this file type")
406 if ctx.os == "windows" then
407 return bcsave_peobj(ctx, output, s, ffi)
408 elseif ctx.os == "osx" then
409 return bcsave_machobj(ctx, output, s, ffi)
410 else
411 return bcsave_elfobj(ctx, output, s, ffi)
412 end
413end
414
415------------------------------------------------------------------------------
416
417local function bclist(input, output)
418 local f = readfile(input)
419 require("jit.bc").dump(f, savefile(output, "w"), true)
420end
421
422local function bcsave(ctx, input, output)
423 local f = readfile(input)
424 local s = string.dump(f, ctx.strip)
425 local t = ctx.type
426 if not t then
427 t = detecttype(output)
428 ctx.type = t
429 end
430 if t == "raw" then
431 bcsave_raw(output, s)
432 else
433 if not ctx.modname then ctx.modname = detectmodname(input) end
434 if t == "obj" then
435 bcsave_obj(ctx, output, s)
436 else
437 bcsave_c(ctx, output, s)
438 end
439 end
440end
441
442local function docmd(...)
443 local arg = {...}
444 local n = 1
445 local list = false
446 local ctx = {
447 strip = true, arch = jit.arch, os = string.lower(jit.os),
448 type = false, modname = false,
449 }
450 while n <= #arg do
451 local a = arg[n]
452 if type(a) == "string" and string.sub(a, 1, 1) == "-" and a ~= "-" then
453 table.remove(arg, n)
454 if a == "--" then break end
455 for m=2,#a do
456 local opt = string.sub(a, m, m)
457 if opt == "l" then
458 list = true
459 elseif opt == "s" then
460 ctx.strip = true
461 elseif opt == "g" then
462 ctx.strip = false
463 else
464 if arg[n] == nil or m ~= #a then usage() end
465 if opt == "e" then
466 if n ~= 1 then usage() end
467 arg[1] = check(loadstring(arg[1]))
468 elseif opt == "n" then
469 ctx.modname = checkmodname(table.remove(arg, n))
470 elseif opt == "t" then
471 ctx.type = checkarg(table.remove(arg, n), map_type, "file type")
472 elseif opt == "a" then
473 ctx.arch = checkarg(table.remove(arg, n), map_arch, "architecture")
474 elseif opt == "o" then
475 ctx.os = checkarg(table.remove(arg, n), map_os, "OS name")
476 else
477 usage()
478 end
479 end
480 end
481 else
482 n = n + 1
483 end
484 end
485 if list then
486 if #arg == 0 or #arg > 2 then usage() end
487 bclist(arg[1], arg[2] or "-")
488 else
489 if #arg ~= 2 then usage() end
490 bcsave(ctx, arg[1], arg[2])
491 end
492end
493
494------------------------------------------------------------------------------
495
496-- Public module functions.
497module(...)
498
499start = docmd -- Process -b command line option.
500