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