1#![allow(dead_code)]
2
3use std::cell::RefCell;
4
5use nom::{multi::count, number::complete::le_u8};
6use nom_leb128::{leb128_u32, leb128_u64, leb128_usize};
7
8use super::*;
9
10pub const FLAG_IS_BIG_ENDIAN: u8 = 0b00000001;
12pub const FLAG_IS_STRIPPED: u8 = 0b00000010;
13pub const FLAG_HAS_FFI: u8 = 0b00000100;
14pub const FLAG_F_FR2: u8 = 0x08;
16
17pub const PROTO_CHILD: u8 = 0x01; pub const PROTO_VARARG: u8 = 0x02; pub const PROTO_FFI: u8 = 0x04; pub const PROTO_NOJIT: u8 = 0x08; pub const PROTO_ILOOP: u8 = 0x10; pub const PROTO_HAS_RETURN: u8 = 0x20; pub const PROTO_FIXUP_RETURN: u8 = 0x40; pub const BCDUMP_KGC_CHILD: u64 = 0;
28pub const BCDUMP_KGC_TAB: u64 = 1;
29pub const BCDUMP_KGC_I64: u64 = 2;
30pub const BCDUMP_KGC_U64: u64 = 3;
31pub const BCDUMP_KGC_COMPLEX: u64 = 4;
32pub const BCDUMP_KGC_STR: u64 = 5;
33
34pub const BCDUMP_KTAB_NIL: usize = 0;
35pub const BCDUMP_KTAB_FALSE: usize = 1;
36pub const BCDUMP_KTAB_TRUE: usize = 2;
37pub const BCDUMP_KTAB_INT: usize = 3;
38pub const BCDUMP_KTAB_NUM: usize = 4;
39pub const BCDUMP_KTAB_STR: usize = 5;
40
41pub fn uleb128_33(mut input: &[u8]) -> IResult<&[u8], u32, ErrorTree<&[u8]>> {
42 let v;
43 (input, v) = le_u8(input)?;
44 let mut v = v as u32 >> 1; if v >= 0x40 {
46 let mut sh = -1i32;
47 v &= 0x3f;
48 loop {
49 sh += 7;
50 let mut p = le_u8(input)?.1;
51 v |= (p as u32 & 0x7f) << (sh as u32);
52 (input, p) = le_u8(input)?;
53 if p < 0x80 {
54 break;
55 } }
57 }
58 Ok((input, v))
59}
60
61pub fn lj_header(input: &[u8]) -> IResult<&[u8], LuaHeader, ErrorTree<&[u8]>> {
62 let (rest, (_, result)) = tuple((
63 tag(b"\x1bLJ"),
64 alt((
65 map(tuple((tag(b"\x01"), be_u8)), |(_, lj_flags)| LuaHeader {
66 lua_version: LUAJ1.0,
67 format_version: 0,
68 big_endian: lj_flags & FLAG_IS_BIG_ENDIAN != 0,
69 int_size: 4,
70 size_t_size: 4,
71 instruction_size: 4,
72 number_size: 4,
73 number_integral: false,
74 lj_flags,
75 })
76 .context("luajit1"),
77 map(tuple((tag(b"\x02"), be_u8)), |(_, lj_flags)| LuaHeader {
78 lua_version: LUAJ2.0,
79 format_version: 0,
80 big_endian: lj_flags & FLAG_IS_BIG_ENDIAN != 0,
81 int_size: 4,
82 size_t_size: 4,
83 instruction_size: 4,
84 number_size: 4,
85 number_integral: false,
86 lj_flags,
87 })
88 .context("luajit2"),
89 )),
90 ))(input)?;
91 Ok((rest, result))
92}
93
94pub fn lj_complex_constant<'a, 'h>(
95 stack: &'h RefCell<Vec<LuaChunk>>,
96 protos: &'h RefCell<Vec<LuaChunk>>,
97 endian: Endianness,
98) -> impl Parser<&'a [u8], LuaConstant, ErrorTree<&'a [u8]>> + 'h {
99 move |input| {
100 let (input, ty) = leb128_u64(input)?;
101 Ok(match ty {
102 BCDUMP_KGC_I64 => map(
103 tuple((nom_leb128::leb128_u32, nom_leb128::leb128_u32)),
104 |(lo, hi)| LuaConstant::Number(LuaNumber::Integer(lo as i64 | ((hi as i64) << 32))),
105 )(input)?,
106 BCDUMP_KGC_U64 => map(
107 tuple((nom_leb128::leb128_u32, nom_leb128::leb128_u32)),
108 |(lo, hi)| {
109 LuaConstant::Number(LuaNumber::Integer(
110 (lo as u64 | ((hi as u64) << 32)) as i64,
111 ))
112 },
113 )(input)?,
114 BCDUMP_KGC_TAB => lj_tab(endian).context("read table").parse(input)?,
115 BCDUMP_KGC_CHILD => match stack.borrow_mut().pop() {
116 Some(proto) => {
117 let result = LuaConstant::Proto(protos.borrow().len());
118 protos.borrow_mut().push(proto);
119 (input, result)
120 }
121 None => context("pop proto", fail).parse(input)?,
122 },
123 _ if ty >= BCDUMP_KGC_STR => {
124 let len = ty - BCDUMP_KGC_STR;
125 let (input, s) = take(len as usize)(input)?;
126 (input, LuaConstant::from(s.to_vec()))
127 }
128 _ => unreachable!("BCDUMP_KGC: {ty}"),
129 })
130 }
131}
132
133pub fn lj_tab<'a>(endian: Endianness) -> impl Parser<&'a [u8], LuaConstant, ErrorTree<&'a [u8]>> {
134 move |input: &'a [u8]| {
135 let (input, (narray, nhash)) = tuple((leb128_u32, leb128_u32))(input)?;
136 let (input, (array, hash)) = tuple((
137 count(lj_tabk(endian).context("count table array"), narray as _),
138 count(
139 tuple((lj_tabk(endian), lj_tabk(endian))).context("count table hash"),
140 nhash as _,
141 ),
142 ))(input)?;
143 Ok((input, LuaConstant::Table(ConstTable { array, hash }.into())))
144 }
145}
146
147fn combine_number(lo: u32, hi: u32, endian: Endianness) -> f64 {
148 f64::from_bits(if endian == Endianness::Big {
149 ((lo as u64) << 32) | hi as u64
150 } else {
151 ((hi as u64) << 32) | lo as u64
152 })
153}
154
155pub fn lj_tabk<'a>(endian: Endianness) -> impl Parser<&'a [u8], LuaConstant, ErrorTree<&'a [u8]>> {
156 move |input: &'a [u8]| {
157 let (input, ty) = leb128_usize(input)?;
158 Ok(match ty {
160 BCDUMP_KTAB_NIL => (input, LuaConstant::Null),
161 BCDUMP_KTAB_FALSE => (input, LuaConstant::Bool(false)),
162 BCDUMP_KTAB_TRUE => (input, LuaConstant::Bool(true)),
163 BCDUMP_KTAB_INT => map(leb128_u32, |n| {
164 LuaConstant::Number(LuaNumber::Integer(n as _))
165 })(input)?,
166 BCDUMP_KTAB_NUM => map(tuple((leb128_u32, leb128_u32)), |(lo, hi)| {
167 LuaConstant::Number(LuaNumber::Float(combine_number(lo, hi, endian)))
168 })(input)?,
169 _ if ty >= BCDUMP_KTAB_STR as usize => {
170 let len = ty - BCDUMP_KTAB_STR as usize;
171 let (input, s) = take(len)(input)?;
172 (input, LuaConstant::from(s.to_vec()))
173 }
174 _ => unreachable!("BCDUMP_KTAB: {ty}"),
175 })
176 }
177}
178
179fn lj_num_constant<'a>(
180 endian: Endianness,
181) -> impl Parser<&'a [u8], LuaNumber, ErrorTree<&'a [u8]>> {
182 move |input: &'a [u8]| {
183 let isnum = be_u8(input)?.1 & 1 != 0;
184 let (input, lo) = uleb128_33(input)?;
185 if isnum {
186 map(leb128_u32, |hi| {
187 LuaNumber::Float(combine_number(lo, hi, endian))
188 })(input)
189 } else {
190 Ok((input, LuaNumber::Integer(lo as i32 as _)))
191 }
192 }
193}
194
195fn lj_proto<'a, 'h>(
196 header: &'h LuaHeader,
197 stack: &'h RefCell<Vec<LuaChunk>>,
198) -> impl Parser<&'a [u8], Option<LuaChunk>, ErrorTree<&'a [u8]>> + 'h {
199 move |input| {
200 let (input, size) = leb128_u32.parse(input)?;
202 if size == 0 {
203 return Ok((input, None));
204 }
205 let (
206 mut input,
207 (
208 flags,
209 num_params,
210 framesize,
211 num_upvalues,
212 complex_constants_count,
213 numeric_constants_count,
214 instructions_count,
215 ),
216 ) = tuple((
217 be_u8, be_u8, be_u8, be_u8, leb128_u32, leb128_u32, leb128_u32,
218 ))(input)?;
219
220 let mut line_defined = 0;
221 let mut numline = 0;
222 let mut debuginfo_size = 0;
223 if !header.test_luajit_flag(FLAG_IS_STRIPPED) {
224 (input, (debuginfo_size, line_defined, numline)) =
225 tuple((leb128_u64, leb128_u64, leb128_u64))(input)?;
226 }
227 let last_line_defined = line_defined + numline;
228
229 let instructions;
230 let upvalue_infos;
231 let mut constants;
232 let num_constants;
233 let protos = RefCell::new(vec![]);
234 (
235 input,
236 (instructions, upvalue_infos, constants, num_constants),
237 ) = tuple((
238 count(complete::u32(header.endian()), instructions_count as usize)
239 .context("count instruction"),
240 count(
241 map(complete::u16(header.endian()), |v| UpVal {
242 on_stack: v & 0x8000 != 0,
243 id: (v & 0x7FFF) as _,
244 kind: 0,
245 }),
246 num_upvalues as usize,
247 )
248 .context("count upvals"),
249 count(
250 lj_complex_constant(stack, &protos, header.endian()),
251 complex_constants_count as usize,
252 )
253 .context("count complex_constant"),
254 count(
255 lj_num_constant(header.endian()),
256 numeric_constants_count as usize,
257 )
258 .context("count numeric_constants"),
259 ))(input)?;
260 constants.reverse();
261
262 if debuginfo_size > 0 {
263 (input, _) = take(debuginfo_size as usize)(input)?;
264 }
265
266 Ok((
267 input,
268 Some(LuaChunk {
269 name: vec![],
270 num_upvalues,
271 num_params,
272 line_defined,
273 last_line_defined,
274 flags,
275 instructions,
276 upvalue_infos,
277 constants,
278 num_constants,
279 max_stack: framesize,
280 is_vararg: if flags & PROTO_VARARG != 0 {
281 Some(LuaVarArgInfo::new())
282 } else {
283 None
284 },
285 prototypes: protos.into_inner(),
286 ..Default::default()
287 }),
288 ))
289 }
290}
291
292pub fn lj_chunk<'h, 'a: 'h>(
293 header: &'h LuaHeader,
294) -> impl Parser<&'a [u8], LuaChunk, ErrorTree<&'a [u8]>> + 'h {
295 move |mut input| {
296 let mut name = &b""[..];
297 if !header.test_luajit_flag(FLAG_IS_STRIPPED) {
298 let namelen;
299 (input, namelen) = leb128_u32.parse(input)?;
300 (input, name) = take(namelen as usize)(input)?;
301 }
302 let protos = RefCell::new(vec![]);
303 while let (i, Some(proto)) = lj_proto(&header, &protos).parse(input)? {
304 protos.borrow_mut().push(proto);
305 input = i;
306 }
307 let mut protos = protos.into_inner();
308 Ok((
309 input,
310 if let Some(mut chunk) = protos.pop().filter(|_| protos.is_empty()) {
311 chunk.name = name.to_vec();
312 chunk
313 } else {
314 context("stack unbalanced", fail).parse(input)?.1
315 },
316 ))
317 }
318}
319
320bitflags::bitflags! {
321 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
322 pub struct ProtoFlags: u8 {
323 const HAS_CHILD = 0b00000001;
324 const IS_VARIADIC = 0b00000010;
325 const HAS_FFI = 0b00000100;
326 const JIT_DISABLED = 0b00001000;
327 const HAS_ILOOP = 0b0001000;
328 }
329}