MetaDataOut.cs 20 KB

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