ogwasm.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. {
  2. Copyright (c) 2021 by Nikolay Nikolov
  3. Contains the WebAssembly binary module format reader and writer
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit ogwasm;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. { common }
  22. cclasses,globtype,
  23. { target }
  24. systems,
  25. { assembler }
  26. aasmbase,assemble,
  27. { WebAssembly module format definitions }
  28. wasmbase,
  29. { output }
  30. ogbase,
  31. owbase;
  32. type
  33. { TWasmObjSection }
  34. TWasmObjSection = class(TObjSection)
  35. public
  36. SegIdx: Integer;
  37. SegOfs: qword;
  38. function IsCode: Boolean;
  39. function IsData: Boolean;
  40. end;
  41. { TWasmObjData }
  42. TWasmObjData = class(TObjData)
  43. private
  44. function is_smart_section(atype:TAsmSectiontype):boolean;
  45. function sectionname_gas(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string;
  46. public
  47. constructor create(const n:string);override;
  48. function sectionname(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string;override;
  49. procedure writeReloc(Data:TRelocDataInt;len:aword;p:TObjSymbol;Reloctype:TObjRelocationType);override;
  50. end;
  51. { TWasmObjOutput }
  52. TWasmObjOutput = class(tObjOutput)
  53. private
  54. FWasmSections: array [TWasmSectionID] of tdynamicarray;
  55. procedure WriteUleb(d: tdynamicarray; v: uint64);
  56. procedure WriteUleb(w: TObjectWriter; v: uint64);
  57. procedure WriteSleb(d: tdynamicarray; v: int64);
  58. procedure WriteByte(d: tdynamicarray; b: byte);
  59. procedure WriteName(d: tdynamicarray; const s: string);
  60. procedure WriteWasmSection(wsid: TWasmSectionID);
  61. procedure CopyDynamicArray(src, dest: tdynamicarray; size: QWord);
  62. procedure WriteZeros(dest: tdynamicarray; size: QWord);
  63. protected
  64. function writeData(Data:TObjData):boolean;override;
  65. public
  66. constructor create(AWriter:TObjectWriter);override;
  67. destructor destroy;override;
  68. end;
  69. { TWasmAssembler }
  70. TWasmAssembler = class(tinternalassembler)
  71. constructor create(info: pasminfo; smart:boolean);override;
  72. end;
  73. implementation
  74. {****************************************************************************
  75. TWasmObjSection
  76. ****************************************************************************}
  77. function TWasmObjSection.IsCode: Boolean;
  78. const
  79. CodePrefix = '.text';
  80. begin
  81. result:=(Length(Name)>=Length(CodePrefix)) and
  82. (Copy(Name,1,Length(CodePrefix))=CodePrefix);
  83. end;
  84. function TWasmObjSection.IsData: Boolean;
  85. begin
  86. result:=not IsCode;
  87. end;
  88. {****************************************************************************
  89. TWasmObjData
  90. ****************************************************************************}
  91. function TWasmObjData.is_smart_section(atype: TAsmSectiontype): boolean;
  92. begin
  93. { For bss we need to set some flags that are target dependent,
  94. it is easier to disable it for smartlinking. It doesn't take up
  95. filespace }
  96. result:=not(target_info.system in systems_darwin) and
  97. create_smartlink_sections and
  98. (atype<>sec_toc) and
  99. (atype<>sec_user) and
  100. { on embedded systems every byte counts, so smartlink bss too }
  101. ((atype<>sec_bss) or (target_info.system in (systems_embedded+systems_freertos)));
  102. end;
  103. function TWasmObjData.sectionname_gas(atype: TAsmSectiontype;
  104. const aname: string; aorder: TAsmSectionOrder): string;
  105. const
  106. secnames : array[TAsmSectiontype] of string[length('__DATA, __datacoal_nt,coalesced')] = ('','',
  107. '.text',
  108. '.data',
  109. { why doesn't .rodata work? (FK) }
  110. { sometimes we have to create a data.rel.ro instead of .rodata, e.g. for }
  111. { vtables (and anything else containing relocations), otherwise those are }
  112. { not relocated properly on e.g. linux/ppc64. g++ generates there for a }
  113. { vtable for a class called Window: }
  114. { .section .data.rel.ro._ZTV6Window,"awG",@progbits,_ZTV6Window,comdat }
  115. { TODO: .data.ro not yet working}
  116. {$if defined(arm) or defined(riscv64) or defined(powerpc)}
  117. '.rodata',
  118. {$else defined(arm) or defined(riscv64) or defined(powerpc)}
  119. '.data',
  120. {$endif defined(arm) or defined(riscv64) or defined(powerpc)}
  121. '.rodata',
  122. '.bss',
  123. '.threadvar',
  124. '.pdata',
  125. '', { stubs }
  126. '__DATA,__nl_symbol_ptr',
  127. '__DATA,__la_symbol_ptr',
  128. '__DATA,__mod_init_func',
  129. '__DATA,__mod_term_func',
  130. '.stab',
  131. '.stabstr',
  132. '.idata$2','.idata$4','.idata$5','.idata$6','.idata$7','.edata',
  133. '.eh_frame',
  134. '.debug_frame','.debug_info','.debug_line','.debug_abbrev','.debug_aranges','.debug_ranges',
  135. '.fpc',
  136. '.toc',
  137. '.init',
  138. '.fini',
  139. '.objc_class',
  140. '.objc_meta_class',
  141. '.objc_cat_cls_meth',
  142. '.objc_cat_inst_meth',
  143. '.objc_protocol',
  144. '.objc_string_object',
  145. '.objc_cls_meth',
  146. '.objc_inst_meth',
  147. '.objc_cls_refs',
  148. '.objc_message_refs',
  149. '.objc_symbols',
  150. '.objc_category',
  151. '.objc_class_vars',
  152. '.objc_instance_vars',
  153. '.objc_module_info',
  154. '.objc_class_names',
  155. '.objc_meth_var_types',
  156. '.objc_meth_var_names',
  157. '.objc_selector_strs',
  158. '.objc_protocol_ext',
  159. '.objc_class_ext',
  160. '.objc_property',
  161. '.objc_image_info',
  162. '.objc_cstring_object',
  163. '.objc_sel_fixup',
  164. '__DATA,__objc_data',
  165. '__DATA,__objc_const',
  166. '.objc_superrefs',
  167. '__DATA, __datacoal_nt,coalesced',
  168. '.objc_classlist',
  169. '.objc_nlclasslist',
  170. '.objc_catlist',
  171. '.obcj_nlcatlist',
  172. '.objc_protolist',
  173. '.stack',
  174. '.heap',
  175. '.gcc_except_table',
  176. '.ARM.attributes'
  177. );
  178. var
  179. sep : string[3];
  180. secname : string;
  181. begin
  182. secname:=secnames[atype];
  183. if (atype=sec_fpc) and (Copy(aname,1,3)='res') then
  184. begin
  185. result:=secname+'.'+aname;
  186. exit;
  187. end;
  188. if atype=sec_threadvar then
  189. begin
  190. if (target_info.system in (systems_windows+systems_wince)) then
  191. secname:='.tls'
  192. else if (target_info.system in systems_linux) then
  193. secname:='.tbss';
  194. end;
  195. { go32v2 stub only loads .text and .data sections, and allocates space for .bss.
  196. Thus, data which normally goes into .rodata and .rodata_norel sections must
  197. end up in .data section }
  198. if (atype in [sec_rodata,sec_rodata_norel]) and
  199. (target_info.system in [system_i386_go32v2,system_m68k_palmos]) then
  200. secname:='.data';
  201. { Windows correctly handles reallocations in readonly sections }
  202. if (atype=sec_rodata) and
  203. (target_info.system in systems_all_windows+systems_nativent-[system_i8086_win16]) then
  204. secname:='.rodata';
  205. { section type user gives the user full controll on the section name }
  206. if atype=sec_user then
  207. secname:=aname;
  208. if is_smart_section(atype) and (aname<>'') then
  209. begin
  210. case aorder of
  211. secorder_begin :
  212. sep:='.b_';
  213. secorder_end :
  214. sep:='.z_';
  215. else
  216. sep:='.n_';
  217. end;
  218. result:=secname+sep+aname
  219. end
  220. else
  221. result:=secname;
  222. end;
  223. constructor TWasmObjData.create(const n: string);
  224. begin
  225. inherited;
  226. CObjSection:=TWasmObjSection;
  227. end;
  228. function TWasmObjData.sectionname(atype: TAsmSectiontype;
  229. const aname: string; aorder: TAsmSectionOrder): string;
  230. begin
  231. if (atype=sec_fpc) or (atype=sec_threadvar) then
  232. atype:=sec_data;
  233. Result:=sectionname_gas(atype, aname, aorder);
  234. end;
  235. procedure TWasmObjData.writeReloc(Data: TRelocDataInt; len: aword;
  236. p: TObjSymbol; Reloctype: TObjRelocationType);
  237. begin
  238. end;
  239. {****************************************************************************
  240. TWasmObjOutput
  241. ****************************************************************************}
  242. procedure TWasmObjOutput.WriteUleb(d: tdynamicarray; v: uint64);
  243. var
  244. b: byte;
  245. begin
  246. repeat
  247. b:=byte(v) and 127;
  248. v:=v shr 7;
  249. if v<>0 then
  250. b:=b or 128;
  251. d.write(b,1);
  252. until v=0;
  253. end;
  254. procedure TWasmObjOutput.WriteUleb(w: TObjectWriter; v: uint64);
  255. var
  256. b: byte;
  257. begin
  258. repeat
  259. b:=byte(v) and 127;
  260. v:=v shr 7;
  261. if v<>0 then
  262. b:=b or 128;
  263. w.write(b,1);
  264. until v=0;
  265. end;
  266. procedure TWasmObjOutput.WriteSleb(d: tdynamicarray; v: int64);
  267. var
  268. b: byte;
  269. Done: Boolean=false;
  270. begin
  271. repeat
  272. b:=byte(v) and 127;
  273. v:=SarInt64(v,7);
  274. if ((v=0) and ((b and 64)=0)) or ((v=-1) and ((b and 64)<>0)) then
  275. Done:=true
  276. else
  277. b:=b or 128;
  278. d.write(b,1);
  279. until Done;
  280. end;
  281. procedure TWasmObjOutput.WriteByte(d: tdynamicarray; b: byte);
  282. begin
  283. d.write(b,1);
  284. end;
  285. procedure TWasmObjOutput.WriteName(d: tdynamicarray; const s: string);
  286. begin
  287. WriteUleb(d,Length(s));
  288. d.writestr(s);
  289. end;
  290. procedure TWasmObjOutput.WriteWasmSection(wsid: TWasmSectionID);
  291. var
  292. b: byte;
  293. begin
  294. b:=ord(wsid);
  295. Writer.write(b,1);
  296. WriteUleb(Writer,FWasmSections[wsid].size);
  297. Writer.writearray(FWasmSections[wsid]);
  298. end;
  299. procedure TWasmObjOutput.CopyDynamicArray(src, dest: tdynamicarray; size: QWord);
  300. var
  301. buf: array [0..4095] of byte;
  302. bs: Integer;
  303. begin
  304. while size>0 do
  305. begin
  306. if size<SizeOf(buf) then
  307. bs:=Integer(size)
  308. else
  309. bs:=SizeOf(buf);
  310. src.read(buf,bs);
  311. dest.write(buf,bs);
  312. dec(size,bs);
  313. end;
  314. end;
  315. procedure TWasmObjOutput.WriteZeros(dest: tdynamicarray; size: QWord);
  316. var
  317. buf : array[0..1023] of byte;
  318. bs: Integer;
  319. begin
  320. fillchar(buf,sizeof(buf),0);
  321. while size>0 do
  322. begin
  323. if size<SizeOf(buf) then
  324. bs:=Integer(size)
  325. else
  326. bs:=SizeOf(buf);
  327. dest.write(buf,bs);
  328. dec(size,bs);
  329. end;
  330. end;
  331. function TWasmObjOutput.writeData(Data:TObjData):boolean;
  332. var
  333. i: Integer;
  334. objsec: TWasmObjSection;
  335. segment_count: Integer = 0;
  336. cur_seg_ofs: qword = 0;
  337. imports_count: Integer;
  338. objsym: TObjSymbol;
  339. begin
  340. for i:=0 to Data.ObjSectionList.Count-1 do
  341. begin
  342. objsec:=TWasmObjSection(Data.ObjSectionList[i]);
  343. if objsec.IsCode then
  344. objsec.SegIdx:=-1
  345. else
  346. begin
  347. objsec.SegIdx:=segment_count;
  348. objsec.SegOfs:=cur_seg_ofs;
  349. Inc(segment_count);
  350. Inc(cur_seg_ofs,objsec.Size);
  351. end;
  352. end;
  353. WriteUleb(FWasmSections[wsiData],segment_count);
  354. for i:=0 to Data.ObjSectionList.Count-1 do
  355. begin
  356. objsec:=TWasmObjSection(Data.ObjSectionList[i]);
  357. if objsec.IsData then
  358. begin
  359. WriteByte(FWasmSections[wsiData],0);
  360. WriteByte(FWasmSections[wsiData],$41);
  361. WriteSleb(FWasmSections[wsiData],objsec.SegOfs);
  362. WriteByte(FWasmSections[wsiData],$0b);
  363. WriteUleb(FWasmSections[wsiData],objsec.Size);
  364. if oso_Data in objsec.SecOptions then
  365. begin
  366. objsec.Data.seek(0);
  367. CopyDynamicArray(objsec.Data,FWasmSections[wsiData],objsec.Size);
  368. end
  369. else
  370. begin
  371. WriteZeros(FWasmSections[wsiData],objsec.Size);
  372. end;
  373. end;
  374. end;
  375. WriteUleb(FWasmSections[wsiDataCount],segment_count);
  376. imports_count:=3;
  377. WriteUleb(FWasmSections[wsiImport],imports_count);
  378. { import[0] }
  379. WriteName(FWasmSections[wsiImport],'env');
  380. WriteName(FWasmSections[wsiImport],'__linear_memory');
  381. WriteByte(FWasmSections[wsiImport],$02); { mem }
  382. WriteByte(FWasmSections[wsiImport],$00); { min }
  383. WriteUleb(FWasmSections[wsiImport],1); { 1 page }
  384. { import[1] }
  385. WriteName(FWasmSections[wsiImport],'env');
  386. WriteName(FWasmSections[wsiImport],'__stack_pointer');
  387. WriteByte(FWasmSections[wsiImport],$03); { global }
  388. WriteByte(FWasmSections[wsiImport],$7F); { i32 }
  389. WriteByte(FWasmSections[wsiImport],$01); { var }
  390. { import[imports_count-1] }
  391. WriteName(FWasmSections[wsiImport],'env');
  392. WriteName(FWasmSections[wsiImport],'__indirect_function_table');
  393. WriteByte(FWasmSections[wsiImport],$01); { table }
  394. WriteByte(FWasmSections[wsiImport],$70); { funcref }
  395. WriteByte(FWasmSections[wsiImport],$00); { min }
  396. WriteUleb(FWasmSections[wsiImport],1); { 1 }
  397. Writer.write(WasmModuleMagic,SizeOf(WasmModuleMagic));
  398. Writer.write(WasmVersion,SizeOf(WasmVersion));
  399. WriteWasmSection(wsiImport);
  400. WriteWasmSection(wsiDataCount);
  401. WriteWasmSection(wsiData);
  402. Writeln('ObjSymbolList:');
  403. for i:=0 to Data.ObjSymbolList.Count-1 do
  404. begin
  405. objsym:=TObjSymbol(Data.ObjSymbolList[i]);
  406. Writeln(objsym.Name, ' bind=', objsym.Bind);
  407. end;
  408. Writeln('ObjSectionList:');
  409. for i:=0 to Data.ObjSectionList.Count-1 do
  410. begin
  411. objsec:=TWasmObjSection(Data.ObjSectionList[i]);
  412. Writeln(objsec.Name, ' IsCode=', objsec.IsCode, ' IsData=', objsec.IsData, ' Size=', objsec.Size, ' MemPos=', objsec.MemPos, ' DataPos=', objsec.DataPos, ' SegIdx=', objsec.SegIdx);
  413. end;
  414. result:=true;
  415. end;
  416. constructor TWasmObjOutput.create(AWriter: TObjectWriter);
  417. var
  418. i: TWasmSectionID;
  419. begin
  420. inherited;
  421. cobjdata:=TWasmObjData;
  422. for i in TWasmSectionID do
  423. FWasmSections[i] := tdynamicarray.create(SectionDataMaxGrow);
  424. end;
  425. destructor TWasmObjOutput.destroy;
  426. var
  427. i: TWasmSectionID;
  428. begin
  429. for i in TWasmSectionID do
  430. FWasmSections[i].Free;
  431. inherited destroy;
  432. end;
  433. {****************************************************************************
  434. TWasmAssembler
  435. ****************************************************************************}
  436. constructor TWasmAssembler.Create(info: pasminfo; smart:boolean);
  437. begin
  438. inherited;
  439. CObjOutput:=TWasmObjOutput;
  440. end;
  441. {*****************************************************************************
  442. Initialize
  443. *****************************************************************************}
  444. {$ifdef wasm32}
  445. const
  446. as_wasm32_wasm_info : tasminfo =
  447. (
  448. id : as_wasm32_wasm;
  449. idtxt : 'OMF';
  450. asmbin : '';
  451. asmcmd : '';
  452. supported_targets : [system_wasm32_embedded,system_wasm32_wasi];
  453. flags : [af_outputbinary,af_smartlink_sections];
  454. labelprefix : '..@';
  455. labelmaxlen : -1;
  456. comment : '; ';
  457. dollarsign: '$';
  458. );
  459. {$endif wasm32}
  460. initialization
  461. {$ifdef wasm32}
  462. RegisterAssembler(as_wasm32_wasm_info,TWasmAssembler);
  463. {$endif wasm32}
  464. end.