elf.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. "use strict";
  2. // A minimal elf parser for loading 32 bit, x86, little endian, executable elf files
  3. const ELF_MAGIC = 0x464C457F;
  4. let types = DataView.prototype;
  5. let U8 = { size: 1, get: types.getUint8, set: types.setUint8, };
  6. let U16 = { size: 2, get: types.getUint16, set: types.setUint16, };
  7. let U32 = { size: 4, get: types.getUint32, set: types.setUint32, };
  8. let pad = function(size)
  9. {
  10. return {
  11. size,
  12. get: offset => -1,
  13. };
  14. };
  15. let Header = create_struct([
  16. { magic: U32, },
  17. { class: U8, },
  18. { data: U8, },
  19. { version0: U8, },
  20. { osabi: U8, },
  21. { abiversion: U8, },
  22. { pad0: pad(7) },
  23. { type: U16, },
  24. { machine: U16, },
  25. { version1: U32, },
  26. { entry: U32, },
  27. { phoff: U32, },
  28. { shoff: U32, },
  29. { flags: U32, },
  30. { ehsize: U16, },
  31. { phentsize: U16, },
  32. { phnum: U16, },
  33. { shentsize: U16, },
  34. { shnum: U16, },
  35. { shstrndx: U16, },
  36. ]);
  37. console.assert(Header.reduce((a, entry) => a + entry.size, 0) === 52);
  38. let ProgramHeader = create_struct([
  39. { type: U32, },
  40. { offset: U32, },
  41. { vaddr: U32, },
  42. { paddr: U32, },
  43. { filesz: U32, },
  44. { memsz: U32, },
  45. { flags: U32, },
  46. { align: U32, },
  47. ]);
  48. console.assert(ProgramHeader.reduce((a, entry) => a + entry.size, 0) === 32);
  49. let SectionHeader = create_struct([
  50. { name: U32, },
  51. { type: U32, },
  52. { flags: U32, },
  53. { addr: U32, },
  54. { offset: U32, },
  55. { size: U32, },
  56. { link: U32, },
  57. { info: U32, },
  58. { addralign: U32, },
  59. { entsize: U32, },
  60. ]);
  61. console.assert(SectionHeader.reduce((a, entry) => a + entry.size, 0) === 40);
  62. // From [{ name: type }, ...] to [{ name, type, size, get, set }, ...]
  63. function create_struct(struct)
  64. {
  65. return struct.map(function(entry)
  66. {
  67. let keys = Object.keys(entry);
  68. console.assert(keys.length === 1);
  69. let name = keys[0];
  70. let type = entry[name];
  71. console.assert(type.size > 0);
  72. return {
  73. name,
  74. type,
  75. size: type.size,
  76. get: type.get,
  77. set: type.set,
  78. };
  79. });
  80. }
  81. /** @param {ArrayBuffer} buffer */
  82. function read_elf(buffer)
  83. {
  84. let view = new DataView(buffer);
  85. let [header, offset] = read_struct(view, Header);
  86. console.assert(offset === 52);
  87. if(DEBUG)
  88. {
  89. for(let key of Object.keys(header))
  90. {
  91. dbg_log(key + ": 0x" + header[key].toString(16));
  92. }
  93. dbg_log(header);
  94. }
  95. console.assert(header.magic === ELF_MAGIC, "Bad magic");
  96. console.assert(header.class === 1, "Unimplemented: 64 bit elf");
  97. console.assert(header.data === 1, "Unimplemented: big endian");
  98. console.assert(header.version0 === 1, "Bad version0");
  99. // 1, 2, 3, 4 specify whether the object is relocatable, executable,
  100. // shared, or core, respectively.
  101. console.assert(header.type === 2, "Unimplemented type");
  102. console.assert(header.version1 === 1, "Bad version1");
  103. // these are different in 64 bit
  104. console.assert(header.ehsize === 52, "Bad header size");
  105. console.assert(header.phentsize === 32, "Bad program header size");
  106. console.assert(header.shentsize === 40, "Bad section header size");
  107. let [program_headers, ph_offset] = read_structs(
  108. view_slice(view, header.phoff, header.phentsize * header.phnum),
  109. ProgramHeader,
  110. header.phnum);
  111. let [sections_headers, sh_offset] = read_structs(
  112. view_slice(view, header.shoff, header.shentsize * header.shnum),
  113. SectionHeader,
  114. header.shnum);
  115. if(DEBUG && LOG_LEVEL)
  116. {
  117. console.log("%d program headers:", program_headers.length);
  118. for(let program of program_headers)
  119. {
  120. console.log(
  121. "type=%s offset=%s vaddr=%s paddr=%s " +
  122. "filesz=%s memsz=%s flags=%s align=%s",
  123. program.type.toString(16),
  124. program.offset.toString(16),
  125. program.vaddr.toString(16),
  126. program.paddr.toString(16),
  127. program.filesz.toString(16),
  128. program.memsz.toString(16),
  129. program.flags.toString(16),
  130. program.align.toString(16)
  131. );
  132. }
  133. console.log("%d program headers:", sections_headers.length);
  134. for(let section of sections_headers)
  135. {
  136. console.log(
  137. "name=%s type=%s flags=%s addr=%s offset=%s " +
  138. "size=%s link=%s info=%s addralign=%s entsize=%s",
  139. section.name.toString(16),
  140. section.type.toString(16),
  141. section.flags.toString(16),
  142. section.addr.toString(16),
  143. section.offset.toString(16),
  144. section.size.toString(16),
  145. section.link.toString(16),
  146. section.info.toString(16),
  147. section.addralign.toString(16),
  148. section.entsize.toString(16)
  149. );
  150. }
  151. }
  152. return {
  153. header,
  154. program_headers,
  155. sections_headers,
  156. };
  157. }
  158. function read_struct(view, Struct)
  159. {
  160. let result = {};
  161. let offset = 0;
  162. const LITTLE_ENDIAN = true; // big endian not supported yet
  163. for(let entry of Struct)
  164. {
  165. let value = entry.get.call(view, offset, LITTLE_ENDIAN);
  166. console.assert(result[entry.name] === undefined);
  167. result[entry.name] = value;
  168. offset += entry.size;
  169. }
  170. return [result, offset];
  171. }
  172. function read_structs(view, Struct, count)
  173. {
  174. let result = [];
  175. let offset = 0;
  176. for(var i = 0; i < count; i++)
  177. {
  178. let [s, size] = read_struct(view_slice(view, offset), Struct);
  179. result.push(s);
  180. offset += size;
  181. }
  182. return [result, offset];
  183. }
  184. /** @param {number=} length */
  185. function view_slice(view, offset, length)
  186. {
  187. return new DataView(view.buffer, view.byteOffset + offset, length);
  188. }