// // Copyright (c) 2010-2023 Antmicro // Copyright (c) 2011-2015 Realtime Embedded // // This file is licensed under the MIT License. // Full license text is available in 'licenses/MIT.txt'. // using System.Collections.Generic; using ELFSharp.ELF.Sections; using Antmicro.Renode.Utilities.Collections; using System; using Antmicro.Renode.Utilities; using Antmicro.Migrant; using Antmicro.Renode.Logging; using Antmicro.Renode.Peripherals.Bus; using CxxDemangler; namespace Antmicro.Renode.Core { /// /// Class representing a Symbol in memory. The symbol represtented spans from Start inclusive, till End exclusive. /// A symbol of zero length is considered to occupy Start. So a symbol (Start, End) does contain (Start, Start). /// Symbols have optional Name, and can be marked as Thumb symbols (in ARM architecture). /// public class Symbol : IInterval, IEquatable { /// /// Static constructor initializing the demangling mechanisms. /// static Symbol() { } /// /// Allows casting from SymbolEntry to Symbol. . /// public static implicit operator Symbol(SymbolEntry originalSymbol) { return new Symbol(originalSymbol); } /// /// Allows casting from SymbolEntry to Symbol. . /// public static implicit operator Symbol(SymbolEntry originalSymbol) { return new Symbol(originalSymbol); } /// /// Demangles the symbol name using the CxxDemangler library. /// /// Demangled symbol name. public static string DemangleSymbol(string symbolName) { const int globalSymbolPrefixLength = 21; if(string.IsNullOrEmpty(symbolName) || symbolName.Length < 2) { return symbolName; } //length check for the sake of Substring if(symbolName.Length > globalSymbolPrefixLength && symbolName.StartsWith("_GLOBAL__sub_I", StringComparison.Ordinal)) { return string.Format("static initializers for {0}", DemangleSymbol(symbolName.Substring(globalSymbolPrefixLength))); } if(symbolName.Substring(0, 2) != "_Z") { return symbolName; } var result = CxxDemangler.CxxDemangler.Demangle(symbolName); if(result.Length == 0) { return symbolName; } var substringIndex = result.LastIndexOf('('); if(substringIndex != -1) { result = result.Substring(0, substringIndex); } return result; } public Symbol(SymbolAddress start, SymbolAddress end) { Start = start; End = end; } /// /// Initializes a new instance of the class. /// /// Start. /// End. /// Name. /// SymbolType. /// SymbolBinding. /// Set to true if symbol is related to architecture that allows thumb symbols. public Symbol(SymbolAddress start, SymbolAddress end, string name, SymbolType type = SymbolType.NotSpecified, SymbolBinding binding = SymbolBinding.Global, bool mayBeThumb = false) { if(end < start) { throw new ArgumentException(string.Format("Symbol cannot have start before the end. Input was: ({0},{1})", start, end)); } Type = type; Binding = binding; Name = DemangleSymbol(name); Start = start; End = end; thumbArchitecture = mayBeThumb; if(mayBeThumb) { UpdateIsThumbSymbol(); } } /// /// Initializes a new instance of the class. /// /// Original symbol. /// Set to true if symbol is related to architecture that allows thumb symbols. public Symbol(ISymbolEntry originalSymbol, bool mayBeThumb = false) { Start = originalSymbol.GetValue(); IsThumbSymbol = false; thumbArchitecture = mayBeThumb; if(mayBeThumb) { UpdateIsThumbSymbol(); } End = Start + originalSymbol.GetSize(); Name = DemangleSymbol(originalSymbol.Name); Type = originalSymbol.Type; Binding = originalSymbol.Binding; } /// /// Gets the copy of symbol with changed end of the interval. /// If the requested end is before start, the trial fails. /// If the symbol would not have to be changed, i.e. requested the original reference is returned and trial succeeds. /// Otherwise a truncated copy of symbol is returned and trial succeeds. /// /// true if trimming returned a proper symbol, false otherwise. /// New end. /// Trimmed symbol. public bool TryGetRightTrimmed(SymbolAddress end, out Symbol trimmedSymbol) { trimmedSymbol = null; if(end < Start) { return false; } trimmedSymbol = End < end ? this : GetTrimmedCopy(Start, end); return true; } /// /// Gets the copy of symbol with start of the interval changed to 1st legal address equal or larger than given start. /// Trimming from left ensures that the new symbol does not occupy space before new start. /// If the requested start is after end, the trial fails. /// If the symbol would not have to be changed, i.e. requested the original reference is returned and trial succeeds. /// Otherwise a truncated copy of symbol is returned and trial succeeds. /// /// NOTE: If you create a new thumb symbol with interval (13,15), the acutal interval used will be (12,15); /// However, if you trim this symbol from left to the same start value (12,15).LeftTrim(13), the result will be (14,15). /// /// true if trimming returned a proper symbol, false otherwise. /// New start. /// Trimmed symbol public bool TryGetLeftTrimmed(SymbolAddress start, out Symbol trimmedSymbol) { trimmedSymbol = null; if(End < start) { return false; } //if symbol is in thumb architecture and new start is odd, move it to the next legal address if(thumbArchitecture && (start % 2) != 0) { start += 1; } trimmedSymbol = Start >= start ? this : GetTrimmedCopy(start, End); return true; } /// /// Determines whether this instance is more important than the specified argument. /// Two factors are taken into consideration: the Type of the symbol and, if symbol /// types are equal, the Binding of the symbol. For example, Function symbol is more /// important than Object symbol, and Global Function symbol is more important than /// Weak Function symbol. /// As the symbol types may be architecture specific, we only take into consideration /// those types that we are aware of. /// /// true if this instance is more important than the specified second; otherwise, false. /// Symbol to compare with. public bool IsMoreImportantThan(Symbol second) { if(string.IsNullOrWhiteSpace(Name) || !Enum.IsDefined(typeof(SymbolType), Type)) { return false; } if(string.IsNullOrWhiteSpace(second.Name) || !Enum.IsDefined(typeof(SymbolType), second.Type)) { return true; } return (Type == second.Type && (BindingImportance[Binding] - BindingImportance[second.Binding]) > 0) || ((TypeImportance[Type] - TypeImportance[second.Type]) > 0); } public bool Contains(SymbolAddress x) { return Start == x || (Start < x && End > x); } public bool Contains(IInterval other) { return Start <= other.Start && End >= other.End && End > other.Start; } /// /// Checks if two symbols overlap. /// /// Other. public bool Overlaps(IInterval other) { return Contains(other.Start) || other.Contains(Start); } public bool Equals(Symbol other) { return intervalComparer.Compare(this, other) == 0 && string.Compare(Name, other.Name, StringComparison.Ordinal) == 0; } public string ToStringRelative(SymbolAddress offset) { if(string.IsNullOrWhiteSpace(this.Name)) { return String.Empty; } if(this.Start == offset) { return "{0} (entry)".FormatWith(this.Name); } if(this.End == this.Start && offset != this.Start) { return "{0}+0x{1:X} (guessed)".FormatWith(this.Name, offset.RawValue - this.Start.RawValue); } return this.Name; } public override string ToString() { return Name ?? string.Empty; } public SymbolAddress Start { get; private set; } /// /// Closing bound of the symbol. Remeber the closing bound is exclusive, not inclusive, for non-zero-length symbols. /// /// /// First address after the symbol. /// public SymbolAddress End { get; private set; } public SymbolAddress Length { get { return End - Start; } } public string Name { get; private set; } public SymbolType Type { get; private set; } public SymbolBinding Binding { get; private set; } public bool IsThumbSymbol { get; private set; } public bool IsLabel => Type != SymbolType.Function && (Binding == SymbolBinding.Local || Binding == SymbolBinding.Weak) && Length == 0; private void UpdateIsThumbSymbol() { IsThumbSymbol = (Start & 0x1) != 0; Start -= (IsThumbSymbol ? 1 : 0u); } /// /// Gets the copy of symbol with changed interval. /// /// The truncated copy. /// Start. /// End. private Symbol GetTrimmedCopy(SymbolAddress start, SymbolAddress end) { return new Symbol(start, end, Name, Type, Binding, thumbArchitecture); } static private IntervalComparer intervalComparer = new IntervalComparer(); static private Dictionary TypeImportance = new Dictionary { { SymbolType.File, 0 }, { SymbolType.Object, 1 }, { SymbolType.Section, 2 }, { SymbolType.ProcessorSpecific, 3 }, { SymbolType.NotSpecified, 4 }, { SymbolType.Function, 5 } }; static private Dictionary BindingImportance = new Dictionary { { SymbolBinding.Weak, 0 }, { SymbolBinding.ProcessorSpecific, 1 }, { SymbolBinding.Local, 2 }, { SymbolBinding.Global, 3 } }; private readonly bool thumbArchitecture; } }