Mercurial
comparison third_party/luajit/src/jit/bcsave.lua @ 186:8cf4ec5e2191 hg-web
Fixed merge conflict.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 23 Jan 2026 22:38:59 -0800 |
| parents | 94705b5986b3 |
| children |
comparison
equal
deleted
inserted
replaced
| 176:fed99fc04e12 | 186:8cf4ec5e2191 |
|---|---|
| 1 ---------------------------------------------------------------------------- | |
| 2 -- LuaJIT module to save/list bytecode. | |
| 3 -- | |
| 4 -- Copyright (C) 2005-2023 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 == 20199, "LuaJIT core/library version mismatch") | |
| 15 local bit = require("bit") | |
| 16 | |
| 17 -- Symbol name prefix for LuaJIT bytecode. | |
| 18 local LJBC_PREFIX = "luaJIT_BC_" | |
| 19 | |
| 20 local type, assert = type, assert | |
| 21 local format = string.format | |
| 22 local tremove, tconcat = table.remove, table.concat | |
| 23 | |
| 24 ------------------------------------------------------------------------------ | |
| 25 | |
| 26 local function usage() | |
| 27 io.stderr:write[[ | |
| 28 Save LuaJIT bytecode: luajit -b[options] input output | |
| 29 -l Only list bytecode. | |
| 30 -s Strip debug info (default). | |
| 31 -g Keep debug info. | |
| 32 -n name Set module name (default: auto-detect from input name). | |
| 33 -t type Set output file type (default: auto-detect from output name). | |
| 34 -a arch Override architecture for object files (default: native). | |
| 35 -o os Override OS for object files (default: native). | |
| 36 -F name Override filename (default: input filename). | |
| 37 -e chunk Use chunk string as input. | |
| 38 -- Stop handling options. | |
| 39 - Use stdin as input and/or stdout as output. | |
| 40 | |
| 41 File types: c h obj o raw (default) | |
| 42 ]] | |
| 43 os.exit(1) | |
| 44 end | |
| 45 | |
| 46 local function check(ok, ...) | |
| 47 if ok then return ok, ... end | |
| 48 io.stderr:write("luajit: ", ...) | |
| 49 io.stderr:write("\n") | |
| 50 os.exit(1) | |
| 51 end | |
| 52 | |
| 53 local function readfile(ctx, input) | |
| 54 if type(input) == "function" then return input end | |
| 55 if ctx.filename then | |
| 56 local data | |
| 57 if input == "-" then | |
| 58 data = io.stdin:read("*a") | |
| 59 else | |
| 60 local fp = assert(io.open(input, "rb")) | |
| 61 data = assert(fp:read("*a")) | |
| 62 assert(fp:close()) | |
| 63 end | |
| 64 return check(load(data, ctx.filename)) | |
| 65 else | |
| 66 if input == "-" then input = nil end | |
| 67 return check(loadfile(input)) | |
| 68 end | |
| 69 end | |
| 70 | |
| 71 local function savefile(name, mode) | |
| 72 if name == "-" then return io.stdout end | |
| 73 return check(io.open(name, mode)) | |
| 74 end | |
| 75 | |
| 76 local function set_stdout_binary(ffi) | |
| 77 ffi.cdef[[int _setmode(int fd, int mode);]] | |
| 78 ffi.C._setmode(1, 0x8000) | |
| 79 end | |
| 80 | |
| 81 ------------------------------------------------------------------------------ | |
| 82 | |
| 83 local map_type = { | |
| 84 raw = "raw", c = "c", h = "h", o = "obj", obj = "obj", | |
| 85 } | |
| 86 | |
| 87 local map_arch = { | |
| 88 x86 = { e = "le", b = 32, m = 3, p = 0x14c, }, | |
| 89 x64 = { e = "le", b = 64, m = 62, p = 0x8664, }, | |
| 90 arm = { e = "le", b = 32, m = 40, p = 0x1c0, }, | |
| 91 arm64 = { e = "le", b = 64, m = 183, p = 0xaa64, }, | |
| 92 arm64be = { e = "be", b = 64, m = 183, }, | |
| 93 ppc = { e = "be", b = 32, m = 20, }, | |
| 94 mips = { e = "be", b = 32, m = 8, f = 0x50001006, }, | |
| 95 mipsel = { e = "le", b = 32, m = 8, f = 0x50001006, }, | |
| 96 mips64 = { e = "be", b = 64, m = 8, f = 0x80000007, }, | |
| 97 mips64el = { e = "le", b = 64, m = 8, f = 0x80000007, }, | |
| 98 mips64r6 = { e = "be", b = 64, m = 8, f = 0xa0000407, }, | |
| 99 mips64r6el = { e = "le", b = 64, m = 8, f = 0xa0000407, }, | |
| 100 } | |
| 101 | |
| 102 local map_os = { | |
| 103 linux = true, windows = true, osx = true, freebsd = true, netbsd = true, | |
| 104 openbsd = true, dragonfly = true, solaris = true, | |
| 105 } | |
| 106 | |
| 107 local function checkarg(str, map, err) | |
| 108 str = str:lower() | |
| 109 local s = check(map[str], "unknown ", err) | |
| 110 return type(s) == "string" and s or str | |
| 111 end | |
| 112 | |
| 113 local function detecttype(str) | |
| 114 local ext = str:lower():match("%.(%a+)$") | |
| 115 return map_type[ext] or "raw" | |
| 116 end | |
| 117 | |
| 118 local function checkmodname(str) | |
| 119 check(str:match("^[%w_.%-]+$"), "bad module name") | |
| 120 return str:gsub("[%.%-]", "_") | |
| 121 end | |
| 122 | |
| 123 local function detectmodname(str) | |
| 124 if type(str) == "string" then | |
| 125 local tail = str:match("[^/\\]+$") | |
| 126 if tail then str = tail end | |
| 127 local head = str:match("^(.*)%.[^.]*$") | |
| 128 if head then str = head end | |
| 129 str = str:match("^[%w_.%-]+") | |
| 130 else | |
| 131 str = nil | |
| 132 end | |
| 133 check(str, "cannot derive module name, use -n name") | |
| 134 return str:gsub("[%.%-]", "_") | |
| 135 end | |
| 136 | |
| 137 ------------------------------------------------------------------------------ | |
| 138 | |
| 139 local function bcsave_tail(fp, output, s) | |
| 140 local ok, err = fp:write(s) | |
| 141 if ok and output ~= "-" then ok, err = fp:close() end | |
| 142 check(ok, "cannot write ", output, ": ", err) | |
| 143 end | |
| 144 | |
| 145 local function bcsave_raw(output, s) | |
| 146 if output == "-" and jit.os == "Windows" then | |
| 147 local ok, ffi = pcall(require, "ffi") | |
| 148 check(ok, "FFI library required to write binary file to stdout") | |
| 149 set_stdout_binary(ffi) | |
| 150 end | |
| 151 local fp = savefile(output, "wb") | |
| 152 bcsave_tail(fp, output, s) | |
| 153 end | |
| 154 | |
| 155 local function bcsave_c(ctx, output, s) | |
| 156 local fp = savefile(output, "w") | |
| 157 if ctx.type == "c" then | |
| 158 fp:write(format([[ | |
| 159 #ifdef __cplusplus | |
| 160 extern "C" | |
| 161 #endif | |
| 162 #ifdef _WIN32 | |
| 163 __declspec(dllexport) | |
| 164 #endif | |
| 165 const unsigned char %s%s[] = { | |
| 166 ]], LJBC_PREFIX, ctx.modname)) | |
| 167 else | |
| 168 fp:write(format([[ | |
| 169 #define %s%s_SIZE %d | |
| 170 static const unsigned char %s%s[] = { | |
| 171 ]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname)) | |
| 172 end | |
| 173 local t, n, m = {}, 0, 0 | |
| 174 for i=1,#s do | |
| 175 local b = tostring(string.byte(s, i)) | |
| 176 m = m + #b + 1 | |
| 177 if m > 78 then | |
| 178 fp:write(tconcat(t, ",", 1, n), ",\n") | |
| 179 n, m = 0, #b + 1 | |
| 180 end | |
| 181 n = n + 1 | |
| 182 t[n] = b | |
| 183 end | |
| 184 bcsave_tail(fp, output, tconcat(t, ",", 1, n).."\n};\n") | |
| 185 end | |
| 186 | |
| 187 local function bcsave_elfobj(ctx, output, s, ffi) | |
| 188 ffi.cdef[[ | |
| 189 typedef struct { | |
| 190 uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; | |
| 191 uint16_t type, machine; | |
| 192 uint32_t version; | |
| 193 uint32_t entry, phofs, shofs; | |
| 194 uint32_t flags; | |
| 195 uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; | |
| 196 } ELF32header; | |
| 197 typedef struct { | |
| 198 uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; | |
| 199 uint16_t type, machine; | |
| 200 uint32_t version; | |
| 201 uint64_t entry, phofs, shofs; | |
| 202 uint32_t flags; | |
| 203 uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; | |
| 204 } ELF64header; | |
| 205 typedef struct { | |
| 206 uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize; | |
| 207 } ELF32sectheader; | |
| 208 typedef struct { | |
| 209 uint32_t name, type; | |
| 210 uint64_t flags, addr, ofs, size; | |
| 211 uint32_t link, info; | |
| 212 uint64_t align, entsize; | |
| 213 } ELF64sectheader; | |
| 214 typedef struct { | |
| 215 uint32_t name, value, size; | |
| 216 uint8_t info, other; | |
| 217 uint16_t sectidx; | |
| 218 } ELF32symbol; | |
| 219 typedef struct { | |
| 220 uint32_t name; | |
| 221 uint8_t info, other; | |
| 222 uint16_t sectidx; | |
| 223 uint64_t value, size; | |
| 224 } ELF64symbol; | |
| 225 typedef struct { | |
| 226 ELF32header hdr; | |
| 227 ELF32sectheader sect[6]; | |
| 228 ELF32symbol sym[2]; | |
| 229 uint8_t space[4096]; | |
| 230 } ELF32obj; | |
| 231 typedef struct { | |
| 232 ELF64header hdr; | |
| 233 ELF64sectheader sect[6]; | |
| 234 ELF64symbol sym[2]; | |
| 235 uint8_t space[4096]; | |
| 236 } ELF64obj; | |
| 237 ]] | |
| 238 local symname = LJBC_PREFIX..ctx.modname | |
| 239 local ai = assert(map_arch[ctx.arch]) | |
| 240 local is64, isbe = ai.b == 64, ai.e == "be" | |
| 241 | |
| 242 -- Handle different host/target endianess. | |
| 243 local function f32(x) return x end | |
| 244 local f16, fofs = f32, f32 | |
| 245 if ffi.abi("be") ~= isbe then | |
| 246 f32 = bit.bswap | |
| 247 function f16(x) return bit.rshift(bit.bswap(x), 16) end | |
| 248 if is64 then | |
| 249 local two32 = ffi.cast("int64_t", 2^32) | |
| 250 function fofs(x) return bit.bswap(x)*two32 end | |
| 251 else | |
| 252 fofs = f32 | |
| 253 end | |
| 254 end | |
| 255 | |
| 256 -- Create ELF object and fill in header. | |
| 257 local o = ffi.new(is64 and "ELF64obj" or "ELF32obj") | |
| 258 local hdr = o.hdr | |
| 259 if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi. | |
| 260 local bf = assert(io.open("/bin/ls", "rb")) | |
| 261 local bs = bf:read(9) | |
| 262 bf:close() | |
| 263 ffi.copy(o, bs, 9) | |
| 264 check(hdr.emagic[0] == 127, "no support for writing native object files") | |
| 265 else | |
| 266 hdr.emagic = "\127ELF" | |
| 267 hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0 | |
| 268 end | |
| 269 hdr.eclass = is64 and 2 or 1 | |
| 270 hdr.eendian = isbe and 2 or 1 | |
| 271 hdr.eversion = 1 | |
| 272 hdr.type = f16(1) | |
| 273 hdr.machine = f16(ai.m) | |
| 274 hdr.flags = f32(ai.f or 0) | |
| 275 hdr.version = f32(1) | |
| 276 hdr.shofs = fofs(ffi.offsetof(o, "sect")) | |
| 277 hdr.ehsize = f16(ffi.sizeof(hdr)) | |
| 278 hdr.shentsize = f16(ffi.sizeof(o.sect[0])) | |
| 279 hdr.shnum = f16(6) | |
| 280 hdr.shstridx = f16(2) | |
| 281 | |
| 282 -- Fill in sections and symbols. | |
| 283 local sofs, ofs = ffi.offsetof(o, "space"), 1 | |
| 284 for i,name in ipairs{ | |
| 285 ".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack", | |
| 286 } do | |
| 287 local sect = o.sect[i] | |
| 288 sect.align = fofs(1) | |
| 289 sect.name = f32(ofs) | |
| 290 ffi.copy(o.space+ofs, name) | |
| 291 ofs = ofs + #name+1 | |
| 292 end | |
| 293 o.sect[1].type = f32(2) -- .symtab | |
| 294 o.sect[1].link = f32(3) | |
| 295 o.sect[1].info = f32(1) | |
| 296 o.sect[1].align = fofs(8) | |
| 297 o.sect[1].ofs = fofs(ffi.offsetof(o, "sym")) | |
| 298 o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0])) | |
| 299 o.sect[1].size = fofs(ffi.sizeof(o.sym)) | |
| 300 o.sym[1].name = f32(1) | |
| 301 o.sym[1].sectidx = f16(4) | |
| 302 o.sym[1].size = fofs(#s) | |
| 303 o.sym[1].info = 17 | |
| 304 o.sect[2].type = f32(3) -- .shstrtab | |
| 305 o.sect[2].ofs = fofs(sofs) | |
| 306 o.sect[2].size = fofs(ofs) | |
| 307 o.sect[3].type = f32(3) -- .strtab | |
| 308 o.sect[3].ofs = fofs(sofs + ofs) | |
| 309 o.sect[3].size = fofs(#symname+2) | |
| 310 ffi.copy(o.space+ofs+1, symname) | |
| 311 ofs = ofs + #symname + 2 | |
| 312 o.sect[4].type = f32(1) -- .rodata | |
| 313 o.sect[4].flags = fofs(2) | |
| 314 o.sect[4].ofs = fofs(sofs + ofs) | |
| 315 o.sect[4].size = fofs(#s) | |
| 316 o.sect[5].type = f32(1) -- .note.GNU-stack | |
| 317 o.sect[5].ofs = fofs(sofs + ofs + #s) | |
| 318 | |
| 319 -- Write ELF object file. | |
| 320 local fp = savefile(output, "wb") | |
| 321 fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs)) | |
| 322 bcsave_tail(fp, output, s) | |
| 323 end | |
| 324 | |
| 325 local function bcsave_peobj(ctx, output, s, ffi) | |
| 326 ffi.cdef[[ | |
| 327 typedef struct { | |
| 328 uint16_t arch, nsects; | |
| 329 uint32_t time, symtabofs, nsyms; | |
| 330 uint16_t opthdrsz, flags; | |
| 331 } PEheader; | |
| 332 typedef struct { | |
| 333 char name[8]; | |
| 334 uint32_t vsize, vaddr, size, ofs, relocofs, lineofs; | |
| 335 uint16_t nreloc, nline; | |
| 336 uint32_t flags; | |
| 337 } PEsection; | |
| 338 typedef struct __attribute((packed)) { | |
| 339 union { | |
| 340 char name[8]; | |
| 341 uint32_t nameref[2]; | |
| 342 }; | |
| 343 uint32_t value; | |
| 344 int16_t sect; | |
| 345 uint16_t type; | |
| 346 uint8_t scl, naux; | |
| 347 } PEsym; | |
| 348 typedef struct __attribute((packed)) { | |
| 349 uint32_t size; | |
| 350 uint16_t nreloc, nline; | |
| 351 uint32_t cksum; | |
| 352 uint16_t assoc; | |
| 353 uint8_t comdatsel, unused[3]; | |
| 354 } PEsymaux; | |
| 355 typedef struct { | |
| 356 PEheader hdr; | |
| 357 PEsection sect[2]; | |
| 358 // Must be an even number of symbol structs. | |
| 359 PEsym sym0; | |
| 360 PEsymaux sym0aux; | |
| 361 PEsym sym1; | |
| 362 PEsymaux sym1aux; | |
| 363 PEsym sym2; | |
| 364 PEsym sym3; | |
| 365 uint32_t strtabsize; | |
| 366 uint8_t space[4096]; | |
| 367 } PEobj; | |
| 368 ]] | |
| 369 local symname = LJBC_PREFIX..ctx.modname | |
| 370 local ai = assert(map_arch[ctx.arch]) | |
| 371 local is64 = ai.b == 64 | |
| 372 local symexport = " /EXPORT:"..symname..",DATA " | |
| 373 | |
| 374 -- The file format is always little-endian. Swap if the host is big-endian. | |
| 375 local function f32(x) return x end | |
| 376 local f16 = f32 | |
| 377 if ffi.abi("be") then | |
| 378 f32 = bit.bswap | |
| 379 function f16(x) return bit.rshift(bit.bswap(x), 16) end | |
| 380 end | |
| 381 | |
| 382 -- Create PE object and fill in header. | |
| 383 local o = ffi.new("PEobj") | |
| 384 local hdr = o.hdr | |
| 385 hdr.arch = f16(assert(ai.p)) | |
| 386 hdr.nsects = f16(2) | |
| 387 hdr.symtabofs = f32(ffi.offsetof(o, "sym0")) | |
| 388 hdr.nsyms = f32(6) | |
| 389 | |
| 390 -- Fill in sections and symbols. | |
| 391 o.sect[0].name = ".drectve" | |
| 392 o.sect[0].size = f32(#symexport) | |
| 393 o.sect[0].flags = f32(0x00100a00) | |
| 394 o.sym0.sect = f16(1) | |
| 395 o.sym0.scl = 3 | |
| 396 o.sym0.name = ".drectve" | |
| 397 o.sym0.naux = 1 | |
| 398 o.sym0aux.size = f32(#symexport) | |
| 399 o.sect[1].name = ".rdata" | |
| 400 o.sect[1].size = f32(#s) | |
| 401 o.sect[1].flags = f32(0x40300040) | |
| 402 o.sym1.sect = f16(2) | |
| 403 o.sym1.scl = 3 | |
| 404 o.sym1.name = ".rdata" | |
| 405 o.sym1.naux = 1 | |
| 406 o.sym1aux.size = f32(#s) | |
| 407 o.sym2.sect = f16(2) | |
| 408 o.sym2.scl = 2 | |
| 409 o.sym2.nameref[1] = f32(4) | |
| 410 o.sym3.sect = f16(-1) | |
| 411 o.sym3.scl = 2 | |
| 412 o.sym3.value = f32(1) | |
| 413 o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant. | |
| 414 ffi.copy(o.space, symname) | |
| 415 local ofs = #symname + 1 | |
| 416 o.strtabsize = f32(ofs + 4) | |
| 417 o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs) | |
| 418 ffi.copy(o.space + ofs, symexport) | |
| 419 ofs = ofs + #symexport | |
| 420 o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs) | |
| 421 | |
| 422 -- Write PE object file. | |
| 423 local fp = savefile(output, "wb") | |
| 424 fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs)) | |
| 425 bcsave_tail(fp, output, s) | |
| 426 end | |
| 427 | |
| 428 local function bcsave_machobj(ctx, output, s, ffi) | |
| 429 ffi.cdef[[ | |
| 430 typedef struct | |
| 431 { | |
| 432 uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags; | |
| 433 } mach_header; | |
| 434 typedef struct | |
| 435 { | |
| 436 mach_header; uint32_t reserved; | |
| 437 } mach_header_64; | |
| 438 typedef struct { | |
| 439 uint32_t cmd, cmdsize; | |
| 440 char segname[16]; | |
| 441 uint32_t vmaddr, vmsize, fileoff, filesize; | |
| 442 uint32_t maxprot, initprot, nsects, flags; | |
| 443 } mach_segment_command; | |
| 444 typedef struct { | |
| 445 uint32_t cmd, cmdsize; | |
| 446 char segname[16]; | |
| 447 uint64_t vmaddr, vmsize, fileoff, filesize; | |
| 448 uint32_t maxprot, initprot, nsects, flags; | |
| 449 } mach_segment_command_64; | |
| 450 typedef struct { | |
| 451 char sectname[16], segname[16]; | |
| 452 uint32_t addr, size; | |
| 453 uint32_t offset, align, reloff, nreloc, flags; | |
| 454 uint32_t reserved1, reserved2; | |
| 455 } mach_section; | |
| 456 typedef struct { | |
| 457 char sectname[16], segname[16]; | |
| 458 uint64_t addr, size; | |
| 459 uint32_t offset, align, reloff, nreloc, flags; | |
| 460 uint32_t reserved1, reserved2, reserved3; | |
| 461 } mach_section_64; | |
| 462 typedef struct { | |
| 463 uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize; | |
| 464 } mach_symtab_command; | |
| 465 typedef struct { | |
| 466 int32_t strx; | |
| 467 uint8_t type, sect; | |
| 468 int16_t desc; | |
| 469 uint32_t value; | |
| 470 } mach_nlist; | |
| 471 typedef struct { | |
| 472 int32_t strx; | |
| 473 uint8_t type, sect; | |
| 474 uint16_t desc; | |
| 475 uint64_t value; | |
| 476 } mach_nlist_64; | |
| 477 typedef struct | |
| 478 { | |
| 479 int32_t magic, nfat_arch; | |
| 480 } mach_fat_header; | |
| 481 typedef struct | |
| 482 { | |
| 483 int32_t cputype, cpusubtype, offset, size, align; | |
| 484 } mach_fat_arch; | |
| 485 typedef struct { | |
| 486 struct { | |
| 487 mach_header hdr; | |
| 488 mach_segment_command seg; | |
| 489 mach_section sec; | |
| 490 mach_symtab_command sym; | |
| 491 } arch[1]; | |
| 492 mach_nlist sym_entry; | |
| 493 uint8_t space[4096]; | |
| 494 } mach_obj; | |
| 495 typedef struct { | |
| 496 struct { | |
| 497 mach_header_64 hdr; | |
| 498 mach_segment_command_64 seg; | |
| 499 mach_section_64 sec; | |
| 500 mach_symtab_command sym; | |
| 501 } arch[1]; | |
| 502 mach_nlist_64 sym_entry; | |
| 503 uint8_t space[4096]; | |
| 504 } mach_obj_64; | |
| 505 typedef struct { | |
| 506 mach_fat_header fat; | |
| 507 mach_fat_arch fat_arch[2]; | |
| 508 struct { | |
| 509 mach_header hdr; | |
| 510 mach_segment_command seg; | |
| 511 mach_section sec; | |
| 512 mach_symtab_command sym; | |
| 513 } arch[2]; | |
| 514 mach_nlist sym_entry; | |
| 515 uint8_t space[4096]; | |
| 516 } mach_fat_obj; | |
| 517 typedef struct { | |
| 518 mach_fat_header fat; | |
| 519 mach_fat_arch fat_arch[2]; | |
| 520 struct { | |
| 521 mach_header_64 hdr; | |
| 522 mach_segment_command_64 seg; | |
| 523 mach_section_64 sec; | |
| 524 mach_symtab_command sym; | |
| 525 } arch[2]; | |
| 526 mach_nlist_64 sym_entry; | |
| 527 uint8_t space[4096]; | |
| 528 } mach_fat_obj_64; | |
| 529 ]] | |
| 530 local symname = '_'..LJBC_PREFIX..ctx.modname | |
| 531 local isfat, is64, align, mobj = false, false, 4, "mach_obj" | |
| 532 if ctx.arch == "x64" then | |
| 533 is64, align, mobj = true, 8, "mach_obj_64" | |
| 534 elseif ctx.arch == "arm" then | |
| 535 isfat, mobj = true, "mach_fat_obj" | |
| 536 elseif ctx.arch == "arm64" then | |
| 537 is64, align, isfat, mobj = true, 8, true, "mach_fat_obj_64" | |
| 538 else | |
| 539 check(ctx.arch == "x86", "unsupported architecture for OSX") | |
| 540 end | |
| 541 local function aligned(v, a) return bit.band(v+a-1, -a) end | |
| 542 local be32 = bit.bswap -- Mach-O FAT is BE, supported archs are LE. | |
| 543 | |
| 544 -- Create Mach-O object and fill in header. | |
| 545 local o = ffi.new(mobj) | |
| 546 local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, align) | |
| 547 local cputype = ({ x86={7}, x64={0x01000007}, arm={7,12}, arm64={0x01000007,0x0100000c} })[ctx.arch] | |
| 548 local cpusubtype = ({ x86={3}, x64={3}, arm={3,9}, arm64={3,0} })[ctx.arch] | |
| 549 if isfat then | |
| 550 o.fat.magic = be32(0xcafebabe) | |
| 551 o.fat.nfat_arch = be32(#cpusubtype) | |
| 552 end | |
| 553 | |
| 554 -- Fill in sections and symbols. | |
| 555 for i=0,#cpusubtype-1 do | |
| 556 local ofs = 0 | |
| 557 if isfat then | |
| 558 local a = o.fat_arch[i] | |
| 559 a.cputype = be32(cputype[i+1]) | |
| 560 a.cpusubtype = be32(cpusubtype[i+1]) | |
| 561 -- Subsequent slices overlap each other to share data. | |
| 562 ofs = ffi.offsetof(o, "arch") + i*ffi.sizeof(o.arch[0]) | |
| 563 a.offset = be32(ofs) | |
| 564 a.size = be32(mach_size-ofs+#s) | |
| 565 end | |
| 566 local a = o.arch[i] | |
| 567 a.hdr.magic = is64 and 0xfeedfacf or 0xfeedface | |
| 568 a.hdr.cputype = cputype[i+1] | |
| 569 a.hdr.cpusubtype = cpusubtype[i+1] | |
| 570 a.hdr.filetype = 1 | |
| 571 a.hdr.ncmds = 2 | |
| 572 a.hdr.sizeofcmds = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)+ffi.sizeof(a.sym) | |
| 573 a.seg.cmd = is64 and 0x19 or 0x1 | |
| 574 a.seg.cmdsize = ffi.sizeof(a.seg)+ffi.sizeof(a.sec) | |
| 575 a.seg.vmsize = #s | |
| 576 a.seg.fileoff = mach_size-ofs | |
| 577 a.seg.filesize = #s | |
| 578 a.seg.maxprot = 1 | |
| 579 a.seg.initprot = 1 | |
| 580 a.seg.nsects = 1 | |
| 581 ffi.copy(a.sec.sectname, "__data") | |
| 582 ffi.copy(a.sec.segname, "__DATA") | |
| 583 a.sec.size = #s | |
| 584 a.sec.offset = mach_size-ofs | |
| 585 a.sym.cmd = 2 | |
| 586 a.sym.cmdsize = ffi.sizeof(a.sym) | |
| 587 a.sym.symoff = ffi.offsetof(o, "sym_entry")-ofs | |
| 588 a.sym.nsyms = 1 | |
| 589 a.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)-ofs | |
| 590 a.sym.strsize = aligned(#symname+2, align) | |
| 591 end | |
| 592 o.sym_entry.type = 0xf | |
| 593 o.sym_entry.sect = 1 | |
| 594 o.sym_entry.strx = 1 | |
| 595 ffi.copy(o.space+1, symname) | |
| 596 | |
| 597 -- Write Macho-O object file. | |
| 598 local fp = savefile(output, "wb") | |
| 599 fp:write(ffi.string(o, mach_size)) | |
| 600 bcsave_tail(fp, output, s) | |
| 601 end | |
| 602 | |
| 603 local function bcsave_obj(ctx, output, s) | |
| 604 local ok, ffi = pcall(require, "ffi") | |
| 605 check(ok, "FFI library required to write this file type") | |
| 606 if output == "-" and jit.os == "Windows" then | |
| 607 set_stdout_binary(ffi) | |
| 608 end | |
| 609 if ctx.os == "windows" then | |
| 610 return bcsave_peobj(ctx, output, s, ffi) | |
| 611 elseif ctx.os == "osx" then | |
| 612 return bcsave_machobj(ctx, output, s, ffi) | |
| 613 else | |
| 614 return bcsave_elfobj(ctx, output, s, ffi) | |
| 615 end | |
| 616 end | |
| 617 | |
| 618 ------------------------------------------------------------------------------ | |
| 619 | |
| 620 local function bclist(ctx, input, output) | |
| 621 local f = readfile(ctx, input) | |
| 622 require("jit.bc").dump(f, savefile(output, "w"), true) | |
| 623 end | |
| 624 | |
| 625 local function bcsave(ctx, input, output) | |
| 626 local f = readfile(ctx, input) | |
| 627 local s = string.dump(f, ctx.strip) | |
| 628 local t = ctx.type | |
| 629 if not t then | |
| 630 t = detecttype(output) | |
| 631 ctx.type = t | |
| 632 end | |
| 633 if t == "raw" then | |
| 634 bcsave_raw(output, s) | |
| 635 else | |
| 636 if not ctx.modname then ctx.modname = detectmodname(input) end | |
| 637 if t == "obj" then | |
| 638 bcsave_obj(ctx, output, s) | |
| 639 else | |
| 640 bcsave_c(ctx, output, s) | |
| 641 end | |
| 642 end | |
| 643 end | |
| 644 | |
| 645 local function docmd(...) | |
| 646 local arg = {...} | |
| 647 local n = 1 | |
| 648 local list = false | |
| 649 local ctx = { | |
| 650 strip = true, arch = jit.arch, os = jit.os:lower(), | |
| 651 type = false, modname = false, | |
| 652 } | |
| 653 while n <= #arg do | |
| 654 local a = arg[n] | |
| 655 if type(a) == "string" and a:sub(1, 1) == "-" and a ~= "-" then | |
| 656 tremove(arg, n) | |
| 657 if a == "--" then break end | |
| 658 for m=2,#a do | |
| 659 local opt = a:sub(m, m) | |
| 660 if opt == "l" then | |
| 661 list = true | |
| 662 elseif opt == "s" then | |
| 663 ctx.strip = true | |
| 664 elseif opt == "g" then | |
| 665 ctx.strip = false | |
| 666 else | |
| 667 if arg[n] == nil or m ~= #a then usage() end | |
| 668 if opt == "e" then | |
| 669 if n ~= 1 then usage() end | |
| 670 arg[1] = check(loadstring(arg[1])) | |
| 671 elseif opt == "n" then | |
| 672 ctx.modname = checkmodname(tremove(arg, n)) | |
| 673 elseif opt == "t" then | |
| 674 ctx.type = checkarg(tremove(arg, n), map_type, "file type") | |
| 675 elseif opt == "a" then | |
| 676 ctx.arch = checkarg(tremove(arg, n), map_arch, "architecture") | |
| 677 elseif opt == "o" then | |
| 678 ctx.os = checkarg(tremove(arg, n), map_os, "OS name") | |
| 679 elseif opt == "F" then | |
| 680 ctx.filename = "@"..tremove(arg, n) | |
| 681 else | |
| 682 usage() | |
| 683 end | |
| 684 end | |
| 685 end | |
| 686 else | |
| 687 n = n + 1 | |
| 688 end | |
| 689 end | |
| 690 if list then | |
| 691 if #arg == 0 or #arg > 2 then usage() end | |
| 692 bclist(ctx, arg[1], arg[2] or "-") | |
| 693 else | |
| 694 if #arg ~= 2 then usage() end | |
| 695 bcsave(ctx, arg[1], arg[2]) | |
| 696 end | |
| 697 end | |
| 698 | |
| 699 ------------------------------------------------------------------------------ | |
| 700 | |
| 701 -- Public module functions. | |
| 702 return { | |
| 703 start = docmd -- Process -b command line option. | |
| 704 } | |
| 705 |