8 Commits
Author SHA1 Message Date
Vladimir Panteleev fe93f5e764 Update Changelog 2014-09-10 11:41:27 +00:00
Vladimir Panteleev 0fbfba1186 README: Update 2.066 note 2014-09-10 11:41:27 +00:00
Vladimir Panteleev 6bdf6dae39 abcfile: 64-bit fix 2014-09-10 11:41:26 +00:00
Vladimir Panteleev 62ef183d95 Do not attempt to disassemble unreachable code
Also, improve handling of disassembly errors -
methods will be partially disassembled as far as possible.
2014-09-03 15:12:26 +00:00
Vladimir Panteleev f6eb080be7 disassembler: Print new lines after OP_throw 2014-09-03 15:12:26 +00:00
Vladimir Panteleev a17a03e6f6 asprogram: Work around issue 13420 in DMD 2.066 2014-09-03 15:11:26 +00:00
Vladimir Panteleev 0486c1a54a lzma: Provide 10% slack space for uncompressable data 2014-07-19 23:28:40 +00:00
Vladimir Panteleev a2d22e7606 README: Add link to liblzma 2014-06-10 03:05:07 +00:00
6 changed files with 277 additions and 127 deletions
+8
View File
@@ -1,6 +1,14 @@
RABCDAsm Changelog
==================
RABCDAsm v1.17 (2014.09.10)
---------------------------
* Do not attempt to disassemble unreachable code
* Improve handling of disassembly errors:
methods will be partially disassembled as far as possible.
* Fix LZMA errors with uncompressable data.
RABCDAsm v1.16 (2014.04.21)
---------------------------
+4 -3
View File
@@ -72,15 +72,16 @@ Substitute `dmd` with `gdmd` if you're using gdc. You can use the `DC` and
compilation flags (`-O -inline`).
To be able to manipulate SWF files packed with LZMA compression, you'll need
to have the liblzma library and development files installed on your system.
to have the [liblzma library][] and development files installed on your system.
Note: DMD 2.066 (as of this moment not yet released) is required for long path
support on Windows since RABCDAsm 1.16.
Note: DMD 2.066 is required for long path support on Windows since RABCDAsm
1.16.
[d2]: http://dlang.org/
[dmd]: http://www.digitalmars.com/d/download.html
[gdc]: http://bitbucket.org/goshawk/gdc/
[git]: http://git-scm.com/
[liblzma library]: http://tukaani.org/xz/
Pre-compiled binaries
---------------------
+233 -98
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -208,8 +208,7 @@ class ABCFile
ExceptionInfo[] exceptions;
TraitsInfo[] traits;
string error;
ubyte[] rawBytes;
Error[] errors;
}
/// Destination for a jump or exception block boundary
@@ -219,13 +218,20 @@ class ABCFile
{
struct
{
uint index; /// instruction index
int offset; /// signed offset relative to said instruction
uint index = uint.max; /// instruction index
int offset = int.max; /// signed offset relative to said instruction
}
private ptrdiff_t absoluteOffset; /// internal temporary value used during reading and writing
}
}
/// Disassembly/decoding error
struct Error
{
Label loc;
string msg;
}
struct Instruction
{
Opcode opcode;
@@ -439,6 +445,7 @@ const string[4] TraitAttributeNames = ["FINAL", "OVERRIDE", "METADATA", "0x08"];
enum Opcode : ubyte
{
OP_raw = 0x00, /// Used internally by RABCDAsm
OP_bkpt = 0x01,
OP_nop = 0x02,
OP_throw = 0x03,
@@ -659,7 +666,7 @@ struct OpcodeInfo
}
const OpcodeInfo[256] opcodeInfo = [
/* 0x00 */ {"0x00", [OpcodeArgumentType.Unknown]},
/* 0x00 */ {"db", [OpcodeArgumentType.UByteLiteral]},
/* 0x01 */ {"bkpt", [OpcodeArgumentType.Unknown]},
/* 0x02 */ {"nop", []},
/* 0x03 */ {"throw", []},
@@ -926,6 +933,16 @@ static this()
OpcodeByName = OpcodeByName.rehash;
}
bool[256] genLookup(Opcode[] opcodes)
{
bool[256] result;
foreach (op; opcodes)
result[op] = true;
return result;
}
const bool[256] stopsExecution = genLookup([Opcode.OP_returnvalue, Opcode.OP_returnvoid, Opcode.OP_throw, Opcode.OP_jump, Opcode.OP_lookupswitch]);
private final class ABCReader
{
ubyte[] buf;
@@ -1291,8 +1308,194 @@ private final class ABCReader
r.instructions = null;
size_t len = readU30();
size_t start = pos;
size_t end = pos + len;
pos = end;
r.exceptions.length = readU30();
foreach (ref value; r.exceptions)
value = readExceptionInfo();
size_t postExceptions = pos;
enum TraceState : ubyte
{
unexplored,
pending,
instruction,
instructionBody,
error,
}
auto traceState = new TraceState[len];
auto instructions = new ABCFile.Instruction[len];
@property size_t offset() { return pos - start; }
bool havePending;
void queue(size_t traceOffset)
{
if (traceOffset < len && traceState[traceOffset] == TraceState.unexplored)
{
traceState[traceOffset] = TraceState.pending;
havePending = true;
}
}
queue(0);
foreach (ref value; r.exceptions)
queue(value.target.absoluteOffset);
while (havePending)
{
havePending = false;
pos = start;
while (pos < end)
{
if (traceState[offset] == TraceState.pending)
{
size_t instructionOffset;
try
{
while (pos < end)
{
instructionOffset = offset;
enforce(traceState[instructionOffset] != TraceState.instructionBody, "Overlapping instruction");
if (traceState[instructionOffset] == TraceState.instruction)
break; // already decoded
ABCFile.Instruction instruction;
instruction.opcode = cast(Opcode)readU8();
enforce(instruction.opcode != Opcode.OP_raw, "Null opcode");
instruction.arguments.length = opcodeInfo[instruction.opcode].argumentTypes.length;
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
final switch (type)
{
case OpcodeArgumentType.Unknown:
throw new Exception("Don't know how to decode OP_" ~ opcodeInfo[instruction.opcode].name);
case OpcodeArgumentType.UByteLiteral:
instruction.arguments[i].ubytev = readU8();
break;
case OpcodeArgumentType.IntLiteral:
instruction.arguments[i].intv = readS32();
break;
case OpcodeArgumentType.UIntLiteral:
instruction.arguments[i].uintv = readU32();
break;
case OpcodeArgumentType.Int:
case OpcodeArgumentType.UInt:
case OpcodeArgumentType.Double:
case OpcodeArgumentType.String:
case OpcodeArgumentType.Namespace:
case OpcodeArgumentType.Multiname:
case OpcodeArgumentType.Class:
case OpcodeArgumentType.Method:
{
auto index = readU30();
size_t length;
switch (type)
{
case OpcodeArgumentType.Int: length = abc.ints .length; break;
case OpcodeArgumentType.UInt: length = abc.uints .length; break;
case OpcodeArgumentType.Double: length = abc.doubles .length; break;
case OpcodeArgumentType.String: length = abc.strings .length; break;
case OpcodeArgumentType.Namespace: length = abc.namespaces.length; break;
case OpcodeArgumentType.Multiname: length = abc.multinames.length; break;
case OpcodeArgumentType.Class: length = abc.classes .length; break;
case OpcodeArgumentType.Method: length = abc.methods .length; break;
default: assert(false);
}
enforce(index < length, "Out-of-bounds constant index");
instruction.arguments[i].index = index;
break;
}
case OpcodeArgumentType.JumpTarget:
{
auto delta = readS24();
auto target = offset + delta;
instruction.arguments[i].jumpTarget.absoluteOffset = target;
queue(target);
break;
}
case OpcodeArgumentType.SwitchDefaultTarget:
{
auto target = instructionOffset + readS24();
instruction.arguments[i].jumpTarget.absoluteOffset = target;
queue(target);
break;
}
case OpcodeArgumentType.SwitchTargets:
instruction.arguments[i].switchTargets.length = readU30()+1;
foreach (ref label; instruction.arguments[i].switchTargets)
{
label.absoluteOffset = instructionOffset + readS24();
queue(label.absoluteOffset);
}
break;
}
enforce(offset <= len, "Out-of-bounds code read error");
instructions[instructionOffset] = instruction;
traceState[instructionOffset] = TraceState.instruction;
traceState[instructionOffset+1..offset] = TraceState.instructionBody;
if (stopsExecution[instruction.opcode])
break;
}
}
catch (Exception e)
{
traceState[instructionOffset] = TraceState.error;
ABCFile.Label loc;
loc.absoluteOffset = instructionOffset;
r.errors ~= ABCFile.Error(loc, e.msg);
pos = start + instructionOffset + 1;
}
}
else
pos++;
}
}
size_t[] instructionOffsets;
auto instructionAtOffset = new uint[len];
r.rawBytes = buf[pos..pos+len];
instructionAtOffset[] = uint.max;
void addInstruction(ref ABCFile.Instruction i, size_t offset)
{
instructionAtOffset[offset] = to!uint(r.instructions.length);
r.instructions ~= i;
instructionOffsets ~= offset;
}
foreach (o, state; traceState)
{
assert(state != TraceState.pending);
if (state == TraceState.instruction)
addInstruction(instructions[o], o);
else
if (state == TraceState.unexplored || state == TraceState.error)
{
ABCFile.Instruction instruction;
instruction.opcode = Opcode.OP_raw;
instruction.arguments.length = 1;
instruction.arguments[0].ubytev = buf[start + o];
addInstruction(instruction, o);
}
else
assert(state == TraceState.instructionBody);
}
void translateLabel(ref ABCFile.Label label)
{
@@ -1322,105 +1525,37 @@ private final class ABCReader
label.offset = to!int(absoluteOffset-instructionOffset);
}
size_t start = pos;
size_t end = pos + len;
// convert jump target offsets to instruction indices
foreach (ii, ref instruction; r.instructions)
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
switch (type)
{
case OpcodeArgumentType.JumpTarget:
case OpcodeArgumentType.SwitchDefaultTarget:
translateLabel(instruction.arguments[i].jumpTarget);
break;
case OpcodeArgumentType.SwitchTargets:
foreach (ref x; instruction.arguments[i].switchTargets)
translateLabel(x);
break;
default:
break;
}
@property size_t offset() { return pos - start; }
// convert error offsets to instruction indices
foreach (ref e; r.errors)
translateLabel(e.loc);
try
{
instructionAtOffset[] = uint.max;
size_t[] instructionOffsets;
while (pos < end)
{
auto instructionOffset = offset;
scope(failure) pos = start + instructionOffset;
instructionAtOffset[instructionOffset] = to!uint(r.instructions.length);
ABCFile.Instruction instruction;
instruction.opcode = cast(Opcode)readU8();
instruction.arguments.length = opcodeInfo[instruction.opcode].argumentTypes.length;
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
final switch (type)
{
case OpcodeArgumentType.Unknown:
throw new Exception("Don't know how to decode OP_" ~ opcodeInfo[instruction.opcode].name);
case OpcodeArgumentType.UByteLiteral:
instruction.arguments[i].ubytev = readU8();
break;
case OpcodeArgumentType.IntLiteral:
instruction.arguments[i].intv = readS32();
break;
case OpcodeArgumentType.UIntLiteral:
instruction.arguments[i].uintv = readU32();
break;
case OpcodeArgumentType.Int:
case OpcodeArgumentType.UInt:
case OpcodeArgumentType.Double:
case OpcodeArgumentType.String:
case OpcodeArgumentType.Namespace:
case OpcodeArgumentType.Multiname:
case OpcodeArgumentType.Class:
case OpcodeArgumentType.Method:
instruction.arguments[i].index = readU30();
break;
case OpcodeArgumentType.JumpTarget:
int delta = readS24();
instruction.arguments[i].jumpTarget.absoluteOffset = offset + delta;
break;
case OpcodeArgumentType.SwitchDefaultTarget:
instruction.arguments[i].jumpTarget.absoluteOffset = instructionOffset + readS24();
break;
case OpcodeArgumentType.SwitchTargets:
instruction.arguments[i].switchTargets.length = readU30()+1;
foreach (ref label; instruction.arguments[i].switchTargets)
label.absoluteOffset = instructionOffset + readS24();
break;
}
r.instructions ~= instruction;
instructionOffsets ~= instructionOffset;
}
if (pos > end)
throw new Exception("Out-of-bounds code read error");
// convert jump target offsets to instruction indices
foreach (ii, ref instruction; r.instructions)
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
switch (type)
{
case OpcodeArgumentType.JumpTarget:
case OpcodeArgumentType.SwitchDefaultTarget:
translateLabel(instruction.arguments[i].jumpTarget);
break;
case OpcodeArgumentType.SwitchTargets:
foreach (ref x; instruction.arguments[i].switchTargets)
translateLabel(x);
break;
default:
break;
}
}
catch (Exception e)
{
r.instructions = null;
r.error = e.msg;
instructionAtOffset[] = 0;
}
pos = end;
r.exceptions.length = readU30();
// convert exception offsets to instruction indices
foreach (ref value; r.exceptions)
{
value = readExceptionInfo();
translateLabel(value.from);
translateLabel(value.to);
translateLabel(value.target);
}
pos = postExceptions;
r.traits.length = readU30();
foreach (ref value; r.traits)
value = readTrait();
+9 -5
View File
@@ -261,8 +261,7 @@ final class ASProgram
Exception[] exceptions;
Trait[] traits;
string error;
ubyte[] rawBytes;
ABCFile.Error[] errors;
}
struct Instruction
@@ -583,8 +582,7 @@ private final class ABCtoAS
e.varName = multinames[exc.varName];
}
n.traits = convertTraits(vbody.traits);
n.error = vbody.error;
n.rawBytes = vbody.rawBytes;
n.errors = vbody.errors;
return n;
}
@@ -747,12 +745,18 @@ private final class AStoABC : ASVisitor
{
static if (byRef)
alias void* Key;
else
static if (is(T == double))
alias ulong Key; // https://issues.dlang.org/show_bug.cgi?id=13420
else
alias immutable(T) Key;
Key toKey(T value)
{
return cast(Key)value;
static if (is(T == double))
return *cast(ulong*)&value;
else
return cast(Key)value;
}
struct Entry
+21 -19
View File
@@ -1575,13 +1575,6 @@ final class Disassembler
void dumpMethodBody(StringBuilder sb, ASProgram.MethodBody mbody)
{
if (mbody.error)
{
sb ~= "; Error while disassembling method: " ~ mbody.error;
sb.newLine();
sb.linePrefix = "; ";
}
sb ~= "body";
sb.indent++; sb.newLine();
dumpUIntField(sb, "maxstack", mbody.maxStack);
@@ -1597,17 +1590,7 @@ final class Disassembler
labels[e.from.index] = labels[e.to.index] = labels[e.target.index] = true;
sb.indent++;
if (mbody.error)
foreach (i, b; mbody.rawBytes)
{
sb ~= format("0x%02X", b);
if (i%16==15 || i==mbody.rawBytes.length-1)
sb.newLine();
else
sb ~= " ";
}
else
dumpInstructions(sb, mbody.instructions, labels);
dumpInstructions(sb, mbody.instructions, labels, mbody.errors);
sb.indent--;
sb ~= "end ; code";
@@ -1632,7 +1615,7 @@ final class Disassembler
sb.linePrefix = null;
}
void dumpInstructions(StringBuilder sb, ASProgram.Instruction[] instructions, bool[] labels)
void dumpInstructions(StringBuilder sb, ASProgram.Instruction[] instructions, bool[] labels, ABCFile.Error[] errors)
{
foreach (ref instruction; instructions)
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
@@ -1662,6 +1645,10 @@ final class Disassembler
}
}
string[] iErrors = new string[instructions.length + 1];
foreach (ref error; errors)
iErrors[error.loc.index] = error.msg;
bool extraNewLine = false;
foreach (uint ii, ref instruction; instructions)
{
@@ -1670,6 +1657,20 @@ final class Disassembler
extraNewLine = newLineAfter[instruction.opcode];
checkLabel(ii);
if (iErrors[ii])
{
sb ~= "; Error: ";
sb ~= iErrors[ii];
sb.newLine();
}
if (instruction.opcode == Opcode.OP_raw)
{
sb ~= "; 0x%02X".format(instruction.arguments[0].ubytev);
sb.newLine();
continue;
}
sb ~= opcodeInfo[instruction.opcode].name;
auto argTypes = opcodeInfo[instruction.opcode].argumentTypes;
if (argTypes.length)
@@ -1803,6 +1804,7 @@ static this()
Opcode.OP_si32,
Opcode.OP_sf32,
Opcode.OP_sf64,
Opcode.OP_throw,
])
newLineAfter[o] = true;
}
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2012, 2013, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -79,7 +79,7 @@ ubyte[] lzmaCompress(in ubyte[] decompressedData, LZMAHeader* header)
lzmaEnforce(lzma_alone_encoder(&strm, &opts), "lzma_alone_encoder");
scope(exit) lzma_end(&strm);
auto outBuf = new ubyte[decompressedData.length + 1024];
auto outBuf = new ubyte[decompressedData.length * 11 / 10 + 1024];
strm.next_out = outBuf.ptr;
strm.avail_out = outBuf.length;
strm.next_in = decompressedData.ptr;