diff options
author | Mike Pall <mike> | 2012-06-08 20:54:47 +0200 |
---|---|---|
committer | Mike Pall <mike> | 2012-06-08 21:14:33 +0200 |
commit | cccf7638989275e57377e2e25595213caacb7da3 (patch) | |
tree | 40aa697533f22adbd3cbef8f36148abb29ec0de1 /lib/bcsave.lua | |
parent | 7266e27404caf007e3ff3fc4e204ea6d4c6a4670 (diff) | |
download | luajit-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.lua | 500 |
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 | |||
13 | local jit = require("jit") | ||
14 | assert(jit.version_num == 20000, "LuaJIT core/library version mismatch") | ||
15 | |||
16 | -- Symbol name prefix for LuaJIT bytecode. | ||
17 | local LJBC_PREFIX = "luaJIT_BC_" | ||
18 | |||
19 | ------------------------------------------------------------------------------ | ||
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 | -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 | |||
35 | File types: c h obj o raw (default) | ||
36 | ]] | ||
37 | os.exit(1) | ||
38 | end | ||
39 | |||
40 | local function check(ok, ...) | ||
41 | if ok then return ok, ... end | ||
42 | io.stderr:write("luajit: ", ...) | ||
43 | io.stderr:write("\n") | ||
44 | os.exit(1) | ||
45 | end | ||
46 | |||
47 | local function readfile(input) | ||
48 | if type(input) == "function" then return input end | ||
49 | if input == "-" then input = nil end | ||
50 | return check(loadfile(input)) | ||
51 | end | ||
52 | |||
53 | local function savefile(name, mode) | ||
54 | if name == "-" then return io.stdout end | ||
55 | return check(io.open(name, mode)) | ||
56 | end | ||
57 | |||
58 | ------------------------------------------------------------------------------ | ||
59 | |||
60 | local map_type = { | ||
61 | raw = "raw", c = "c", h = "h", o = "obj", obj = "obj", | ||
62 | } | ||
63 | |||
64 | local map_arch = { | ||
65 | x86 = true, x64 = true, arm = true, ppc = true, ppcspe = true, | ||
66 | mips = true, mipsel = true, | ||
67 | } | ||
68 | |||
69 | local map_os = { | ||
70 | linux = true, windows = true, osx = true, freebsd = true, netbsd = true, | ||
71 | openbsd = true, solaris = true, | ||
72 | } | ||
73 | |||
74 | local 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 | ||
78 | end | ||
79 | |||
80 | local function detecttype(str) | ||
81 | local ext = string.match(string.lower(str), "%.(%a+)$") | ||
82 | return map_type[ext] or "raw" | ||
83 | end | ||
84 | |||
85 | local function checkmodname(str) | ||
86 | check(string.match(str, "^[%w_.%-]+$"), "bad module name") | ||
87 | return string.gsub(str, "[%.%-]", "_") | ||
88 | end | ||
89 | |||
90 | local 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, "[%.%-]", "_") | ||
102 | end | ||
103 | |||
104 | ------------------------------------------------------------------------------ | ||
105 | |||
106 | local 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) | ||
110 | end | ||
111 | |||
112 | local function bcsave_raw(output, s) | ||
113 | local fp = savefile(output, "wb") | ||
114 | bcsave_tail(fp, output, s) | ||
115 | end | ||
116 | |||
117 | local 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 | ||
122 | extern "C" | ||
123 | #endif | ||
124 | #ifdef _WIN32 | ||
125 | __declspec(dllexport) | ||
126 | #endif | ||
127 | const char %s%s[] = { | ||
128 | ]], LJBC_PREFIX, ctx.modname)) | ||
129 | else | ||
130 | fp:write(string.format([[ | ||
131 | #define %s%s_SIZE %d | ||
132 | static 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") | ||
147 | end | ||
148 | |||
149 | local function bcsave_elfobj(ctx, output, s, ffi) | ||
150 | ffi.cdef[[ | ||
151 | typedef 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; | ||
159 | typedef 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; | ||
167 | typedef struct { | ||
168 | uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize; | ||
169 | } ELF32sectheader; | ||
170 | typedef 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; | ||
176 | typedef struct { | ||
177 | uint32_t name, value, size; | ||
178 | uint8_t info, other; | ||
179 | uint16_t sectidx; | ||
180 | } ELF32symbol; | ||
181 | typedef struct { | ||
182 | uint32_t name; | ||
183 | uint8_t info, other; | ||
184 | uint16_t sectidx; | ||
185 | uint64_t value, size; | ||
186 | } ELF64symbol; | ||
187 | typedef struct { | ||
188 | ELF32header hdr; | ||
189 | ELF32sectheader sect[6]; | ||
190 | ELF32symbol sym[2]; | ||
191 | uint8_t space[4096]; | ||
192 | } ELF32obj; | ||
193 | typedef 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) | ||
290 | end | ||
291 | |||
292 | local function bcsave_peobj(ctx, output, s, ffi) | ||
293 | ffi.cdef[[ | ||
294 | typedef struct { | ||
295 | uint16_t arch, nsects; | ||
296 | uint32_t time, symtabofs, nsyms; | ||
297 | uint16_t opthdrsz, flags; | ||
298 | } PEheader; | ||
299 | typedef 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; | ||
305 | typedef 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; | ||
315 | typedef 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; | ||
322 | typedef 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) | ||
397 | end | ||
398 | |||
399 | local function bcsave_machobj(ctx, output, s, ffi) | ||
400 | check(false, "NYI: no support for writing OSX object files") | ||
401 | end | ||
402 | |||
403 | local 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 | ||
413 | end | ||
414 | |||
415 | ------------------------------------------------------------------------------ | ||
416 | |||
417 | local function bclist(input, output) | ||
418 | local f = readfile(input) | ||
419 | require("jit.bc").dump(f, savefile(output, "w"), true) | ||
420 | end | ||
421 | |||
422 | local 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 | ||
440 | end | ||
441 | |||
442 | local 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 | ||
492 | end | ||
493 | |||
494 | ------------------------------------------------------------------------------ | ||
495 | |||
496 | -- Public module functions. | ||
497 | module(...) | ||
498 | |||
499 | start = docmd -- Process -b command line option. | ||
500 | |||