2016-02-22 11:00:01 -05:00
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.IO ;
using IKVM.Reflection ;
2017-06-07 13:16:24 +00:00
using IKVM.Reflection.Metadata ;
2016-02-22 11:00:01 -05:00
namespace Ildasm
{
enum MetadataTableIndex {
2017-08-21 15:34:15 +00:00
Module = 0x0 ,
2017-06-07 13:16:24 +00:00
CustomAttribute = 0xc ,
2016-02-22 11:00:01 -05:00
ModuleRef = 0x1a ,
Assembly = 0x20 ,
AssemblyRef = 0x23 ,
2017-06-07 13:16:24 +00:00
ExportedType = 0x27 ,
2017-08-21 15:34:15 +00:00
// Portable PDB tables
Document = 0x30 ,
MethodDebugInformation = 0x31 ,
LocalScope = 0x32 ,
LocalVariable = 0x33 ,
LocalConstant = 0x34 ,
ImportScope = 0x35 ,
StateMachineMethod = 0x36 ,
CustomDebugInformation = 0x37
2016-02-22 11:00:01 -05:00
}
class TableDumper
{
Universe universe ;
Assembly assembly ;
Module module ;
public TableDumper ( string inputFile ) {
universe = new Universe ( UniverseOptions . None ) ;
var raw = universe . OpenRawModule ( System . IO . File . OpenRead ( inputFile ) , System . IO . Path . GetTempPath ( ) + "/Dummy" ) ;
if ( raw . IsManifestModule )
{
assembly = universe . LoadAssembly ( raw ) ;
module = assembly . ManifestModule ;
}
else
{
var ab = universe . DefineDynamicAssembly ( new AssemblyName ( "<ModuleContainer>" ) , IKVM . Reflection . Emit . AssemblyBuilderAccess . ReflectionOnly ) ;
assembly = ab ;
module = ab . __AddModule ( raw ) ;
}
}
public void DumpTable ( TextWriter w , MetadataTableIndex tableIndex ) {
switch ( tableIndex ) {
case MetadataTableIndex . Assembly :
DumpAssemblyTable ( w ) ;
break ;
case MetadataTableIndex . AssemblyRef :
DumpAssemblyRefTable ( w ) ;
break ;
2019-04-12 14:10:50 +00:00
case MetadataTableIndex . Module :
DumpModuleTable ( w ) ;
break ;
2016-02-22 11:00:01 -05:00
case MetadataTableIndex . ModuleRef :
DumpModuleRefTable ( w ) ;
break ;
2017-06-07 13:16:24 +00:00
case MetadataTableIndex . ExportedType :
DumpExportedTypeTable ( w ) ;
break ;
case MetadataTableIndex . CustomAttribute :
DumpCustomAttributeTable ( w ) ;
break ;
2017-08-21 15:34:15 +00:00
case MetadataTableIndex . Document :
DumpDocumentTable ( w ) ;
break ;
case MetadataTableIndex . MethodDebugInformation :
DumpMethodDebugInformationTable ( w ) ;
break ;
case MetadataTableIndex . LocalScope :
DumpLocalScopeTable ( w ) ;
break ;
case MetadataTableIndex . ImportScope :
DumpImportScopeTable ( w ) ;
break ;
case MetadataTableIndex . CustomDebugInformation :
DumpCustomDebugInfoTable ( w ) ;
break ;
2016-02-22 11:00:01 -05:00
default :
throw new NotImplementedException ( ) ;
}
}
void HexDump ( TextWriter w , byte [ ] bytes , int len ) {
for ( int i = 0 ; i < len ; + + i ) {
if ( ( i % 16 ) = = 0 )
w . Write ( String . Format ( "\n0x{0:x08}: " , i ) ) ;
w . Write ( String . Format ( "{0:X02} " , bytes [ i ] ) ) ;
}
}
void DumpAssemblyTable ( TextWriter w ) {
var t = module . AssemblyTable ;
w . WriteLine ( "Assembly Table" ) ;
foreach ( var r in t . records ) {
w . WriteLine ( String . Format ( "Name: {0}" , module . GetString ( r . Name ) ) ) ;
w . WriteLine ( String . Format ( "Hash Algoritm: 0x{0:x08}" , r . HashAlgId ) ) ;
w . WriteLine ( String . Format ( "Version: {0}.{1}.{2}.{3}" , r . MajorVersion , r . MinorVersion , r . BuildNumber , r . RevisionNumber ) ) ;
w . WriteLine ( String . Format ( "Flags: 0x{0:x08}" , r . Flags ) ) ;
w . WriteLine ( String . Format ( "PublicKey: BlobPtr (0x{0:x08})" , r . PublicKey ) ) ;
var blob = module . GetBlob ( r . PublicKey ) ;
if ( blob . Length = = 0 ) {
w . WriteLine ( "\tZero sized public key" ) ;
} else {
w . Write ( "\tDump:" ) ;
byte [ ] bytes = blob . ReadBytes ( blob . Length ) ;
HexDump ( w , bytes , bytes . Length ) ;
w . WriteLine ( ) ;
}
w . WriteLine ( String . Format ( "Culture: {0}" , module . GetString ( r . Culture ) ) ) ;
w . WriteLine ( ) ;
}
}
void DumpAssemblyRefTable ( TextWriter w ) {
var t = module . AssemblyRef ;
w . WriteLine ( "AssemblyRef Table" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
w . WriteLine ( String . Format ( "{0}: Version={1}.{2}.{3}.{4}" , rowIndex , r . MajorVersion , r . MinorVersion , r . BuildNumber , r . RevisionNumber ) ) ;
w . WriteLine ( String . Format ( "\tName={0}" , module . GetString ( r . Name ) ) ) ;
w . WriteLine ( String . Format ( "\tFlags=0x{0:x08}" , r . Flags ) ) ;
var blob = module . GetBlob ( r . PublicKeyOrToken ) ;
if ( blob . Length = = 0 ) {
w . WriteLine ( "\tZero sized public key" ) ;
} else {
w . Write ( "\tPublic Key:" ) ;
byte [ ] bytes = blob . ReadBytes ( blob . Length ) ;
HexDump ( w , bytes , bytes . Length ) ;
w . WriteLine ( ) ;
}
w . WriteLine ( ) ;
rowIndex + + ;
}
}
2019-04-12 14:10:50 +00:00
void DumpModuleTable ( TextWriter w ) {
var t = module . ModuleTable ;
w . WriteLine ( "Module Table (1.." + t . RowCount + ")" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
w . WriteLine ( String . Format ( "{0}: {1} {2} {{{3}}}" , rowIndex , module . GetString ( r . Name ) , r . Generation , module . GetGuid ( r . Mvid ) . ToString ( ) . ToUpper ( ) ) ) ;
rowIndex + + ;
}
}
2016-02-22 11:00:01 -05:00
void DumpModuleRefTable ( TextWriter w ) {
var t = module . ModuleRef ;
w . WriteLine ( "ModuleRef Table (1.." + t . RowCount + ")" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
w . WriteLine ( String . Format ( "{0}: {1}" , rowIndex , module . GetString ( r ) ) ) ;
rowIndex + + ;
}
}
2017-06-07 13:16:24 +00:00
string GetManifestImpl ( int idx ) {
if ( idx = = 0 )
return "current module" ;
uint table = ( uint ) idx > > 24 ;
uint row = ( uint ) idx & 0xffffff ;
switch ( table ) {
case FileTable . Index :
return "file " + row ;
case ( uint ) AssemblyRefTable . Index :
return "assemblyref " + row ;
case ( uint ) ExportedTypeTable . Index :
return "exportedtype " + row ;
default :
return "" ;
}
}
void DumpExportedTypeTable ( TextWriter w ) {
var t = module . ExportedType ;
w . WriteLine ( "ExportedType Table (1.." + t . RowCount + ")" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
string name = module . GetString ( r . TypeName ) ;
string nspace = module . GetString ( r . TypeNamespace ) ;
w . WriteLine ( String . Format ( "{0}: {1}{2}{3} is in {4}, index={5:x}, flags=0x{6:x}" , rowIndex , nspace , nspace ! = "" ? "." : "" , name , GetManifestImpl ( r . Implementation ) , r . TypeDefId , r . Flags ) ) ;
rowIndex + + ;
}
}
string StringifyCattrValue ( object val ) {
if ( val . GetType ( ) = = typeof ( string ) )
return String . Format ( "\"{0}\"" , val ) ;
else if ( val = = null )
return "null" ;
else
return val . ToString ( ) ;
}
void DumpCustomAttributeTable ( TextWriter w ) {
var t = module . CustomAttribute ;
w . WriteLine ( "CustomAttribute Table (1.." + t . RowCount + ")" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
}
Dictionary < int , string > table_names = new Dictionary < int , string > ( ) {
{ MethodDefTable . Index , "MethodDef" } ,
{ FieldTable . Index , "FieldDef" } ,
{ TypeRefTable . Index , "TypeRef" } ,
{ TypeDefTable . Index , "TypeDef" } ,
{ ParamTable . Index , "Param" } ,
{ InterfaceImplTable . Index , "InterfaceImpl" } ,
{ MemberRefTable . Index , "MemberRef" } ,
{ AssemblyTable . Index , "Assembly" } ,
{ ModuleTable . Index , "Module" } ,
{ PropertyTable . Index , "Property" } ,
{ EventTable . Index , "Event" } ,
{ StandAloneSigTable . Index , "StandAloneSignature" } ,
{ ModuleRefTable . Index , "ModuleRef" } ,
{ TypeSpecTable . Index , "TypeSpec" } ,
{ AssemblyRefTable . Index , "AssemblyRef" } ,
{ FileTable . Index , "File" } ,
{ ExportedTypeTable . Index , "ExportedType" } ,
{ ManifestResourceTable . Index , "Manifest" } ,
{ GenericParamTable . Index , "GenericParam" }
} ;
foreach ( var cattr in module . __EnumerateCustomAttributeTable ( ) ) {
//Console.WriteLine (cattr);
int parent_token = cattr . __Parent ;
string parent ;
int table_idx = parent_token > > 24 ;
int row = parent_token & 0xffffff ;
if ( ! table_names . TryGetValue ( table_idx , out parent ) )
parent = "Unknown" ;
var args = new StringBuilder ( ) ;
args . Append ( "[" ) ;
var sep = "" ;
foreach ( var arg in cattr . ConstructorArguments ) {
args . Append ( sep ) . Append ( StringifyCattrValue ( arg . Value ) ) ;
sep = ", " ;
}
foreach ( var named_arg in cattr . NamedArguments ) {
args . Append ( sep ) ;
args . Append ( "{" ) ;
args . Append ( String . Format ( "{0} = {1}" , named_arg . MemberName , StringifyCattrValue ( named_arg . TypedValue . Value ) ) ) ;
args . Append ( "}" ) ;
sep = ", " ;
}
args . Append ( "]" ) ;
var ctor = cattr . Constructor ;
var method = new StringBuilder ( ) ;
method . Append ( "instance void class " ) ;
method . Append ( String . Format ( "[{0}]{1}" , ctor . DeclaringType . Assembly . GetName ( ) . Name , ctor . DeclaringType . ToString ( ) ) ) ;
method . Append ( "::'.ctor'(" ) ;
sep = "" ;
foreach ( var arg in ctor . GetParameters ( ) ) {
method . Append ( sep ) . Append ( arg . ParameterType ) ;
sep = ", " ;
}
method . Append ( ")" ) ;
w . WriteLine ( String . Format ( "{0}: {1}: {2} {3} {4}" , rowIndex , parent , row , method , args ) ) ;
rowIndex + + ;
}
}
2017-08-21 15:34:15 +00:00
public byte [ ] GetBlobCopy ( int blobIndex ) {
var r = module . GetBlob ( blobIndex ) ;
return r . ReadBytes ( r . Length ) ;
}
public string GetBlobString ( int blobIndex ) {
var r = module . GetBlob ( blobIndex ) ;
return new String ( Encoding . UTF8 . GetChars ( r . ReadBytes ( r . Length ) ) ) ;
}
static string HexStringFromBytes ( byte [ ] bytes )
{
var sb = new StringBuilder ( ) ;
foreach ( byte b in bytes )
sb . Append ( b . ToString ( "x2" ) ) ;
return sb . ToString ( ) ;
}
string DecodeDocumentName ( DocumentTable . Record r ) {
var nameReader = module . GetBlob ( r . Name ) ;
// FIXME: UTF8
var sep = nameReader . ReadChar ( ) ;
var name = "" ;
while ( nameReader . Length > 0 ) {
var part = nameReader . ReadCompressedUInt ( ) ;
if ( part ! = 0 ) {
var partReader = module . GetBlob ( part ) ;
var partString = new String ( Encoding . UTF8 . GetChars ( partReader . ReadBytes ( partReader . Length ) ) ) ;
if ( name = = "" )
name = sep + partString ;
else
name = name + sep + partString ;
}
}
return name ;
}
public void DumpDocumentTable ( TextWriter w ) {
var t = module . Document ;
w . WriteLine ( "Document table (1.." + t . RowCount + ")" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
string name ;
string hashAlg ;
string hash ;
string lang ;
Guid g ;
name = DecodeDocumentName ( r ) ;
// FIXME: SHA256
if ( r . HashAlgorithm ! = 0 ) {
g = module . GetGuid ( r . HashAlgorithm ) ;
if ( g = = DocumentTable . SHA1Guid ) {
hashAlg = "sha1" ;
hash = HexStringFromBytes ( GetBlobCopy ( r . Hash ) ) ;
} else {
hashAlg = g . ToString ( ) ;
hash = "<>" ;
}
} else {
hashAlg = "" ;
hash = "<>" ;
}
// FIXME: VB/F#
if ( r . Language ! = 0 ) {
g = module . GetGuid ( r . Language ) ;
if ( g = = DocumentTable . CSharpGuid ) {
lang = "C#" ;
} else {
lang = g . ToString ( ) ;
}
} else {
lang = "" ;
}
w . WriteLine ( "" + rowIndex + ": " + name + " lang=" + lang + " hash=[" + hashAlg + " " + hash + "]" ) ;
rowIndex + + ;
}
}
public void DumpMethodDebugInformationTable ( TextWriter w ) {
var t = module . MethodDebugInformation ;
w . WriteLine ( "MethodDebugInformation table (1.." + t . RowCount + ")" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
w . WriteLine ( "" + rowIndex + ":" ) ;
if ( r . SequencePoints = = 0 ) {
rowIndex + + ;
continue ;
}
var docIndex = r . Document ;
var reader = module . GetBlob ( r . SequencePoints ) ;
var localSig = reader . ReadCompressedUInt ( ) ;
if ( docIndex = = 0 )
docIndex = reader . ReadCompressedUInt ( ) ;
var docName = DecodeDocumentName ( module . Document . records [ docIndex - 1 ] ) ;
int ilOffset = 0 ;
int startLine = 0 ;
int startColumn = 0 ;
bool first = true ;
bool firstNonHidden = false ;
while ( reader . Length > 0 ) {
var deltaIlOffset = reader . ReadCompressedUInt ( ) ;
if ( ! first & & deltaIlOffset = = 0 ) {
/* subsequent-document-record */
docIndex = reader . ReadCompressedUInt ( ) ;
docName = DecodeDocumentName ( module . Document . records [ docIndex - 1 ] ) ;
continue ;
}
ilOffset + = deltaIlOffset ;
first = false ;
var deltaLines = reader . ReadCompressedUInt ( ) ;
int deltaColumns ;
if ( deltaLines = = 0 )
deltaColumns = reader . ReadCompressedUInt ( ) ;
else
deltaColumns = reader . ReadCompressedInt ( ) ;
if ( deltaLines = = 0 & & deltaColumns = = 0 ) {
/* Hidden sequence point */
continue ;
}
if ( firstNonHidden ) {
startLine = reader . ReadCompressedUInt ( ) ;
startColumn = reader . ReadCompressedUInt ( ) ;
} else {
int advLine = reader . ReadCompressedInt ( ) ;
int advCol = reader . ReadCompressedInt ( ) ;
startLine + = advLine ;
startColumn + = advCol ;
}
firstNonHidden = false ;
w . WriteLine ( docName + " il=" + ilOffset + " line=" + startLine + " col=" + startColumn + " endline=" + ( startLine + deltaLines ) + " endcol=" + ( startColumn + deltaColumns ) ) ;
}
rowIndex + + ;
}
}
public void DumpLocalScopeTable ( TextWriter w ) {
var t = module . LocalScope ;
w . WriteLine ( "LocalScope table (1.." + t . RowCount + ")" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
w . WriteLine ( "" + rowIndex + ": method=" + r . Method + " import=" + r . ImportScope + " locals=" + r . VariableList ) ;
rowIndex + + ;
}
}
public void DumpImportScopeTable ( TextWriter w ) {
var t = module . ImportScope ;
w . WriteLine ( "ImportScope table (1.." + t . RowCount + ")" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
w . WriteLine ( "" + rowIndex + ": parent=" + r . Parent + " imports=" + r . Imports ) ;
var reader = module . GetBlob ( r . Imports ) ;
while ( reader . Length > 0 ) {
var kind = reader . ReadCompressedUInt ( ) ;
// FIXME: Constants
switch ( kind ) {
case 1 :
var nsIndex = reader . ReadCompressedUInt ( ) ;
w . WriteLine ( "\t ns=" + GetBlobString ( nsIndex ) ) ;
break ;
default :
w . WriteLine ( "K: " + kind ) ;
throw new NotImplementedException ( ) ;
}
}
rowIndex + + ;
}
}
static Guid StateMachineHoistedLocalScopesGuid = new Guid ( "6da9a61e-f8c7-4874-be62-68bc5630df71" ) ;
2019-04-12 14:10:50 +00:00
static Guid SourceLinkGuid = new Guid ( "cc110556-a091-4d38-9fec-25ab9a351a6a" ) ;
2017-08-21 15:34:15 +00:00
public void DumpCustomDebugInfoTable ( TextWriter w ) {
var t = module . CustomDebugInformation ;
w . WriteLine ( "CustomDebugInformation table (1.." + t . RowCount + ")" ) ;
int rowIndex = 1 ;
foreach ( var r in t . records ) {
var g = module . GetGuid ( r . Kind ) ;
string kind = "" ;
if ( g = = StateMachineHoistedLocalScopesGuid )
kind = "<state-machine hoisted local scopes>" ;
2019-04-12 14:10:50 +00:00
else if ( g = = SourceLinkGuid )
kind = "<source link>" ;
2017-08-21 15:34:15 +00:00
else
kind = g . ToString ( ) ;
int parent_kind = r . Parent & 0x1f ;
int parent_idx = r . Parent > > 5 ;
w . WriteLine ( "" + rowIndex + ": " + kind + " [" + parent_kind + "-" + parent_idx + "]" ) ;
if ( g = = StateMachineHoistedLocalScopesGuid ) {
var blob = module . GetBlob ( r . Value ) ;
while ( blob . Length > 0 ) {
int start_offset = blob . ReadInt32 ( ) ;
int len = blob . ReadInt32 ( ) ;
w . WriteLine ( String . Format ( "\t0x{0:x}-0x{1:x}" , start_offset , start_offset + len ) ) ;
}
2019-04-12 14:10:50 +00:00
} else if ( g = = SourceLinkGuid ) {
var blob = module . GetBlob ( r . Value ) ;
var bytes = blob . ReadBytes ( blob . Length ) ;
Console . WriteLine ( "======================================" ) ;
Console . Write ( Encoding . UTF8 . GetString ( bytes ) ) ;
Console . WriteLine ( "======================================" ) ;
2017-08-21 15:34:15 +00:00
}
rowIndex + + ;
}
}
2016-02-22 11:00:01 -05:00
}
}