ogwasm.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  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,cpubase,
  25. { assembler }
  26. aasmbase,assemble,aasmcpu,
  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. FFuncTypes: array of TWasmFuncType;
  45. function is_smart_section(atype:TAsmSectiontype):boolean;
  46. function sectionname_gas(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string;
  47. public
  48. constructor create(const n:string);override;
  49. destructor destroy; override;
  50. function sectionname(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string;override;
  51. procedure writeReloc(Data:TRelocDataInt;len:aword;p:TObjSymbol;Reloctype:TObjRelocationType);override;
  52. procedure DeclareFuncType(ft: tai_functype);
  53. end;
  54. { TWasmObjOutput }
  55. TWasmObjOutput = class(tObjOutput)
  56. private
  57. FWasmSections: array [TWasmSectionID] of tdynamicarray;
  58. procedure WriteUleb(d: tdynamicarray; v: uint64);
  59. procedure WriteUleb(w: TObjectWriter; v: uint64);
  60. procedure WriteSleb(d: tdynamicarray; v: int64);
  61. procedure WriteByte(d: tdynamicarray; b: byte);
  62. procedure WriteName(d: tdynamicarray; const s: string);
  63. procedure WriteWasmSection(wsid: TWasmSectionID);
  64. procedure CopyDynamicArray(src, dest: tdynamicarray; size: QWord);
  65. procedure WriteZeros(dest: tdynamicarray; size: QWord);
  66. procedure WriteWasmResultType(dest: tdynamicarray; wrt: TWasmResultType);
  67. procedure WriteWasmBasicType(dest: tdynamicarray; wbt: TWasmBasicType);
  68. protected
  69. function writeData(Data:TObjData):boolean;override;
  70. public
  71. constructor create(AWriter:TObjectWriter);override;
  72. destructor destroy;override;
  73. end;
  74. { TWasmAssembler }
  75. TWasmAssembler = class(tinternalassembler)
  76. constructor create(info: pasminfo; smart:boolean);override;
  77. end;
  78. implementation
  79. uses
  80. verbose;
  81. {****************************************************************************
  82. TWasmObjSection
  83. ****************************************************************************}
  84. function TWasmObjSection.IsCode: Boolean;
  85. const
  86. CodePrefix = '.text';
  87. begin
  88. result:=(Length(Name)>=Length(CodePrefix)) and
  89. (Copy(Name,1,Length(CodePrefix))=CodePrefix);
  90. end;
  91. function TWasmObjSection.IsData: Boolean;
  92. begin
  93. result:=not IsCode;
  94. end;
  95. {****************************************************************************
  96. TWasmObjData
  97. ****************************************************************************}
  98. function TWasmObjData.is_smart_section(atype: TAsmSectiontype): boolean;
  99. begin
  100. { For bss we need to set some flags that are target dependent,
  101. it is easier to disable it for smartlinking. It doesn't take up
  102. filespace }
  103. result:=not(target_info.system in systems_darwin) and
  104. create_smartlink_sections and
  105. (atype<>sec_toc) and
  106. (atype<>sec_user) and
  107. { on embedded systems every byte counts, so smartlink bss too }
  108. ((atype<>sec_bss) or (target_info.system in (systems_embedded+systems_freertos)));
  109. end;
  110. function TWasmObjData.sectionname_gas(atype: TAsmSectiontype;
  111. const aname: string; aorder: TAsmSectionOrder): string;
  112. const
  113. secnames : array[TAsmSectiontype] of string[length('__DATA, __datacoal_nt,coalesced')] = ('','',
  114. '.text',
  115. '.data',
  116. { why doesn't .rodata work? (FK) }
  117. { sometimes we have to create a data.rel.ro instead of .rodata, e.g. for }
  118. { vtables (and anything else containing relocations), otherwise those are }
  119. { not relocated properly on e.g. linux/ppc64. g++ generates there for a }
  120. { vtable for a class called Window: }
  121. { .section .data.rel.ro._ZTV6Window,"awG",@progbits,_ZTV6Window,comdat }
  122. { TODO: .data.ro not yet working}
  123. {$if defined(arm) or defined(riscv64) or defined(powerpc)}
  124. '.rodata',
  125. {$else defined(arm) or defined(riscv64) or defined(powerpc)}
  126. '.data',
  127. {$endif defined(arm) or defined(riscv64) or defined(powerpc)}
  128. '.rodata',
  129. '.bss',
  130. '.threadvar',
  131. '.pdata',
  132. '', { stubs }
  133. '__DATA,__nl_symbol_ptr',
  134. '__DATA,__la_symbol_ptr',
  135. '__DATA,__mod_init_func',
  136. '__DATA,__mod_term_func',
  137. '.stab',
  138. '.stabstr',
  139. '.idata$2','.idata$4','.idata$5','.idata$6','.idata$7','.edata',
  140. '.eh_frame',
  141. '.debug_frame','.debug_info','.debug_line','.debug_abbrev','.debug_aranges','.debug_ranges',
  142. '.fpc',
  143. '.toc',
  144. '.init',
  145. '.fini',
  146. '.objc_class',
  147. '.objc_meta_class',
  148. '.objc_cat_cls_meth',
  149. '.objc_cat_inst_meth',
  150. '.objc_protocol',
  151. '.objc_string_object',
  152. '.objc_cls_meth',
  153. '.objc_inst_meth',
  154. '.objc_cls_refs',
  155. '.objc_message_refs',
  156. '.objc_symbols',
  157. '.objc_category',
  158. '.objc_class_vars',
  159. '.objc_instance_vars',
  160. '.objc_module_info',
  161. '.objc_class_names',
  162. '.objc_meth_var_types',
  163. '.objc_meth_var_names',
  164. '.objc_selector_strs',
  165. '.objc_protocol_ext',
  166. '.objc_class_ext',
  167. '.objc_property',
  168. '.objc_image_info',
  169. '.objc_cstring_object',
  170. '.objc_sel_fixup',
  171. '__DATA,__objc_data',
  172. '__DATA,__objc_const',
  173. '.objc_superrefs',
  174. '__DATA, __datacoal_nt,coalesced',
  175. '.objc_classlist',
  176. '.objc_nlclasslist',
  177. '.objc_catlist',
  178. '.obcj_nlcatlist',
  179. '.objc_protolist',
  180. '.stack',
  181. '.heap',
  182. '.gcc_except_table',
  183. '.ARM.attributes'
  184. );
  185. var
  186. sep : string[3];
  187. secname : string;
  188. begin
  189. secname:=secnames[atype];
  190. if (atype=sec_fpc) and (Copy(aname,1,3)='res') then
  191. begin
  192. result:=secname+'.'+aname;
  193. exit;
  194. end;
  195. if atype=sec_threadvar then
  196. begin
  197. if (target_info.system in (systems_windows+systems_wince)) then
  198. secname:='.tls'
  199. else if (target_info.system in systems_linux) then
  200. secname:='.tbss';
  201. end;
  202. { go32v2 stub only loads .text and .data sections, and allocates space for .bss.
  203. Thus, data which normally goes into .rodata and .rodata_norel sections must
  204. end up in .data section }
  205. if (atype in [sec_rodata,sec_rodata_norel]) and
  206. (target_info.system in [system_i386_go32v2,system_m68k_palmos]) then
  207. secname:='.data';
  208. { Windows correctly handles reallocations in readonly sections }
  209. if (atype=sec_rodata) and
  210. (target_info.system in systems_all_windows+systems_nativent-[system_i8086_win16]) then
  211. secname:='.rodata';
  212. { section type user gives the user full controll on the section name }
  213. if atype=sec_user then
  214. secname:=aname;
  215. if is_smart_section(atype) and (aname<>'') then
  216. begin
  217. case aorder of
  218. secorder_begin :
  219. sep:='.b_';
  220. secorder_end :
  221. sep:='.z_';
  222. else
  223. sep:='.n_';
  224. end;
  225. result:=secname+sep+aname
  226. end
  227. else
  228. result:=secname;
  229. end;
  230. constructor TWasmObjData.create(const n: string);
  231. begin
  232. inherited;
  233. CObjSection:=TWasmObjSection;
  234. end;
  235. destructor TWasmObjData.destroy;
  236. var
  237. i: Integer;
  238. begin
  239. for i:=low(FFuncTypes) to high(FFuncTypes) do
  240. begin
  241. FFuncTypes[i].free;
  242. FFuncTypes[i]:=nil;
  243. end;
  244. inherited destroy;
  245. end;
  246. function TWasmObjData.sectionname(atype: TAsmSectiontype;
  247. const aname: string; aorder: TAsmSectionOrder): string;
  248. begin
  249. if (atype=sec_fpc) or (atype=sec_threadvar) then
  250. atype:=sec_data;
  251. Result:=sectionname_gas(atype, aname, aorder);
  252. end;
  253. procedure TWasmObjData.writeReloc(Data: TRelocDataInt; len: aword;
  254. p: TObjSymbol; Reloctype: TObjRelocationType);
  255. begin
  256. end;
  257. procedure TWasmObjData.DeclareFuncType(ft: tai_functype);
  258. begin
  259. { todo: check and avoid adding duplicates }
  260. SetLength(FFuncTypes,Length(FFuncTypes)+1);
  261. FFuncTypes[High(FFuncTypes)]:=TWasmFuncType.Create(ft.functype);
  262. end;
  263. {****************************************************************************
  264. TWasmObjOutput
  265. ****************************************************************************}
  266. procedure TWasmObjOutput.WriteUleb(d: tdynamicarray; v: uint64);
  267. var
  268. b: byte;
  269. begin
  270. repeat
  271. b:=byte(v) and 127;
  272. v:=v shr 7;
  273. if v<>0 then
  274. b:=b or 128;
  275. d.write(b,1);
  276. until v=0;
  277. end;
  278. procedure TWasmObjOutput.WriteUleb(w: TObjectWriter; v: uint64);
  279. var
  280. b: byte;
  281. begin
  282. repeat
  283. b:=byte(v) and 127;
  284. v:=v shr 7;
  285. if v<>0 then
  286. b:=b or 128;
  287. w.write(b,1);
  288. until v=0;
  289. end;
  290. procedure TWasmObjOutput.WriteSleb(d: tdynamicarray; v: int64);
  291. var
  292. b: byte;
  293. Done: Boolean=false;
  294. begin
  295. repeat
  296. b:=byte(v) and 127;
  297. v:=SarInt64(v,7);
  298. if ((v=0) and ((b and 64)=0)) or ((v=-1) and ((b and 64)<>0)) then
  299. Done:=true
  300. else
  301. b:=b or 128;
  302. d.write(b,1);
  303. until Done;
  304. end;
  305. procedure TWasmObjOutput.WriteByte(d: tdynamicarray; b: byte);
  306. begin
  307. d.write(b,1);
  308. end;
  309. procedure TWasmObjOutput.WriteName(d: tdynamicarray; const s: string);
  310. begin
  311. WriteUleb(d,Length(s));
  312. d.writestr(s);
  313. end;
  314. procedure TWasmObjOutput.WriteWasmSection(wsid: TWasmSectionID);
  315. var
  316. b: byte;
  317. begin
  318. b:=ord(wsid);
  319. Writer.write(b,1);
  320. WriteUleb(Writer,FWasmSections[wsid].size);
  321. Writer.writearray(FWasmSections[wsid]);
  322. end;
  323. procedure TWasmObjOutput.CopyDynamicArray(src, dest: tdynamicarray; size: QWord);
  324. var
  325. buf: array [0..4095] of byte;
  326. bs: Integer;
  327. begin
  328. while size>0 do
  329. begin
  330. if size<SizeOf(buf) then
  331. bs:=Integer(size)
  332. else
  333. bs:=SizeOf(buf);
  334. src.read(buf,bs);
  335. dest.write(buf,bs);
  336. dec(size,bs);
  337. end;
  338. end;
  339. procedure TWasmObjOutput.WriteZeros(dest: tdynamicarray; size: QWord);
  340. var
  341. buf : array[0..1023] of byte;
  342. bs: Integer;
  343. begin
  344. fillchar(buf,sizeof(buf),0);
  345. while size>0 do
  346. begin
  347. if size<SizeOf(buf) then
  348. bs:=Integer(size)
  349. else
  350. bs:=SizeOf(buf);
  351. dest.write(buf,bs);
  352. dec(size,bs);
  353. end;
  354. end;
  355. procedure TWasmObjOutput.WriteWasmResultType(dest: tdynamicarray; wrt: TWasmResultType);
  356. var
  357. i: Integer;
  358. begin
  359. WriteUleb(dest,Length(wrt));
  360. for i:=low(wrt) to high(wrt) do
  361. WriteWasmBasicType(dest,wrt[i]);
  362. end;
  363. procedure TWasmObjOutput.WriteWasmBasicType(dest: tdynamicarray; wbt: TWasmBasicType);
  364. begin
  365. case wbt of
  366. wbt_i32:
  367. WriteByte(dest,$7F);
  368. wbt_i64:
  369. WriteByte(dest,$7E);
  370. wbt_f32:
  371. WriteByte(dest,$7D);
  372. wbt_f64:
  373. WriteByte(dest,$7C);
  374. else
  375. internalerror(2021092020);
  376. end;
  377. end;
  378. function TWasmObjOutput.writeData(Data:TObjData):boolean;
  379. var
  380. i: Integer;
  381. objsec: TWasmObjSection;
  382. segment_count: Integer = 0;
  383. cur_seg_ofs: qword = 0;
  384. types_count,
  385. imports_count: Integer;
  386. objsym: TObjSymbol;
  387. begin
  388. types_count:=Length(TWasmObjData(Data).FFuncTypes);
  389. WriteUleb(FWasmSections[wsiType],types_count);
  390. for i:=0 to types_count-1 do
  391. with TWasmObjData(Data).FFuncTypes[i] do
  392. begin
  393. WriteByte(FWasmSections[wsiType],$60);
  394. WriteWasmResultType(FWasmSections[wsiType],params);
  395. WriteWasmResultType(FWasmSections[wsiType],results);
  396. end;
  397. for i:=0 to Data.ObjSectionList.Count-1 do
  398. begin
  399. objsec:=TWasmObjSection(Data.ObjSectionList[i]);
  400. if objsec.IsCode then
  401. objsec.SegIdx:=-1
  402. else
  403. begin
  404. objsec.SegIdx:=segment_count;
  405. objsec.SegOfs:=cur_seg_ofs;
  406. Inc(segment_count);
  407. Inc(cur_seg_ofs,objsec.Size);
  408. end;
  409. end;
  410. WriteUleb(FWasmSections[wsiData],segment_count);
  411. for i:=0 to Data.ObjSectionList.Count-1 do
  412. begin
  413. objsec:=TWasmObjSection(Data.ObjSectionList[i]);
  414. if objsec.IsData then
  415. begin
  416. WriteByte(FWasmSections[wsiData],0);
  417. WriteByte(FWasmSections[wsiData],$41);
  418. WriteSleb(FWasmSections[wsiData],objsec.SegOfs);
  419. WriteByte(FWasmSections[wsiData],$0b);
  420. WriteUleb(FWasmSections[wsiData],objsec.Size);
  421. if oso_Data in objsec.SecOptions then
  422. begin
  423. objsec.Data.seek(0);
  424. CopyDynamicArray(objsec.Data,FWasmSections[wsiData],objsec.Size);
  425. end
  426. else
  427. begin
  428. WriteZeros(FWasmSections[wsiData],objsec.Size);
  429. end;
  430. end;
  431. end;
  432. WriteUleb(FWasmSections[wsiDataCount],segment_count);
  433. imports_count:=3;
  434. WriteUleb(FWasmSections[wsiImport],imports_count);
  435. { import[0] }
  436. WriteName(FWasmSections[wsiImport],'env');
  437. WriteName(FWasmSections[wsiImport],'__linear_memory');
  438. WriteByte(FWasmSections[wsiImport],$02); { mem }
  439. WriteByte(FWasmSections[wsiImport],$00); { min }
  440. WriteUleb(FWasmSections[wsiImport],1); { 1 page }
  441. { import[1] }
  442. WriteName(FWasmSections[wsiImport],'env');
  443. WriteName(FWasmSections[wsiImport],'__stack_pointer');
  444. WriteByte(FWasmSections[wsiImport],$03); { global }
  445. WriteByte(FWasmSections[wsiImport],$7F); { i32 }
  446. WriteByte(FWasmSections[wsiImport],$01); { var }
  447. { import[imports_count-1] }
  448. WriteName(FWasmSections[wsiImport],'env');
  449. WriteName(FWasmSections[wsiImport],'__indirect_function_table');
  450. WriteByte(FWasmSections[wsiImport],$01); { table }
  451. WriteByte(FWasmSections[wsiImport],$70); { funcref }
  452. WriteByte(FWasmSections[wsiImport],$00); { min }
  453. WriteUleb(FWasmSections[wsiImport],1); { 1 }
  454. Writer.write(WasmModuleMagic,SizeOf(WasmModuleMagic));
  455. Writer.write(WasmVersion,SizeOf(WasmVersion));
  456. WriteWasmSection(wsiType);
  457. WriteWasmSection(wsiImport);
  458. WriteWasmSection(wsiDataCount);
  459. WriteWasmSection(wsiData);
  460. Writeln('ObjSymbolList:');
  461. for i:=0 to Data.ObjSymbolList.Count-1 do
  462. begin
  463. objsym:=TObjSymbol(Data.ObjSymbolList[i]);
  464. Writeln(objsym.Name, ' bind=', objsym.Bind);
  465. end;
  466. Writeln('ObjSectionList:');
  467. for i:=0 to Data.ObjSectionList.Count-1 do
  468. begin
  469. objsec:=TWasmObjSection(Data.ObjSectionList[i]);
  470. Writeln(objsec.Name, ' IsCode=', objsec.IsCode, ' IsData=', objsec.IsData, ' Size=', objsec.Size, ' MemPos=', objsec.MemPos, ' DataPos=', objsec.DataPos, ' SegIdx=', objsec.SegIdx);
  471. end;
  472. result:=true;
  473. end;
  474. constructor TWasmObjOutput.create(AWriter: TObjectWriter);
  475. var
  476. i: TWasmSectionID;
  477. begin
  478. inherited;
  479. cobjdata:=TWasmObjData;
  480. for i in TWasmSectionID do
  481. FWasmSections[i] := tdynamicarray.create(SectionDataMaxGrow);
  482. end;
  483. destructor TWasmObjOutput.destroy;
  484. var
  485. i: TWasmSectionID;
  486. begin
  487. for i in TWasmSectionID do
  488. FWasmSections[i].Free;
  489. inherited destroy;
  490. end;
  491. {****************************************************************************
  492. TWasmAssembler
  493. ****************************************************************************}
  494. constructor TWasmAssembler.Create(info: pasminfo; smart:boolean);
  495. begin
  496. inherited;
  497. CObjOutput:=TWasmObjOutput;
  498. end;
  499. {*****************************************************************************
  500. Initialize
  501. *****************************************************************************}
  502. {$ifdef wasm32}
  503. const
  504. as_wasm32_wasm_info : tasminfo =
  505. (
  506. id : as_wasm32_wasm;
  507. idtxt : 'OMF';
  508. asmbin : '';
  509. asmcmd : '';
  510. supported_targets : [system_wasm32_embedded,system_wasm32_wasi];
  511. flags : [af_outputbinary,af_smartlink_sections];
  512. labelprefix : '..@';
  513. labelmaxlen : -1;
  514. comment : '; ';
  515. dollarsign: '$';
  516. );
  517. {$endif wasm32}
  518. initialization
  519. {$ifdef wasm32}
  520. RegisterAssembler(as_wasm32_wasm_info,TWasmAssembler);
  521. {$endif wasm32}
  522. end.