MetaDataOut.cs 20 KB

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