PdbFunction.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. // Copyright (c) Microsoft. All rights reserved.
  2. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. #pragma warning disable 1591 // TODO: doc comments
  7. namespace Microsoft.Cci.Pdb {
  8. internal class PdbFunction {
  9. static internal readonly Guid msilMetaData = new Guid(0xc6ea3fc9, 0x59b3, 0x49d6, 0xbc, 0x25,
  10. 0x09, 0x02, 0xbb, 0xab, 0xb4, 0x60);
  11. static internal readonly IComparer byAddress = new PdbFunctionsByAddress();
  12. static internal readonly IComparer byAddressAndToken = new PdbFunctionsByAddressAndToken();
  13. //static internal readonly IComparer byToken = new PdbFunctionsByToken();
  14. internal uint token;
  15. internal uint slotToken;
  16. internal uint tokenOfMethodWhoseUsingInfoAppliesToThisMethod;
  17. //internal string name;
  18. //internal string module;
  19. //internal ushort flags;
  20. internal uint segment;
  21. internal uint address;
  22. internal uint length;
  23. //internal byte[] metadata;
  24. internal PdbScope[] scopes;
  25. internal PdbSlot[] slots;
  26. internal PdbConstant[] constants;
  27. internal string[] usedNamespaces;
  28. internal PdbLines[] lines;
  29. internal ushort[]/*?*/ usingCounts;
  30. internal IEnumerable<INamespaceScope>/*?*/ namespaceScopes;
  31. internal string/*?*/ iteratorClass;
  32. internal List<ILocalScope>/*?*/ iteratorScopes;
  33. internal PdbSynchronizationInformation/*?*/ synchronizationInformation;
  34. /// <summary>
  35. /// Flag saying whether the method has been identified as a product of VB compilation using
  36. /// the legacy Windows PDB symbol format, in which case scope ends need to be shifted by 1
  37. /// due to different semantics of scope limits in VB and C# compilers.
  38. /// </summary>
  39. private bool visualBasicScopesAdjusted = false;
  40. private static string StripNamespace(string module) {
  41. int li = module.LastIndexOf('.');
  42. if (li > 0) {
  43. return module.Substring(li + 1);
  44. }
  45. return module;
  46. }
  47. /// <summary>
  48. /// When the Windows PDB reader identifies a PdbFunction as having 'Basic' as its source language,
  49. /// it calls this method which adjusts all scopes by adding 1 to their lengths to compensate
  50. /// for different behavior of VB vs. the C# compiler w.r.t. emission of scope info.
  51. /// </summary>
  52. internal void AdjustVisualBasicScopes()
  53. {
  54. if (!visualBasicScopesAdjusted)
  55. {
  56. visualBasicScopesAdjusted = true;
  57. // Don't adjust root scope as that one is correct
  58. foreach (PdbScope scope in scopes)
  59. {
  60. AdjustVisualBasicScopes(scope.scopes);
  61. }
  62. }
  63. }
  64. /// <summary>
  65. /// Recursively update the entire scope tree by adding 1 to the length of each scope.
  66. /// </summary>
  67. private void AdjustVisualBasicScopes(PdbScope[] scopes)
  68. {
  69. foreach (PdbScope scope in scopes)
  70. {
  71. scope.length++;
  72. AdjustVisualBasicScopes(scope.scopes);
  73. }
  74. }
  75. internal static PdbFunction[] LoadManagedFunctions(/*string module,*/
  76. BitAccess bits, uint limit,
  77. bool readStrings) {
  78. //string mod = StripNamespace(module);
  79. int begin = bits.Position;
  80. int count = 0;
  81. while (bits.Position < limit) {
  82. ushort siz;
  83. ushort rec;
  84. bits.ReadUInt16(out siz);
  85. int star = bits.Position;
  86. int stop = bits.Position + siz;
  87. bits.Position = star;
  88. bits.ReadUInt16(out rec);
  89. switch ((SYM)rec) {
  90. case SYM.S_GMANPROC:
  91. case SYM.S_LMANPROC:
  92. ManProcSym proc;
  93. bits.ReadUInt32(out proc.parent);
  94. bits.ReadUInt32(out proc.end);
  95. bits.Position = (int)proc.end;
  96. count++;
  97. break;
  98. case SYM.S_END:
  99. bits.Position = stop;
  100. break;
  101. default:
  102. //Console.WriteLine("{0,6}: {1:x2} {2}",
  103. // bits.Position, rec, (SYM)rec);
  104. bits.Position = stop;
  105. break;
  106. }
  107. }
  108. if (count == 0) {
  109. return null;
  110. }
  111. bits.Position = begin;
  112. PdbFunction[] funcs = new PdbFunction[count];
  113. int func = 0;
  114. while (bits.Position < limit) {
  115. ushort siz;
  116. ushort rec;
  117. bits.ReadUInt16(out siz);
  118. int star = bits.Position;
  119. int stop = bits.Position + siz;
  120. bits.ReadUInt16(out rec);
  121. switch ((SYM)rec) {
  122. case SYM.S_GMANPROC:
  123. case SYM.S_LMANPROC:
  124. ManProcSym proc;
  125. //int offset = bits.Position;
  126. bits.ReadUInt32(out proc.parent);
  127. bits.ReadUInt32(out proc.end);
  128. bits.ReadUInt32(out proc.next);
  129. bits.ReadUInt32(out proc.len);
  130. bits.ReadUInt32(out proc.dbgStart);
  131. bits.ReadUInt32(out proc.dbgEnd);
  132. bits.ReadUInt32(out proc.token);
  133. bits.ReadUInt32(out proc.off);
  134. bits.ReadUInt16(out proc.seg);
  135. bits.ReadUInt8(out proc.flags);
  136. bits.ReadUInt16(out proc.retReg);
  137. if (readStrings) {
  138. bits.ReadCString(out proc.name);
  139. } else {
  140. bits.SkipCString(out proc.name);
  141. }
  142. //Console.WriteLine("token={0:X8} [{1}::{2}]", proc.token, module, proc.name);
  143. bits.Position = stop;
  144. funcs[func++] = new PdbFunction(/*module,*/ proc, bits);
  145. break;
  146. default: {
  147. //throw new PdbDebugException("Unknown SYMREC {0}", (SYM)rec);
  148. bits.Position = stop;
  149. break;
  150. }
  151. }
  152. }
  153. return funcs;
  154. }
  155. internal static void CountScopesAndSlots(BitAccess bits, uint limit,
  156. out int constants, out int scopes, out int slots, out int usedNamespaces) {
  157. int pos = bits.Position;
  158. BlockSym32 block;
  159. constants = 0;
  160. slots = 0;
  161. scopes = 0;
  162. usedNamespaces = 0;
  163. while (bits.Position < limit) {
  164. ushort siz;
  165. ushort rec;
  166. bits.ReadUInt16(out siz);
  167. int star = bits.Position;
  168. int stop = bits.Position + siz;
  169. bits.Position = star;
  170. bits.ReadUInt16(out rec);
  171. switch ((SYM)rec) {
  172. case SYM.S_BLOCK32: {
  173. bits.ReadUInt32(out block.parent);
  174. bits.ReadUInt32(out block.end);
  175. scopes++;
  176. bits.Position = (int)block.end;
  177. break;
  178. }
  179. case SYM.S_MANSLOT:
  180. slots++;
  181. bits.Position = stop;
  182. break;
  183. case SYM.S_UNAMESPACE:
  184. usedNamespaces++;
  185. bits.Position = stop;
  186. break;
  187. case SYM.S_MANCONSTANT:
  188. constants++;
  189. bits.Position = stop;
  190. break;
  191. default:
  192. bits.Position = stop;
  193. break;
  194. }
  195. }
  196. bits.Position = pos;
  197. }
  198. internal PdbFunction() {
  199. }
  200. internal PdbFunction(/*string module, */ManProcSym proc, BitAccess bits) {
  201. this.token = proc.token;
  202. //this.module = module;
  203. //this.name = proc.name;
  204. //this.flags = proc.flags;
  205. this.segment = proc.seg;
  206. this.address = proc.off;
  207. this.length = proc.len;
  208. if (proc.seg != 1) {
  209. throw new PdbDebugException("Segment is {0}, not 1.", proc.seg);
  210. }
  211. if (proc.parent != 0 || proc.next != 0) {
  212. throw new PdbDebugException("Warning parent={0}, next={1}",
  213. proc.parent, proc.next);
  214. }
  215. //if (proc.dbgStart != 0 || proc.dbgEnd != 0) {
  216. // throw new PdbDebugException("Warning DBG start={0}, end={1}",
  217. // proc.dbgStart, proc.dbgEnd);
  218. //}
  219. int constantCount;
  220. int scopeCount;
  221. int slotCount;
  222. int usedNamespacesCount;
  223. CountScopesAndSlots(bits, proc.end, out constantCount, out scopeCount, out slotCount, out usedNamespacesCount);
  224. int scope = constantCount > 0 || slotCount > 0 || usedNamespacesCount > 0 ? 1 : 0;
  225. int slot = 0;
  226. int constant = 0;
  227. int usedNs = 0;
  228. scopes = new PdbScope[scopeCount+scope];
  229. slots = new PdbSlot[slotCount];
  230. constants = new PdbConstant[constantCount];
  231. usedNamespaces = new string[usedNamespacesCount];
  232. if (scope > 0)
  233. scopes[0] = new PdbScope(this.address, proc.len, slots, constants, usedNamespaces);
  234. while (bits.Position < proc.end) {
  235. ushort siz;
  236. ushort rec;
  237. bits.ReadUInt16(out siz);
  238. int star = bits.Position;
  239. int stop = bits.Position + siz;
  240. bits.Position = star;
  241. bits.ReadUInt16(out rec);
  242. switch ((SYM)rec) {
  243. case SYM.S_OEM: { // 0x0404
  244. OemSymbol oem;
  245. bits.ReadGuid(out oem.idOem);
  246. bits.ReadUInt32(out oem.typind);
  247. // internal byte[] rgl; // user data, force 4-byte alignment
  248. if (oem.idOem == msilMetaData) {
  249. string name = bits.ReadString();
  250. if (name == "MD2") {
  251. ReadMD2CustomMetadata(bits);
  252. } else if (name == "asyncMethodInfo") {
  253. this.synchronizationInformation = new PdbSynchronizationInformation(bits);
  254. }
  255. bits.Position = stop;
  256. break;
  257. } else {
  258. throw new PdbDebugException("OEM section: guid={0} ti={1}",
  259. oem.idOem, oem.typind);
  260. // bits.Position = stop;
  261. }
  262. }
  263. case SYM.S_BLOCK32: {
  264. BlockSym32 block = new BlockSym32();
  265. bits.ReadUInt32(out block.parent);
  266. bits.ReadUInt32(out block.end);
  267. bits.ReadUInt32(out block.len);
  268. bits.ReadUInt32(out block.off);
  269. bits.ReadUInt16(out block.seg);
  270. bits.SkipCString(out block.name);
  271. bits.Position = stop;
  272. scopes[scope++] = new PdbScope(this.address, block, bits, out slotToken);
  273. bits.Position = (int)block.end;
  274. break;
  275. }
  276. case SYM.S_MANSLOT:
  277. slots[slot++] = new PdbSlot(bits);
  278. bits.Position = stop;
  279. break;
  280. case SYM.S_MANCONSTANT:
  281. constants[constant++] = new PdbConstant(bits);
  282. bits.Position = stop;
  283. break;
  284. case SYM.S_UNAMESPACE:
  285. bits.ReadCString(out usedNamespaces[usedNs++]);
  286. bits.Position = stop;
  287. break;
  288. case SYM.S_END:
  289. bits.Position = stop;
  290. break;
  291. default: {
  292. //throw new PdbDebugException("Unknown SYM: {0}", (SYM)rec);
  293. bits.Position = stop;
  294. break;
  295. }
  296. }
  297. }
  298. if (bits.Position != proc.end) {
  299. throw new PdbDebugException("Not at S_END");
  300. }
  301. ushort esiz;
  302. ushort erec;
  303. bits.ReadUInt16(out esiz);
  304. bits.ReadUInt16(out erec);
  305. if (erec != (ushort)SYM.S_END) {
  306. throw new PdbDebugException("Missing S_END");
  307. }
  308. }
  309. internal void ReadMD2CustomMetadata(BitAccess bits)
  310. {
  311. byte version;
  312. bits.ReadUInt8(out version);
  313. if (version == 4) {
  314. byte count;
  315. bits.ReadUInt8(out count);
  316. bits.Align(4);
  317. while (count-- > 0)
  318. this.ReadCustomMetadata(bits);
  319. }
  320. }
  321. private void ReadCustomMetadata(BitAccess bits)
  322. {
  323. int savedPosition = bits.Position;
  324. byte version;
  325. bits.ReadUInt8(out version);
  326. byte kind;
  327. bits.ReadUInt8(out kind);
  328. bits.Position += 2; // 2-bytes padding
  329. uint numberOfBytesInItem;
  330. bits.ReadUInt32(out numberOfBytesInItem);
  331. if (version == 4)
  332. {
  333. switch (kind)
  334. {
  335. case 0: this.ReadUsingInfo(bits); break;
  336. case 1: this.ReadForwardInfo(bits); break;
  337. case 2: break; // this.ReadForwardedToModuleInfo(bits); break;
  338. case 3: this.ReadIteratorLocals(bits); break;
  339. case 4: this.ReadForwardIterator(bits); break;
  340. case 5: break; // dynamic locals - see http://index/#Microsoft.VisualStudio.LanguageServices/Shared/CustomDebugInfoReader.cs,a3031f7681d76e93
  341. case 6: break; // EnC data
  342. case 7: break; // EnC data for lambdas and closures
  343. // ignore any other unknown record types that may be added in future, instead of throwing an exception
  344. // see more details here: https://github.com/tmat/roslyn/blob/portable-pdb/docs/specs/PortablePdb-Metadata.md
  345. default: break; // throw new PdbDebugException("Unknown custom metadata item kind: {0}", kind);
  346. }
  347. }
  348. bits.Position = savedPosition + (int)numberOfBytesInItem;
  349. }
  350. private void ReadForwardIterator(BitAccess bits) {
  351. this.iteratorClass = bits.ReadString();
  352. }
  353. private void ReadIteratorLocals(BitAccess bits) {
  354. uint numberOfLocals;
  355. bits.ReadUInt32(out numberOfLocals);
  356. this.iteratorScopes = new List<ILocalScope>((int)numberOfLocals);
  357. while (numberOfLocals-- > 0) {
  358. uint ilStartOffset;
  359. uint ilEndOffset;
  360. bits.ReadUInt32(out ilStartOffset);
  361. bits.ReadUInt32(out ilEndOffset);
  362. this.iteratorScopes.Add(new PdbIteratorScope(ilStartOffset, ilEndOffset-ilStartOffset));
  363. }
  364. }
  365. //private void ReadForwardedToModuleInfo(BitAccess bits) {
  366. //}
  367. private void ReadForwardInfo(BitAccess bits) {
  368. bits.ReadUInt32(out this.tokenOfMethodWhoseUsingInfoAppliesToThisMethod);
  369. }
  370. private void ReadUsingInfo(BitAccess bits) {
  371. ushort numberOfNamespaces;
  372. bits.ReadUInt16(out numberOfNamespaces);
  373. this.usingCounts = new ushort[numberOfNamespaces];
  374. for (ushort i = 0; i < numberOfNamespaces; i++) {
  375. bits.ReadUInt16(out this.usingCounts[i]);
  376. }
  377. }
  378. internal class PdbFunctionsByAddress : IComparer {
  379. public int Compare(Object x, Object y) {
  380. PdbFunction fx = (PdbFunction)x;
  381. PdbFunction fy = (PdbFunction)y;
  382. if (fx.segment < fy.segment) {
  383. return -1;
  384. } else if (fx.segment > fy.segment) {
  385. return 1;
  386. } else if (fx.address < fy.address) {
  387. return -1;
  388. } else if (fx.address > fy.address) {
  389. return 1;
  390. } else {
  391. return 0;
  392. }
  393. }
  394. }
  395. internal class PdbFunctionsByAddressAndToken : IComparer {
  396. public int Compare(Object x, Object y) {
  397. PdbFunction fx = (PdbFunction)x;
  398. PdbFunction fy = (PdbFunction)y;
  399. if (fx.segment < fy.segment) {
  400. return -1;
  401. } else if (fx.segment > fy.segment) {
  402. return 1;
  403. } else if (fx.address < fy.address) {
  404. return -1;
  405. } else if (fx.address > fy.address) {
  406. return 1;
  407. } else {
  408. if (fx.token < fy.token)
  409. return -1;
  410. else if (fx.token > fy.token)
  411. return 1;
  412. else
  413. return 0;
  414. }
  415. }
  416. }
  417. //internal class PdbFunctionsByToken : IComparer {
  418. // public int Compare(Object x, Object y) {
  419. // PdbFunction fx = (PdbFunction)x;
  420. // PdbFunction fy = (PdbFunction)y;
  421. // if (fx.token < fy.token) {
  422. // return -1;
  423. // } else if (fx.token > fy.token) {
  424. // return 1;
  425. // } else {
  426. // return 0;
  427. // }
  428. // }
  429. //}
  430. }
  431. internal class PdbSynchronizationInformation {
  432. internal uint kickoffMethodToken;
  433. internal uint generatedCatchHandlerIlOffset;
  434. internal PdbSynchronizationPoint[] synchronizationPoints;
  435. internal PdbSynchronizationInformation(BitAccess bits) {
  436. uint asyncStepInfoCount;
  437. bits.ReadUInt32(out this.kickoffMethodToken);
  438. bits.ReadUInt32(out this.generatedCatchHandlerIlOffset);
  439. bits.ReadUInt32(out asyncStepInfoCount);
  440. this.synchronizationPoints = new PdbSynchronizationPoint[asyncStepInfoCount];
  441. for (uint i = 0; i < asyncStepInfoCount; i += 1) {
  442. this.synchronizationPoints[i] = new PdbSynchronizationPoint(bits);
  443. }
  444. }
  445. public uint GeneratedCatchHandlerOffset {
  446. get { return this.generatedCatchHandlerIlOffset; }
  447. }
  448. }
  449. internal class PdbSynchronizationPoint {
  450. internal uint synchronizeOffset;
  451. internal uint continuationMethodToken;
  452. internal uint continuationOffset;
  453. internal PdbSynchronizationPoint(BitAccess bits) {
  454. bits.ReadUInt32(out this.synchronizeOffset);
  455. bits.ReadUInt32(out this.continuationMethodToken);
  456. bits.ReadUInt32(out this.continuationOffset);
  457. }
  458. public uint SynchronizeOffset {
  459. get { return this.synchronizeOffset; }
  460. }
  461. public uint ContinuationOffset {
  462. get { return this.continuationOffset; }
  463. }
  464. }
  465. }