From d56bd81a0a68b91649a1e639b93c039caca3ce51 Mon Sep 17 00:00:00 2001 From: arookas Date: Wed, 30 Dec 2015 22:27:03 -0500 Subject: [PATCH] Added preliminary sbdump code. --- sbdump/AssemblyInfo.cs | 15 +++ sbdump/main.cs | 275 +++++++++++++++++++++++++++++++++++++++++ sbdump/settings.cs | 45 +++++++ sbdump/symbol.cs | 29 +++++ 4 files changed, 364 insertions(+) create mode 100644 sbdump/AssemblyInfo.cs create mode 100644 sbdump/main.cs create mode 100644 sbdump/settings.cs create mode 100644 sbdump/symbol.cs diff --git a/sbdump/AssemblyInfo.cs b/sbdump/AssemblyInfo.cs new file mode 100644 index 0000000..896cf10 --- /dev/null +++ b/sbdump/AssemblyInfo.cs @@ -0,0 +1,15 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("sbdump")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("arookas")] +[assembly: AssemblyProduct("sbdump")] +[assembly: AssemblyCopyright("Copyright © 2015 arookas")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: Guid("e2610ef0-cb2d-4f90-a18a-a2e7db316202")] +[assembly: AssemblyVersion("0.1.0.0")] +[assembly: AssemblyFileVersion("0.1.0.0")] diff --git a/sbdump/main.cs b/sbdump/main.cs new file mode 100644 index 0000000..7d89a75 --- /dev/null +++ b/sbdump/main.cs @@ -0,0 +1,275 @@ +using arookas.IO.Binary; +using System; +using System.IO; +using System.Linq; +using System.Text; + +namespace arookas { + class sbdump { + static CommandLineSettings sSettings; + static aBinaryReader sReader; + static TextWriter sWriter; + static uint sTextOffset, sDataOffset, sDynsymOffset; + static int sDataCount, sDynsymCount, sBssCount; + + const string cTitle = "sbdump v0.1 arookas"; + static readonly string[] sSymbolTypes = { "builtin", "function", "var", }; + static readonly string[] sCommandNames = { + "int", "flt", "str", "adr", "var", "nop", "inc", "dec", + "add", "sub", "mul", "div", "mod", "ass", "eq", "ne", + "gt", "lt", "ge", "le", "neg", "not", "and", "or", + "band", "bor", "shl", "shr", "call", "func", "mkfr", "mkds", + "ret", "ret0", "jne", "jmp", "pop", "int0", "int1", "end", + }; + + static int Main(string[] args) { + try { + Console.WriteLine(cTitle); + ReadCommandLine(args); + Console.WriteLine("Opening input file..."); + using (var sb = File.OpenRead(sSettings.Input)) { + CreateReader(sb); + Console.WriteLine("Creating output file..."); + using (sWriter = File.CreateText(sSettings.Output)) { + ReadHeader(); + WritePreamble(); + if (sSettings.OutputHeader) { + WriteHeader(); + } + if (sSettings.OutputText) { + WriteText(); + } + if (sSettings.OutputData) { + WriteData(); + } + if (sSettings.OutputDynsym) { + WriteDynsym(); + } + if (sSettings.OutputBss) { + WriteBss(); + } + Console.WriteLine("Closing output file..."); + } + Console.WriteLine("Closing input file..."); + } + Console.WriteLine("Done."); + } + catch (Exception e) { + Console.WriteLine(e.Message); + return 1; + } + return 0; + } + + static void ReadCommandLine(string[] args) { + Console.WriteLine("Reading command line..."); + sSettings = new CommandLineSettings(new CommandLine(args)); + } + static void CreateReader(Stream stream) { + Console.WriteLine("Creating binary reader..."); + sReader = new aBinaryReader(stream, Endianness.Big, Encoding.GetEncoding(932)); + } + static void WritePreamble() { + Console.WriteLine("Writing preamble..."); + sWriter.WriteLine("# {0}", cTitle); + sWriter.WriteLine("# {0}", DateTime.Now); + sWriter.WriteLine("# {0}", Path.GetFileName(sSettings.Input)); + sWriter.WriteLine(); + } + static void ReadHeader() { + Console.WriteLine("Reading header..."); + if (sReader.Read32() != 0x53504342u) { // 'SPCB' + throw new Exception("Invalid magic."); + } + sTextOffset = sReader.Read32(); + sDataOffset = sReader.Read32(); + sDataCount = sReader.ReadS32(); + sDynsymOffset = sReader.Read32(); + sDynsymCount = sReader.ReadS32(); + sBssCount = sReader.ReadS32(); + } + static void WriteHeader() { + Console.WriteLine("Outputting header..."); + sWriter.WriteLine("# Header information"); + sWriter.WriteLine("# .text offset : {0:X8}", sTextOffset); + sWriter.WriteLine("# .data offset : {0:X8}", sDataOffset); + sWriter.WriteLine("# .data count : {0}", sDataCount); + sWriter.WriteLine("# .dynsym offset : {0:X8}", sDynsymOffset); + sWriter.WriteLine("# .dynsym count : {0}", sDynsymCount); + sWriter.WriteLine("# .bss count : {0}", sBssCount); + sWriter.WriteLine(); + } + static void WriteText() { + Console.WriteLine("Outputting .text..."); + sWriter.WriteLine(".text"); + WriteText(0u); + var symbols = new Symbol[sDynsymCount]; + for (var i = 0; i < sDynsymCount; ++i) { + symbols[i] = FetchSymbol(i); + } + foreach (var symbol in symbols.Where(i => i.Type == SymbolType.Function).OrderBy(i => i.Data)) { + sWriter.WriteLine("{0}: ", FetchSymbolName(symbol)); + WriteText(symbol.Data); + } + } + static void WriteText(uint ofs) { + byte command; + sReader.Keep(); + sReader.Goto(sTextOffset + ofs); + do { + var pos = sReader.Position - sTextOffset; + command = sReader.Read8(); + sWriter.Write(" {0:X8} {1}", pos, sCommandNames[command]); + switch (command) { + case 0x00: sWriter.Write(" {0}", sReader.ReadS32()); break; + case 0x01: sWriter.Write(" {0}", sReader.ReadF32()); break; + case 0x02: { + var data = sReader.ReadS32(); + var value = FetchDataValue(data); + sWriter.Write(" {0} # \"{1}\"", data, value); + break; + } + case 0x03: sWriter.Write(" ${0:X8}", sReader.Read32()); break; + case 0x04: { + var display = sReader.ReadS32(); + sWriter.Write(" {0}", FetchSymbolName(FetchSymbol(sReader.ReadS32()))); break; + } + case 0x06: WriteVar(); break; + case 0x07: WriteVar(); break; + case 0x0D: { + sReader.Read8(); // TSpcInterp skips this byte + WriteVar(); + break; + } + case 0x1C: { + var dest = sReader.Read32(); + var args = sReader.ReadS32(); + var symbol = FetchSymbol(i => i.Data == dest); + if (symbol != null) { + sWriter.Write(" {0}, {1}", FetchSymbolName(symbol), args); + } + else { + sWriter.Write(" ${0:X8}, {1}", dest, args); + } + break; + } + case 0x1D: sWriter.Write(" {0}, {1}", FetchSymbolName(FetchSymbol(sReader.ReadS32())), sReader.ReadS32()); break; + case 0x1E: sWriter.Write(" {0}", sReader.ReadS32()); break; + case 0x1F: sWriter.Write(" {0}", sReader.ReadS32()); break; + case 0x22: WriteJmp(ofs); break; + case 0x23: WriteJmp(ofs); break; + } + sWriter.WriteLine(); + + } while (command != 0x21 && command != 0x27); + sWriter.WriteLine(); + sReader.Back(); + } + static void WriteVar() { + var display = sReader.ReadS32(); + switch (display) { + case 0: sWriter.Write(" {0}", FetchSymbolName(FetchSymbol(sReader.ReadS32()))); break; + case 1: sWriter.Write(" locl{0}", sReader.ReadS32()); break; + } + } + static void WriteJmp(uint ofs) { + var dest = sReader.Read32(); + var symbol = FetchSymbol(i => i.Data == ofs); + if (ofs > 0 && symbol != null) { + var name = FetchSymbolName(symbol); + sWriter.Write(" {0} + ${1:X4} # ${2:X8}", name, dest - ofs, ofs); + } + else { + sWriter.Write(" ${0:X8}", dest); + } + } + static void WriteData() { + Console.WriteLine("Outputting .data..."); + sWriter.WriteLine(".data"); + sReader.Goto(sDataOffset); + for (int i = 0; i < sDataCount; ++i) { + var ofs = sReader.Read32(); + var data = FetchDataValue(ofs); + sWriter.WriteLine(" .string \"{0}\"", data); + } + sWriter.WriteLine(); + } + static void WriteDynsym() { + Console.WriteLine("Outputting .dynsym..."); + sWriter.WriteLine(".dynsym"); + sReader.Goto(sDynsymOffset); + for (int i = 0; i < sDynsymCount; ++i) { + var symbol = new Symbol(sReader); + var name = FetchSymbolName(symbol); + sWriter.WriteLine(" .{0} {1}", sSymbolTypes[(int)symbol.Type], name); + } + sWriter.WriteLine(); + } + static void WriteBss() { + Console.WriteLine("Outputting .bss..."); + sWriter.WriteLine(".bss"); + for (int i = 0; i < sBssCount; ++i) { + var symbol = FetchSymbol(j => j.Type == SymbolType.Variable && j.Data == i); + if (symbol != null) { + sWriter.WriteLine(" .var {0}", FetchSymbolName(symbol)); + } + else { + sWriter.WriteLine(" .var"); + } + } + sWriter.WriteLine(); + } + + static uint FetchData(int i) { + sReader.Keep(); + sReader.Goto(sDataOffset + (4 * i)); + var data = sReader.Read32(); + sReader.Back(); + return data; + } + static string FetchDataValue(int i) { + if (i < 0 || i >= sDataCount) { + return "null"; + } + return FetchDataValue(FetchData(i)); + } + static string FetchDataValue(uint ofs) { + sReader.Keep(); + sReader.Goto(sDataOffset + (4 * sDataCount) + ofs); + var data = sReader.ReadString(aBinaryStringFormat.NullTerminated); + sReader.Back(); + return data; + } + static Symbol FetchSymbol(int i) { + sReader.Keep(); + sReader.Goto(sDynsymOffset + (20 * i)); + var symbol = new Symbol(sReader); + sReader.Back(); + return symbol; + } + static Symbol FetchSymbol(Predicate predicate) { + if (predicate == null) { + throw new ArgumentNullException("predicate"); + } + Symbol found = null; + sReader.Keep(); + sReader.Goto(sDynsymOffset); + for (int i = 0; i < sDynsymCount; ++i) { + var symbol = new Symbol(sReader); + if (predicate(symbol)) { + found = symbol; + break; + } + } + sReader.Back(); + return found; + } + static string FetchSymbolName(Symbol symbol) { + sReader.Keep(); + sReader.Goto(sDynsymOffset + (20 * sDynsymCount) + symbol.StringOffset); + var name = sReader.ReadString(aBinaryStringFormat.NullTerminated); + sReader.Back(); + return name; + } + } +} diff --git a/sbdump/settings.cs b/sbdump/settings.cs new file mode 100644 index 0000000..f570ad1 --- /dev/null +++ b/sbdump/settings.cs @@ -0,0 +1,45 @@ +using System; +using System.IO; + +namespace arookas { + class CommandLineSettings { + string mInput, mOutput; + bool mOutputHeader, mOutputText, mOutputData, mOutputDynsym, mOutputBss; + + public string Input { get { return mInput; } } + public string Output { get { return mOutput; } } + public bool OutputHeader { get { return mOutputHeader; } } + public bool OutputText { get { return mOutputText; } } + public bool OutputData { get { return mOutputData; } } + public bool OutputDynsym { get { return mOutputDynsym; } } + public bool OutputBss { get { return mOutputBss; } } + + public CommandLineSettings(CommandLine cmd) { + if (cmd == null) { + throw new ArgumentNullException("cmd"); + } + foreach (var param in cmd) { + switch (param.Name) { + case "-in": mInput = param[0]; continue; + case "-out": mInput = param[0]; continue; + case "-H": mOutputHeader = true; continue; + case "-h": mOutputHeader = false; continue; + case "-T": mOutputText = true; continue; + case "-t": mOutputText = false; continue; + case "-D": mOutputData = true; continue; + case "-d": mOutputData = false; continue; + case "-S": mOutputDynsym = true; continue; + case "-s": mOutputDynsym = false; continue; + case "-B": mOutputBss = true; continue; + case "-b": mOutputBss = false; continue; + } + } + if (mInput == null) { + throw new Exception("Missing input file setting."); + } + if (mOutput == null) { + mOutput = Path.ChangeExtension(mInput, ".txt"); + } + } + } +} diff --git a/sbdump/symbol.cs b/sbdump/symbol.cs new file mode 100644 index 0000000..ec3611a --- /dev/null +++ b/sbdump/symbol.cs @@ -0,0 +1,29 @@ +using arookas.IO.Binary; + +namespace arookas { + class Symbol { + SymbolType mType; + uint mStringOffset; + uint mData; + // NOTE: the other two fields are runtime fields (hash and linker storage) + + public SymbolType Type { get { return mType; } } + public uint StringOffset { get { return mStringOffset; } } + public uint Data { get { return mData; } } + + public Symbol(aBinaryReader reader) { + mType = (SymbolType)reader.Read32(); + mStringOffset = reader.Read32(); + mData = reader.Read32(); + // skip the last two fields + reader.Read32(); + reader.Read32(); + } + } + + enum SymbolType { + Builtin, + Function, + Variable, + } +}