WebAssembly bekommt einen Decompiler

Mit wasm-decompile lässt sich der Binärcode in ein Textformat umsetzen, das einen besseren Einblick in die Struktur der Programme geben soll als .wat-Dateien.

In Pocket speichern vorlesen Druckansicht 27 Kommentare lesen
WebAssembly bekommt einen Decompiler

(Bild: EFKS/Shutterstock.com)

Lesezeit: 4 Min.
Inhaltsverzeichnis

Das WebAssembly Binary Toolkit (WABT) enthält nun einen speziellen Decompiler. Er überträgt den Binärcode von .wasm-Dateien in ein Textformat, das an höhere Programmiersprachen angelehnt und damit besser lesbar ist als das WebAssembly Text Format (WAT). Ziel ist, Entwicklern einen Einblick in Wasm-Programme zu geben, damit sie beispielsweise die Performance optimieren können.

WebAssembly, das seit Ende 2019 ein offizieller W3C-Standard ist, bietet zwar von Haus aus ein Format, das eine textuelle Repräsentierung des Bytecodes wiedergibt. WAT ist eine Zwischensprache für die Anzeige im Browser oder Entwicklertools wie einem Sourcecodeeditor und ist standardmäßig in .wat-Dateien gespeichert. Das WABT bietet mit wasm2wat und wat2wams zwei Tools zum Übersetzen von Bytecode in das Textformat und umgekehrt von .wat- in .wasm-Dateien.

Das WAT-Format lässt sich zwar lesen, entspricht in der Struktur jedoch dem Bytecode und wirkt unter anderem mit direkten Speicherzugriffen statt Strukturen wie eine Mischform aus höheren Programmiersprachen und Assemblersprachen. Der Beitrag im V8-Blog führt beispielhaft die Berechnung eines Skalarprodukts auf, die in C folgende Form hat:

typedef struct { float x, y, z; } vec3;

float dot(const vec3 *a, const vec3 *b) {
return a->x * b->x +
a->y * b->y +
a->z * b->z;
}

Nach dem Kompilieren in WebAssembly und dem anschließenden Disassemblieren mit wasm2wat, enthält die .wat-Datei folgende Umsetzung:

(func $dot (type 0) (param i32 i32) (result f32)
(f32.add
(f32.add
(f32.mul
(f32.load
(local.get 0))
(f32.load
(local.get 1)))
(f32.mul
(f32.load offset=4
(local.get 0))
(f32.load offset=4
(local.get 1))))
(f32.mul
(f32.load offset=8
(local.get 0))
(f32.load offset=8
(local.get 1))))))

Im Gegensatz dazu versucht wasm-decompile Code zu erzeugen, der wie "eine sehr durchschnittliche Programmiersprache" ausschaut. Tatsächlich erzeugt der Einsatz des Decompilers auf obiges Beispiel folgenden Code, der sich im Gegensatz zu dem WAT-Inhalt auf Anhieb nachvollziehen lässt:

function dot(a:{ a:float, b:float, c:float },
b:{ a:float, b:float, c:float }):float {
return a.a * b.a + a.b * b.b + a.c * b.c
}

Die vermeintliche Programmiersprache ist freilich mehr Schein als Sein: Es geht den Entwicklern ausschließlich um die lesbare Darstellung von WASM-Inhalten. Eine Rückkompilierung in das Binärformat für WebAssembly ist zumindest derzeit – anders als bei WAT – nicht möglich.

Der Decompiler bietet vorgegebene Darstellungen für typische Kontrollflusselemente innerhalb von WebAssembly-Code wie Schleifen und Blöcke. Erstere sind als loop L { ...; continue L; } umgesetzt. Blöcke repräsentiert wasm-decompile über if-Anweisungen.

Mehr Infos

Wasm

Die br_table-Konstrukte erinnern in der dekompilierten Form an switch-Anweisungen, denen sie auch von der Logik her weitgehend ähneln:

br_table[A, B, C, ..D](a);
label A:
return 0;
label B:
return 1;
label C:
return 2;
label D:

Der Blogbeitrag weist jedoch darauf hin, dass der dekompilierte Code kaum dem in den ursprünglichen Sourcen entspricht, da beim Kompilieren von C nach WASM die Compilerinfrastruktur LLVM Optimierungen durchführt, die anders als JVM-Bytecode (Java Virtual Machine) maßgebliche Änderungen an der ursprünglichen Struktur mit sich bringt.

Weitere Infos zum Decompiler für WebAssembly stehen im V8-Blog. Eine umfangreiche Beschreibung des Formats ist im GitHub-Repository von wasm-decompile zu finden. (rme)