0

i plan on comercially releasing games soon, so i would like to find out a way to compile to bytecode and then fuse it into an executable. I have been searching for a way for literal months, but i haven't found a way, let me tell you what happens when i do try to do a method.

So doing jit.version, i can see Love2D uses 2.1.0beta3, which is the version i use to do luajit -b test.lua test.bc. After i do do that i proceed to remove the contents of the .lua file, and add different code to it.

Let's show you what i originally had inside of test.lua (the code i compiled):

local test = {}

function test:load()
    print("Worked.")
end

return test

So this is the code i compile, i then remove the contents of it and add this instead:

local f = assert(love.filesystem.load("test.bc"))

return f()

and then in my main.lua file i do this:

function love.load()
    local testclass = require("test")
    testclass:load()
end

When i do that, i produce this error (Before you ask, there's no syntax error in the Lua code before compilation.):

Error

test.lua:1: Syntax error: test.bc: cannot load incompatible bytecode



Traceback

[love "callbacks.lua"]:228: in function 'handler'
[C]: in function 'load'
test.lua:1: in main chunk
[C]: in function 'require'
main.lua:3: in function 'load'
[love "callbacks.lua"]:136: in function <[love "callbacks.lua"]:135>
[C]: in function 'xpcall'
[C]: in function 'xpcall'

I genuinly have been looking for a way for literal months, if someone could help out, it'd be greatly appreciated!

5
  • The incompatible bytecode error might be from the LuaJIT version you used not being built to use GC64 mode while Love2D could of been built with it enabled. If you build LuaJIT from source from using the HEAD of v2.1 branch its enabled by default. Commented Jun 30, 2023 at 23:43
  • 1
    Precompiling is neither recommended with JIT nor compatible across different operating systems. It also doesn't make your project more secure or prevents piracy. Why do you want to compile? Commented Jul 1, 2023 at 9:03
  • 1
    @Luke100000 JIT bytecode is portable: "The generated bytecode is portable and can be loaded on any architecture that LuaJIT supports, independent of word size or endianess. However, the bytecode compatibility versions must match. Bytecode stays compatible for dot releases (x.y.0 → x.y.1), but may change with major or minor releases (2.0 → 2.1) or between any beta release. Foreign bytecode (e.g. from Lua 5.1) is incompatible and cannot be loaded." - luajit.org/extensions.html Commented Jul 1, 2023 at 10:37
  • @Luke100000 also, re "[it] also doesn't make your project more secure or prevents piracy": It does make your project more secure by obscuring the sources ("security by obscurity"), making it harder to reverse. Of course you shouldn't rely on this as your sole means of security as it's easy to break. It doesn't prevent piracy either, but it does make modifying your game much harder, esp. if you strip debug symbols; this may help against cheating and against pirated, modded copies "selling" better than the original. Disclaimer: I've never distributed compiled Lua, I only made free software. Commented Jul 1, 2023 at 11:46
  • 1
    Obfuscation might be more helpful than bytecode for these purposes however. Commented Jul 1, 2023 at 11:47

1 Answer 1

6

So doing jit.version, i can see Love2D uses 2.1.0beta3, which is the version i use to do luajit [...]

Unfortunately, the assumption that LuaJIT version numbers would be sane is wrong. LuaJIT has been on 2.1.0-beta3 for years, despite numerous changes; Mike Pall's motivation for stopping to explicitly release new versions is unclear, but it seems that you are supposed to just use latest master.

Thus, to make sure that you're compiling your bytecode with the same LuaJIT version LÖVE uses, compile it from within LÖVE using string.dump.

Note that - contrary to PUC Lua - string.dump generates portable bytecode on LuaJIT:

The generated bytecode is portable and can be loaded on any architecture that LuaJIT supports, independent of word size or endianess. However, the bytecode compatibility versions must match. Bytecode stays compatible for dot releases (x.y.0 → x.y.1), but may change with major or minor releases (2.0 → 2.1) or between any beta release. Foreign bytecode (e.g. from Lua 5.1) is incompatible and cannot be loaded.

You can also set a strip flag, which will be helpful to remove debug information to make reversing harder:

An extra argument has been added to string.dump(). If set to true, 'stripped' bytecode without debug information is generated. This speeds up later bytecode loading and reduces memory usage. See also the -b command line option.

So, let's first use the following main.lua, which assumes your project folder as the current working directory:

-- Compile `test.lua` to bytecode, strip debug info
local bytecode = string.dump(assert(love.filesystem.load("test.lua")), true)

local f = assert(io.open("test.bc", "wb"))
f:write(bytecode)
f:close()

print("Compiled `test.lua` to `test.bc`")
love.event.quit()

Now execute love . inside your project directory to compile test.lua to test.bc. After doing this, you can replace test.lua with:

return assert(love.filesystem.load("test.bc"))()

and replace main.lua with

function love.load()
    local testclass = require("test")
    testclass:load()
end

Running this using love . prints Worked. as expected.


Automatizing this build process using Lua scripts, shell scripts or Makefiles is left as an exercise to the reader.

You could also try figuring out the exact LuaJIT commit LÖVE uses to compile that LuaJIT version yourself, which would allow you to use luajit -b test.lua test.bc.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you for responding. I have done the exact steps, putting the original code in test.lua, compiling it by doing string.dump (the code you provided) in main.lua, then replacing everything in test.lua by the return statement, then changing main.lua to require test.lua as testclass. When i do this, i get the following error: ``` Error test.lua:1: Syntax error: test.bc: cannot load malformed bytecode Traceback etc.. ``` I tried compiling it twice, and i get the same error.
@MielLand are you on Windows? I noticed I made a small mistake: I opened the file using w. On Unix (where I tested) this doesn't matter, there is no difference between w and wb. But Windows performs line feed normalization to CRLF. I've edited my answer to use wb for opening the file. Could you try again?
Oh my god. You may be the smartest human being i have ever met. You DO NOT understand how happy this makes me, i have literally, literally been searching for MONTHS and everyone always raises "why would you want to do that?" "no reason to." instead of helping. You are honestly the best, if i could EVER do anything to repay you, i would, thank you, thank you so so much for helping.
@MielLand Thanks, you're welcome :) consider using an obfuscator if you want to make reversing and modifying your game harder; LuaJIT bytecode can be decompiled (although local variable names and much of the code structure will be lost in the process).
@MielLand - A NeoVIM (Texteditor with syntaxhighlight) for Windows exists and uses LuaJIT / FFI (with modified Lua 5.1) as LÖVE do - I suggest it and i use it not only for Lua as a "Standard App" for file extensions - It contains a help system and a good start could be in command mode: :help lua or :help luado or :help luafile

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.