MetaDataOut.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /*
  2. * PERWAPI - An API for Reading and Writing PE Files
  3. *
  4. * Copyright (c) Diane Corney, Queensland University of Technology, 2004.
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the PERWAPI Copyright as included with this
  8. * distribution in the file PERWAPIcopyright.rtf.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY as is explained in the copyright notice.
  12. *
  13. * The author may be contacted at d.corney@qut.edu.au
  14. *
  15. * Version Date: 26/01/07
  16. */
  17. using System;
  18. using System.IO;
  19. using System.Text;
  20. using System.Collections;
  21. namespace QUT.PERWAPI
  22. {
  23. /**************************************************************************/
  24. // MetaData generated from user created descriptors
  25. /**************************************************************************/
  26. internal class MetaDataOut : MetaData
  27. {
  28. MetaDataStream strings, us, guid, blob;
  29. MetaDataStream[] streams;
  30. uint numStreams = 5;
  31. uint tildeTide = 0, tildePadding = 0, tildeStart = 0;
  32. uint numTables = 0, resourcesSize = 0;
  33. ArrayList byteCodes = new ArrayList();
  34. uint codeSize = 0, byteCodePadding = 0, metaDataSize = 0;
  35. internal PEWriter output;
  36. private byte heapSizes = 0;
  37. MetaDataElement entryPoint;
  38. long mdStart;
  39. ArrayList resources;
  40. private ArrayList[] tables = new ArrayList[NumMetaDataTables];
  41. // Allow the debug mode to be set.
  42. public bool Debug = false;
  43. internal MetaDataOut()
  44. : base()
  45. {
  46. }
  47. // Out of line generation of metadata for MethodDef code.
  48. // This avoids the ordering errors for method and param indices
  49. // when the code of a method refers to methods/fields of classes
  50. // that have not been entered in the table.
  51. // (kjg March 2010)
  52. internal void BuildCode() {
  53. foreach (object method in tables[(int)MDTable.Method])
  54. (method as MethodDef).TraverseCode(this);
  55. }
  56. Hashtable debugsigs = new Hashtable();
  57. /// <summary>
  58. /// Get the debug signature for a local.
  59. /// </summary>
  60. /// <param name="loc">The local.</param>
  61. /// <returns>The signature.</returns>
  62. internal DebugLocalSig GetDebugSig(Local loc)
  63. {
  64. byte[] b = loc.GetSig();
  65. string s = BitConverter.ToString(b);
  66. DebugLocalSig sig = (DebugLocalSig)debugsigs[s];
  67. if (sig != null) return sig;
  68. sig = new DebugLocalSig(b);
  69. debugsigs.Add(s, sig);
  70. return sig;
  71. }
  72. internal void InitMetaDataOut(PEWriter file)
  73. {
  74. // tilde = new MetaDataStream(tildeNameArray,false,0);
  75. this.output = file;
  76. streams = new MetaDataStream[5];
  77. strings = new MetaDataStream(MetaData.stringsNameArray, new UTF8Encoding(), true);
  78. us = new MetaDataStream(MetaData.usNameArray, new UnicodeEncoding(), true);
  79. guid = new MetaDataStream(MetaData.guidNameArray, false);
  80. blob = new MetaDataStream(MetaData.blobNameArray, new UnicodeEncoding(), true);
  81. streams[1] = strings;
  82. streams[2] = us;
  83. streams[3] = guid;
  84. streams[4] = blob;
  85. }
  86. internal uint Size()
  87. {
  88. //Console.WriteLine("metaData size = " + metaDataSize);
  89. return metaDataSize;
  90. }
  91. internal uint AddToUSHeap(string str)
  92. {
  93. if (str == null) return 0;
  94. return us.Add(str, true);
  95. }
  96. internal uint AddToStringsHeap(string str)
  97. {
  98. if ((str == null) || (str == "")) return 0;
  99. return strings.Add(str, false);
  100. }
  101. internal uint AddToGUIDHeap(Guid guidNum)
  102. {
  103. return guid.Add(guidNum);
  104. }
  105. internal uint AddToBlobHeap(byte[] blobBytes)
  106. {
  107. if (blobBytes == null) return 0;
  108. return blob.Add(blobBytes);
  109. }
  110. internal uint AddToBlobHeap(long val, uint numBytes)
  111. {
  112. return blob.Add(val, numBytes);
  113. }
  114. internal uint AddToBlobHeap(ulong val, uint numBytes)
  115. {
  116. return blob.Add(val, numBytes);
  117. }
  118. internal uint AddToBlobHeap(char ch)
  119. {
  120. return blob.Add(ch);
  121. }
  122. /*
  123. internal uint AddToBlobHeap(byte val) {
  124. return blob.Add(val);
  125. }
  126. internal uint AddToBlobHeap(sbyte val) {
  127. return blob.Add(val);
  128. }
  129. internal uint AddToBlobHeap(ushort val) {
  130. return blob.Add(val);
  131. }
  132. internal uint AddToBlobHeap(short val) {
  133. return blob.Add(val);
  134. }
  135. internal uint AddToBlobHeap(uint val) {
  136. return blob.Add(val);
  137. }
  138. internal uint AddToBlobHeap(int val) {
  139. return blob.Add(val);
  140. }
  141. internal uint AddToBlobHeap(ulong val) {
  142. return blob.Add(val);
  143. }
  144. internal uint AddToBlobHeap(long val) {
  145. return blob.Add(val);
  146. }
  147. */
  148. internal uint AddToBlobHeap(float val)
  149. {
  150. return blob.Add(val);
  151. }
  152. internal uint AddToBlobHeap(double val)
  153. {
  154. return blob.Add(val);
  155. }
  156. internal uint AddToBlobHeap(string val)
  157. {
  158. return blob.Add(val, true);
  159. }
  160. private ArrayList GetTable(MDTable tableIx)
  161. {
  162. int tabIx = (int)tableIx;
  163. if (tables[tabIx] == null)
  164. {
  165. tables[tabIx] = new ArrayList();
  166. valid |= ((ulong)0x1 << tabIx);
  167. // Console.WriteLine("after creating table " + tableIx + "(" + tabIx + ") valid = " + valid);
  168. numTables++;
  169. }
  170. return tables[tabIx];
  171. }
  172. internal void AddToTable(MDTable tableIx, MetaDataElement elem) {
  173. // Console.WriteLine("Adding element to table " + (uint)tableIx);
  174. ArrayList table = GetTable(tableIx);
  175. if (table.Contains(elem)) {
  176. Console.Out.WriteLine("ERROR - element already in table " + tableIx);
  177. return;
  178. }
  179. elem.Row = (uint)table.Count + 1;
  180. table.Add(elem);
  181. }
  182. internal void ConditionalAddTypeSpec(MetaDataElement elem) {
  183. ArrayList table = GetTable(MDTable.TypeSpec);
  184. if (!table.Contains(elem)) {
  185. elem.Row = (uint)table.Count + 1;
  186. table.Add(elem);
  187. }
  188. }
  189. internal uint TableIndex(MDTable tableIx)
  190. {
  191. if (tables[(int)tableIx] == null) return 1;
  192. return (uint)tables[(int)tableIx].Count + 1;
  193. }
  194. internal uint AddCode(CILInstructions byteCode)
  195. {
  196. byteCodes.Add(byteCode);
  197. uint offset = codeSize;
  198. codeSize += byteCode.GetCodeSize();
  199. return offset;
  200. }
  201. internal void SetEntryPoint(MetaDataElement ep)
  202. {
  203. entryPoint = ep;
  204. }
  205. internal uint AddResource(byte[] resBytes)
  206. {
  207. if (resources == null) resources = new ArrayList();
  208. resources.Add(resBytes);
  209. uint offset = resourcesSize;
  210. resourcesSize += (uint)resBytes.Length + 4;
  211. return offset;
  212. }
  213. internal void AddData(DataConstant cVal)
  214. {
  215. output.AddInitData(cVal);
  216. }
  217. internal static void CompressNum(byte[] arr, MemoryStream sig)
  218. {
  219. for (int ix = 0; ix < arr.Length; ix++) sig.WriteByte(arr[ix]);
  220. }
  221. internal uint CodeSize()
  222. {
  223. return codeSize + byteCodePadding;
  224. }
  225. internal uint GetResourcesSize() { return resourcesSize; }
  226. private void SetStreamOffsets()
  227. {
  228. uint sizeOfHeaders = StreamHeaderSize + (uint)tildeNameArray.Length;
  229. for (int i = 1; i < numStreams; i++)
  230. {
  231. sizeOfHeaders += streams[i].headerSize();
  232. }
  233. metaDataSize = MetaDataHeaderSize + sizeOfHeaders;
  234. //Console.WriteLine("Size of meta data headers (tildeStart) = " + Hex.Long(metaDataSize));
  235. tildeStart = metaDataSize;
  236. metaDataSize += tildeTide + tildePadding;
  237. //Console.WriteLine(tildeNameArray + " - size = " + (tildeTide + tildePadding));
  238. for (int i = 1; i < numStreams; i++)
  239. {
  240. //Console.WriteLine("Stream " + i + " " + new String(streams[i].name) + " starts at " + Hex.Long(metaDataSize));
  241. streams[i].Start = metaDataSize;
  242. metaDataSize += streams[i].Size();
  243. streams[i].WriteDetails();
  244. }
  245. if (largeStrings) heapSizes |= 0x01;
  246. if (largeGUID) heapSizes |= 0x02;
  247. if (largeBlob) heapSizes |= 0x04;
  248. }
  249. internal void CalcTildeStreamSize()
  250. {
  251. largeStrings = strings.LargeIx();
  252. largeBlob = blob.LargeIx();
  253. largeGUID = guid.LargeIx();
  254. largeUS = us.LargeIx();
  255. CalcElemSize();
  256. //tilde.SetIndexSizes(strings.LargeIx(),us.LargeIx(),guid.LargeIx(),blob.LargeIx());
  257. tildeTide = TildeHeaderSize;
  258. tildeTide += 4 * numTables;
  259. //Console.WriteLine("Tilde header + sizes = " + tildeTide);
  260. for (int i = 0; i < NumMetaDataTables; i++)
  261. {
  262. if (tables[i] != null)
  263. {
  264. ArrayList table = tables[i];
  265. // Console.WriteLine("Meta data table " + i + " at offset " + tildeTide);
  266. tildeTide += (uint)table.Count * elemSize[i];
  267. // Console.WriteLine("Metadata table " + i + " has size " + table.Count);
  268. // Console.WriteLine("tildeTide = " + tildeTide);
  269. }
  270. }
  271. if ((tildeTide % 4) != 0) tildePadding = 4 - (tildeTide % 4);
  272. //Console.WriteLine("tildePadding = " + tildePadding);
  273. }
  274. internal void WriteTildeStream(PEWriter output)
  275. {
  276. long startTilde = output.Seek(0, SeekOrigin.Current);
  277. //Console.WriteLine("Starting tilde output at offset " + Hex.Long(startTilde));
  278. output.Write((uint)0); // Reserved
  279. output.Write(output.verInfo.tsMajVer); // MajorVersion
  280. output.Write(output.verInfo.tsMinVer); // MinorVersion
  281. output.Write(heapSizes);
  282. output.Write((byte)1); // Reserved
  283. output.Write(valid);
  284. output.Write(sorted);
  285. for (int i = 0; i < NumMetaDataTables; i++)
  286. {
  287. if (tables[i] != null)
  288. {
  289. uint count = (uint)tables[i].Count;
  290. output.Write(count);
  291. }
  292. }
  293. long tabStart = output.Seek(0, SeekOrigin.Current);
  294. //Console.WriteLine("Starting metaData tables at " + tabStart);
  295. for (int i = 0; i < NumMetaDataTables; i++)
  296. {
  297. if (tables[i] != null)
  298. {
  299. //Console.WriteLine("Starting metaData table " + i + " at " + (output.Seek(0,SeekOrigin.Current) - startTilde));
  300. ArrayList table = tables[i];
  301. for (int j = 0; j < table.Count; j++)
  302. {
  303. ((MetaDataElement)table[j]).Write(output);
  304. }
  305. }
  306. }
  307. // reset the typespec flags
  308. if (tables[(int)MDTable.TypeSpec] != null)
  309. {
  310. ArrayList typeSpecTable = tables[(int)MDTable.TypeSpec];
  311. for (int i = 0; i < typeSpecTable.Count; i++)
  312. {
  313. ((Type)typeSpecTable[i]).typeSpecAdded = false;
  314. }
  315. }
  316. //Console.WriteLine("Writing padding at " + output.Seek(0,SeekOrigin.Current));
  317. for (int i = 0; i < tildePadding; i++) output.Write((byte)0);
  318. }
  319. private void SortTable(ArrayList mTable)
  320. {
  321. //Console.WriteLine("Sorting table");
  322. if (mTable == null) return;
  323. mTable.Sort();
  324. for (int i = 0; i < mTable.Count; i++)
  325. {
  326. ((MetaDataElement)mTable[i]).Row = (uint)i + 1;
  327. }
  328. }
  329. internal void BuildMDTables()
  330. {
  331. // Check ordering of specific tables
  332. // Constant, CustomAttribute, FieldMarshal, DeclSecurity, MethodSemantics
  333. // ImplMap, NestedClass, GenericParam
  334. // Need to load GenericParamConstraint AFTER GenericParam table in correct order
  335. // The tables:
  336. // InterfaceImpl, ClassLayout, FieldLayout, MethodImpl, FieldRVA
  337. // will _ALWAYS_ be in the correct order as embedded in BuildMDTables
  338. SortTable(tables[(int)MDTable.Constant]);
  339. SortTable(tables[(int)MDTable.CustomAttribute]);
  340. SortTable(tables[(int)MDTable.FieldMarshal]);
  341. SortTable(tables[(int)MDTable.DeclSecurity]);
  342. SortTable(tables[(int)MDTable.MethodSemantics]);
  343. SortTable(tables[(int)MDTable.ImplMap]);
  344. SortTable(tables[(int)MDTable.NestedClass]);
  345. if (tables[(int)MDTable.GenericParam] != null)
  346. {
  347. SortTable(tables[(int)MDTable.GenericParam]);
  348. // Now add GenericParamConstraints
  349. for (int i = 0; i < tables[(int)MDTable.GenericParam].Count; i++)
  350. {
  351. ((GenericParam)tables[(int)MDTable.GenericParam][i]).AddConstraints(this);
  352. }
  353. }
  354. /*
  355. // for bug in Whidbey GenericParam table ordering
  356. int end = tables[(int)MDTable.TypeDef].Count;
  357. int methEnd = 0;
  358. if (tables[(int)MDTable.Method] != null) {
  359. methEnd = tables[(int)MDTable.Method].Count;
  360. }
  361. for (int i=0; i < end; i++) {
  362. ((ClassDef)tables[(int)MDTable.TypeDef][i]).AddGenericsToTable(this);
  363. if (methEnd > i)
  364. ((MethodDef)tables[(int)MDTable.Method][i]).AddGenericsToTable(this);
  365. }
  366. for (int i=end; i < methEnd; i++) {
  367. ((MethodDef)tables[(int)MDTable.Method][i]).AddGenericsToTable(this);
  368. }
  369. // end of bug fix
  370. */
  371. for (int i = 0; i < tables.Length; i++)
  372. {
  373. if (tables[i] != null)
  374. {
  375. for (int j = 0; j < tables[i].Count; j++)
  376. {
  377. ((MetaDataElement)tables[i][j]).BuildSignatures(this);
  378. }
  379. }
  380. }
  381. }
  382. internal void SetIndexSizes()
  383. {
  384. for (int i = 0; i < NumMetaDataTables; i++)
  385. {
  386. if (tables[i] != null)
  387. {
  388. largeIx[i] = (uint)tables[i].Count > maxSmlIxSize;
  389. }
  390. }
  391. for (int i = 0; i < CIxTables.Length; i++)
  392. {
  393. for (int j = 0; j < CIxTables[i].Length; j++)
  394. {
  395. int tabIx = CIxTables[i][j];
  396. if (tables[tabIx] != null)
  397. {
  398. lgeCIx[i] = lgeCIx[i] | tables[tabIx].Count > CIxMaxMap[i];
  399. }
  400. }
  401. }
  402. }
  403. internal void BuildMetaData()
  404. {
  405. SetIndexSizes();
  406. for (int i = 1; i < numStreams; i++)
  407. {
  408. if (streams[i].Size() <= 1)
  409. {
  410. //Console.WriteLine("Stream " + new String(streams[i].name) + " has size 0");
  411. for (int j = i + 1; j < numStreams; j++)
  412. {
  413. streams[i] = streams[j];
  414. }
  415. i--;
  416. numStreams--;
  417. }
  418. else
  419. streams[i].EndStream();
  420. }
  421. //Console.WriteLine("numStreams = " + numStreams);
  422. CalcTildeStreamSize();
  423. SetStreamOffsets();
  424. byteCodePadding = NumToAlign(codeSize, 4);
  425. if (entryPoint != null) output.SetEntryPoint(entryPoint.Token());
  426. }
  427. internal void WriteByteCodes(PEWriter output)
  428. {
  429. for (int i = 0; i < byteCodes.Count; i++)
  430. {
  431. ((CILInstructions)byteCodes[i]).Write(output);
  432. }
  433. for (int i = 0; i < byteCodePadding; i++)
  434. {
  435. output.Write((byte)0);
  436. }
  437. }
  438. internal void WriteResources(PEWriter output)
  439. {
  440. if (resources == null) return;
  441. for (int i = 0; i < resources.Count; i++)
  442. {
  443. byte[] resBytes = (byte[])resources[i];
  444. output.Write((uint)resBytes.Length);
  445. output.Write(resBytes);
  446. }
  447. }
  448. internal void WriteMetaData(PEWriter output)
  449. {
  450. this.output = output;
  451. if (Diag.DiagOn)
  452. {
  453. mdStart = output.Seek(0, SeekOrigin.Current);
  454. Console.WriteLine("Writing metaData at " + Hex.Long(mdStart));
  455. }
  456. output.Write(MetaDataSignature);
  457. output.Write(output.verInfo.mdMajVer);
  458. output.Write(output.verInfo.mdMinVer);
  459. output.Write(0); // Reserved
  460. output.Write(output.verInfo.netVerString.Length);
  461. output.Write(output.verInfo.netVerString.ToCharArray()); // version string is already zero padded
  462. output.Write((short)0); // Flags, reserved
  463. output.Write((ushort)numStreams);
  464. // write tilde header
  465. output.Write(tildeStart);
  466. output.Write(tildeTide + tildePadding);
  467. output.Write(tildeNameArray);
  468. for (int i = 1; i < numStreams; i++)
  469. {
  470. if (Diag.DiagOn)
  471. Console.WriteLine("Stream " + new String(streams[i].name) + " should start at " + Hex.Long(streams[i].Start + mdStart));
  472. streams[i].WriteHeader(output);
  473. }
  474. if (Diag.DiagOn)
  475. {
  476. Console.Write("Writing tilde stream at " + Hex.Long(output.Seek(0, SeekOrigin.Current)));
  477. Console.WriteLine(" should be at " + Hex.Long(tildeStart + mdStart));
  478. }
  479. WriteTildeStream(output);
  480. for (int i = 1; i < numStreams; i++)
  481. {
  482. if (Diag.DiagOn)
  483. Console.WriteLine("Writing stream " + new String(streams[i].name) + " at " + Hex.Long(output.Seek(0, SeekOrigin.Current)));
  484. streams[i].Write(output);
  485. }
  486. //Console.WriteLine("Finished Writing metaData at " + output.Seek(0,SeekOrigin.Current));
  487. }
  488. // internal bool LargeStringsIndex() { return strings.LargeIx(); }
  489. // internal bool LargeGUIDIndex() { return guid.LargeIx(); }
  490. // internal bool LargeUSIndex() { return us.LargeIx(); }
  491. // internal bool LargeBlobIndex() { return blob.LargeIx(); }
  492. private uint NumToAlign(uint val, uint alignVal)
  493. {
  494. if ((val % alignVal) == 0) return 0;
  495. return alignVal - (val % alignVal);
  496. }
  497. internal void WriteCodedIndex(CIx code, MetaDataElement elem, PEWriter output)
  498. {
  499. uint ix = 0;
  500. if (elem != null)
  501. {
  502. ix = (elem.Row << CIxShiftMap[(uint)code]) | elem.GetCodedIx(code);
  503. // Console.WriteLine("coded index = " + ix + " row = " + elem.Row);
  504. //} else {
  505. // Console.WriteLine("elem for coded index is null");
  506. }
  507. if (lgeCIx[(uint)code])
  508. output.Write(ix);
  509. else
  510. output.Write((ushort)ix);
  511. }
  512. }
  513. }