This page follows Lua 5.2’s ldump.c, lundump.c,
lundump.h and lopcodes.h (5.2.4). The Lua authors do not
document the format. Numbers are written in the byte order and sizes of the machine
that wrote the chunk; the header records them and the loader refuses a mismatch.
Header (18 bytes)
| Offset | Size | Field | Meaning |
|---|---|---|---|
0 | 4 | signature | 1B 4C 75 61: escape and Lua. |
4 | 1 | version | 0x52. |
5 | 1 | format | 0, the official format. |
6 | 1 | endianness | 1 little-endian, 0 big-endian. |
7 | 1 | sizeof(int) | Bytes in an int. |
8 | 1 | sizeof(size_t) | Bytes in a size_t. |
9 | 1 | sizeof(Instruction) | Bytes in an instruction. |
10 | 1 | sizeof(lua_Number) | Bytes in a number. |
11 | 1 | integral flag | 0 floating point, 1 integer. |
12 | 6 | tail | 19 93 0D 0A 1A 0A (LUAC_TAIL, “data to catch conversion errors”). |
Basic types
| Type | Encoding |
|---|---|
| byte | One byte. |
| int | sizeof(int) bytes, in the header’s byte order. |
| number | sizeof(lua_Number) bytes. |
| string | A size_t length counting the terminating zero, then the bytes and the zero. 0 means no string. |
| list of T | An int count, then that many T. |
Function prototype
The main function follows the header. The loader makes it a closure; if the function does not have exactly one upvalue, the loader creates a closure with the right number.
| Field | Type | Meaning |
|---|---|---|
| linedefined | int | First line. |
| lastlinedefined | int | Last line. |
| numparams | byte | Number of fixed parameters. |
| is_vararg | byte | Whether the function takes .... |
| maxstacksize | byte | Registers needed. |
| code | list of Instruction | 4 bytes each. |
| constants | list of constant | Tag byte and value, below. |
| protos | list of function | Nested prototypes in this layout. |
| upvalues | list of (byte, byte) | instack and idx, one byte each. |
| source | string | Source name. Debug information starts here. |
| lineinfo | list of int | Line of each instruction. |
| locvars | list of (string, int, int) | Local name, start pc, end pc. |
| upvalue names | list of string | Names assigned to the upvalues in order. |
Constants
| Tag byte | Type | Then |
|---|---|---|
0 | nil | nothing |
1 | boolean | one byte |
3 | number | a number |
4 | string | a string |
Instructions
| Mode | Bits 31–23 | Bits 22–14 | Bits 13–6 | Bits 5–0 |
|---|---|---|---|---|
iABC | B (9) | C (9) | A (8) | opcode (6) |
iABx | Bx (18) | A (8) | opcode (6) | |
iAsBx | sBx (18), stored + 131,071 | A (8) | opcode (6) | |
iAx | Ax (26) | opcode (6) | ||
The opcode numbers and the Ax field are from 5.2’s lopcodes.h. The
actions, RK(x) (constant x − 256 when bit 8 is set, otherwise
register x) and the SETLIST batch of 50 are written from the
5.1 and 5.3 headers, which agree on them; 5.2’s own comments could not be re-read
(lua.org refused the connection), so treat them as unconfirmed for 5.2.
| Op | Name | Mode | Action |
|---|---|---|---|
| 0 | MOVE | A B | R(A) := R(B) |
| 1 | LOADK | A Bx | R(A) := Kst(Bx) |
| 2 | LOADKX | A | R(A) := Kst(Ax of the next instruction, an EXTRAARG) |
| 3 | LOADBOOL | A B C | R(A) := (Bool)B; if C, skip next |
| 4 | LOADNIL | A B | R(A), …, R(A+B) := nil |
| 5 | GETUPVAL | A B | R(A) := UpValue[B] |
| 6 | GETTABUP | A B C | R(A) := UpValue[B][RK(C)] (globals, through _ENV) |
| 7 | GETTABLE | A B C | R(A) := R(B)[RK(C)] |
| 8 | SETTABUP | A B C | UpValue[A][RK(B)] := RK(C) |
| 9 | SETUPVAL | A B | UpValue[B] := R(A) |
| 10 | SETTABLE | A B C | R(A)[RK(B)] := RK(C) |
| 11 | NEWTABLE | A B C | R(A) := {} (array size B, hash size C) |
| 12 | SELF | A B C | R(A+1) := R(B); R(A) := R(B)[RK(C)] |
| 13 | ADD | A B C | R(A) := RK(B) + RK(C) |
| 14 | SUB | A B C | R(A) := RK(B) − RK(C) |
| 15 | MUL | A B C | R(A) := RK(B) × RK(C) |
| 16 | DIV | A B C | R(A) := RK(B) / RK(C) |
| 17 | MOD | A B C | R(A) := RK(B) % RK(C) |
| 18 | POW | A B C | R(A) := RK(B) ^ RK(C) |
| 19 | UNM | A B | R(A) := −R(B) |
| 20 | NOT | A B | R(A) := not R(B) |
| 21 | LEN | A B | R(A) := length of R(B) |
| 22 | CONCAT | A B C | R(A) := R(B) .. … .. R(C) |
| 23 | JMP | A sBx | pc += sBx; if A, close upvalues at or above R(A−1) |
| 24 | EQ | A B C | if (RK(B) == RK(C)) ~= A then skip next |
| 25 | LT | A B C | if (RK(B) < RK(C)) ~= A then skip next |
| 26 | LE | A B C | if (RK(B) <= RK(C)) ~= A then skip next |
| 27 | TEST | A C | if not (R(A) <=> C) then skip next |
| 28 | TESTSET | A B C | if R(B) <=> C then R(A) := R(B) else skip next |
| 29 | CALL | A B C | R(A), …, R(A+C−2) := R(A)(R(A+1), …, R(A+B−1)) |
| 30 | TAILCALL | A B C | return R(A)(R(A+1), …, R(A+B−1)) |
| 31 | RETURN | A B | return R(A), …, R(A+B−2) |
| 32 | FORLOOP | A sBx | R(A) += R(A+2); if within R(A+1), pc += sBx and R(A+3) := R(A) |
| 33 | FORPREP | A sBx | R(A) −= R(A+2); pc += sBx |
| 34 | TFORCALL | A C | R(A+3), …, R(A+2+C) := R(A)(R(A+1), R(A+2)) |
| 35 | TFORLOOP | A sBx | if R(A+1) ~= nil then R(A) := R(A+1); pc += sBx |
| 36 | SETLIST | A B C | R(A)[(C−1) × 50 + i] := R(A+i), 1 ≤ i ≤ B; C = 0 means C is the Ax of a following EXTRAARG |
| 37 | CLOSURE | A Bx | R(A) := closure(KPROTO[Bx]); upvalues come from the prototype’s descriptors |
| 38 | VARARG | A B | R(A), …, R(A+B−2) := vararg |
| 39 | EXTRAARG | Ax | A larger argument for the previous instruction |
Compared with 5.1, globals are reached through the _ENV upvalue
(GETTABUP, SETTABUP replace GETGLOBAL and
SETGLOBAL), CLOSE is folded into JMP, and the
generic for is split into TFORCALL and
TFORLOOP.
Example
1B 4C 75 61 52 00 01 04 08 04 08 00 header fields (64-bit little-endian)
19 93 0D 0A 1A 0A LUAC_TAIL
00 00 00 00 linedefined 0 (main chunk)
00 00 00 00 lastlinedefined 0
00 01 02 numparams 0, is_vararg 1, maxstacksize 2
The values after the header are illustrative.
Sources
Lua 5.2 source on lua.org:
lundump.c (header check and the order of every field),
lundump.h (LUAC_TAIL), lua.h (type tags) and
lopcodes.h (opcode numbers, Ax size and position). Opcode actions: see
the note under Instructions.