diff options
Diffstat (limited to 'src/ToolBox/SOS/DacTableGen/MapSymbolProvider.cs')
-rw-r--r-- | src/ToolBox/SOS/DacTableGen/MapSymbolProvider.cs | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/src/ToolBox/SOS/DacTableGen/MapSymbolProvider.cs b/src/ToolBox/SOS/DacTableGen/MapSymbolProvider.cs new file mode 100644 index 0000000000..018ed9fa89 --- /dev/null +++ b/src/ToolBox/SOS/DacTableGen/MapSymbolProvider.cs @@ -0,0 +1,503 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// #define DACTABLEGEN_DEBUG + +using System; +using System.IO; +using System.Text.RegularExpressions; +using System.Collections; +using System.Globalization; +/*************************************************************************************** + * + ***************************************************************************************/ + +public class MapSymbolProvider : SymbolProvider +{ + public MapSymbolProvider(String symbolFilename) { + mf = new MapFile(symbolFilename); + } + + public override UInt32 GetGlobalRVA(String symbolName, + SymType symType) { + return GetRVA(symbolName); + + } + + public override UInt32 GetVTableRVA(String symbolName, + String keyBaseName) { + if (keyBaseName != null) { + return GetRVA(symbolName + "__" + keyBaseName); + } else { + return GetRVA(symbolName); + } + } + + UInt32 GetRVA(String symbolName) { + + SymbolInfo si = mf.FindSymbol(symbolName); + + if (si == null) { + // Ideally this would throw an exception and + // cause the whole process to fail but + // currently it's too complicated to get + // all the ifdef'ing right for all the + // mix of debug/checked/free multiplied by + // x86/AMD64/IA64/etc. + return UInt32.MaxValue; + } + + return mf.GetRVA(si); + } + + MapFile mf = null; +} + + +public class SymbolInfo +{ + public SymbolInfo(int Segment, UInt32 Address) + { + m_Segment = Segment; + m_Address = Address; + m_dupFound = false; + } + + public int Segment + { + get + { return m_Segment; } + } + + public UInt32 Address + { + get + { return m_Address; } + } + + public bool dupFound + { + get + { return m_dupFound; } + set + { m_dupFound = value; } + } + + int m_Segment; // The segment index. (Used only in Windows MAP file.) + UInt32 m_Address; + + bool m_dupFound; // Have we found a duplicated entry for this key? +} + + +public class MapFile +{ + const String Reg_ExWhiteSpaces = @"\s+"; + + // + // These are regular expression strings for Windows MAP file. + // + const String Reg_MapAddress = @"^ (?<addrPart1>[0-9a-f]{4}):(?<addrPart2>[0-9a-f]{8})"; + + enum WindowsSymbolTypes { + ModuleNameClassNameFieldName, + ClassNameFieldName, + GlobalVarName, + GlobalVarName2, + GlobalVarName3, + SingleVtAddr, + MultiVtAddr, + }; + + readonly String[] RegExps_WindowsMapfile = { + + // ModuleNameClassNameFieldName + // Example: ?ephemeral_heap_segment@gc_heap@WKS@@ + Reg_MapAddress + + Reg_ExWhiteSpaces + + @"\?(?<fieldName>[^\?@]+)@(?<className>[^\?@]+)@(?<moduleName>[^\?@]+)@@", + + // ClassNameFieldName + // Example: ?m_RangeTree@ExecutionManager@@ + Reg_MapAddress + + Reg_ExWhiteSpaces + + @"\?(?<fieldName>[^\?@]+)@(?<className>[^\?@]+)@@", + + // GlobalVarName + // Example: ?g_pNotificationTable@@ + // (or) ?JIT_LMul@@ + Reg_MapAddress + + Reg_ExWhiteSpaces + + @"\?(?<globalVarName>[^\?@]+)@@", + + // GlobalVarName2 + // Example: @JIT_WriteBarrier@ + // (or) _JIT_FltRem@ + // (or) _JIT_Dbl2Lng@ + // (or) _JIT_LLsh@ + Reg_MapAddress + + Reg_ExWhiteSpaces + + @"[@_](?<globalVarName>[^\?@]+)@", + + // GlobalVarName3 + // Example: _g_card_table 795e53a4 + Reg_MapAddress + + Reg_ExWhiteSpaces + + @"_(?<globalVarName>[^\s@]+)" + + Reg_ExWhiteSpaces + + @"[0-9a-f]{8}", + + // Single-inheritance VtAddr + // Example: ??_7Thread@@6B@ + Reg_MapAddress + + Reg_ExWhiteSpaces + + @"\?\?_7(?<FunctionName>[^\?@]+)@@6B@", + + // Multiple-inheritance VtAddr + // Example: ??_7CompilationDomain@@6BAppDomain@@@ + Reg_MapAddress + + Reg_ExWhiteSpaces + + @"\?\?_7(?<FunctionName>[^\?@]+)@@6B(?<BaseName>[^@]+)@@@" + }; + const String Reg_Length = @"(?<length>[0-9a-f]{8})H"; + const String Reg_SegmentInfo = Reg_MapAddress + " " + Reg_Length; + + // + // These are regular expression strings for Unix NM file. + // + const String Reg_NmAddress = @"^(?<address>[0-9a-f]{8})"; + const String Reg_TypeChar = @"[a-zA-Z]"; + + enum UnixSymbolTypes { + CxxFieldName, + CxxFunctionName, + CGlobal, + VtAddr, + }; + + // Rather than try and encode a particular C++ mangling format here, we rely on the nm output + // having been unmanagled through the c++filt tool. + readonly String[] RegExps_UnixNmfile = { + + // C++ field name + // Examples: + // d WKS::gc_heap::ephemeral_heap_segment + // d WKS::GCHeap::hEventFinalizer + // s ExecutionManager::m_CodeRangeList + // d ExecutionManager::m_dwReaderCount + Reg_NmAddress + + " " + + Reg_TypeChar + + " " + + @"(?<symName>\w+(::\w+)+)$", + + // C++ function/method name + // Note: may have duplicates due to overloading by argument types + // Examples: + // t JIT_LMulOvf(int, int, int, long long, long long) + // t ThreadpoolMgr::AsyncCallbackCompletion(void*) + // + // Counter-examples we don't want to include: + // b JIT_NewFast(int, int, CORINFO_CLASS_STRUCT_*)::__haveCheckedRestoreState + // + Reg_NmAddress + + " " + + Reg_TypeChar + + " " + + @"(?<symName>\w+(::\w+)*)\(.*\)$", + + // C global variable / function name + // Examples: + // s _g_pNotificationTable + // t _JIT_LLsh + Reg_NmAddress + + " " + + Reg_TypeChar + + " " + + @"_(?<symName>\w+)$", + + // VtAddr + // Examples: + // s vtable for Thread + // + // Note that at the moment we don't have any multiple-inheritance vtables that we need + // on UNIX (CompilationDomain is the only one which is only present on FEATURE_PREJIT). + // It looks like classes with multiple inheritance only list a single vtable in nm output + // (eg. RegMeta). + // We also don't support nested classes here because there is currently no syntax for them + // for daccess.i. + // + Reg_NmAddress + + " " + + Reg_TypeChar + + " " + + @"vtable for (?<symName>\w+)$", + }; + + public UInt32 GetRVA(SymbolInfo si) + { + UInt32 addr = si.Address; + if (bIsWindowsMapfile) + { + addr += (UInt32)SegmentBase[si.Segment]; + } + return addr; + } + + public MapFile(String symdumpFile) + { + m_symdumpFile = symdumpFile; + loadDataFromMapFile(); + } + + void ReadMapHeader(StreamReader strm) + { + Regex regExNmFile = new Regex("^[0-9a-f]{8} "); + Regex regEx = new Regex(Reg_SegmentInfo); + Regex regHeaderSection = new Regex(@"\s*Address\s*Publics by Value\s*Rva\+Base\s*Lib:Object", RegexOptions.IgnoreCase); + Regex regnonNMHeader = new Regex(@"\s*Start\s*Length\s*Name\s*Class", RegexOptions.IgnoreCase); + Match match = null; + + UInt32 lastSegmentIndex = 0; + UInt32 segmentIndex; + UInt32 sectionStart = 0; + UInt32 sectionLength = 0; + + String line; + bool bInSegmentDecl = false; + + const UInt32 baseOfCode = 0x1000; + SegmentBase.Add((UInt32)baseOfCode); + + for (;;) + { + line = strm.ReadLine(); + + if (bInSegmentDecl) { + + if (regHeaderSection.IsMatch(line)) { + // Header section ends. + break; + } +#if DACTABLEGEN_DEBUG + Console.WriteLine("SegmentDecl: " + line); +#endif + + match = regEx.Match(line); + if (match.Success) { + segmentIndex = UInt32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier); + if (segmentIndex != lastSegmentIndex) { + // Enter the new segment. Record what we have. + // Note, SegmentBase[i] is built upon SegmentBase[i-1] + SegmentBase.Add((UInt32)SegmentBase[SegmentBase.Count-1] + (sectionStart + sectionLength + (UInt32)0xFFF) & (~((UInt32)0xFFF))); + lastSegmentIndex = segmentIndex; + } + sectionStart = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier); + sectionLength = UInt32.Parse(match.Groups["length"].ToString(), NumberStyles.AllowHexSpecifier); + } + + if (line == null) + { + throw new InvalidOperationException("Invalid MAP header format."); + } + } + else { + if (!regnonNMHeader.IsMatch(line)) { + match = regExNmFile.Match(line); + if (match.Success) { + // It's a nm file format. There is no header for it. + break; + } + continue; + } + bInSegmentDecl = true; + bIsWindowsMapfile = true; + + } + } + + if (bIsWindowsMapfile) { + // + // Only Windows map file has SegmentBase + // + for (int i=1 ; i<= lastSegmentIndex; i++) { +#if DACTABLEGEN_DEBUG + Console.WriteLine("SegmentBase[{0}] = {1:x8}", i, SegmentBase[i]); +#endif + } + } + + } + + public void loadDataFromMapFile() + { + + StreamReader strm = + new StreamReader(m_symdumpFile, System.Text.Encoding.ASCII); + String line; + int i; + String[] RegExps; + + // Read the head of the symbol dump file and + // determind the format of it. + ReadMapHeader(strm); + + // + // Scan through the symbol dump file looking + // for the globals structure. + // + if (bIsWindowsMapfile) { + RegExps = RegExps_WindowsMapfile; + } + else { + RegExps = RegExps_UnixNmfile; + } + + Console.WriteLine("It is a {0} file.", (bIsWindowsMapfile)?"Windows MAP":"Unix NM"); + + Regex[] RegExs = new Regex[RegExps.Length]; + for (i = 0; i < RegExps.Length; i++) { +#if DACTABLEGEN_DEBUG + Console.WriteLine("RegEx[{0}]: {1}", i, RegExps[i]); +#endif + RegExs[i] = new Regex(RegExps[i]); + } + + Match match = null; + SymbolInfo si; + String key; + int segment; + UInt32 address; + + for (;;) + { + line = strm.ReadLine(); + + if (line == null) + { + // No more to read. + break; + } + + // Console.WriteLine(">{0}", line); + for (i = 0; i < RegExps.Length; i++) { + + match = RegExs[i].Match(line); + if (match.Success) { + // Console.WriteLine(line); + + segment = 0; + address = 0; + if (bIsWindowsMapfile) { + switch ((WindowsSymbolTypes)i) { + + case WindowsSymbolTypes.ModuleNameClassNameFieldName: + key = match.Groups["moduleName"].ToString() + "::" + match.Groups["className"].ToString() + "::" + match.Groups["fieldName"]; + segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier); + address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier); + break; + + case WindowsSymbolTypes.ClassNameFieldName: + key = match.Groups["className"].ToString() + "::" + match.Groups["fieldName"]; + segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier); + address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier); + break; + + case WindowsSymbolTypes.GlobalVarName: + case WindowsSymbolTypes.GlobalVarName2: + case WindowsSymbolTypes.GlobalVarName3: + key = match.Groups["globalVarName"].ToString(); + segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier); + address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier); + break; + + case WindowsSymbolTypes.SingleVtAddr: + key = match.Groups["FunctionName"].ToString(); + segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier); + address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier); + break; + + case WindowsSymbolTypes.MultiVtAddr: + key = match.Groups["FunctionName"].ToString() + + "__" + + match.Groups["BaseName"].ToString(); + segment = Int32.Parse(match.Groups["addrPart1"].ToString(), NumberStyles.AllowHexSpecifier); + address = UInt32.Parse(match.Groups["addrPart2"].ToString(), NumberStyles.AllowHexSpecifier); + break; + + default: + throw new ApplicationException("Unknown symbolType" + i); + } + } + else + { + // We've got a UNIX nm file + // The full unmanaged symbol name is already included in the RegEx + // We could consider treating VTable's differently (a vt-specific key or different + // hash), but we want to be consistent with the windows map case here. + + key = match.Groups["symName"].ToString(); + address = UInt32.Parse(match.Groups["address"].ToString(), NumberStyles.AllowHexSpecifier); + + if (i == (int)UnixSymbolTypes.VtAddr) + { + // For VTables, what we really want is the vtAddr used at offset zero of objects. + // GCC has the vtAddr point to the 3rd slot of the VTable (in contrast to MSVC where + // the vtAddr points to the base of the VTable). + // Slot 0 appears to typically be NULL + // Slot 1 points to the run-time type info for the type + // Slot 2 is the first virtual method + // Fix up the symbol address here to match the value used in object headers. + // Note that we don't know a lot about the target here (eg. what platform it's + // running on), so it might be better to do this adjustment in DAC itself. But + // for now doing it here is simpler and more reliable (only one place to update + // to make it consistent). + address += 2 * 4; // assumes 32-bit + } + } + + si = (SymbolInfo)SymbolHash[key]; + if (si != null) + { + // Some duplicates are expected (eg. functions with overloads), but we should never + // actually care about the address of such symbols. Record that this is a dup + // so that we can warn/fail if we try to use it. + si.dupFound = true; + } + else { + si = new SymbolInfo(segment, address); + // Console.WriteLine("{0:x8} {1}", si.Segment, si.Address, key); + SymbolHash.Add(key, si); + } + + } + + } + + } + + strm.Close(); + } + + + public SymbolInfo FindSymbol(String key) + { + SymbolInfo si = (SymbolInfo)SymbolHash[key]; + if (si != null) + { + if (si.dupFound) + { + Console.WriteLine("Warning: Symbol " + key + " has duplicated entry in the symbol dump file."); + } + } + + return si; + } + + bool bIsWindowsMapfile = false; + String m_symdumpFile; + Hashtable SymbolHash = new Hashtable(); + + ArrayList SegmentBase = new ArrayList(); +} |