Skip to content
macabeusPublic

About

๐Ÿ“œ A spirit that lifts fallen assembly back into the light

Resources

Stars

19 stars

Watchers

0 watching

Forks

Latest commit

ย 

History

410 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

asmlift

๐Ÿ“œ A spirit that lifts fallen assembly back into the light

A matching decompiler: assembly in, C / C++ / Pascal out, aiming for source that recompiles byte-identical to the original object. Designed for retro game decompilation.

Check the playground to see it in action or the benchmark report to see its current performance.

image
image
Explore the benchmark overview
image
Check its performance per function

โš™๏ธ What is Matching Decompilation?

Matching decompilation is the art of converting assembly back into C source code that, when compiled, produces byte-for-byte identical machine code. Itโ€™s popular in the retro gaming community for recreating the source code of classic games. For example, Super Mario 64 and The Legend of Zelda: Ocarina of Time have been fully match-decompiled.

Learn more by watching my talk.

Why not m2c?

m2c is a great tool built over years of reverse engineering experience from many contributors, and its learnings were very helpful for asmlift.

But I wanted to explore a different approach: using AI to design, from scratch, a modular matching decompiler, plus using an AI loop to automatically iterate on the decompiler itself.

The driving question is: what if, instead of using AI to match a single function, we used AI to build a machine that matches functions programmatically?

asmlift is the result of this exploration.

๐Ÿ“— Check asmlift-101.md for an introduction on how asmlift is designed and how decompilers works.

Quick start

1. Install @asmlift/cli:

# globally, so you can run `asmlift` from anywhere on your system
npm install -g @asmlift/cli

# or inside a decomp project, as a dev dependency you run with `npx asmlift`
npm install --save-dev @asmlift/cli

2. Configure decomp.yaml. It must meet the decomp_settings spec: name, platform, and each version with its fullname and its target, build_dir, map and compiled_target paths. Then add, optionally, tools.asmlift.target, tools.asmlift.compiler and tools.asmlift.elf. Check for more examples here.

# example decomp.yaml for a GBA project

name: My Project
platform: gba
versions:
  - name: us
    fullname: US
    paths:
      target: baserom.gba
      build_dir: build
      map: my-project.map
      compiled_target: my-project.gba

tools:
  asmlift:
    # Optional. Used only for disambiguation when multiple toolchains are available.
    # Possible values: agbcc, ido7.1, gcc2.7.2kmc, gcc2.7.2,
    #                 mwcc_242_81, mwcc_233_163n, mwcc_247_107
    target: agbcc

    # Optional. The project's built ELF. asmlift reads its symbol table and debug info to
    # name the globals and struct fields the assembly only addresses, recover callee
    # signatures, and adopt the project's address-cast macro names. How to produce a good
    # one is covered in the CLI documentation linked below.
    elf: rom.elf

    # Optional. The command that compiles a candidate for `--score-against` โ€” and the place
    # asmlift reads this file's codegen flags from when nothing else states them. A project whose
    # units build with several flag sets writes `{{flags}}` here instead and lets asmlift find
    # each function's unit; `--cflags` overrides both.
    compiler: |
      arm-none-eabi-cpp -nostdinc -I tools/agbcc/include {{inputPath}} -o {{outputPath}}.i
      ./tools/agbcc/bin/agbcc {{outputPath}}.i -o {{outputPath}}.s -mthumb-interwork -O2 -fhex-asm
      arm-none-eabi-as -mcpu=arm7tdmi -mthumb-interwork {{outputPath}}.s -o {{outputPath}}

๐ŸŽฎ A GameCube/Wii project (platform: gc) wires up differently, because its units build with many flag sets: compiler takes {{flags}} โ€” plus {{cc}} where a unit names its own CodeWarrior build โ€” and asmlift reads each function's flags from the objdiff.json beside decomp.yaml. A function in a REL module needs --module <name>, which both narrows that lookup and points asmlift at the module's own symbols. See Compiler flags and REL modules.

3. Decompile and verify in one step:

asmlift build/src/gfx.s --name ReadUnalignedU16 --score-against build/src/gfx.o
s32 ReadUnalignedU16(u8 * a0) {
    return *a0 | a0[1] << 8;
}
asmlift: [config] target agbcc (tools.asmlift.target in ./decomp.yaml)
asmlift: [flags] -mthumb-interwork -O2 -fhex-asm (compiler command)
asmlift: [score] unsigned: 0/6 (match)

The run also ends with a [ranked] summary naming the winner; drop target: from the config above and the [config] trace becomes platform 'gba' instead.

๐Ÿ“š Check packages/cli to learn about all the configuration options and flags of asmlift.

Layout (pnpm workspace monorepo)

Package / app What it is
packages/core (@asmlift/core) The decompile pipeline
packages/cli (@asmlift/cli) The user-facing CLI package
packages/toolchains The pinned calibration toolchains. Used only for the tests and benchmark
packages/bench-schema The shared benchmark contract: result/manifest types + feature vocabulary
apps/web The webapp including the Playground and the Benchmark
apps/benchmark The asmlift and m2c harness

Releasing

@asmlift/core and @asmlift/cli are the two public packages โ€” see docs/releasing.md for the gates (including the two that hosted CI cannot run) and the publish order.

License

MIT (LICENSE) for the whole monorepo, with two carve-outs:

  • apps/web is GPL-2.0-only, since it imports agbcc.
  • The game-derived benchmark data is not covered by either license. Check the original game repositories for their licensing.

About

๐Ÿ“œ A spirit that lifts fallen assembly back into the light

Resources

Stars

19 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages