elf.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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) >>> 0));
  92. }
  93. }
  94. console.assert(header.magic === ELF_MAGIC, "Bad magic");
  95. console.assert(header.class === 1, "Unimplemented: 64 bit elf");
  96. console.assert(header.data === 1, "Unimplemented: big endian");
  97. console.assert(header.version0 === 1, "Bad version0");
  98. // 1, 2, 3, 4 specify whether the object is relocatable, executable,
  99. // shared, or core, respectively.
  100. console.assert(header.type === 2, "Unimplemented type");
  101. console.assert(header.version1 === 1, "Bad version1");
  102. // these are different in 64 bit
  103. console.assert(header.ehsize === 52, "Bad header size");
  104. console.assert(header.phentsize === 32, "Bad program header size");
  105. console.assert(header.shentsize === 40, "Bad section header size");
  106. let [program_headers, ph_offset] = read_structs(
  107. view_slice(view, header.phoff, header.phentsize * header.phnum),
  108. ProgramHeader,
  109. header.phnum);
  110. let [sections_headers, sh_offset] = read_structs(
  111. view_slice(view, header.shoff, header.shentsize * header.shnum),
  112. SectionHeader,
  113. header.shnum);
  114. if(DEBUG && LOG_LEVEL)
  115. {
  116. console.log("%d program headers:", program_headers.length);
  117. for(let program of program_headers)
  118. {
  119. console.log(
  120. "type=%s offset=%s vaddr=%s paddr=%s " +
  121. "filesz=%s memsz=%s flags=%s align=%s",
  122. program.type.toString(16),
  123. program.offset.toString(16),
  124. program.vaddr.toString(16),
  125. program.paddr.toString(16),
  126. program.filesz.toString(16),
  127. program.memsz.toString(16),
  128. program.flags.toString(16),
  129. program.align.toString(16)
  130. );
  131. }
  132. console.log("%d section headers:", sections_headers.length);
  133. for(let section of sections_headers)
  134. {
  135. console.log(
  136. "name=%s type=%s flags=%s addr=%s offset=%s " +
  137. "size=%s link=%s info=%s addralign=%s entsize=%s",
  138. section.name.toString(16),
  139. section.type.toString(16),
  140. section.flags.toString(16),
  141. section.addr.toString(16),
  142. section.offset.toString(16),
  143. section.size.toString(16),
  144. section.link.toString(16),
  145. section.info.toString(16),
  146. section.addralign.toString(16),
  147. section.entsize.toString(16)
  148. );
  149. }
  150. }
  151. return {
  152. header,
  153. program_headers,
  154. sections_headers,
  155. };
  156. }
  157. function read_struct(view, Struct)
  158. {
  159. let result = {};
  160. let offset = 0;
  161. const LITTLE_ENDIAN = true; // big endian not supported yet
  162. for(let entry of Struct)
  163. {
  164. let value = entry.get.call(view, offset, LITTLE_ENDIAN);
  165. console.assert(result[entry.name] === undefined);
  166. result[entry.name] = value;
  167. offset += entry.size;
  168. }
  169. return [result, offset];
  170. }
  171. function read_structs(view, Struct, count)
  172. {
  173. let result = [];
  174. let offset = 0;
  175. for(var i = 0; i < count; i++)
  176. {
  177. let [s, size] = read_struct(view_slice(view, offset), Struct);
  178. result.push(s);
  179. offset += size;
  180. }
  181. return [result, offset];
  182. }
  183. /** @param {number=} length */
  184. function view_slice(view, offset, length)
  185. {
  186. return new DataView(view.buffer, view.byteOffset + offset, length);
  187. }