iced/Iced/Intel/SymbolResolver.cs

390 lines
14 KiB
C#
Raw Normal View History

2018-09-05 23:38:53 +00:00
/*
Copyright (C) 2018 de4dot@gmail.com
This file is part of Iced.
Iced is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Iced is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Iced. If not, see <https://www.gnu.org/licenses/>.
*/
#if (!NO_GAS_FORMATTER || !NO_INTEL_FORMATTER || !NO_MASM_FORMATTER || !NO_NASM_FORMATTER) && !NO_FORMATTER
using System;
namespace Iced.Intel {
/// <summary>
/// Used by a <see cref="Formatter"/> to resolve symbols. It can also override number formatting options
/// </summary>
public abstract class SymbolResolver {
/// <summary>
/// This method is called if you don't override any of the other virtual methods. It should return true if
/// <paramref name="symbol"/> was updated. If false is returned, <paramref name="options"/> can be updated to
/// override the default number formatting options.
/// </summary>
/// <param name="operand">Operand number, 0-based. This is a formatter operand and isn't necessarily the same as an instruction operand.</param>
/// <param name="instruction">Instruction</param>
2018-09-05 23:38:53 +00:00
/// <param name="address">Address</param>
/// <param name="addressSize">Size of <paramref name="address"/> in bytes</param>
2018-09-05 23:38:53 +00:00
/// <param name="symbol">Updated with symbol information if this method returns true</param>
/// <param name="options">Number formatting options if this method returns false</param>
/// <returns></returns>
protected virtual bool TryGetSymbol(int operand, ref Instruction instruction, ulong address, int addressSize, out SymbolResult symbol, ref NumberFormattingOptions options) {
2018-09-05 23:38:53 +00:00
symbol = default;
return false;
}
/// <summary>
/// Updates <paramref name="symbol"/> with the symbol and returns true, else it returns false and can update <paramref name="options"/>
/// to override the default number formatting options
/// </summary>
/// <param name="operand">Operand number, 0-based. This is a formatter operand and isn't necessarily the same as an instruction operand.</param>
/// <param name="instruction">Instruction</param>
2018-09-05 23:38:53 +00:00
/// <param name="address">Address</param>
/// <param name="addressSize">Size of <paramref name="address"/> in bytes</param>
2018-09-05 23:38:53 +00:00
/// <param name="symbol">Updated with symbol information if this method returns true</param>
/// <param name="showBranchSize">true if branch info (short, near ptr, far ptr) should be shown</param>
/// <param name="options">Number formatting options if this method returns false</param>
/// <returns></returns>
public virtual bool TryGetBranchSymbol(int operand, ref Instruction instruction, ulong address, int addressSize, out SymbolResult symbol, ref bool showBranchSize, ref NumberFormattingOptions options) =>
TryGetSymbol(operand, ref instruction, address, addressSize, out symbol, ref options);
2018-09-05 23:38:53 +00:00
/// <summary>
/// Returns true if <paramref name="symbol"/> was updated with a symbol. <paramref name="symbolSelector"/> can be set to default value
/// if it should be formatted as a number. <paramref name="options"/> is used if this method returns false or if <paramref name="symbolSelector"/>
/// has the default value.
/// </summary>
/// <param name="operand">Operand number, 0-based. This is a formatter operand and isn't necessarily the same as an instruction operand.</param>
/// <param name="instruction">Instruction</param>
2018-09-05 23:38:53 +00:00
/// <param name="selector">Selector/segment</param>
/// <param name="address">Address</param>
/// <param name="addressSize">Size of <paramref name="address"/> in bytes</param>
2018-09-05 23:38:53 +00:00
/// <param name="symbolSelector">Updated with the selector symbol or with the default value if it should be formatted as a number</param>
/// <param name="symbol">Updated with symbol information if this method returns true</param>
/// <param name="showBranchSize">true if branch info (short, near ptr, far ptr) should be shown</param>
/// <param name="options">Number formatting options if this method returns false or if <paramref name="symbolSelector"/> has the default value</param>
/// <returns></returns>
public virtual bool TryGetFarBranchSymbol(int operand, ref Instruction instruction, ushort selector, uint address, int addressSize, out SymbolResult symbolSelector, out SymbolResult symbol, ref bool showBranchSize, ref NumberFormattingOptions options) {
2018-09-05 23:38:53 +00:00
symbolSelector = default;
return TryGetSymbol(operand, ref instruction, address, addressSize, out symbol, ref options);
2018-09-05 23:38:53 +00:00
}
/// <summary>
/// Gets a symbol and returns true. If it returns false, <paramref name="options"/> can be updated to override
/// the default number formatting options.
/// </summary>
/// <param name="operand">Operand number, 0-based. This is a formatter operand and isn't necessarily the same as an instruction operand.</param>
/// <param name="instruction">Instruction</param>
2018-09-05 23:38:53 +00:00
/// <param name="immediate">Immediate value</param>
/// <param name="immediateSize">Size of <paramref name="immediate"/> in bytes</param>
2018-09-05 23:38:53 +00:00
/// <param name="symbol">Updated with symbol information if this method returns true</param>
/// <param name="options">Number formatting options if this method returns false</param>
/// <returns></returns>
public virtual bool TryGetImmediateSymbol(int operand, ref Instruction instruction, ulong immediate, int immediateSize, out SymbolResult symbol, ref NumberFormattingOptions options) =>
TryGetSymbol(operand, ref instruction, immediate, immediateSize, out symbol, ref options);
2018-09-05 23:38:53 +00:00
/// <summary>
/// Gets a symbol and returns true. If it returns false, <paramref name="options"/> can be updated to override
/// the default number formatting options.
///
/// This method gets called even if the memory operand has no displacement, eg. [eax].
/// </summary>
/// <param name="operand">Operand number, 0-based. This is a formatter operand and isn't necessarily the same as an instruction operand.</param>
/// <param name="instruction">Instruction</param>
2018-09-05 23:38:53 +00:00
/// <param name="displacement">Displacement. If it's RIP-relative addressing, this is the absolute address (rip/eip + displ)</param>
/// <param name="displacementSize">Size of <paramref name="displacement"/> in bytes</param>
2018-09-05 23:38:53 +00:00
/// <param name="ripRelativeAddresses">true to use RIP relative addresses</param>
/// <param name="symbol">Updated with symbol information if this method returns true</param>
/// <param name="options">Number formatting options if this method returns false</param>
/// <returns></returns>
public virtual bool TryGetDisplSymbol(int operand, ref Instruction instruction, ulong displacement, int displacementSize, ref bool ripRelativeAddresses, out SymbolResult symbol, ref NumberFormattingOptions options) =>
TryGetSymbol(operand, ref instruction, displacement, displacementSize, out symbol, ref options);
2018-09-05 23:38:53 +00:00
}
/// <summary>
/// Gets initialized with the default options and can be overridden by a <see cref="SymbolResolver"/>
/// </summary>
public struct NumberFormattingOptions {
/// <summary>
/// Digit separator or null/empty string
/// </summary>
public string DigitSeparator;
/// <summary>
/// Number prefix or null/empty string
/// </summary>
public string Prefix;
/// <summary>
/// Number suffix or null/empty string
/// </summary>
public string Suffix;
/// <summary>
2018-10-09 20:43:22 +00:00
/// Size of a digit group
2018-09-05 23:38:53 +00:00
/// </summary>
public byte DigitGroupSize;
/// <summary>
/// Number base
/// </summary>
public NumberBase NumberBase {
get => (NumberBase)numberBaseByteValue;
set => numberBaseByteValue = (byte)value;
}
internal byte numberBaseByteValue;
/// <summary>
/// Use upper case hex digits
/// </summary>
public bool UpperCaseHex;
/// <summary>
/// Small hex numbers (-9 .. 9) are shown in decimal
/// </summary>
public bool SmallHexNumbersInDecimal;
/// <summary>
/// Add a leading zero to numbers if there's no prefix and the number begins with hex digits A-F, eg. Ah vs 0Ah
/// </summary>
public bool AddLeadingZeroToHexNumbers;
/// <summary>
/// If true, use short numbers, and if false, add leading zeroes, eg. '1h' vs '00000001h'
/// </summary>
public bool ShortNumbers;
/// <summary>
/// If true, the number is signed, and if false it's an unsigned number
/// </summary>
public bool SignedNumber;
/// <summary>
/// Sign extend the number to the real size (16-bit, 32-bit, 64-bit), eg. 'mov al,[eax+12h]' vs 'mov al,[eax+00000012h]'
/// </summary>
public bool SignExtendImmediate;
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Options</param>
/// <param name="shortNumbers">If true, use short numbers, and if false, add leading zeroes, eg. '1h' vs '00000001h'</param>
/// <param name="signedNumber">Signed numbers if true, and unsigned numbers if false</param>
/// <param name="signExtendImmediate">Sign extend the number to the real size (16-bit, 32-bit, 64-bit), eg. 'mov al,[eax+12h]' vs 'mov al,[eax+00000012h]'</param>
public NumberFormattingOptions(FormatterOptions options, bool shortNumbers, bool signedNumber, bool signExtendImmediate) {
if (options == null)
throw new ArgumentNullException(nameof(options));
ShortNumbers = shortNumbers;
SignedNumber = signedNumber;
SignExtendImmediate = signExtendImmediate;
numberBaseByteValue = (byte)options.NumberBase;
DigitSeparator = options.DigitSeparator;
UpperCaseHex = options.UpperCaseHex;
SmallHexNumbersInDecimal = options.SmallHexNumbersInDecimal;
AddLeadingZeroToHexNumbers = options.AddLeadingZeroToHexNumbers;
int digitGroupSize;
switch (options.NumberBase) {
case NumberBase.Hexadecimal:
Prefix = options.HexPrefix;
Suffix = options.HexSuffix;
digitGroupSize = options.HexDigitGroupSize;
break;
case NumberBase.Decimal:
Prefix = options.DecimalPrefix;
Suffix = options.DecimalSuffix;
digitGroupSize = options.DecimalDigitGroupSize;
break;
case NumberBase.Octal:
Prefix = options.OctalPrefix;
Suffix = options.OctalSuffix;
digitGroupSize = options.OctalDigitGroupSize;
break;
case NumberBase.Binary:
Prefix = options.BinaryPrefix;
Suffix = options.BinarySuffix;
digitGroupSize = options.BinaryDigitGroupSize;
break;
default:
throw new ArgumentException();
}
if (digitGroupSize < 0)
DigitGroupSize = 0;
else if (digitGroupSize > byte.MaxValue)
DigitGroupSize = byte.MaxValue;
else
DigitGroupSize = (byte)digitGroupSize;
}
}
/// <summary>
/// Symbol flags
/// </summary>
[Flags]
public enum SymbolFlags : uint {
/// <summary>
/// No bit is set
/// </summary>
None = 0,
/// <summary>
/// If set it's the address of a symbol, else it's a symbol relative to the base and index registers (eg. a struct field offset)
/// </summary>
Address = 0x00000001,
/// <summary>
/// It's a signed symbol and it should be displayed as '-symbol' or 'reg-symbol' instead of 'symbol' or 'reg+symbol'
/// </summary>
Signed = 0x00000002,
}
/// <summary>
/// The result of resolving a symbol
/// </summary>
public readonly struct SymbolResult {
/// <summary>
/// Contains the symbol
/// </summary>
public readonly TextInfo Text;
/// <summary>
/// Symbol flags
/// </summary>
public readonly SymbolFlags Flags;
/// <summary>
/// Constructor
/// </summary>
/// <param name="text">Symbol</param>
public SymbolResult(string text) {
Text = new TextInfo(text, FormatterOutputTextKind.Label);
Flags = 0;
}
2018-09-05 23:38:53 +00:00
/// <summary>
/// Constructor
/// </summary>
/// <param name="text">Symbol</param>
/// <param name="color">Color</param>
public SymbolResult(string text, FormatterOutputTextKind color) {
Text = new TextInfo(text, color);
Flags = 0;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="text">Symbol</param>
/// <param name="color">Color</param>
/// <param name="flags">Symbol flags</param>
public SymbolResult(string text, FormatterOutputTextKind color, SymbolFlags flags) {
Text = new TextInfo(text, color);
Flags = flags;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="text">Symbol</param>
public SymbolResult(TextInfo text) {
Text = text;
Flags = 0;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="text">Symbol</param>
/// <param name="flags">Symbol flags</param>
public SymbolResult(TextInfo text, SymbolFlags flags) {
Text = text;
Flags = flags;
}
}
/// <summary>
/// Contains one or more <see cref="TextPart"/>s (text and color)
/// </summary>
public readonly struct TextInfo {
/// <summary>
/// true if this is the default instance
/// </summary>
public bool IsDefault => TextArray == null && Text.Text == null;
/// <summary>
/// The text and color unless <see cref="TextArray"/> is non-null
/// </summary>
public readonly TextPart Text;
/// <summary>
/// Text and color or null if <see cref="Text"/> should be used
/// </summary>
public readonly TextPart[] TextArray;
/// <summary>
/// Constructor
/// </summary>
/// <param name="text">Text</param>
/// <param name="color">Color</param>
public TextInfo(string text, FormatterOutputTextKind color) {
Text = new TextPart(text, color);
TextArray = null;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="text">Text</param>
public TextInfo(TextPart text) {
Text = text;
TextArray = null;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="text">All text parts</param>
public TextInfo(TextPart[] text) {
Text = default;
TextArray = text;
}
}
/// <summary>
/// Contains text and colors
/// </summary>
public readonly struct TextPart {
/// <summary>
/// Text
/// </summary>
public readonly string Text;
/// <summary>
/// Color
/// </summary>
public readonly FormatterOutputTextKind Color;
/// <summary>
/// Constructor
/// </summary>
/// <param name="text">Text</param>
/// <param name="color">Color</param>
public TextPart(string text, FormatterOutputTextKind color) {
Text = text;
Color = color;
}
}
}
#endif