module_init.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. use leb::{write_fixed_leb16_at_idx, write_fixed_leb32_at_idx, write_leb_u32};
  2. use util::{SafeToU16, SafeToU8};
  3. use wasmgen::wasm_opcodes as op;
  4. #[allow(dead_code)]
  5. pub const FN0_TYPE_INDEX: u8 = 0;
  6. #[allow(dead_code)]
  7. pub const FN1_TYPE_INDEX: u8 = 1;
  8. #[allow(dead_code)]
  9. pub const FN2_TYPE_INDEX: u8 = 2;
  10. #[allow(dead_code)]
  11. pub const FN3_TYPE_INDEX: u8 = 3;
  12. #[allow(dead_code)]
  13. pub const FN0_RET_TYPE_INDEX: u8 = 4;
  14. #[allow(dead_code)]
  15. pub const FN1_RET_TYPE_INDEX: u8 = 5;
  16. #[allow(dead_code)]
  17. pub const FN2_RET_TYPE_INDEX: u8 = 6;
  18. pub const NR_FN_TYPE_INDEXES: u8 = 7;
  19. pub struct WasmBuilder {
  20. pub output: Vec<u8>,
  21. pub code_section: Vec<u8>,
  22. pub instruction_body: Vec<u8>,
  23. idx_import_table_size: usize, // for rewriting once finished
  24. idx_import_count: usize, // for rewriting once finished
  25. idx_import_entries: usize, // for searching the imports
  26. import_table_size: usize, // the current import table size (to avoid reading 2 byte leb)
  27. import_count: u16, // same as above
  28. initial_static_size: usize, // size of module after initialization, rest is drained on reset
  29. }
  30. impl WasmBuilder {
  31. pub fn new() -> Self {
  32. WasmBuilder {
  33. output: Vec::with_capacity(256),
  34. code_section: Vec::with_capacity(256),
  35. instruction_body: Vec::with_capacity(256),
  36. idx_import_table_size: 0,
  37. idx_import_count: 0,
  38. idx_import_entries: 0,
  39. import_table_size: 2,
  40. import_count: 0,
  41. initial_static_size: 0,
  42. }
  43. }
  44. pub fn init(&mut self) {
  45. self.output.extend("\0asm".as_bytes());
  46. // wasm version in leb128, 4 bytes
  47. self.output.push(op::WASM_VERSION);
  48. self.output.push(0);
  49. self.output.push(0);
  50. self.output.push(0);
  51. self.write_type_section();
  52. self.write_import_section_preamble();
  53. // store state of current pointers etc. so we can reset them later
  54. self.initial_static_size = self.output.len();
  55. }
  56. pub fn reset(&mut self) {
  57. self.output.drain(self.initial_static_size..);
  58. self.set_import_table_size(2);
  59. self.set_import_count(0);
  60. self.code_section.clear();
  61. self.instruction_body.clear();
  62. }
  63. pub fn finish(&mut self, no_of_locals_i32: u8) -> usize {
  64. self.write_memory_import();
  65. self.write_function_section(1);
  66. self.write_export_section();
  67. // write code section preamble
  68. self.output.push(op::SC_CODE);
  69. let idx_code_section_size = self.output.len(); // we will write to this location later
  70. self.output.push(0);
  71. self.output.push(0); // write temp val for now using 4 bytes
  72. self.output.push(0);
  73. self.output.push(0);
  74. self.output.push(1); // number of function bodies: just 1
  75. // same as above but for body size of the function
  76. let idx_fn_body_size = self.output.len();
  77. self.output.push(0);
  78. self.output.push(0);
  79. self.output.push(0);
  80. self.output.push(0);
  81. self.output.push(1); // count of local blocks
  82. dbg_assert!(no_of_locals_i32 < 128);
  83. self.output.push(no_of_locals_i32);
  84. self.output.push(op::TYPE_I32);
  85. self.output.append(&mut self.code_section);
  86. self.output.push(op::OP_END);
  87. // write the actual sizes to the pointer locations stored above. We subtract 4 from the actual
  88. // value because the ptr itself points to four bytes
  89. let fn_body_size = (self.output.len() - idx_fn_body_size - 4) as u32;
  90. write_fixed_leb32_at_idx(&mut self.output, idx_fn_body_size, fn_body_size);
  91. let code_section_size = (self.output.len() - idx_code_section_size - 4) as u32;
  92. write_fixed_leb32_at_idx(&mut self.output, idx_code_section_size, code_section_size);
  93. self.output.len()
  94. }
  95. pub fn write_type_section(&mut self) {
  96. self.output.push(op::SC_TYPE);
  97. let idx_section_size = self.output.len();
  98. self.output.push(0);
  99. self.output.push(NR_FN_TYPE_INDEXES); // number of type descriptors
  100. // FN0
  101. self.output.push(op::TYPE_FUNC);
  102. self.output.push(0); // no args
  103. self.output.push(0); // no return val
  104. // FN1
  105. self.output.push(op::TYPE_FUNC);
  106. self.output.push(1);
  107. self.output.push(op::TYPE_I32);
  108. self.output.push(0);
  109. // FN2
  110. self.output.push(op::TYPE_FUNC);
  111. self.output.push(2);
  112. self.output.push(op::TYPE_I32);
  113. self.output.push(op::TYPE_I32);
  114. self.output.push(0);
  115. // FN3
  116. self.output.push(op::TYPE_FUNC);
  117. self.output.push(3);
  118. self.output.push(op::TYPE_I32);
  119. self.output.push(op::TYPE_I32);
  120. self.output.push(op::TYPE_I32);
  121. self.output.push(0);
  122. // FN0_RET
  123. self.output.push(op::TYPE_FUNC);
  124. self.output.push(0);
  125. self.output.push(1);
  126. self.output.push(op::TYPE_I32);
  127. // FN1_RET
  128. self.output.push(op::TYPE_FUNC);
  129. self.output.push(1);
  130. self.output.push(op::TYPE_I32);
  131. self.output.push(1);
  132. self.output.push(op::TYPE_I32);
  133. // FN2_RET
  134. self.output.push(op::TYPE_FUNC);
  135. self.output.push(2);
  136. self.output.push(op::TYPE_I32);
  137. self.output.push(op::TYPE_I32);
  138. self.output.push(1);
  139. self.output.push(op::TYPE_I32);
  140. let new_len = self.output.len();
  141. let size = (new_len - 1) - idx_section_size;
  142. self.output[idx_section_size] = size.safe_to_u8();
  143. }
  144. /// Goes over the import block to find index of an import entry by function name
  145. pub fn get_import_index(&self, fn_name: &str) -> Option<u16> {
  146. let mut offset = self.idx_import_entries;
  147. for i in 0..self.import_count {
  148. offset += 1; // skip length of module name
  149. offset += 1; // skip module name itself
  150. let len = self.output[offset] as usize;
  151. offset += 1;
  152. let name = self
  153. .output
  154. .get(offset..(offset + len))
  155. .expect("get function name");
  156. if name == fn_name.as_bytes() {
  157. return Some(i);
  158. }
  159. offset += len; // skip the string
  160. offset += 1; // skip import kind
  161. offset += 1; // skip type index
  162. }
  163. None
  164. }
  165. pub fn set_import_count(&mut self, count: u16) {
  166. dbg_assert!(count < 0x4000);
  167. self.import_count = count;
  168. let idx_import_count = self.idx_import_count;
  169. write_fixed_leb16_at_idx(&mut self.output, idx_import_count, count);
  170. }
  171. pub fn set_import_table_size(&mut self, size: usize) {
  172. dbg_assert!(size < 0x4000);
  173. self.import_table_size = size;
  174. let idx_import_table_size = self.idx_import_table_size;
  175. write_fixed_leb16_at_idx(&mut self.output, idx_import_table_size, size.safe_to_u16());
  176. }
  177. pub fn write_import_section_preamble(&mut self) {
  178. self.output.push(op::SC_IMPORT);
  179. self.idx_import_table_size = self.output.len();
  180. self.output.push(1 | 0b10000000);
  181. self.output.push(2); // 2 in 2 byte leb
  182. self.idx_import_count = self.output.len();
  183. self.output.push(1 | 0b10000000);
  184. self.output.push(0); // 0 in 2 byte leb
  185. // here after starts the actual list of imports
  186. self.idx_import_entries = self.output.len();
  187. }
  188. pub fn write_memory_import(&mut self) {
  189. self.output.push(1);
  190. self.output.push('e' as u8);
  191. self.output.push(1);
  192. self.output.push('m' as u8);
  193. self.output.push(op::EXT_MEMORY);
  194. self.output.push(0); // memory flag, 0 for no maximum memory limit present
  195. write_leb_u32(&mut self.output, 256); // initial memory length of 256 pages, takes 2 bytes in leb128
  196. let new_import_count = self.import_count + 1;
  197. self.set_import_count(new_import_count);
  198. let new_table_size = self.import_table_size + 8;
  199. self.set_import_table_size(new_table_size);
  200. }
  201. pub fn write_import_entry(&mut self, fn_name: &str, type_index: u8) -> u16 {
  202. self.output.push(1); // length of module name
  203. self.output.push('e' as u8); // module name
  204. self.output.push(fn_name.len().safe_to_u8());
  205. self.output.extend(fn_name.as_bytes());
  206. self.output.push(op::EXT_FUNCTION);
  207. self.output.push(type_index);
  208. let new_import_count = self.import_count + 1;
  209. self.set_import_count(new_import_count);
  210. let new_table_size = self.import_table_size + 1 + 1 + 1 + fn_name.len() + 1 + 1;
  211. self.set_import_table_size(new_table_size);
  212. self.import_count - 1
  213. }
  214. pub fn write_function_section(&mut self, count: u8) {
  215. self.output.push(op::SC_FUNCTION);
  216. self.output.push(1 + count); // length of this section
  217. self.output.push(count); // count of signature indices
  218. for _ in 0..count {
  219. self.output.push(FN1_TYPE_INDEX);
  220. }
  221. }
  222. pub fn write_export_section(&mut self) {
  223. self.output.push(op::SC_EXPORT);
  224. self.output.push(1 + 1 + 1 + 1 + 2); // size of this section
  225. self.output.push(1); // count of table: just one function exported
  226. self.output.push(1); // length of exported function name
  227. self.output.push('f' as u8); // function name
  228. self.output.push(op::EXT_FUNCTION);
  229. // index of the exported function
  230. // function space starts with imports. index of last import is import count - 1
  231. // the last import however is a memory, so we subtract one from that
  232. let next_op_idx = self.output.len();
  233. self.output.push(0);
  234. self.output.push(0); // add 2 bytes for writing 16 byte val
  235. write_fixed_leb16_at_idx(&mut self.output, next_op_idx, self.import_count - 1);
  236. }
  237. pub fn get_fn_idx(&mut self, fn_name: &str, type_index: u8) -> u16 {
  238. match self.get_import_index(fn_name) {
  239. Some(idx) => idx,
  240. None => {
  241. let idx = self.write_import_entry(fn_name, type_index);
  242. idx
  243. },
  244. }
  245. }
  246. pub fn get_op_ptr(&self) -> *const u8 { self.output.as_ptr() }
  247. pub fn get_op_len(&self) -> u32 { self.output.len() as u32 }
  248. pub fn commit_instruction_body_to_cs(&mut self) {
  249. self.code_section.append(&mut self.instruction_body);
  250. }
  251. }
  252. #[cfg(test)]
  253. mod tests {
  254. use std::fs::File;
  255. use std::io::prelude::*;
  256. use wasmgen::module_init::*;
  257. use wasmgen::wasm_util::*;
  258. #[test]
  259. fn import_table_management() {
  260. let mut w = WasmBuilder::new();
  261. w.init();
  262. assert_eq!(0, w.get_fn_idx("foo", FN0_TYPE_INDEX));
  263. assert_eq!(1, w.get_fn_idx("bar", FN1_TYPE_INDEX));
  264. assert_eq!(0, w.get_fn_idx("foo", FN0_TYPE_INDEX));
  265. assert_eq!(2, w.get_fn_idx("baz", FN2_TYPE_INDEX));
  266. }
  267. #[test]
  268. fn builder_test() {
  269. let mut m = WasmBuilder::new();
  270. m.init();
  271. let mut foo_index = m.get_fn_idx("foo", FN0_TYPE_INDEX);
  272. call_fn(&mut m.code_section, foo_index);
  273. let bar_index = m.get_fn_idx("bar", FN0_TYPE_INDEX);
  274. call_fn(&mut m.code_section, bar_index);
  275. m.finish(2);
  276. m.reset();
  277. push_i32(&mut m.code_section, 2);
  278. let baz_index = m.get_fn_idx("baz", FN1_RET_TYPE_INDEX);
  279. call_fn(&mut m.instruction_body, baz_index);
  280. foo_index = m.get_fn_idx("foo", FN1_TYPE_INDEX);
  281. call_fn(&mut m.instruction_body, foo_index);
  282. m.commit_instruction_body_to_cs();
  283. m.finish(0);
  284. let op_ptr = m.get_op_ptr();
  285. let op_len = m.get_op_len();
  286. dbg_log!("op_ptr: {:?}, op_len: {:?}", op_ptr, op_len);
  287. let mut f = File::create("build/dummy_output.wasm").expect("creating dummy_output.wasm");
  288. f.write_all(&m.output).expect("write dummy_output.wasm");
  289. }
  290. }