MetaDataOut.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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. // updates Row field of the element
  165. // Console.WriteLine("Adding element to table " + (uint)tableIx);
  166. ArrayList table = GetTable(tableIx);
  167. if (table.Contains(elem)) {
  168. Console.Out.WriteLine("ERROR - element already in table " + tableIx);
  169. return;
  170. }
  171. elem.Row = (uint)table.Count + 1;
  172. table.Add(elem);
  173. }
  174. internal void ConditionalAddToTable(MDTable tableIx, MetaDataElement elem) {
  175. // updates Row field of the element
  176. // Console.WriteLine("Adding element to table " + (uint)tableIx);
  177. ArrayList table = GetTable(tableIx);
  178. if (!table.Contains(elem)) {
  179. elem.Row = (uint)table.Count + 1;
  180. table.Add(elem);
  181. }
  182. }
  183. internal uint TableIndex(MDTable tableIx)
  184. {
  185. if (tables[(int)tableIx] == null) return 1;
  186. return (uint)tables[(int)tableIx].Count + 1;
  187. }
  188. internal uint AddCode(CILInstructions byteCode)
  189. {
  190. byteCodes.Add(byteCode);
  191. uint offset = codeSize;
  192. codeSize += byteCode.GetCodeSize();
  193. return offset;
  194. }
  195. internal void SetEntryPoint(MetaDataElement ep)
  196. {
  197. entryPoint = ep;
  198. }
  199. internal uint AddResource(byte[] resBytes)
  200. {
  201. if (resources == null) resources = new ArrayList();
  202. resources.Add(resBytes);
  203. uint offset = resourcesSize;
  204. resourcesSize += (uint)resBytes.Length + 4;
  205. return offset;
  206. }
  207. internal void AddData(DataConstant cVal)
  208. {
  209. output.AddInitData(cVal);
  210. }
  211. internal static void CompressNum(byte[] arr, MemoryStream sig)
  212. {
  213. for (int ix = 0; ix < arr.Length; ix++) sig.WriteByte(arr[ix]);
  214. }
  215. internal uint CodeSize()
  216. {
  217. return codeSize + byteCodePadding;
  218. }
  219. internal uint GetResourcesSize() { return resourcesSize; }
  220. private void SetStreamOffsets()
  221. {
  222. uint sizeOfHeaders = StreamHeaderSize + (uint)tildeNameArray.Length;
  223. for (int i = 1; i < numStreams; i++)
  224. {
  225. sizeOfHeaders += streams[i].headerSize();
  226. }
  227. metaDataSize = MetaDataHeaderSize + sizeOfHeaders;
  228. //Console.WriteLine("Size of meta data headers (tildeStart) = " + Hex.Long(metaDataSize));
  229. tildeStart = metaDataSize;
  230. metaDataSize += tildeTide + tildePadding;
  231. //Console.WriteLine(tildeNameArray + " - size = " + (tildeTide + tildePadding));
  232. for (int i = 1; i < numStreams; i++)
  233. {
  234. //Console.WriteLine("Stream " + i + " " + new String(streams[i].name) + " starts at " + Hex.Long(metaDataSize));
  235. streams[i].Start = metaDataSize;
  236. metaDataSize += streams[i].Size();
  237. streams[i].WriteDetails();
  238. }
  239. if (largeStrings) heapSizes |= 0x01;
  240. if (largeGUID) heapSizes |= 0x02;
  241. if (largeBlob) heapSizes |= 0x04;
  242. }
  243. internal void CalcTildeStreamSize()
  244. {
  245. largeStrings = strings.LargeIx();
  246. largeBlob = blob.LargeIx();
  247. largeGUID = guid.LargeIx();
  248. largeUS = us.LargeIx();
  249. CalcElemSize();
  250. //tilde.SetIndexSizes(strings.LargeIx(),us.LargeIx(),guid.LargeIx(),blob.LargeIx());
  251. tildeTide = TildeHeaderSize;
  252. tildeTide += 4 * numTables;
  253. //Console.WriteLine("Tilde header + sizes = " + tildeTide);
  254. for (int i = 0; i < NumMetaDataTables; i++)
  255. {
  256. if (tables[i] != null)
  257. {
  258. ArrayList table = tables[i];
  259. // Console.WriteLine("Meta data table " + i + " at offset " + tildeTide);
  260. tildeTide += (uint)table.Count * elemSize[i];
  261. // Console.WriteLine("Metadata table " + i + " has size " + table.Count);
  262. // Console.WriteLine("tildeTide = " + tildeTide);
  263. }
  264. }
  265. if ((tildeTide % 4) != 0) tildePadding = 4 - (tildeTide % 4);
  266. //Console.WriteLine("tildePadding = " + tildePadding);
  267. }
  268. internal void WriteTildeStream(PEWriter output)
  269. {
  270. long startTilde = output.Seek(0, SeekOrigin.Current);
  271. //Console.WriteLine("Starting tilde output at offset " + Hex.Long(startTilde));
  272. output.Write((uint)0); // Reserved
  273. output.Write(output.verInfo.tsMajVer); // MajorVersion
  274. output.Write(output.verInfo.tsMinVer); // MinorVersion
  275. output.Write(heapSizes);
  276. output.Write((byte)1); // Reserved
  277. output.Write(valid);
  278. output.Write(sorted);
  279. for (int i = 0; i < NumMetaDataTables; i++)
  280. {
  281. if (tables[i] != null)
  282. {
  283. uint count = (uint)tables[i].Count;
  284. output.Write(count);
  285. }
  286. }
  287. long tabStart = output.Seek(0, SeekOrigin.Current);
  288. //Console.WriteLine("Starting metaData tables at " + tabStart);
  289. for (int i = 0; i < NumMetaDataTables; i++)
  290. {
  291. if (tables[i] != null)
  292. {
  293. //Console.WriteLine("Starting metaData table " + i + " at " + (output.Seek(0,SeekOrigin.Current) - startTilde));
  294. ArrayList table = tables[i];
  295. for (int j = 0; j < table.Count; j++)
  296. {
  297. ((MetaDataElement)table[j]).Write(output);
  298. }
  299. }
  300. }
  301. // reset the typespec flags
  302. if (tables[(int)MDTable.TypeSpec] != null)
  303. {
  304. ArrayList typeSpecTable = tables[(int)MDTable.TypeSpec];
  305. for (int i = 0; i < typeSpecTable.Count; i++)
  306. {
  307. ((Type)typeSpecTable[i]).typeSpecAdded = false;
  308. }
  309. }
  310. //Console.WriteLine("Writing padding at " + output.Seek(0,SeekOrigin.Current));
  311. for (int i = 0; i < tildePadding; i++) output.Write((byte)0);
  312. }
  313. private void SortTable(ArrayList mTable)
  314. {
  315. //Console.WriteLine("Sorting table");
  316. if (mTable == null) return;
  317. mTable.Sort();
  318. for (int i = 0; i < mTable.Count; i++)
  319. {
  320. ((MetaDataElement)mTable[i]).Row = (uint)i + 1;
  321. }
  322. }
  323. internal void BuildMDTables()
  324. {
  325. // Check ordering of specific tables
  326. // Constant, CustomAttribute, FieldMarshal, DeclSecurity, MethodSemantics
  327. // ImplMap, NestedClass, GenericParam
  328. // Need to load GenericParamConstraint AFTER GenericParam table in correct order
  329. // The tables:
  330. // InterfaceImpl, ClassLayout, FieldLayout, MethodImpl, FieldRVA
  331. // will _ALWAYS_ be in the correct order as embedded in BuildMDTables
  332. SortTable(tables[(int)MDTable.Constant]);
  333. SortTable(tables[(int)MDTable.CustomAttribute]);
  334. SortTable(tables[(int)MDTable.FieldMarshal]);
  335. SortTable(tables[(int)MDTable.DeclSecurity]);
  336. SortTable(tables[(int)MDTable.MethodSemantics]);
  337. SortTable(tables[(int)MDTable.ImplMap]);
  338. SortTable(tables[(int)MDTable.NestedClass]);
  339. if (tables[(int)MDTable.GenericParam] != null)
  340. {
  341. SortTable(tables[(int)MDTable.GenericParam]);
  342. // Now add GenericParamConstraints
  343. for (int i = 0; i < tables[(int)MDTable.GenericParam].Count; i++)
  344. {
  345. ((GenericParam)tables[(int)MDTable.GenericParam][i]).AddConstraints(this);
  346. }
  347. }
  348. /*
  349. // for bug in Whidbey GenericParam table ordering
  350. int end = tables[(int)MDTable.TypeDef].Count;
  351. int methEnd = 0;
  352. if (tables[(int)MDTable.Method] != null) {
  353. methEnd = tables[(int)MDTable.Method].Count;
  354. }
  355. for (int i=0; i < end; i++) {
  356. ((ClassDef)tables[(int)MDTable.TypeDef][i]).AddGenericsToTable(this);
  357. if (methEnd > i)
  358. ((MethodDef)tables[(int)MDTable.Method][i]).AddGenericsToTable(this);
  359. }
  360. for (int i=end; i < methEnd; i++) {
  361. ((MethodDef)tables[(int)MDTable.Method][i]).AddGenericsToTable(this);
  362. }
  363. // end of bug fix
  364. */
  365. for (int i = 0; i < tables.Length; i++)
  366. {
  367. if (tables[i] != null)
  368. {
  369. for (int j = 0; j < tables[i].Count; j++)
  370. {
  371. ((MetaDataElement)tables[i][j]).BuildSignatures(this);
  372. }
  373. }
  374. }
  375. }
  376. internal void SetIndexSizes()
  377. {
  378. for (int i = 0; i < NumMetaDataTables; i++)
  379. {
  380. if (tables[i] != null)
  381. {
  382. largeIx[i] = (uint)tables[i].Count > maxSmlIxSize;
  383. }
  384. }
  385. for (int i = 0; i < CIxTables.Length; i++)
  386. {
  387. for (int j = 0; j < CIxTables[i].Length; j++)
  388. {
  389. int tabIx = CIxTables[i][j];
  390. if (tables[tabIx] != null)
  391. {
  392. lgeCIx[i] = lgeCIx[i] | tables[tabIx].Count > CIxMaxMap[i];
  393. }
  394. }
  395. }
  396. }
  397. internal void BuildMetaData()
  398. {
  399. SetIndexSizes();
  400. for (int i = 1; i < numStreams; i++)
  401. {
  402. if (streams[i].Size() <= 1)
  403. {
  404. //Console.WriteLine("Stream " + new String(streams[i].name) + " has size 0");
  405. for (int j = i + 1; j < numStreams; j++)
  406. {
  407. streams[i] = streams[j];
  408. }
  409. i--;
  410. numStreams--;
  411. }
  412. else
  413. streams[i].EndStream();
  414. }
  415. //Console.WriteLine("numStreams = " + numStreams);
  416. CalcTildeStreamSize();
  417. SetStreamOffsets();
  418. byteCodePadding = NumToAlign(codeSize, 4);
  419. if (entryPoint != null) output.SetEntryPoint(entryPoint.Token());
  420. }
  421. internal void WriteByteCodes(PEWriter output)
  422. {
  423. for (int i = 0; i < byteCodes.Count; i++)
  424. {
  425. ((CILInstructions)byteCodes[i]).Write(output);
  426. }
  427. for (int i = 0; i < byteCodePadding; i++)
  428. {
  429. output.Write((byte)0);
  430. }
  431. }
  432. internal void WriteResources(PEWriter output)
  433. {
  434. if (resources == null) return;
  435. for (int i = 0; i < resources.Count; i++)
  436. {
  437. byte[] resBytes = (byte[])resources[i];
  438. output.Write((uint)resBytes.Length);
  439. output.Write(resBytes);
  440. }
  441. }
  442. internal void WriteMetaData(PEWriter output)
  443. {
  444. this.output = output;
  445. if (Diag.DiagOn)
  446. {
  447. mdStart = output.Seek(0, SeekOrigin.Current);
  448. Console.WriteLine("Writing metaData at " + Hex.Long(mdStart));
  449. }
  450. output.Write(MetaDataSignature);
  451. output.Write(output.verInfo.mdMajVer);
  452. output.Write(output.verInfo.mdMinVer);
  453. output.Write(0); // Reserved
  454. output.Write(output.verInfo.netVerString.Length);
  455. output.Write(output.verInfo.netVerString.ToCharArray()); // version string is already zero padded
  456. output.Write((short)0); // Flags, reserved
  457. output.Write((ushort)numStreams);
  458. // write tilde header
  459. output.Write(tildeStart);
  460. output.Write(tildeTide + tildePadding);
  461. output.Write(tildeNameArray);
  462. for (int i = 1; i < numStreams; i++)
  463. {
  464. if (Diag.DiagOn)
  465. Console.WriteLine("Stream " + new String(streams[i].name) + " should start at " + Hex.Long(streams[i].Start + mdStart));
  466. streams[i].WriteHeader(output);
  467. }
  468. if (Diag.DiagOn)
  469. {
  470. Console.Write("Writing tilde stream at " + Hex.Long(output.Seek(0, SeekOrigin.Current)));
  471. Console.WriteLine(" should be at " + Hex.Long(tildeStart + mdStart));
  472. }
  473. WriteTildeStream(output);
  474. for (int i = 1; i < numStreams; i++)
  475. {
  476. if (Diag.DiagOn)
  477. Console.WriteLine("Writing stream " + new String(streams[i].name) + " at " + Hex.Long(output.Seek(0, SeekOrigin.Current)));
  478. streams[i].Write(output);
  479. }
  480. //Console.WriteLine("Finished Writing metaData at " + output.Seek(0,SeekOrigin.Current));
  481. }
  482. // internal bool LargeStringsIndex() { return strings.LargeIx(); }
  483. // internal bool LargeGUIDIndex() { return guid.LargeIx(); }
  484. // internal bool LargeUSIndex() { return us.LargeIx(); }
  485. // internal bool LargeBlobIndex() { return blob.LargeIx(); }
  486. private uint NumToAlign(uint val, uint alignVal)
  487. {
  488. if ((val % alignVal) == 0) return 0;
  489. return alignVal - (val % alignVal);
  490. }
  491. internal void WriteCodedIndex(CIx code, MetaDataElement elem, PEWriter output)
  492. {
  493. uint ix = 0;
  494. if (elem != null)
  495. {
  496. ix = (elem.Row << CIxShiftMap[(uint)code]) | elem.GetCodedIx(code);
  497. // Console.WriteLine("coded index = " + ix + " row = " + elem.Row);
  498. //} else {
  499. // Console.WriteLine("elem for coded index is null");
  500. }
  501. if (lgeCIx[(uint)code])
  502. output.Write(ix);
  503. else
  504. output.Write((ushort)ix);
  505. }
  506. }
  507. }