{ Copyright (c) 2021 by Nikolay Nikolov Contains the WebAssembly binary module format reader and writer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } unit ogwasm; {$i fpcdefs.inc} interface uses { common } cclasses,globtype, { target } systems,cpubase, { assembler } aasmbase,assemble,aasmcpu, { WebAssembly module format definitions } wasmbase, { output } ogbase, owbase; type TWasmObjSymbolExtraData = class; TGlobalInitializer = record case typ:TWasmBasicType of wbt_i32: (init_i32: Int32); wbt_i64: (init_i64: Int64); wbt_f32: (init_f32: Single); wbt_f64: (init_f64: Double); end; { TWasmObjSymbolLinkingData } TWasmObjSymbolLinkingData = class public ImportModule: string; ImportName: string; FuncType: TWasmFuncType; ExeFunctionIndex: Integer; ExeIndirectFunctionTableIndex: Integer; ExeTypeIndex: Integer; ExeTagIndex: Integer; GlobalType: TWasmBasicType; GlobalIsMutable: Boolean; GlobalInitializer: TGlobalInitializer; IsExported: Boolean; ExportName: ansistring; constructor Create; destructor Destroy;override; end; { TWasmObjSymbol } TWasmObjSymbol = class(TObjSymbol) FuncIndex: Integer; SymbolIndex: Integer; GlobalIndex: Integer; TagIndex: Integer; AliasOf: string; ExtraData: TWasmObjSymbolExtraData; LinkingData: TWasmObjSymbolLinkingData; TlsGlobalSym: TWasmObjSymbol; TlsDataSym: TWasmObjSymbol; constructor create(AList:TFPHashObjectList;const AName:string);override; destructor Destroy;override; function IsAlias: Boolean; end; { TWasmObjRelocation } TWasmObjRelocation = class(TObjRelocation) public TypeIndex: Integer; Addend: LongInt; { used during linking } FuncType: TWasmFuncType; ExeTypeIndex: Integer; IsFunctionOffsetI32: Boolean; constructor CreateTypeIndex(ADataOffset:TObjSectionOfs; ATypeIndex: Integer); constructor CreateFuncType(ADataOffset:TObjSectionOfs; AFuncType: TWasmFuncType); destructor Destroy;override; end; { TWasmObjSymbolExtraData } TWasmObjSymbolExtraData = class(TFPHashObject) TypeIdx: Integer; ExceptionTagTypeIdx: Integer; ImportModule: string; ImportName: string; ExportName: string; GlobalType: TWasmBasicType; GlobalIsImmutable: Boolean; Locals: array of TWasmBasicType; EncodedLocals: tdynamicarray; constructor Create(HashObjectList: TFPHashObjectList; const s: TSymStr); destructor Destroy; override; procedure AddLocals(alocals: TWasmLocalsDynArray); end; { TWasmObjSection } TWasmObjSection = class(TObjSection) public SegIdx: Integer; SegSymIdx: Integer; SegOfs: qword; FileSectionOfs: qword; MainFuncSymbol: TWasmObjSymbol; CustomSectionIdx: Integer; constructor create(AList:TFPHashObjectList;const Aname:string;Aalign:longint;Aoptions:TObjSectionOptions);override; function IsCode: Boolean; function IsData: Boolean; function IsDebug: Boolean; end; { TWasmFuncTypeTable } TWasmFuncTypeTable = class private FFuncTypes: array of TWasmFuncType; function GetCount: Integer; function GetItem(Index: Integer): TWasmFuncType; public destructor Destroy; override; function AddOrGetFuncType(wft: TWasmFuncType): integer; procedure WriteTo(d: tdynamicarray); property Count: Integer read GetCount; property Items[Index: Integer]: TWasmFuncType read GetItem; default; end; { TWasmObjData } TWasmObjData = class(TObjData) private FFuncTypes: TWasmFuncTypeTable; FObjSymbolsExtraDataList: TFPHashObjectList; FLastFuncName: string; function is_smart_section(atype:TAsmSectiontype):boolean; function sectionname_gas(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string; public constructor create(const n:string);override; destructor destroy; override; function sectionname(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string;override; procedure writeReloc(Data:TRelocDataInt;len:aword;p:TObjSymbol;Reloctype:TObjRelocationType);override; function AddOrCreateObjSymbolExtraData(const symname:TSymStr): TWasmObjSymbolExtraData; function globalref(asmsym:TAsmSymbol):TObjSymbol; function ExceptionTagRef(asmsym:TAsmSymbol):TObjSymbol; procedure DeclareGlobalType(gt: tai_globaltype); procedure DeclareFuncType_Pass0(ft: tai_functype); procedure DeclareFuncType_Pass1(ft: tai_functype); procedure DeclareFuncType_Pass2(ft: tai_functype); procedure DeclareTagType(tt: tai_tagtype); procedure DeclareExportName(en: tai_export_name); procedure DeclareImportModule(aim: tai_import_module); procedure DeclareImportName(ain: tai_import_name); procedure DeclareLocals_Pass0(al: tai_local); procedure DeclareLocals_Pass1(al: tai_local); procedure WriteLocals_Pass2(al: tai_local); procedure symbolpairdefine(akind: TSymbolPairKind;const asym, avalue: string);override; property FuncTypes: TWasmFuncTypeTable read FFuncTypes; end; { TWasmObjOutput } TWasmObjOutput = class(tObjOutput) private FData: TWasmObjData; FWasmRelocationCodeTable: tdynamicarray; FWasmRelocationCodeTableEntriesCount: Integer; FWasmRelocationDataTable: tdynamicarray; FWasmRelocationDataTableEntriesCount: Integer; FWasmRelocationDebugFrameTable: tdynamicarray; FWasmRelocationDebugFrameTableEntriesCount: Integer; FWasmRelocationDebugInfoTable: tdynamicarray; FWasmRelocationDebugInfoTableEntriesCount: Integer; FWasmRelocationDebugLineTable: tdynamicarray; FWasmRelocationDebugLineTableEntriesCount: Integer; FWasmRelocationDebugAbbrevTable: tdynamicarray; FWasmRelocationDebugAbbrevTableEntriesCount: Integer; FWasmRelocationDebugArangesTable: tdynamicarray; FWasmRelocationDebugArangesTableEntriesCount: Integer; FWasmRelocationDebugRangesTable: tdynamicarray; FWasmRelocationDebugRangesTableEntriesCount: Integer; FWasmRelocationDebugStrTable: tdynamicarray; FWasmRelocationDebugStrTableEntriesCount: Integer; FWasmSymbolTable: tdynamicarray; FWasmSymbolTableEntriesCount: Integer; FWasmSections: array [TWasmSectionID] of tdynamicarray; FWasmCustomSections: array [TWasmCustomSectionType] of tdynamicarray; FWasmLinkingSubsections: array [low(TWasmLinkingSubsectionType)..high(TWasmLinkingSubsectionType)] of tdynamicarray; procedure WriteWasmSection(wsid: TWasmSectionID); procedure WriteWasmCustomSection(wcst: TWasmCustomSectionType); function IsExternalFunction(sym: TObjSymbol): Boolean; function IsExportedFunction(sym: TWasmObjSymbol): Boolean; procedure WriteFunctionCode(dest: tdynamicarray; objsym: TObjSymbol); procedure WriteSymbolTable; procedure WriteRelocationCodeTable(CodeSectionIndex: Integer); procedure WriteRelocationDataTable(DataSectionIndex: Integer); procedure MaybeWriteRelocationDebugTable(cst: TWasmCustomSectionType; SectionIndex: Integer; EntriesCount: Integer; Table: tdynamicarray); procedure WriteLinkingSubsection(wlst: TWasmLinkingSubsectionType); procedure DoRelocations; procedure WriteRelocations; function FindFunctionSymbol(Symbol: TWasmObjSymbol): TWasmObjSymbol; protected function writeData(Data:TObjData):boolean;override; public constructor create(AWriter:TObjectWriter);override; destructor destroy;override; end; { TWasmObjInput } TWasmObjInput = class(TObjInput) private FFuncTypes: array of TWasmFuncType; public constructor create;override; destructor Destroy;override; class function CanReadObjData(AReader:TObjectreader):boolean;override; function ReadObjData(AReader:TObjectreader;out ObjData:TObjData):boolean;override; end; { TWasmExeOutput } TWasmExeOutput = class(TExeOutput) private const DataSections: array [1..3] of string = ( '.rodata', '.data', 'fpc.resources'); WasmPageSize = 65536; type TCustomSectionNameMapEntry = record idx: UInt32; name: string; end; TCustomSectionNameMap = array of TCustomSectionNameMapEntry; private FImports: TFPHashObjectList; FFuncTypes: TWasmFuncTypeTable; FFunctionImports: array of record ModName: ansistring; Name: ansistring; TypeIdx: uint32; end; FTagImports: array of record end; FIndirectFunctionTable: array of record FuncIdx: Integer; end; FImportedMemories: array of record ModName: ansistring; Name: ansistring; MemType: TWasmMemoryType; end; FMemories: array of TWasmMemoryType; FRelocationPass: Integer; FWasmSections: array [TWasmSectionID] of tdynamicarray; FWasmCustomSections: array [TWasmCustomSectionType] of tdynamicarray; FWasmNameSubsections: array [TWasmNameSubsectionType] of tdynamicarray; FStackPointerSym: TWasmObjSymbol; FTlsBaseSym: TWasmObjSymbol; FTlsSizeSym: TWasmObjSymbol; FTlsAlignSym: TWasmObjSymbol; FInitTlsFunctionSym: TWasmObjSymbol; FInitSharedMemoryFunctionSym: TWasmObjSymbol; FMinMemoryPages, FMaxMemoryPages: Integer; { use for the Name section } FFunctionNameMap: TCustomSectionNameMap; FGlobalNameMap: TCustomSectionNameMap; FDataNameMap: TCustomSectionNameMap; FTagNameMap: TCustomSectionNameMap; procedure AddToNameMap(var nm: TCustomSectionNameMap; aidx: UInt32; const aname: string); procedure AddToFunctionNameMap(aidx: UInt32; const aname: string); procedure AddToGlobalNameMap(aidx: UInt32; const aname: string); procedure AddToDataNameMap(aidx: UInt32; const aname: string); procedure AddToTagNameMap(aidx: UInt32; const aname: string); procedure WriteWasmSection(wsid: TWasmSectionID); procedure WriteWasmSectionIfNotEmpty(wsid: TWasmSectionID); procedure WriteWasmCustomSection(wcst: TWasmCustomSectionType); procedure PrepareImports; procedure PrepareFunctions; procedure PrepareTags; function AddOrGetIndirectFunctionTableIndex(FuncIdx: Integer): integer; procedure SetStackPointer; procedure SetTlsSizeAlignAndBase; procedure SetThreadVarGlobalsInitValues; procedure GenerateCode_InitTls; procedure GenerateCode_InitSharedMemory; procedure GenerateCode_InvokeHelper; procedure WriteExeSectionToDynArray(exesec: TExeSection; dynarr: tdynamicarray); procedure WriteMemoryTo(dest: tdynamicarray;const MemType:TWasmMemoryType); function Memory2String(const MemType:TWasmMemoryType):string; procedure WriteMap_TypeSection; procedure WriteMap_IndirectFunctionTable; protected function writeData:boolean;override; procedure DoRelocationFixup(objsec:TObjSection);override; public constructor create;override; destructor destroy;override; procedure GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);override; procedure AfterUnusedSectionRemoval;override; procedure MemPos_ExeSection(const aname:string);override; procedure Load_Symbol(const aname: string);override; end; { TWasmAssembler } TWasmAssembler = class(tinternalassembler) constructor create(info: pasminfo; smart:boolean);override; end; implementation uses cutils,verbose,version,globals,fmodule,ogmap; const StackPointerSymStr='__stack_pointer'; procedure WriteUleb5(d: tdynamicarray; v: uint64); var b: byte; i: Integer; begin for i:=1 to 5 do begin b:=byte(v) and 127; v:=v shr 7; if i<>5 then b:=b or 128; d.write(b,1); end; end; procedure WriteUleb5(d: tobjsection; v: uint64); var b: byte; i: Integer; begin for i:=1 to 5 do begin b:=byte(v) and 127; v:=v shr 7; if i<>5 then b:=b or 128; d.write(b,1); end; end; procedure WriteSleb5(d: tdynamicarray; v: int64); var b: byte; i: Integer; begin for i:=1 to 5 do begin b:=byte(v) and 127; v:=SarInt64(v,7); if i<>5 then b:=b or 128; d.write(b,1); end; end; procedure WriteSleb5(d: tobjsection; v: int64); var b: byte; i: Integer; begin for i:=1 to 5 do begin b:=byte(v) and 127; v:=SarInt64(v,7); if i<>5 then b:=b or 128; d.write(b,1); end; end; procedure WriteUleb(d: tdynamicarray; v: uint64); var b: byte; begin repeat b:=byte(v) and 127; v:=v shr 7; if v<>0 then b:=b or 128; d.write(b,1); until v=0; end; procedure WriteUleb(d: tobjsection; v: uint64); var b: byte; begin repeat b:=byte(v) and 127; v:=v shr 7; if v<>0 then b:=b or 128; d.write(b,1); until v=0; end; procedure WriteUleb(w: TObjectWriter; v: uint64); var b: byte; begin repeat b:=byte(v) and 127; v:=v shr 7; if v<>0 then b:=b or 128; w.write(b,1); until v=0; end; procedure WriteSleb(d: tdynamicarray; v: int64); var b: byte; Done: Boolean=false; begin repeat b:=byte(v) and 127; v:=SarInt64(v,7); if ((v=0) and ((b and 64)=0)) or ((v=-1) and ((b and 64)<>0)) then Done:=true else b:=b or 128; d.write(b,1); until Done; end; procedure WriteSleb(d: tobjsection; v: int64); var b: byte; Done: Boolean=false; begin repeat b:=byte(v) and 127; v:=SarInt64(v,7); if ((v=0) and ((b and 64)=0)) or ((v=-1) and ((b and 64)<>0)) then Done:=true else b:=b or 128; d.write(b,1); until Done; end; function UlebEncodingSize(v: uint64): Integer; var b: byte; begin Result:=0; repeat b:=byte(v) and 127; v:=v shr 7; if v<>0 then b:=b or 128; Inc(Result); until v=0; end; {$ifdef FPC_LITTLE_ENDIAN} procedure WriteF32LE(d: tdynamicarray; v: Single); begin d.write(v,4); end; procedure WriteF64LE(d: tdynamicarray; v: Double); begin d.write(v,8); end; {$else FPC_LITTLE_ENDIAN} procedure WriteF32LE(d: tdynamicarray; v: Single); var tmpI: UInt32; begin Move(v,tmpI,4); tmpI:=SwapEndian(tmpI); d.write(tmpI,4); end; procedure WriteF64LE(d: tdynamicarray; v: Double); var tmpI: UInt64; begin Move(v,tmpI,8); tmpI:=SwapEndian(tmpI); d.write(tmpI,8); end; {$endif FPC_LITTLE_ENDIAN} procedure WriteByte(d: tdynamicarray; b: byte); begin d.write(b,1); end; procedure WriteName(d: tdynamicarray; const s: string); begin WriteUleb(d,Length(s)); d.writestr(s); end; procedure WriteWasmBasicType(dest: tdynamicarray; wbt: TWasmBasicType); begin WriteByte(dest,encode_wasm_basic_type(wbt)); end; procedure WriteWasmResultType(dest: tdynamicarray; wrt: TWasmResultType); var i: Integer; begin WriteUleb(dest,Length(wrt)); for i:=low(wrt) to high(wrt) do WriteWasmBasicType(dest,wrt[i]); end; function ReadUleb(d: tdynamicarray): uint64; var b: byte; shift:integer; begin b:=0; result:=0; shift:=0; repeat d.read(b,1); result:=result or (uint64(b and 127) shl shift); inc(shift,7); until (b and 128)=0; end; function ReadSleb(d: tdynamicarray): int64; var b: byte; shift:integer; begin b:=0; result:=0; shift:=0; repeat d.read(b,1); result:=result or (uint64(b and 127) shl shift); inc(shift,7); until (b and 128)=0; {$ifopt Q+} {$define overflowon} {$Q-} {$endif} {$ifopt R+} {$define rangeon} {$R-} {$endif} if (b and 64)<>0 then result:=result or (high(uint64) shl shift); end; {$ifdef overflowon} {$Q+} {$undef overflowon} {$endif} {$ifdef rangeon} {$R+} {$undef rangeon} {$endif} procedure AddSleb5(d: tdynamicarray; v: int64); var q: Int64; p: LongWord; begin p:=d.Pos; q:=ReadSleb(d); q:=q+v; d.seek(p); WriteSleb5(d,q); end; procedure AddUleb5(d: tdynamicarray; v: int64); var q: UInt64; p: LongWord; begin p:=d.Pos; q:=ReadUleb(d); q:=q+v; d.seek(p); WriteUleb5(d,q); end; procedure AddInt32(d: tdynamicarray; v: int32); var q: int32; p: LongWord; begin p:=d.Pos; d.read(q,4); {$ifdef FPC_BIG_ENDIAN} q:=SwapEndian(q); {$endif FPC_BIG_ENDIAN} q:=q+v; {$ifdef FPC_BIG_ENDIAN} q:=SwapEndian(q); {$endif FPC_BIG_ENDIAN} d.seek(p); d.write(q,4); end; procedure CopyDynamicArray(src, dest: tdynamicarray; size: QWord); var buf: array [0..4095] of byte; bs: Integer; begin while size>0 do begin if size0 do begin if size''; end; {**************************************************************************** TWasmObjSymbolExtraData ****************************************************************************} constructor TWasmObjSymbolExtraData.Create(HashObjectList: TFPHashObjectList; const s: TSymStr); begin EncodedLocals:=nil; inherited Create(HashObjectList,s); TypeIdx:=-1; ExceptionTagTypeIdx:=-1; end; destructor TWasmObjSymbolExtraData.Destroy; begin EncodedLocals.Free; inherited Destroy; end; procedure TWasmObjSymbolExtraData.AddLocals(alocals: TWasmLocalsDynArray); var i, rle_entries, cnt: Integer; lasttype: TWasmBasicType; begin Locals:=alocals; if Assigned(EncodedLocals) then internalerror(2024081502); EncodedLocals:=tdynamicarray.Create(64); if Length(Locals)=0 then begin WriteUleb(EncodedLocals,0); exit; end; rle_entries:=1; for i:=low(Locals)+1 to high(Locals) do if Locals[i]<>Locals[i-1] then inc(rle_entries); WriteUleb(EncodedLocals,rle_entries); lasttype:=Locals[Low(Locals)]; cnt:=1; for i:=low(Locals)+1 to high(Locals) do if Locals[i]=Locals[i-1] then inc(cnt) else begin WriteUleb(EncodedLocals,cnt); WriteWasmBasicType(EncodedLocals,lasttype); lasttype:=Locals[i]; cnt:=1; end; WriteUleb(EncodedLocals,cnt); WriteWasmBasicType(EncodedLocals,lasttype); end; {**************************************************************************** TWasmObjSection ****************************************************************************} constructor TWasmObjSection.create(AList: TFPHashObjectList; const Aname: string; Aalign: longint; Aoptions: TObjSectionOptions); begin inherited create(AList, Aname, Aalign, Aoptions); SegIdx:=-1; SegSymIdx:=-1; CustomSectionIdx:=-1; MainFuncSymbol:=nil; end; function TWasmObjSection.IsCode: Boolean; const CodePrefix = '.text'; begin result:=(Length(Name)>=Length(CodePrefix)) and (Copy(Name,1,Length(CodePrefix))=CodePrefix); end; function TWasmObjSection.IsData: Boolean; begin result:=not (IsCode or IsDebug); end; function TWasmObjSection.IsDebug: Boolean; const DebugPrefix = '.debug'; begin result:=(Length(Name)>=Length(DebugPrefix)) and (Copy(Name,1,Length(DebugPrefix))=DebugPrefix); end; {**************************************************************************** TWasmFuncTypeTable ****************************************************************************} function TWasmFuncTypeTable.GetCount: Integer; begin Result:=Length(FFuncTypes); end; function TWasmFuncTypeTable.GetItem(Index: Integer): TWasmFuncType; begin if (IndexHigh(FFuncTypes)) then internalerror(2023123101); Result:=FFuncTypes[Index]; end; destructor TWasmFuncTypeTable.Destroy; var i: Integer; begin for i:=low(FFuncTypes) to high(FFuncTypes) do begin FFuncTypes[i].free; FFuncTypes[i]:=nil; end; end; function TWasmFuncTypeTable.AddOrGetFuncType(wft: TWasmFuncType): integer; var i: Integer; begin for i:=low(FFuncTypes) to high(FFuncTypes) do if wft.Equals(FFuncTypes[i]) then exit(i); result:=Length(FFuncTypes); SetLength(FFuncTypes,result+1); FFuncTypes[result]:=TWasmFuncType.Create(wft); end; procedure TWasmFuncTypeTable.WriteTo(d: tdynamicarray); var types_count, i: Integer; begin types_count:=Count; WriteUleb(d,types_count); for i:=0 to types_count-1 do with Items[i] do begin WriteByte(d,$60); WriteWasmResultType(d,params); WriteWasmResultType(d,results); end; end; {**************************************************************************** TWasmObjData ****************************************************************************} function TWasmObjData.is_smart_section(atype: TAsmSectiontype): boolean; begin { For bss we need to set some flags that are target dependent, it is easier to disable it for smartlinking. It doesn't take up filespace } result:=not(target_info.system in systems_darwin) and create_smartlink_sections and (atype<>sec_toc) and (atype<>sec_user) and { on embedded systems every byte counts, so smartlink bss too } ((atype<>sec_bss) or (target_info.system in (systems_embedded+systems_freertos))); end; function TWasmObjData.sectionname_gas(atype: TAsmSectiontype; const aname: string; aorder: TAsmSectionOrder): string; const secnames : array[TAsmSectiontype] of string[length('__DATA, __datacoal_nt,coalesced')] = ('','', '.text', '.data', { why doesn't .rodata work? (FK) } { sometimes we have to create a data.rel.ro instead of .rodata, e.g. for } { vtables (and anything else containing relocations), otherwise those are } { not relocated properly on e.g. linux/ppc64. g++ generates there for a } { vtable for a class called Window: } { .section .data.rel.ro._ZTV6Window,"awG",@progbits,_ZTV6Window,comdat } { TODO: .data.ro not yet working} {$if defined(arm) or defined(riscv64) or defined(powerpc)} '.rodata', {$else defined(arm) or defined(riscv64) or defined(powerpc)} '.data', {$endif defined(arm) or defined(riscv64) or defined(powerpc)} '.rodata', '.bss', '.tbss', '.pdata', '', { stubs } '__DATA,__nl_symbol_ptr', '__DATA,__la_symbol_ptr', '__DATA,__mod_init_func', '__DATA,__mod_term_func', '.stab', '.stabstr', '.idata$2','.idata$4','.idata$5','.idata$6','.idata$7','.edata', '.eh_frame', '.debug_frame','.debug_info','.debug_line','.debug_abbrev','.debug_aranges','.debug_ranges','.debug_loc','.debug_loclists', '.fpc', '.toc', '.init', '.fini', '.objc_class', '.objc_meta_class', '.objc_cat_cls_meth', '.objc_cat_inst_meth', '.objc_protocol', '.objc_string_object', '.objc_cls_meth', '.objc_inst_meth', '.objc_cls_refs', '.objc_message_refs', '.objc_symbols', '.objc_category', '.objc_class_vars', '.objc_instance_vars', '.objc_module_info', '.objc_class_names', '.objc_meth_var_types', '.objc_meth_var_names', '.objc_selector_strs', '.objc_protocol_ext', '.objc_class_ext', '.objc_property', '.objc_image_info', '.objc_cstring_object', '.objc_sel_fixup', '__DATA,__objc_data', '__DATA,__objc_const', '.objc_superrefs', '__DATA, __datacoal_nt,coalesced', '.objc_classlist', '.objc_nlclasslist', '.objc_catlist', '.obcj_nlcatlist', '.objc_protolist', '.stack', '.heap', '.gcc_except_table', '.ARM.attributes' ); var sep : string[3]; secname : string; begin secname:=secnames[atype]; if (atype=sec_fpc) and (Copy(aname,1,3)='res') then begin result:=secname+'.'+aname; exit; end; { go32v2 stub only loads .text and .data sections, and allocates space for .bss. Thus, data which normally goes into .rodata and .rodata_norel sections must end up in .data section } if (atype in [sec_rodata,sec_rodata_norel]) and (target_info.system in [system_i386_go32v2,system_m68k_palmos]) then secname:='.data'; { Windows correctly handles reallocations in readonly sections } if (atype=sec_rodata) and (target_info.system in systems_all_windows+systems_nativent-[system_i8086_win16]) then secname:='.rodata'; { section type user gives the user full controll on the section name } if atype=sec_user then secname:=aname; if is_smart_section(atype) and (aname<>'') then begin case aorder of secorder_begin : sep:='.b_'; secorder_end : sep:='.z_'; else sep:='.n_'; end; result:=secname+sep+aname end else result:=secname; end; constructor TWasmObjData.create(const n: string); begin inherited; CObjSection:=TWasmObjSection; CObjSymbol:=TWasmObjSymbol; FObjSymbolsExtraDataList:=TFPHashObjectList.Create; FFuncTypes:=TWasmFuncTypeTable.Create; end; destructor TWasmObjData.destroy; var i: Integer; begin FObjSymbolsExtraDataList.Free; FFuncTypes.Free; inherited destroy; end; function TWasmObjData.sectionname(atype: TAsmSectiontype; const aname: string; aorder: TAsmSectionOrder): string; begin if (atype=sec_fpc) or ((atype=sec_threadvar) and not (ts_wasm_threads in current_settings.targetswitches)) then atype:=sec_data; Result:=sectionname_gas(atype, aname, aorder); end; procedure TWasmObjData.writeReloc(Data: TRelocDataInt; len: aword; p: TObjSymbol; Reloctype: TObjRelocationType); const leb_zero: array[0..4] of byte=($80,$80,$80,$80,$00); var objreloc: TWasmObjRelocation; begin if CurrObjSec=nil then internalerror(200403072); { workaround crash, when generating debug info for threadvars, when multithreading is turned off. todo: ensure the debug info for threadvars is actually correct, once we've got WebAssembly debug info working in general } if (Reloctype=RELOC_DTPOFF) and not (ts_wasm_threads in current_settings.targetswitches) then Reloctype:=RELOC_ABSOLUTE; objreloc:=nil; case Reloctype of RELOC_FUNCTION_INDEX_LEB: begin if Data<>0 then internalerror(2021092502); if len<>5 then internalerror(2021092503); if not assigned(p) then internalerror(2021092504); objreloc:=TWasmObjRelocation.CreateSymbol(CurrObjSec.Size,p,Reloctype); CurrObjSec.ObjRelocations.Add(objreloc); writebytes(leb_zero,5); end; RELOC_MEMORY_ADDR_LEB, RELOC_MEMORY_ADDR_OR_TABLE_INDEX_SLEB: begin if (Reloctype=RELOC_MEMORY_ADDR_LEB) and (Data<0) then internalerror(2021092602); if len<>5 then internalerror(2021092503); if not assigned(p) then internalerror(2021092504); objreloc:=TWasmObjRelocation.CreateSymbol(CurrObjSec.Size,p,Reloctype); objreloc.Addend:=Data; CurrObjSec.ObjRelocations.Add(objreloc); if RelocType=RELOC_MEMORY_ADDR_LEB then WriteUleb5(CurrObjSec,Data) else WriteSleb5(CurrObjSec,Data); end; RELOC_ABSOLUTE: begin if len<>4 then internalerror(2021092607); if not assigned(p) then internalerror(2021092608); if (p.objsection<>nil) and TWasmObjSection(p.objsection).IsDebug and (p.bind<>AB_COMMON) and (p.bind<>AB_EXTERNAL) then begin objreloc:=TWasmObjRelocation.CreateSection(CurrObjSec.Size,p.objsection,RELOC_ABSOLUTE); objreloc.Addend:=Data+p.Address; CurrObjSec.ObjRelocations.Add(objreloc); {inc(data,p.address);} data:=0; Data:=NtoLE(Data); writebytes(Data,4); end else begin objreloc:=TWasmObjRelocation.CreateSymbol(CurrObjSec.Size,p,Reloctype); objreloc.Addend:=Data; CurrObjSec.ObjRelocations.Add(objreloc); Data:=NtoLE(Data); writebytes(Data,4); end; end; RELOC_TYPE_INDEX_LEB: begin if len<>5 then internalerror(2021092612); if assigned(p) then internalerror(2021092613); objreloc:=TWasmObjRelocation.CreateTypeIndex(CurrObjSec.Size,Data); CurrObjSec.ObjRelocations.Add(objreloc); WriteUleb5(CurrObjSec,Data); end; RELOC_GLOBAL_INDEX_LEB: begin if len<>5 then internalerror(2021092701); if Data<>0 then internalerror(2021092702); if not assigned(p) then internalerror(2021092703); objreloc:=TWasmObjRelocation.CreateSymbol(CurrObjSec.Size,p,Reloctype); CurrObjSec.ObjRelocations.Add(objreloc); WriteUleb5(CurrObjSec,0); end; RELOC_TAG_INDEX_LEB: begin if len<>5 then internalerror(2021092712); if Data<>0 then internalerror(2021092713); if not assigned(p) then internalerror(2021092714); objreloc:=TWasmObjRelocation.CreateSymbol(CurrObjSec.Size,p,Reloctype); CurrObjSec.ObjRelocations.Add(objreloc); WriteSleb5(CurrObjSec,0); end; else internalerror(2021092501); end; end; function TWasmObjData.AddOrCreateObjSymbolExtraData(const symname: TSymStr): TWasmObjSymbolExtraData; begin result:=TWasmObjSymbolExtraData(FObjSymbolsExtraDataList.Find(symname)); if not assigned(result) then result:=TWasmObjSymbolExtraData.Create(FObjSymbolsExtraDataList,symname); end; function TWasmObjData.globalref(asmsym: TAsmSymbol): TObjSymbol; begin if assigned(asmsym) then begin if (asmsym.typ<>AT_WASM_GLOBAL) and (asmsym.typ<>AT_TLS) then internalerror(2021092706); result:=symbolref(asmsym); result.typ:=asmsym.typ; end else result:=nil; end; function TWasmObjData.ExceptionTagRef(asmsym: TAsmSymbol): TObjSymbol; begin if assigned(asmsym) then begin if asmsym.typ<>AT_WASM_EXCEPTION_TAG then internalerror(2021092707); result:=symbolref(asmsym); result.typ:=AT_WASM_EXCEPTION_TAG; end else result:=nil; end; procedure TWasmObjData.DeclareGlobalType(gt: tai_globaltype); var ObjSymExtraData: TWasmObjSymbolExtraData; ObjSym: TObjSymbol; begin if not gt.is_external then begin ObjSym:=symboldefine(gt.sym); ObjSym.typ:=AT_WASM_GLOBAL; end; ObjSymExtraData:=AddOrCreateObjSymbolExtraData(gt.globalname); ObjSymExtraData.GlobalType:=gt.gtype; ObjSymExtraData.GlobalIsImmutable:=gt.immutable; end; procedure TWasmObjData.DeclareFuncType_Pass0(ft: tai_functype); var i: Integer; ObjSymExtraData: TWasmObjSymbolExtraData; begin FLastFuncName:=ft.funcname; i:=FFuncTypes.AddOrGetFuncType(ft.functype); ObjSymExtraData:=AddOrCreateObjSymbolExtraData(ft.funcname); ObjSymExtraData.TypeIdx:=i; end; procedure TWasmObjData.DeclareFuncType_Pass1(ft: tai_functype); begin FLastFuncName:=ft.funcname; end; procedure TWasmObjData.DeclareFuncType_Pass2(ft: tai_functype); begin FLastFuncName:=ft.funcname; end; procedure TWasmObjData.DeclareTagType(tt: tai_tagtype); var ObjSymExtraData: TWasmObjSymbolExtraData; ft: TWasmFuncType; i: Integer; begin ObjSymExtraData:=AddOrCreateObjSymbolExtraData(tt.tagname); ft:=TWasmFuncType.Create([],tt.params); i:=FFuncTypes.AddOrGetFuncType(ft); ft.free; ObjSymExtraData.ExceptionTagTypeIdx:=i; end; procedure TWasmObjData.DeclareExportName(en: tai_export_name); var ObjSymExtraData: TWasmObjSymbolExtraData; begin ObjSymExtraData:=AddOrCreateObjSymbolExtraData(en.intname); ObjSymExtraData.ExportName:=en.extname; end; procedure TWasmObjData.DeclareImportModule(aim: tai_import_module); var ObjSymExtraData: TWasmObjSymbolExtraData; begin ObjSymExtraData:=AddOrCreateObjSymbolExtraData(aim.symname); ObjSymExtraData.ImportModule:=aim.importmodule; end; procedure TWasmObjData.DeclareImportName(ain: tai_import_name); var ObjSymExtraData: TWasmObjSymbolExtraData; begin ObjSymExtraData:=AddOrCreateObjSymbolExtraData(ain.symname); ObjSymExtraData.ImportName:=ain.importname; end; procedure TWasmObjData.DeclareLocals_Pass0(al: tai_local); var ObjSymExtraData: TWasmObjSymbolExtraData; begin ObjSymExtraData:=TWasmObjSymbolExtraData(FObjSymbolsExtraDataList.Find(FLastFuncName)); ObjSymExtraData.AddLocals(al.locals); alloc(ObjSymExtraData.EncodedLocals.size); end; procedure TWasmObjData.DeclareLocals_Pass1(al: tai_local); var ObjSymExtraData: TWasmObjSymbolExtraData; begin ObjSymExtraData:=TWasmObjSymbolExtraData(FObjSymbolsExtraDataList.Find(FLastFuncName)); alloc(ObjSymExtraData.EncodedLocals.size); end; procedure TWasmObjData.WriteLocals_Pass2(al: tai_local); var ObjSymExtraData: TWasmObjSymbolExtraData; d: tdynamicarray; buf: array [0..4095] of byte; bs,size: Integer; begin ObjSymExtraData:=TWasmObjSymbolExtraData(FObjSymbolsExtraDataList.Find(FLastFuncName)); d:=ObjSymExtraData.EncodedLocals; d.seek(0); size:=d.size; while size>0 do begin if sizenil) and (ExtraData.TypeIdx<>-1); end else result:=false; end; function TWasmObjOutput.IsExportedFunction(sym: TWasmObjSymbol): Boolean; var ExtraData: TWasmObjSymbolExtraData; begin if (sym.typ=AT_FUNCTION) and not sym.IsAlias then begin ExtraData:=TWasmObjSymbolExtraData(TWasmObjData(sym.ObjData).FObjSymbolsExtraDataList.Find(sym.Name)); result:=(ExtraData<>nil) and (ExtraData.ExportName<>''); end else result:=false; end; procedure TWasmObjOutput.WriteFunctionCode(dest: tdynamicarray; objsym: TObjSymbol); var ObjSymExtraData: TWasmObjSymbolExtraData; ObjSection: TWasmObjSection; codelen: QWord; begin ObjSymExtraData:=TWasmObjSymbolExtraData(FData.FObjSymbolsExtraDataList.Find(objsym.Name)); ObjSection:=TWasmObjSection(objsym.objsection); ObjSection.Data.seek(objsym.address); codelen:=objsym.size; WriteUleb(dest,codelen); ObjSection.FileSectionOfs:=dest.size-objsym.offset; CopyDynamicArray(ObjSection.Data,dest,codelen); end; procedure TWasmObjOutput.WriteSymbolTable; begin WriteUleb(FWasmLinkingSubsections[WASM_SYMBOL_TABLE],FWasmSymbolTableEntriesCount); FWasmSymbolTable.seek(0); CopyDynamicArray(FWasmSymbolTable,FWasmLinkingSubsections[WASM_SYMBOL_TABLE],FWasmSymbolTable.size); end; procedure TWasmObjOutput.WriteRelocationCodeTable(CodeSectionIndex: Integer); begin WriteUleb(FWasmCustomSections[wcstRelocCode],CodeSectionIndex); WriteUleb(FWasmCustomSections[wcstRelocCode],FWasmRelocationCodeTableEntriesCount); FWasmRelocationCodeTable.seek(0); CopyDynamicArray(FWasmRelocationCodeTable,FWasmCustomSections[wcstRelocCode],FWasmRelocationCodeTable.size); end; procedure TWasmObjOutput.WriteRelocationDataTable(DataSectionIndex: Integer); begin WriteUleb(FWasmCustomSections[wcstRelocData],DataSectionIndex); WriteUleb(FWasmCustomSections[wcstRelocData],FWasmRelocationDataTableEntriesCount); FWasmRelocationDataTable.seek(0); CopyDynamicArray(FWasmRelocationDataTable,FWasmCustomSections[wcstRelocData],FWasmRelocationDataTable.size); end; procedure TWasmObjOutput.MaybeWriteRelocationDebugTable(cst: TWasmCustomSectionType; SectionIndex: Integer; EntriesCount: Integer; Table: tdynamicarray); begin if EntriesCount>0 then begin WriteUleb(FWasmCustomSections[cst],SectionIndex); WriteUleb(FWasmCustomSections[cst],EntriesCount); Table.seek(0); CopyDynamicArray(Table,FWasmCustomSections[cst],Table.size); WriteWasmCustomSection(cst); end; end; procedure TWasmObjOutput.WriteLinkingSubsection(wlst: TWasmLinkingSubsectionType); begin if FWasmLinkingSubsections[wlst].size>0 then begin WriteByte(FWasmCustomSections[wcstLinking],Ord(wlst)); WriteUleb(FWasmCustomSections[wcstLinking],FWasmLinkingSubsections[wlst].size); FWasmLinkingSubsections[wlst].seek(0); CopyDynamicArray(FWasmLinkingSubsections[wlst],FWasmCustomSections[wcstLinking],FWasmLinkingSubsections[wlst].size); end; end; procedure TWasmObjOutput.DoRelocations; var si, ri: Integer; objsec: TWasmObjSection; objrel: TWasmObjRelocation; begin for si:=0 to FData.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(FData.ObjSectionList[si]); for ri:=0 to objsec.ObjRelocations.Count-1 do begin objrel:=TWasmObjRelocation(objsec.ObjRelocations[ri]); case objrel.typ of RELOC_FUNCTION_INDEX_LEB: begin if not assigned(objrel.symbol) then internalerror(2021092509); objsec.Data.seek(objrel.DataOffset); if TWasmObjSymbol(objrel.symbol).FuncIndex<0 then message1(asmw_e_illegal_unset_index,objrel.symbol.name) else WriteUleb5(objsec.Data,TWasmObjSymbol(objrel.symbol).FuncIndex); end; RELOC_MEMORY_ADDR_OR_TABLE_INDEX_SLEB: begin if not assigned(objrel.symbol) then internalerror(2021092605); if not (IsExternalFunction(objrel.symbol) or (objrel.symbol.typ=AT_FUNCTION) or (objrel.symbol.bind=AB_EXTERNAL)) then begin objsec.Data.seek(objrel.DataOffset); AddSleb5(objsec.Data,objrel.symbol.offset+TWasmObjSection(objrel.symbol.objsection).SegOfs); end; end; RELOC_MEMORY_ADDR_LEB: begin if not assigned(objrel.symbol) then internalerror(2021092606); if IsExternalFunction(objrel.symbol) or (objrel.symbol.typ=AT_FUNCTION) then internalerror(2021092628); if objrel.symbol.bind<>AB_EXTERNAL then begin objsec.Data.seek(objrel.DataOffset); AddUleb5(objsec.Data,objrel.symbol.offset+TWasmObjSection(objrel.symbol.objsection).SegOfs); end; end; RELOC_ABSOLUTE: begin if assigned(objrel.ObjSection) then begin { todo: should we do something here? } //Writeln('todo: section relocation'); end else if not (IsExternalFunction(objrel.symbol) or (objrel.symbol.typ=AT_FUNCTION) or (objrel.symbol.bind=AB_EXTERNAL)) then begin objsec.Data.seek(objrel.DataOffset); AddInt32(objsec.Data,objrel.symbol.offset+TWasmObjSection(objrel.symbol.objsection).SegOfs); end; end; RELOC_TYPE_INDEX_LEB: ; RELOC_GLOBAL_INDEX_LEB: begin if not assigned(objrel.symbol) then internalerror(2021092509); objsec.Data.seek(objrel.DataOffset); if TWasmObjSymbol(objrel.symbol).GlobalIndex<0 then message1(asmw_e_illegal_unset_index,objrel.symbol.name) else WriteUleb5(objsec.Data,TWasmObjSymbol(objrel.symbol).GlobalIndex); end; RELOC_TAG_INDEX_LEB: begin if not assigned(objrel.symbol) then internalerror(2021092716); objsec.Data.seek(objrel.DataOffset); if TWasmObjSymbol(objrel.symbol).TagIndex<0 then message1(asmw_e_illegal_unset_index,objrel.symbol.name) else WriteSleb5(objsec.Data,TWasmObjSymbol(objrel.symbol).TagIndex); end; else internalerror(2021092510); end; end; end; end; procedure TWasmObjOutput.WriteRelocations; var si, ri: Integer; objsec: TWasmObjSection; objrel: TWasmObjRelocation; relout: tdynamicarray; relcount: PInteger; FuncSym: TWasmObjSymbol; begin for si:=0 to FData.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(FData.ObjSectionList[si]); if objsec.IsCode then begin relout:=FWasmRelocationCodeTable; relcount:=@FWasmRelocationCodeTableEntriesCount; end else if objsec.IsData then begin relout:=FWasmRelocationDataTable; relcount:=@FWasmRelocationDataTableEntriesCount; end else if objsec.IsDebug then begin case objsec.Name of '.debug_frame': begin relout:=FWasmRelocationDebugFrameTable; relcount:=@FWasmRelocationDebugFrameTableEntriesCount; end; '.debug_info': begin relout:=FWasmRelocationDebugInfoTable; relcount:=@FWasmRelocationDebugInfoTableEntriesCount; end; '.debug_line': begin relout:=FWasmRelocationDebugLineTable; relcount:=@FWasmRelocationDebugLineTableEntriesCount; end; '.debug_abbrev': begin relout:=FWasmRelocationDebugAbbrevTable; relcount:=@FWasmRelocationDebugAbbrevTableEntriesCount; end; '.debug_aranges': begin relout:=FWasmRelocationDebugArangesTable; relcount:=@FWasmRelocationDebugArangesTableEntriesCount; end; '.debug_ranges': begin relout:=FWasmRelocationDebugRangesTable; relcount:=@FWasmRelocationDebugRangesTableEntriesCount; end; '.debug_str': begin relout:=FWasmRelocationDebugStrTable; relcount:=@FWasmRelocationDebugStrTableEntriesCount; end; else internalerror(2022071601); end; end else continue; for ri:=0 to objsec.ObjRelocations.Count-1 do begin objrel:=TWasmObjRelocation(objsec.ObjRelocations[ri]); case objrel.typ of RELOC_FUNCTION_INDEX_LEB: begin if not assigned(objrel.symbol) then internalerror(2021092508); Inc(relcount^); WriteByte(relout,Ord(R_WASM_FUNCTION_INDEX_LEB)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); WriteUleb(relout,TWasmObjSymbol(objrel.symbol).SymbolIndex); end; RELOC_MEMORY_ADDR_LEB: begin if not assigned(objrel.symbol) then internalerror(2021092603); Inc(relcount^); if IsExternalFunction(objrel.symbol) or (objrel.symbol.typ=AT_FUNCTION) then internalerror(2021092628); WriteByte(relout,Ord(R_WASM_MEMORY_ADDR_LEB)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); WriteUleb(relout,TWasmObjSymbol(objrel.symbol).SymbolIndex); WriteSleb(relout,objrel.Addend); { addend to add to the address } end; RELOC_MEMORY_ADDR_OR_TABLE_INDEX_SLEB: begin if not assigned(objrel.symbol) then internalerror(2021092604); Inc(relcount^); if IsExternalFunction(objrel.symbol) or (objrel.symbol.typ=AT_FUNCTION) then begin WriteByte(relout,Ord(R_WASM_TABLE_INDEX_SLEB)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); WriteUleb(relout,TWasmObjSymbol(objrel.symbol).SymbolIndex); end else begin WriteByte(relout,Ord(R_WASM_MEMORY_ADDR_SLEB)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); WriteUleb(relout,TWasmObjSymbol(objrel.symbol).SymbolIndex); WriteSleb(relout,objrel.Addend); { addend to add to the address } end; end; RELOC_ABSOLUTE: begin // todo: figure this out, why do these exist? //if assigned(objrel.symbol) and not assigned(objrel.symbol.objsection) then // Writeln('!!! ', objrel.symbol.name); if assigned(objrel.objsection) then begin Inc(relcount^); WriteByte(relout,Ord(R_WASM_SECTION_OFFSET_I32)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); if (TWasmObjSection(objrel.objsection).SegSymIdx<0) then message1(asmw_e_illegal_unset_index,objrel.objsection.name) else WriteUleb(relout,TWasmObjSection(objrel.objsection).SegSymIdx); WriteSleb(relout,objrel.Addend); { addend to add to the address } end else if (IsExternalFunction(objrel.symbol) or (objrel.symbol.typ=AT_FUNCTION)) and not objsec.IsDebug then begin Inc(relcount^); WriteByte(relout,Ord(R_WASM_TABLE_INDEX_I32)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); WriteUleb(relout,TWasmObjSymbol(objrel.symbol).SymbolIndex); end else if assigned(objrel.symbol) and assigned(objrel.symbol.objsection) and TWasmObjSection(objrel.symbol.objsection).IsCode then begin Inc(relcount^); WriteByte(relout,Ord(R_WASM_FUNCTION_OFFSET_I32)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); FuncSym:=FindFunctionSymbol(TWasmObjSymbol(objrel.Symbol)); if FuncSym.SymbolIndex<0 then message1(asmw_e_illegal_unset_index,FuncSym.Name) else WriteUleb(relout,FuncSym.SymbolIndex); WriteSleb(relout,objrel.Addend+objrel.symbol.address) { addend to add to the address } end else if assigned(objrel.symbol) and (objrel.symbol.typ=AT_WASM_GLOBAL) then begin Inc(relcount^); WriteByte(relout,Ord(R_WASM_GLOBAL_INDEX_I32)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); if (TWasmObjSymbol(objrel.symbol).SymbolIndex<0) then message1(asmw_e_illegal_unset_index,objrel.symbol.name) else WriteUleb(relout,TWasmObjSymbol(objrel.symbol).SymbolIndex); end else begin Inc(relcount^); WriteByte(relout,Ord(R_WASM_MEMORY_ADDR_I32)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); if (TWasmObjSymbol(objrel.symbol).SymbolIndex<0) then begin Writeln(objrel.symbol.objsection.Name, ' ', objrel.symbol.name, ' ', objsec.Name); message1(asmw_e_illegal_unset_index,objrel.symbol.name); end else WriteUleb(relout,TWasmObjSymbol(objrel.symbol).SymbolIndex); WriteSleb(relout,objrel.Addend); { addend to add to the address } end; end; RELOC_TYPE_INDEX_LEB: begin Inc(relcount^); WriteByte(relout,Ord(R_WASM_TYPE_INDEX_LEB)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); WriteUleb(relout,objrel.TypeIndex); end; RELOC_GLOBAL_INDEX_LEB: begin if not assigned(objrel.symbol) then internalerror(2021092704); Inc(relcount^); WriteByte(relout,Ord(R_WASM_GLOBAL_INDEX_LEB)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); if (TWasmObjSymbol(objrel.symbol).SymbolIndex<0) then message1(asmw_e_illegal_unset_index,objrel.symbol.name) else WriteUleb(relout,TWasmObjSymbol(objrel.symbol).SymbolIndex); end; RELOC_TAG_INDEX_LEB: begin if not assigned(objrel.symbol) then internalerror(2021092717); Inc(relcount^); WriteByte(relout,Ord(R_WASM_TAG_INDEX_LEB)); WriteUleb(relout,objrel.DataOffset+objsec.FileSectionOfs); if (TWasmObjSymbol(objrel.symbol).SymbolIndex<0) then message1(asmw_e_illegal_unset_index,objrel.symbol.name) else WriteUleb(relout,TWasmObjSymbol(objrel.symbol).SymbolIndex); end; else internalerror(2021092507); end; end; end; end; function TWasmObjOutput.FindFunctionSymbol(Symbol: TWasmObjSymbol): TWasmObjSymbol; begin Result:=TWasmObjSection(Symbol.objsection).MainFuncSymbol; end; function TWasmObjOutput.writeData(Data:TObjData):boolean; var section_nr: Integer; procedure MaybeAddDebugSectionToSymbolTable(st: TWasmCustomDebugSectionType; var debug_section_nr: Integer); var objsec: TWasmObjSection; begin objsec:=TWasmObjSection(Data.ObjSectionList.Find(WasmCustomSectionName[st])); if Assigned(objsec) then begin debug_section_nr:=section_nr; Inc(section_nr); objsec.SegSymIdx:=FWasmSymbolTableEntriesCount; objsec.CustomSectionIdx:=debug_section_nr; Inc(FWasmSymbolTableEntriesCount); WriteByte(FWasmSymbolTable,Ord(SYMTAB_SECTION)); WriteUleb(FWasmSymbolTable,WASM_SYM_BINDING_LOCAL); WriteUleb(FWasmSymbolTable,debug_section_nr); end; end; procedure MaybeWriteDebugSection(st: TWasmCustomDebugSectionType); var objsec: TWasmObjSection; begin objsec:=TWasmObjSection(Data.ObjSectionList.Find(WasmCustomSectionName[st])); if Assigned(objsec) then begin if oso_Data in objsec.SecOptions then begin objsec.Data.seek(0); CopyDynamicArray(objsec.Data,FWasmCustomSections[st],objsec.Size); end else WriteZeros(FWasmCustomSections[st],objsec.Size); WriteWasmCustomSection(st); end; end; var i: Integer; objsec: TWasmObjSection; segment_count: Integer = 0; cur_seg_ofs: qword = 0; imports_count, NextImportFunctionIndex, NextFunctionIndex, code_section_nr, data_section_nr, debug_abbrev_section_nr,debug_info_section_nr,debug_str_section_nr, debug_line_section_nr,debug_frame_section_nr,debug_aranges_section_nr, debug_ranges_section_nr, NextGlobalIndex, NextTagIndex: Integer; import_globals_count: Integer = 0; globals_count: Integer = 0; import_functions_count: Integer = 0; export_functions_count: Integer = 0; functions_count: Integer = 0; import_exception_tags_count: Integer = 0; exception_tags_count: Integer = 0; objsym, ObjSymAlias: TWasmObjSymbol; cust_sec: TWasmCustomSectionType; SegmentFlags, SymbolFlags: UInt64; begin FData:=TWasmObjData(Data); { each custom sections starts with its name } for cust_sec in TWasmCustomSectionType do WriteName(FWasmCustomSections[cust_sec],WasmCustomSectionName[cust_sec]); WriteUleb(FWasmCustomSections[wcstLinking],2); { linking metadata version } for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if objsym.typ=AT_WASM_EXCEPTION_TAG then if objsym.bind=AB_EXTERNAL then Inc(import_exception_tags_count) else Inc(exception_tags_count); if objsym.typ=AT_WASM_GLOBAL then if objsym.bind=AB_EXTERNAL then Inc(import_globals_count) else Inc(globals_count); if (objsym.typ=AT_TLS) and (ts_wasm_threads in current_settings.targetswitches) then Inc(import_globals_count); if IsExternalFunction(objsym) then Inc(import_functions_count); if (objsym.typ=AT_FUNCTION) and not objsym.IsAlias then begin TWasmObjSection(objsym.objsection).MainFuncSymbol:=objsym; Inc(functions_count); end; if IsExportedFunction(objsym) then Inc(export_functions_count); end; FData.FFuncTypes.WriteTo(FWasmSections[wsiType]); for i:=0 to Data.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(Data.ObjSectionList[i]); if objsec.IsCode then objsec.SegIdx:=-1 else if objsec.IsData then begin objsec.SegIdx:=segment_count; objsec.SegOfs:=cur_seg_ofs; Inc(segment_count); Inc(cur_seg_ofs,objsec.Size); end; end; imports_count:=2+import_globals_count+import_functions_count+import_exception_tags_count; WriteUleb(FWasmSections[wsiImport],imports_count); { import memories } WriteName(FWasmSections[wsiImport],'env'); WriteName(FWasmSections[wsiImport],'__linear_memory'); WriteByte(FWasmSections[wsiImport],$02); { mem } WriteByte(FWasmSections[wsiImport],$00); { min } WriteUleb(FWasmSections[wsiImport],1); { 1 page } { import globals } NextGlobalIndex:=0; for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if (objsym.bind=AB_EXTERNAL) and (objsym.typ=AT_WASM_GLOBAL) then begin objsym.GlobalIndex:=NextGlobalIndex; Inc(NextGlobalIndex); objsym.ExtraData:=TWasmObjSymbolExtraData(FData.FObjSymbolsExtraDataList.Find(objsym.Name)); if objsym.ExtraData.ImportModule<>'' then WriteName(FWasmSections[wsiImport],objsym.ExtraData.ImportModule) else WriteName(FWasmSections[wsiImport],'env'); WriteName(FWasmSections[wsiImport],objsym.Name); WriteByte(FWasmSections[wsiImport],$03); { global } WriteWasmBasicType(FWasmSections[wsiImport],objsym.ExtraData.GlobalType); if objsym.ExtraData.GlobalIsImmutable then WriteByte(FWasmSections[wsiImport],$00) { const } else WriteByte(FWasmSections[wsiImport],$01); { var } end else if (objsym.typ=AT_TLS) and (ts_wasm_threads in current_settings.targetswitches) then begin objsym.GlobalIndex:=NextGlobalIndex; Inc(NextGlobalIndex); objsym.ExtraData:=nil; WriteName(FWasmSections[wsiImport],'GOT.mem'); WriteName(FWasmSections[wsiImport],objsym.Name); WriteByte(FWasmSections[wsiImport],$03); { global } WriteWasmBasicType(FWasmSections[wsiImport],wbt_i32); { i32 } WriteByte(FWasmSections[wsiImport],$01); { var } end; end; { import functions } NextImportFunctionIndex:=0; for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if IsExternalFunction(objsym) then begin objsym.FuncIndex:=NextImportFunctionIndex; Inc(NextImportFunctionIndex); objsym.ExtraData:=TWasmObjSymbolExtraData(FData.FObjSymbolsExtraDataList.Find(objsym.Name)); if objsym.ExtraData.ImportModule<>'' then WriteName(FWasmSections[wsiImport],objsym.ExtraData.ImportModule) else WriteName(FWasmSections[wsiImport],'env'); WriteName(FWasmSections[wsiImport],objsym.Name); WriteByte(FWasmSections[wsiImport],$00); { func } WriteUleb(FWasmSections[wsiImport],TWasmObjSymbolExtraData(FData.FObjSymbolsExtraDataList.Find(objsym.Name)).TypeIdx); end; end; { import tables } WriteName(FWasmSections[wsiImport],'env'); WriteName(FWasmSections[wsiImport],'__indirect_function_table'); WriteByte(FWasmSections[wsiImport],$01); { table } WriteByte(FWasmSections[wsiImport],$70); { funcref } WriteByte(FWasmSections[wsiImport],$00); { min } WriteUleb(FWasmSections[wsiImport],1); { 1 } { import tags } NextTagIndex:=0; for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if (objsym.typ=AT_WASM_EXCEPTION_TAG) and (objsym.bind=AB_EXTERNAL) then begin objsym.TagIndex:=NextTagIndex; Inc(NextTagIndex); objsym.ExtraData:=TWasmObjSymbolExtraData(FData.FObjSymbolsExtraDataList.Find(objsym.Name)); if objsym.ExtraData.ImportModule<>'' then WriteName(FWasmSections[wsiImport],objsym.ExtraData.ImportModule) else WriteName(FWasmSections[wsiImport],'env'); WriteName(FWasmSections[wsiImport],objsym.Name); WriteByte(FWasmSections[wsiImport],$04); { tag } WriteByte(FWasmSections[wsiImport],$00); { exception } WriteUleb(FWasmSections[wsiImport],objsym.ExtraData.ExceptionTagTypeIdx); end; end; WriteUleb(FWasmSections[wsiFunction],functions_count); NextFunctionIndex:=NextImportFunctionIndex; for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if (objsym.typ=AT_FUNCTION) and not objsym.IsAlias then begin objsym.FuncIndex:=NextFunctionIndex; Inc(NextFunctionIndex); WriteUleb(FWasmSections[wsiFunction],TWasmObjSymbolExtraData(FData.FObjSymbolsExtraDataList.Find(objsym.Name)).TypeIdx); end; end; if exception_tags_count>0 then begin WriteUleb(FWasmSections[wsiTag],exception_tags_count); for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if (objsym.typ=AT_WASM_EXCEPTION_TAG) and (objsym.bind<>AB_EXTERNAL) then begin objsym.TagIndex:=NextTagIndex; Inc(NextTagIndex); objsym.ExtraData:=TWasmObjSymbolExtraData(FData.FObjSymbolsExtraDataList.Find(objsym.Name)); WriteByte(FWasmSections[wsiTag],$00); { exception } WriteUleb(FWasmSections[wsiTag],objsym.ExtraData.ExceptionTagTypeIdx); end; end; end; if globals_count>0 then begin WriteUleb(FWasmSections[wsiGlobal],globals_count); for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if (objsym.typ=AT_WASM_GLOBAL) and (objsym.bind<>AB_EXTERNAL) then begin objsym.GlobalIndex:=NextGlobalIndex; Inc(NextGlobalIndex); objsym.ExtraData:=TWasmObjSymbolExtraData(FData.FObjSymbolsExtraDataList.Find(objsym.Name)); WriteWasmBasicType(FWasmSections[wsiGlobal],objsym.ExtraData.GlobalType); if objsym.ExtraData.GlobalIsImmutable then WriteByte(FWasmSections[wsiGlobal],$00) { const } else WriteByte(FWasmSections[wsiGlobal],$01); { var } { init expr } case objsym.ExtraData.GlobalType of wbt_i32: begin WriteByte(FWasmSections[wsiGlobal],$41); { i32.const } WriteByte(FWasmSections[wsiGlobal],0); { 0 (in signed LEB128 format) } WriteByte(FWasmSections[wsiGlobal],$0B); { end } end; wbt_i64: begin WriteByte(FWasmSections[wsiGlobal],$42); { i64.const } WriteByte(FWasmSections[wsiGlobal],0); { 0 (in signed LEB128 format) } WriteByte(FWasmSections[wsiGlobal],$0B); { end } end; wbt_f32: begin WriteByte(FWasmSections[wsiGlobal],$43); { f32.const } WriteByte(FWasmSections[wsiGlobal],$00); { 0 (in little endian IEEE single precision floating point format) } WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$0B); { end } end; wbt_f64: begin WriteByte(FWasmSections[wsiGlobal],$44); { f64.const } WriteByte(FWasmSections[wsiGlobal],$00); { 0 (in little endian IEEE double precision floating point format) } WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$00); WriteByte(FWasmSections[wsiGlobal],$0B); { end } end; wbt_externref: begin WriteByte(FWasmSections[wsiGlobal],$D0); { ref.null extern } WriteByte(FWasmSections[wsiGlobal],$6F); WriteByte(FWasmSections[wsiGlobal],$0B); { end } end; wbt_funcref: begin WriteByte(FWasmSections[wsiGlobal],$D0); { ref.null func } WriteByte(FWasmSections[wsiGlobal],$70); WriteByte(FWasmSections[wsiGlobal],$0B); { end } end; else internalerror(2022052801); end; end; end; end; if export_functions_count>0 then begin WriteUleb(FWasmSections[wsiExport],export_functions_count); for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if IsExportedFunction(objsym) then begin WriteName(FWasmSections[wsiExport],TWasmObjSymbolExtraData(FData.FObjSymbolsExtraDataList.Find(objsym.Name)).ExportName); WriteByte(FWasmSections[wsiExport],0); { func } if (objsym.FuncIndex<0) then message1(asmw_e_illegal_unset_index,objsym.name) else WriteUleb(FWasmSections[wsiExport],objsym.FuncIndex); end; end; end; Writer.write(WasmModuleMagic,SizeOf(WasmModuleMagic)); Writer.write(WasmVersion,SizeOf(WasmVersion)); if ts_wasm_threads in current_settings.targetswitches then begin WriteUleb(FWasmCustomSections[wcstTargetFeatures],4); WriteUleb(FWasmCustomSections[wcstTargetFeatures],$2B); WriteName(FWasmCustomSections[wcstTargetFeatures],'atomics'); WriteUleb(FWasmCustomSections[wcstTargetFeatures],$2B); WriteName(FWasmCustomSections[wcstTargetFeatures],'bulk-memory'); WriteUleb(FWasmCustomSections[wcstTargetFeatures],$2B); WriteName(FWasmCustomSections[wcstTargetFeatures],'mutable-globals'); WriteUleb(FWasmCustomSections[wcstTargetFeatures],$2B); WriteName(FWasmCustomSections[wcstTargetFeatures],'sign-ext'); end else begin WriteUleb(FWasmCustomSections[wcstTargetFeatures],3); WriteUleb(FWasmCustomSections[wcstTargetFeatures],$2B); WriteName(FWasmCustomSections[wcstTargetFeatures],'bulk-memory'); WriteUleb(FWasmCustomSections[wcstTargetFeatures],$2B); WriteName(FWasmCustomSections[wcstTargetFeatures],'mutable-globals'); WriteUleb(FWasmCustomSections[wcstTargetFeatures],$2B); WriteName(FWasmCustomSections[wcstTargetFeatures],'sign-ext'); end; { Write the producers section: https://github.com/WebAssembly/tool-conventions/blob/main/ProducersSection.md } WriteUleb(FWasmCustomSections[wcstProducers],2); WriteName(FWasmCustomSections[wcstProducers],'language'); WriteUleb(FWasmCustomSections[wcstProducers],1); WriteName(FWasmCustomSections[wcstProducers],'Pascal'); WriteName(FWasmCustomSections[wcstProducers],''); WriteName(FWasmCustomSections[wcstProducers],'processed-by'); WriteUleb(FWasmCustomSections[wcstProducers],1); WriteName(FWasmCustomSections[wcstProducers],'Free Pascal Compiler (FPC)'); WriteName(FWasmCustomSections[wcstProducers],full_version_string+' ['+date_string+'] for '+target_cpu_string+' - '+target_info.shortname); code_section_nr:=-1; data_section_nr:=-1; debug_abbrev_section_nr:=-1; debug_info_section_nr:=-1; debug_str_section_nr:=-1; debug_line_section_nr:=-1; debug_frame_section_nr:=-1; debug_aranges_section_nr:=-1; debug_ranges_section_nr:=-1; section_nr:=0; WriteWasmSection(wsiType); Inc(section_nr); WriteWasmSection(wsiImport); Inc(section_nr); WriteWasmSection(wsiFunction); Inc(section_nr); if exception_tags_count>0 then begin WriteWasmSection(wsiTag); Inc(section_nr); end; if globals_count>0 then begin WriteWasmSection(wsiGlobal); Inc(section_nr); end; if export_functions_count>0 then begin WriteWasmSection(wsiExport); Inc(section_nr); end; { determine the section numbers for the datacount, code, data and debug sections ahead of time } if segment_count>0 then Inc(section_nr); { the DataCount section } code_section_nr:=section_nr; { the Code section } Inc(section_nr); if segment_count>0 then begin data_section_nr:=section_nr; { the Data section } Inc(section_nr); end; { the debug sections } MaybeAddDebugSectionToSymbolTable(wcstDebugAbbrev,debug_abbrev_section_nr); MaybeAddDebugSectionToSymbolTable(wcstDebugInfo,debug_info_section_nr); MaybeAddDebugSectionToSymbolTable(wcstDebugStr,debug_str_section_nr); MaybeAddDebugSectionToSymbolTable(wcstDebugLine,debug_line_section_nr); MaybeAddDebugSectionToSymbolTable(wcstDebugFrame,debug_frame_section_nr); MaybeAddDebugSectionToSymbolTable(wcstDebugAranges,debug_aranges_section_nr); MaybeAddDebugSectionToSymbolTable(wcstDebugRanges,debug_ranges_section_nr); for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if objsym.typ=AT_WASM_EXCEPTION_TAG then begin objsym.SymbolIndex:=FWasmSymbolTableEntriesCount; Inc(FWasmSymbolTableEntriesCount); WriteByte(FWasmSymbolTable,Ord(SYMTAB_EVENT)); if objsym.bind=AB_GLOBAL then WriteUleb(FWasmSymbolTable,0) else if objsym.bind=AB_LOCAL then WriteUleb(FWasmSymbolTable,WASM_SYM_BINDING_LOCAL) else if objsym.bind=AB_EXTERNAL then WriteUleb(FWasmSymbolTable,WASM_SYM_UNDEFINED) else if objsym.bind=AB_WEAK then WriteUleb(FWasmSymbolTable,WASM_SYM_BINDING_WEAK) else internalerror(2021092715); if (objsym.TagIndex<0) then message1(asmw_e_illegal_unset_index,objsym.name) else WriteUleb(FWasmSymbolTable,objsym.TagIndex); if objsym.bind<>AB_EXTERNAL then WriteName(FWasmSymbolTable,objsym.Name); end else if objsym.typ=AT_WASM_GLOBAL then begin objsym.SymbolIndex:=FWasmSymbolTableEntriesCount; Inc(FWasmSymbolTableEntriesCount); WriteByte(FWasmSymbolTable,Ord(SYMTAB_GLOBAL)); if objsym.bind=AB_EXTERNAL then begin WriteUleb(FWasmSymbolTable,WASM_SYM_UNDEFINED); if (objsym.GlobalIndex<0) then message1(asmw_e_illegal_unset_index,objsym.name) else WriteUleb(FWasmSymbolTable,objsym.GlobalIndex); end else begin WriteUleb(FWasmSymbolTable,0); if (objsym.GlobalIndex<0) then message1(asmw_e_illegal_unset_index,objsym.name) else WriteUleb(FWasmSymbolTable,objsym.GlobalIndex); WriteName(FWasmSymbolTable,objsym.Name); end; end else if IsExternalFunction(objsym) then begin objsym.SymbolIndex:=FWasmSymbolTableEntriesCount; Inc(FWasmSymbolTableEntriesCount); WriteByte(FWasmSymbolTable,Ord(SYMTAB_FUNCTION)); if objsym.ExtraData.ImportModule<>'' then begin WriteUleb(FWasmSymbolTable,WASM_SYM_UNDEFINED or WASM_SYM_EXPLICIT_NAME); if (objsym.FuncIndex<0) then message1(asmw_e_illegal_unset_index,objsym.name) else WriteUleb(FWasmSymbolTable,objsym.FuncIndex); WriteName(FWasmSymbolTable,objsym.Name); end else begin WriteUleb(FWasmSymbolTable,WASM_SYM_UNDEFINED); if (objsym.FuncIndex<0) then message1(asmw_e_illegal_unset_index,objsym.name) else WriteUleb(FWasmSymbolTable,objsym.FuncIndex); end; end else if objsym.typ=AT_FUNCTION then begin objsym.SymbolIndex:=FWasmSymbolTableEntriesCount; Inc(FWasmSymbolTableEntriesCount); WriteByte(FWasmSymbolTable,Ord(SYMTAB_FUNCTION)); if objsym.IsAlias then begin ObjSymAlias:=TWasmObjSymbol(Data.ObjSymbolList.Find(objsym.AliasOf)); ObjSym.FuncIndex:=ObjSymAlias.FuncIndex; WriteUleb(FWasmSymbolTable,WASM_SYM_EXPLICIT_NAME or WASM_SYM_NO_STRIP); WriteUleb(FWasmSymbolTable,ObjSymAlias.FuncIndex); end else begin if IsExportedFunction(objsym) then WriteUleb(FWasmSymbolTable,WASM_SYM_EXPORTED) else WriteUleb(FWasmSymbolTable,0); if (objsym.FuncIndex<0) then message1(asmw_e_illegal_unset_index,objsym.name) else WriteUleb(FWasmSymbolTable,objsym.FuncIndex); end; WriteName(FWasmSymbolTable,objsym.Name); end else if (objsym.typ in [AT_DATA,AT_TLS,AT_METADATA]) or ((objsym.typ=AT_NONE) and (objsym.bind=AB_EXTERNAL)) then begin if (objsym.bind<>AB_EXTERNAL) and TWasmObjSection(objsym.objsection).IsDebug then begin objsym.SymbolIndex:=FWasmSymbolTableEntriesCount; Inc(FWasmSymbolTableEntriesCount); WriteByte(FWasmSymbolTable,Ord(SYMTAB_FPC_CUSTOM)); if objsym.bind=AB_GLOBAL then SymbolFlags:=0 else if objsym.bind=AB_LOCAL then SymbolFlags:=WASM_SYM_BINDING_LOCAL else if objsym.bind=AB_EXTERNAL then SymbolFlags:=WASM_SYM_UNDEFINED else internalerror(2024090701); WriteUleb(FWasmSymbolTable,SymbolFlags); WriteName(FWasmSymbolTable,objsym.Name); WriteUleb(FWasmSymbolTable,TWasmObjSection(objsym.objsection).CustomSectionIdx); WriteUleb(FWasmSymbolTable,objsym.offset); WriteUleb(FWasmSymbolTable,objsym.size); end else begin objsym.SymbolIndex:=FWasmSymbolTableEntriesCount; Inc(FWasmSymbolTableEntriesCount); WriteByte(FWasmSymbolTable,Ord(SYMTAB_DATA)); if objsym.bind=AB_GLOBAL then SymbolFlags:=0 else if objsym.bind=AB_LOCAL then SymbolFlags:=WASM_SYM_BINDING_LOCAL else if objsym.bind=AB_EXTERNAL then SymbolFlags:=WASM_SYM_UNDEFINED else internalerror(2021092506); if (objsym.typ=AT_TLS) and (ts_wasm_threads in current_settings.targetswitches) then SymbolFlags:=(SymbolFlags and not WASM_SYM_BINDING_LOCAL) or WASM_SYM_TLS; WriteUleb(FWasmSymbolTable,SymbolFlags); WriteName(FWasmSymbolTable,objsym.Name); if objsym.bind<>AB_EXTERNAL then begin WriteUleb(FWasmSymbolTable,TWasmObjSection(objsym.objsection).SegIdx); WriteUleb(FWasmSymbolTable,objsym.offset); WriteUleb(FWasmSymbolTable,objsym.size); end; end; end; end; DoRelocations; if segment_count>0 then begin WriteUleb(FWasmSections[wsiData],segment_count); WriteUleb(FWasmSections[wsiDataCount],segment_count); WriteUleb(FWasmLinkingSubsections[WASM_SEGMENT_INFO],segment_count); for i:=0 to Data.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(Data.ObjSectionList[i]); if objsec.IsData then begin WriteName(FWasmLinkingSubsections[WASM_SEGMENT_INFO],objsec.Name); WriteUleb(FWasmLinkingSubsections[WASM_SEGMENT_INFO],BsrQWord(objsec.SecAlign)); SegmentFlags:=0; if (ts_wasm_threads in current_settings.targetswitches) and (oso_threadvar in objsec.SecOptions) then SegmentFlags:=SegmentFlags or WASM_SEG_FLAG_TLS; WriteUleb(FWasmLinkingSubsections[WASM_SEGMENT_INFO],SegmentFlags); { flags } WriteByte(FWasmSections[wsiData],0); WriteByte(FWasmSections[wsiData],$41); WriteSleb(FWasmSections[wsiData],objsec.SegOfs); WriteByte(FWasmSections[wsiData],$0b); WriteUleb(FWasmSections[wsiData],objsec.Size); objsec.FileSectionOfs:=FWasmSections[wsiData].size; if oso_Data in objsec.SecOptions then begin objsec.Data.seek(0); CopyDynamicArray(objsec.Data,FWasmSections[wsiData],objsec.Size); end else begin WriteZeros(FWasmSections[wsiData],objsec.Size); end; end; end; end; WriteUleb(FWasmSections[wsiCode],functions_count); for i:=0 to Data.ObjSymbolList.Count-1 do begin objsym:=TWasmObjSymbol(Data.ObjSymbolList[i]); if (objsym.typ=AT_FUNCTION) and not objsym.IsAlias then WriteFunctionCode(FWasmSections[wsiCode],objsym); end; if segment_count>0 then WriteWasmSection(wsiDataCount); WriteWasmSection(wsiCode); if segment_count>0 then WriteWasmSection(wsiData); MaybeWriteDebugSection(wcstDebugAbbrev); MaybeWriteDebugSection(wcstDebugInfo); MaybeWriteDebugSection(wcstDebugStr); MaybeWriteDebugSection(wcstDebugLine); MaybeWriteDebugSection(wcstDebugFrame); MaybeWriteDebugSection(wcstDebugAranges); MaybeWriteDebugSection(wcstDebugRanges); WriteRelocations; WriteSymbolTable; WriteLinkingSubsection(WASM_SYMBOL_TABLE); if segment_count>0 then WriteLinkingSubsection(WASM_SEGMENT_INFO); WriteRelocationCodeTable(code_section_nr); if segment_count>0 then WriteRelocationDataTable(data_section_nr); WriteWasmCustomSection(wcstLinking); Inc(section_nr); WriteWasmCustomSection(wcstRelocCode); Inc(section_nr); if segment_count>0 then begin WriteWasmCustomSection(wcstRelocData); Inc(section_nr); end; MaybeWriteRelocationDebugTable(wcstRelocDebugAbbrev,debug_abbrev_section_nr,FWasmRelocationDebugAbbrevTableEntriesCount,FWasmRelocationDebugAbbrevTable); MaybeWriteRelocationDebugTable(wcstRelocDebugInfo,debug_info_section_nr,FWasmRelocationDebugInfoTableEntriesCount,FWasmRelocationDebugInfoTable); MaybeWriteRelocationDebugTable(wcstRelocDebugStr,debug_str_section_nr,FWasmRelocationDebugStrTableEntriesCount,FWasmRelocationDebugStrTable); MaybeWriteRelocationDebugTable(wcstRelocDebugLine,debug_line_section_nr,FWasmRelocationDebugLineTableEntriesCount,FWasmRelocationDebugLineTable); MaybeWriteRelocationDebugTable(wcstRelocDebugFrame,debug_frame_section_nr,FWasmRelocationDebugFrameTableEntriesCount,FWasmRelocationDebugFrameTable); MaybeWriteRelocationDebugTable(wcstRelocDebugAranges,debug_aranges_section_nr,FWasmRelocationDebugArangesTableEntriesCount,FWasmRelocationDebugArangesTable); MaybeWriteRelocationDebugTable(wcstRelocDebugRanges,debug_ranges_section_nr,FWasmRelocationDebugRangesTableEntriesCount,FWasmRelocationDebugRangesTable); WriteWasmCustomSection(wcstProducers); Inc(section_nr); WriteWasmCustomSection(wcstTargetFeatures); Inc(section_nr); result:=true; end; constructor TWasmObjOutput.create(AWriter: TObjectWriter); var i: TWasmSectionID; j: TWasmCustomSectionType; k: TWasmLinkingSubsectionType; begin inherited; cobjdata:=TWasmObjData; for i in TWasmSectionID do FWasmSections[i] := tdynamicarray.create(SectionDataMaxGrow); for j in TWasmCustomSectionType do FWasmCustomSections[j] := tdynamicarray.create(SectionDataMaxGrow); for k:=low(TWasmLinkingSubsectionType) to high(TWasmLinkingSubsectionType) do FWasmLinkingSubsections[k] := tdynamicarray.create(SectionDataMaxGrow); FWasmSymbolTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmSymbolTableEntriesCount:=0; FWasmRelocationCodeTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmRelocationCodeTableEntriesCount:=0; FWasmRelocationDataTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmRelocationDataTableEntriesCount:=0; FWasmRelocationDebugFrameTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmRelocationDebugFrameTableEntriesCount:=0; FWasmRelocationDebugInfoTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmRelocationDebugInfoTableEntriesCount:=0; FWasmRelocationDebugLineTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmRelocationDebugLineTableEntriesCount:=0; FWasmRelocationDebugAbbrevTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmRelocationDebugAbbrevTableEntriesCount:=0; FWasmRelocationDebugArangesTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmRelocationDebugArangesTableEntriesCount:=0; FWasmRelocationDebugRangesTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmRelocationDebugRangesTableEntriesCount:=0; FWasmRelocationDebugStrTable:=tdynamicarray.create(SectionDataMaxGrow); FWasmRelocationDebugStrTableEntriesCount:=0; end; destructor TWasmObjOutput.destroy; var i: TWasmSectionID; j: TWasmCustomSectionType; k: TWasmLinkingSubsectionType; begin for i in TWasmSectionID do FWasmSections[i].Free; for j in TWasmCustomSectionType do FWasmCustomSections[j].Free; for k:=low(TWasmLinkingSubsectionType) to high(TWasmLinkingSubsectionType) do FWasmLinkingSubsections[k].Free; FWasmSymbolTable.Free; FWasmRelocationCodeTable.Free; FWasmRelocationDataTable.Free; FWasmRelocationDebugFrameTable.Free; FWasmRelocationDebugInfoTable.Free; FWasmRelocationDebugLineTable.Free; FWasmRelocationDebugAbbrevTable.Free; FWasmRelocationDebugArangesTable.Free; FWasmRelocationDebugRangesTable.Free; FWasmRelocationDebugStrTable.Free; inherited destroy; end; {**************************************************************************** TWasmObjInput ****************************************************************************} constructor TWasmObjInput.create; begin inherited create; cobjdata:=TWasmObjData; end; destructor TWasmObjInput.Destroy; var i: Integer; begin for i:=low(FFuncTypes) to high(FFuncTypes) do begin FFuncTypes[i].free; FFuncTypes[i]:=nil; end; inherited Destroy; end; class function TWasmObjInput.CanReadObjData(AReader: TObjectreader): boolean; var ModuleMagic: array [0..3] of Byte; ModuleVersion: array [0..3] of Byte; i: Integer; begin result:=false; if not AReader.read(ModuleMagic,4) then exit; for i:=0 to 3 do if ModuleMagic[i]<>WasmModuleMagic[i] then exit; if not AReader.read(ModuleVersion,4) then exit; for i:=0 to 3 do if ModuleVersion[i]<>WasmVersion[i] then exit; result:=true; end; function TWasmObjInput.ReadObjData(AReader: TObjectreader; out ObjData: TObjData): boolean; type TLimits = record Min, Max: uint32; HasMax: Boolean; end; var SectionIndex: Integer = -1; SectionId: Byte; SectionSize: uint32; SectionStart: LongInt; CheckSectionBounds: Boolean; TypeSectionRead: Boolean = false; ImportSectionRead: Boolean = false; FunctionSectionRead: Boolean = false; GlobalSectionRead: Boolean = false; ExportSectionRead: Boolean = false; ElementSectionRead: Boolean = false; TagSectionRead: Boolean = false; CodeSectionRead: Boolean = false; DataSectionRead: Boolean = false; DataCountSectionRead: Boolean = false; SegmentInfoSectionRead: Boolean = false; SymbolTableSectionRead: Boolean = false; CodeSectionIndex: Integer = -1; DataSectionIndex: Integer = -1; DebugSectionIndex: array [TWasmCustomDebugSectionType] of Integer = (-1,-1,-1,-1,-1,-1,-1); FuncTypes: array of record IsImport: Boolean; ImportName: ansistring; ImportModName: ansistring; typidx: uint32; IsExported: Boolean; ExportName: ansistring; end; FuncTypeImportsCount: uint32; TableTypes: array of record IsImport: Boolean; ImportName: ansistring; ImportModName: ansistring; reftype: TWasmBAsicType; limits: TLimits; IsExported: Boolean; ExportName: ansistring; end; TableTypeImportsCount: uint32; MemTypes: array of record IsImport: Boolean; ImportName: ansistring; ImportModName: ansistring; limits: TLimits; IsExported: Boolean; ExportName: ansistring; end; MemTypeImportsCount: uint32; GlobalTypes: array of record IsImport: Boolean; ImportName: ansistring; ImportModName: ansistring; valtype: TWasmBasicType; IsMutable: Boolean; IsExported: Boolean; ExportName: ansistring; GlobalInit: TGlobalInitializer; end; GlobalTypeImportsCount: uint32; TagTypes: array of record IsImport: Boolean; ImportName: ansistring; ImportModName: ansistring; TagAttr: Byte; TagTypeIdx: uint32; IsExported: Boolean; ExportName: ansistring; end; TagTypeImportsCount: uint32; CodeSegments: array of record CodeSectionOffset: uint32; CodeSize: uint32; DataPos: LongInt; SegName: ansistring; SegIsExported: Boolean; end; DataSegments: array of record DataSectionOffset: uint32; Active: Boolean; MemIdx: uint32; Len: uint32; Offset: int32; DataPos: LongInt; SegName: ansistring; SegAlignment: uint32; SegFlags: uint32; end; SymbolTable: array of record SymFlags: uint32; TargetSection: uint32; SymIndex: uint32; SymOffset: uint32; SymSize: uint32; SymCustomSectionIndex: uint32; SymCustomSectionType: TWasmCustomDebugSectionType; SymKind: TWasmSymbolType; SymName: ansistring; ObjSym: TWasmObjSymbol; ObjSec: TWasmObjSection; end; { meaning of first index: } { table 0 is code relocs } { table 1 is data relocs } { tables 2.. are custom section relocs for debug sections } RelocationTable: array of array of record RelocType: TWasmRelocationType; RelocOffset: uint32; RelocIndex: uint32; RelocAddend: int32; end; function FindDebugSectionByIndex(SectionIndex: Integer; out res: TWasmCustomDebugSectionType): Boolean; var ds: TWasmCustomDebugSectionType; begin for ds in TWasmCustomDebugSectionType do if DebugSectionIndex[ds]=SectionIndex then begin Res:=ds; Result:=True; exit; end; Res:=low(TWasmCustomDebugSectionType); Result:=False; end; function ReadSection: Boolean; function read(out b;len:longint):boolean; begin result:=false; if not CheckSectionBounds or ((AReader.Pos+len)<=(SectionStart+SectionSize)) then result:=AReader.read(b,len) else begin { trying to read beyond the end of the section } AReader.read(b,SectionStart+SectionSize-AReader.Pos); result:=false; end; end; function ReadUleb(out v: uint64): boolean; var b: byte; shift:integer; begin result:=false; b:=0; v:=0; shift:=0; repeat if not read(b,1) then exit; v:=v or (uint64(b and 127) shl shift); inc(shift,7); until (b and 128)=0; result:=true; end; function ReadUleb32(out v: uint32): boolean; var vv: uint64; begin result:=false; v:=default(uint32); if not ReadUleb(vv) then exit; if vv>high(uint32) then exit; v:=vv; result:=true; end; function ReadSleb(out v: int64): boolean; var b: byte; shift:integer; begin result:=false; b:=0; v:=0; shift:=0; repeat if not read(b,1) then exit; v:=v or (uint64(b and 127) shl shift); inc(shift,7); until (b and 128)=0; {$ifopt Q+} {$define overflowon} {$Q-} {$endif} {$ifopt R+} {$define rangeon} {$R-} {$endif} if (b and 64)<>0 then v:=v or (high(uint64) shl shift); result:=true; end; {$ifdef overflowon} {$Q+} {$undef overflowon} {$endif} {$ifdef rangeon} {$R+} {$undef rangeon} {$endif} function ReadSleb32(out v: int32): boolean; var vv: int64; begin result:=false; v:=default(int32); if not ReadSleb(vv) then exit; if (vv>high(int32)) or (vv0 then result:=read(v[1],len) else result:=true; end; function ReadCustomSection: Boolean; function ReadRelocationSection: Boolean; var TargetSection, RelocCount: uint32; i: Integer; RelocTableIndex: Integer; ds: TWasmCustomDebugSectionType; begin Result:=False; if not ReadUleb32(TargetSection) then begin InputError('Error reading the index of the target section of a relocation section'); exit; end; if TargetSection=CodeSectionIndex then RelocTableIndex:=0 else if TargetSection=DataSectionIndex then RelocTableIndex:=1 else begin RelocTableIndex:=-1; for ds:=Low(DebugSectionIndex) to High(DebugSectionIndex) do if DebugSectionIndex[ds]=TargetSection then begin RelocTableIndex:=2+(Ord(ds)-Ord(Low(TWasmCustomDebugSectionType))); break; end; if RelocTableIndex=-1 then begin InputError('Relocation found for a custom section, that is not supported'); exit; end; end; if not ReadUleb32(RelocCount) then begin InputError('Error reading the relocation entries count from a relocation section'); exit; end; SetLength(RelocationTable[RelocTableIndex],RelocCount); for i:=0 to RelocCount-1 do with RelocationTable[RelocTableIndex,i] do begin if not Read(RelocType,1) then begin InputError('Error reading the relocation type of a relocation entry'); exit; end; if not (RelocType in [R_WASM_FUNCTION_INDEX_LEB, R_WASM_MEMORY_ADDR_LEB, R_WASM_TABLE_INDEX_SLEB, R_WASM_MEMORY_ADDR_SLEB, R_WASM_SECTION_OFFSET_I32, R_WASM_TABLE_INDEX_I32, R_WASM_FUNCTION_OFFSET_I32, R_WASM_MEMORY_ADDR_I32, R_WASM_TYPE_INDEX_LEB, R_WASM_GLOBAL_INDEX_LEB, R_WASM_TAG_INDEX_LEB, R_WASM_GLOBAL_INDEX_I32]) then begin InputError('Unsupported relocation type: ' + tostr(Ord(RelocType))); exit; end; if not ReadUleb32(RelocOffset) then begin InputError('Error reading the relocation offset of a relocation entry'); exit; end; if not ReadUleb32(RelocIndex) then begin InputError('Error reading the relocation index of a relocation entry'); exit; end; if RelocType in [R_WASM_FUNCTION_OFFSET_I32,R_WASM_SECTION_OFFSET_I32,R_WASM_MEMORY_ADDR_LEB,R_WASM_MEMORY_ADDR_SLEB,R_WASM_MEMORY_ADDR_I32] then begin if not ReadSleb32(RelocAddend) then begin InputError('Error reading the relocation addend of a relocation entry'); exit; end; end; if (RelocType in [ R_WASM_SECTION_OFFSET_I32, R_WASM_FUNCTION_INDEX_LEB, R_WASM_TABLE_INDEX_SLEB, R_WASM_TABLE_INDEX_I32, R_WASM_MEMORY_ADDR_LEB, R_WASM_MEMORY_ADDR_SLEB, R_WASM_MEMORY_ADDR_I32, R_WASM_FUNCTION_OFFSET_I32, R_WASM_GLOBAL_INDEX_LEB, R_WASM_GLOBAL_INDEX_I32]) and (RelocIndex>High(SymbolTable)) then begin InputError('Relocation index outside the bounds of the symbol table'); exit; end; if (RelocType=R_WASM_TYPE_INDEX_LEB) and (RelocIndex>High(FFuncTypes)) then begin InputError('Relocation index of R_WASM_TYPE_INDEX_LEB outside the bounds of the func types, defined in the func section of the module'); exit; end; if (RelocType=R_WASM_SECTION_OFFSET_I32) and (SymbolTable[RelocIndex].SymKind<>SYMTAB_SECTION) then begin InputError('R_WASM_SECTION_OFFSET_I32 must point to a SYMTAB_SECTION symbol'); exit; end; if (RelocType in [R_WASM_GLOBAL_INDEX_LEB,R_WASM_GLOBAL_INDEX_I32]) and not ((SymbolTable[RelocIndex].SymKind=SYMTAB_GLOBAL) or ((ts_wasm_threads in current_settings.targetswitches) and (SymbolTable[RelocIndex].SymKind=SYMTAB_DATA) and ((SymbolTable[RelocIndex].SymFlags and WASM_SYM_TLS)<>0))) then begin if ts_wasm_threads in current_settings.targetswitches then InputError('Relocation must point to a SYMTAB_GLOBAL symbol or a SYMTAB_DATA symbol with the WASM_SYM_TLS flag set') else InputError('Relocation must point to a SYMTAB_GLOBAL symbol'); exit; end; if (RelocType=R_WASM_TAG_INDEX_LEB) and (SymbolTable[RelocIndex].SymKind<>SYMTAB_EVENT) then begin InputError('Relocation must point to a SYMTAB_EVENT symbol'); exit; end; if (RelocType in [ R_WASM_FUNCTION_INDEX_LEB, R_WASM_TABLE_INDEX_SLEB, R_WASM_TABLE_INDEX_I32, R_WASM_FUNCTION_OFFSET_I32]) and (SymbolTable[RelocIndex].SymKind<>SYMTAB_FUNCTION) then begin InputError('Relocation must point to a SYMTAB_FUNCTION symbol'); exit; end; if (RelocType in [ R_WASM_MEMORY_ADDR_LEB, R_WASM_MEMORY_ADDR_SLEB, R_WASM_MEMORY_ADDR_I32]) and (SymbolTable[RelocIndex].SymKind<>SYMTAB_DATA) then begin InputError('Relocation must point to a SYMTAB_DATA symbol'); exit; end; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected relocation section size'); exit; end; Result:=True; end; function ReadLinkingSection: Boolean; function ReadSegmentInfo: Boolean; var SegmentCount: uint32; i: Integer; begin Result:=False; if SegmentInfoSectionRead then begin InputError('The WASM_SEGMENT_INFO subsection is duplicated'); exit; end; SegmentInfoSectionRead:=True; if not ReadUleb32(SegmentCount) then begin InputError('Error reading the segment count from the WASM_SEGMENT_INFO subsection of the ''linking'' section'); exit; end; if SegmentCount<>Length(DataSegments) then begin InputError('Segment count in the WASM_SEGMENT_INFO subsection does not match the data count in the data section'); exit; end; for i:=0 to SegmentCount-1 do with DataSegments[i] do begin if not ReadName(SegName) then begin InputError('Error reading segment name from the WASM_SEGMENT_INFO subsection of the ''linking'' section'); exit; end; if not ReadUleb32(SegAlignment) then begin InputError('Error reading segment alignment from the WASM_SEGMENT_INFO subsection of the ''linking'' section'); exit; end; if not ReadUleb32(SegFlags) then begin InputError('Error reading segment flags from the WASM_SEGMENT_INFO subsection of the ''linking'' section'); exit; end; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected WASM_SEGMENT_INFO section size'); exit; end; Result:=True; end; function ReadSymbolTable: Boolean; var SymCount: uint32; i: Integer; SymKindName: string; SymKindB: Byte; begin Result:=False; if SymbolTableSectionRead then begin InputError('The WASM_SYMBOL_TABLE subsection is duplicated'); exit; end; SymbolTableSectionRead:=True; if not ReadUleb32(SymCount) then begin InputError('Error reading the symbol count from the WASM_SYMBOL_TABLE subsection of the ''linking'' section'); exit; end; SetLength(SymbolTable,SymCount); for i:=0 to SymCount-1 do with SymbolTable[i] do begin if not Read(SymKindB,1) then begin InputError('Error reading symbol type from the WASM_SYMBOL_TABLE subsection of the ''linking'' section'); exit; end; if SymKindB>Ord(High(TWasmSymbolType)) then begin InputError('Unsupported symbol type from the WASM_SYMBOL_TABLE subsection of the ''linking'' section'); exit; end; SymKind:=TWasmSymbolType(SymKindB); if not ReadUleb32(SymFlags) then begin InputError('Error reading symbol flags from the WASM_SYMBOL_TABLE subsection of the ''linking'' section'); exit; end; case SymKind of SYMTAB_FUNCTION, SYMTAB_GLOBAL, SYMTAB_EVENT, SYMTAB_TABLE: begin WriteStr(SymKindName, SymKind); if not ReadUleb32(SymIndex) then begin InputError('Error reading the index of a ' + SymKindName + ' symbol'); exit; end; if ((SymKind=SYMTAB_FUNCTION) and (SymIndex>high(FuncTypes))) or ((SymKind=SYMTAB_EVENT) and (SymIndex>high(TagTypes))) then begin InputError('Symbol index too high'); exit; end; if ((SymFlags and WASM_SYM_EXPLICIT_NAME)<>0) or ((SymFlags and WASM_SYM_UNDEFINED)=0) then begin if not ReadName(SymName) then begin InputError('Error reading symbol name of a ' + SymKindName + ' symbol'); exit; end; end; end; SYMTAB_DATA: begin if not ReadName(SymName) then begin InputError('Error reading symbol name of a SYMTAB_DATA symbol'); exit; end; if (SymFlags and WASM_SYM_UNDEFINED)=0 then begin if not ReadUleb32(SymIndex) then begin InputError('Error reading the data segment index of a SYMTAB_DATA symbol'); exit; end; if SymIndex>high(DataSegments) then begin InputError('Data segment index of SYMTAB_DATA symbol out of bounds'); exit; end; if not ReadUleb32(SymOffset) then begin InputError('Error reading the offset of a SYMTAB_DATA symbol'); exit; end; if not ReadUleb32(SymSize) then begin InputError('Error reading the size of a SYMTAB_DATA symbol'); exit; end; end; end; SYMTAB_FPC_CUSTOM: begin if not ReadName(SymName) then begin InputError('Error reading symbol name of a SYMTAB_FPC_CUSTOM symbol'); exit; end; if (SymFlags and WASM_SYM_UNDEFINED)=0 then begin if not ReadUleb32(SymCustomSectionIndex) then begin InputError('Error reading the custom section index of a SYMTAB_FPC_CUSTOM symbol'); exit; end; if not FindDebugSectionByIndex(SymCustomSectionIndex,SymCustomSectionType) then begin InputError('Custom section index of SYMTAB_FPC_CUSTOM symbol not pointing to a debug section'); exit; end; if not ReadUleb32(SymOffset) then begin InputError('Error reading the offset of a SYMTAB_FPC_CUSTOM symbol'); exit; end; if not ReadUleb32(SymSize) then begin InputError('Error reading the size of a SYMTAB_FPC_CUSTOM symbol'); exit; end; end; end; SYMTAB_SECTION: begin if not ReadUleb32(TargetSection) then begin InputError('Error reading the target section of a SYMTAB_SECTION symbol'); exit; end; end; end; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected WASM_SYMBOL_TABLE section size'); exit; end; Result:=True; end; const ExpectedVersion = 2; var Version, SubsectionSize, SaveSectionSize: uint32; SubsectionType: Byte; SaveSectionStart: LongInt; begin Result:=False; if not ReadUleb32(Version) then begin InputError('Error reading the version of the ''linking'' section'); exit; end; if Version<>ExpectedVersion then begin InputError('The ''linking'' section has an unsupported version (expected version ' + tostr(ExpectedVersion) + ', got version ' + tostr(Version) + ')'); exit; end; while AReader.Pos<(SectionStart+SectionSize) do begin if not read(SubsectionType, 1) then begin InputError('Error reading subsection type in the ''linking'' section'); exit; end; if not ReadUleb32(SubsectionSize) then begin InputError('Error reading subsection size in the ''linking'' section'); exit; end; if (AReader.Pos+SubsectionSize)>(SectionStart+SectionSize) then begin InputError('Subsection size exceeds bounds of its parent ''linking'' section'); exit; end; SaveSectionStart:=SectionStart; SaveSectionSize:=SectionSize; SectionStart:=AReader.Pos; SectionSize:=SubsectionSize; case SubsectionType of Byte(WASM_SEGMENT_INFO): if not ReadSegmentInfo then begin InputError('Error reading the WASM_SEGMENT_INFO subsection of the ''linking'' section'); exit; end; Byte(WASM_SYMBOL_TABLE): if not ReadSymbolTable then begin InputError('Error reading the WASM_SYMBOL_TABLE subsection of the ''linking'' section'); exit; end; else begin InputError('Unsupported ''linking'' section subsection type ' + tostr(SubsectionType)); exit; end; end; AReader.Seek(SectionStart+SectionSize); SectionStart:=SaveSectionStart; SectionSize:=SaveSectionSize; end; result:=True; end; function ReadProducersSection: Boolean; begin Result:=False; end; function ReadTargetFeaturesSection: Boolean; begin Result:=False; end; function ReadDebugSection(const SectionName: string; SectionType: TWasmCustomDebugSectionType): Boolean; var ObjSec: TObjSection; begin Result:=False; if DebugSectionIndex[SectionType]<>-1 then begin InputError('Duplicated debug section: ' + SectionName); exit; end; DebugSectionIndex[SectionType]:=SectionIndex; ObjSec:=ObjData.createsection(SectionName,1,[oso_Data,oso_debug],false); ObjSec.DataPos:=AReader.Pos; ObjSec.Size:=SectionStart+SectionSize-AReader.Pos; Result:=True; end; const RelocationSectionPrefix = 'reloc.'; var SectionName: ansistring; begin Result:=False; ReadName(SectionName); if Copy(SectionName,1,Length(RelocationSectionPrefix)) = RelocationSectionPrefix then begin if not ReadRelocationSection then begin InputError('Error reading the relocation section ''' + SectionName + ''''); exit; end; end else case SectionName of 'linking': if not ReadLinkingSection then begin InputError('Error reading the ''linking'' section'); exit; end; 'producers': Result:=ReadProducersSection; 'target_features': Result:=ReadTargetFeaturesSection; '.debug_frame': if not ReadDebugSection(SectionName, wcstDebugFrame) then begin InputError('Error reading section ' + SectionName); exit; end; '.debug_info': if not ReadDebugSection(SectionName, wcstDebugInfo) then begin InputError('Error reading section ' + SectionName); exit; end; '.debug_line': if not ReadDebugSection(SectionName, wcstDebugLine) then begin InputError('Error reading section ' + SectionName); exit; end; '.debug_abbrev': if not ReadDebugSection(SectionName, wcstDebugAbbrev) then begin InputError('Error reading section ' + SectionName); exit; end; '.debug_aranges': if not ReadDebugSection(SectionName, wcstDebugAranges) then begin InputError('Error reading section ' + SectionName); exit; end; '.debug_ranges': if not ReadDebugSection(SectionName, wcstDebugRanges) then begin InputError('Error reading section ' + SectionName); exit; end; '.debug_str': if not ReadDebugSection(SectionName, wcstDebugStr) then begin InputError('Error reading section ' + SectionName); exit; end; else InputError('Unsupported custom section: ''' + SectionName + ''''); end; Result:=True; end; function ReadTypeSection: Boolean; var FuncTypesCount, ParamsCount, ResultsCount: uint32; FuncTypeId, WasmTypeId: Byte; i, j: Integer; wbt: TWasmBasicType; begin Result:=False; if TypeSectionRead then begin InputError('Type section is duplicated'); exit; end; TypeSectionRead:=True; if not ReadUleb32(FuncTypesCount) then begin InputError('Error reading the func types count'); exit; end; SetLength(FFuncTypes,FuncTypesCount); for i:=0 to FuncTypesCount - 1 do begin FFuncTypes[i]:=TWasmFuncType.Create([],[]); if not AReader.read(FuncTypeId,1) then begin InputError('Error reading the function type identifier'); exit; end; if FuncTypeId<>$60 then begin InputError('Incorrect function type identifier (expected $60, got $' + HexStr(FuncTypeId,2) + ')'); exit; end; if not ReadUleb32(ParamsCount) then begin InputError('Error reading the function parameters count'); exit; end; for j:=0 to ParamsCount-1 do begin if not AReader.read(WasmTypeId,1) then begin InputError('Error reading a function parameter basic type'); exit; end; if not decode_wasm_basic_type(WasmTypeId,wbt) then begin InputError('Unknown function parameter basic type: $' + HexStr(WasmTypeId,2)); exit; end; FFuncTypes[i].add_param(wbt); end; if not ReadUleb32(ResultsCount) then begin InputError('Error reading the function results count'); exit; end; for j:=0 to ResultsCount-1 do begin if not AReader.read(WasmTypeId,1) then begin InputError('Error reading a function result basic type'); exit; end; if not decode_wasm_basic_type(WasmTypeId,wbt) then begin InputError('Unknown function result basic type: $' + HexStr(WasmTypeId,2)); exit; end; FFuncTypes[i].add_result(wbt); end; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected type section size'); exit; end; Result:=true; end; function ReadImportSection: Boolean; var ImportsCount: uint32; i: Integer; ModName, Name: ansistring; ImportType, TableElemTyp, TableLimitsKind, MemoryLimitsKind, GlobalType, GlobalMutabilityType: Byte; begin Result:=False; if ImportSectionRead then begin InputError('Import section is duplicated'); exit; end; ImportSectionRead:=True; if not ReadUleb32(ImportsCount) then begin InputError('Error reading the imports count'); exit; end; for i:=0 to ImportsCount-1 do begin if not ReadName(ModName) then begin InputError('Error reading import module name'); exit; end; if not ReadName(Name) then begin InputError('Error import name'); exit; end; if not AReader.Read(ImportType,1) then begin InputError('Error reading import type'); exit; end; case ImportType of $00: { func } begin Inc(FuncTypeImportsCount); SetLength(FuncTypes,FuncTypeImportsCount); with FuncTypes[FuncTypeImportsCount-1] do begin IsImport:=True; ImportName:=Name; ImportModName:=ModName; if not ReadUleb32(typidx) then begin InputError('Error reading type index for func import'); exit; end; if typidx>high(FFuncTypes) then begin InputError('Type index in func import exceeds bounds of the types table'); exit; end; end; end; $01: { table } begin Inc(TableTypeImportsCount); SetLength(TableTypes,TableTypeImportsCount); with TableTypes[TableTypeImportsCount-1] do begin IsImport:=True; ImportName:=Name; ImportModName:=ModName; if not AReader.read(TableElemTyp,1) then begin InputError('Error reading table element type for table import'); exit; end; if not decode_wasm_basic_type(TableElemTyp,reftype) then begin InputError('Invalid table element type for table import: $' + HexStr(TableElemTyp,2)); exit; end; if not (reftype in WasmReferenceTypes) then begin InputError('Table element type for table import must be a reference type'); exit; end; if not AReader.read(TableLimitsKind,1) then begin InputError('Error reading table limits kind for table import'); exit; end; case TableLimitsKind of $00: begin limits.HasMax:=False; limits.Max:=high(limits.Max); if not ReadUleb32(limits.min) then begin InputError('Error reading table limits min for table import'); exit; end; end; $01: begin limits.HasMax:=True; if not ReadUleb32(limits.min) then begin InputError('Error reading table limits min for table import'); exit; end; if not ReadUleb32(limits.max) then begin InputError('Error reading table limits max for table import'); exit; end; if limits.min>limits.max then begin InputError('Table limits min exceed table limits max in table import'); exit; end; end; else begin InputError('Unsupported table limits kind for table import: $' + HexStr(TableLimitsKind,2)); exit; end; end; end; end; $02: { mem } begin Inc(MemTypeImportsCount); SetLength(MemTypes,MemTypeImportsCount); with MemTypes[MemTypeImportsCount-1] do begin IsImport:=True; ImportName:=Name; ImportModName:=ModName; if not AReader.read(MemoryLimitsKind,1) then begin InputError('Error reading memory limits kind for memory import'); exit; end; case MemoryLimitsKind of $00: begin limits.HasMax:=False; limits.Max:=high(limits.Max); if not ReadUleb32(limits.min) then begin InputError('Error reading memory limits min for memory import'); exit; end; end; $01: begin limits.HasMax:=True; if not ReadUleb32(limits.min) then begin InputError('Error reading memory limits min for memory import'); exit; end; if not ReadUleb32(limits.max) then begin InputError('Error reading memory limits max for memory import'); exit; end; if limits.Min>limits.Max then begin InputError('Memory limits min exceed memory limits max in memory import'); exit; end; end; else begin InputError('Unsupported memory limits kind for memory import: $' + HexStr(MemoryLimitsKind,2)); exit; end; end; end; end; $03: { global } begin Inc(GlobalTypeImportsCount); SetLength(GlobalTypes,GlobalTypeImportsCount); with GlobalTypes[GlobalTypeImportsCount-1] do begin IsImport:=True; ImportName:=Name; ImportModName:=ModName; if not AReader.read(GlobalType,1) then begin InputError('Error reading global type for global import'); exit; end; if not decode_wasm_basic_type(GlobalType,valtype) then begin InputError('Unsupported global type for global import: ' + HexStr(GlobalType,2)); exit; end; if not AReader.read(GlobalMutabilityType,1) then begin InputError('Error reading global mutability flag for global import'); exit; end; case GlobalMutabilityType of $00: IsMutable:=False; $01: IsMutable:=True; else begin InputError('Unknown global mutability flag for global import: $' + HexStr(GlobalMutabilityType,2)); exit; end; end; end; end; $04: { tag } begin Inc(TagTypeImportsCount); SetLength(TagTypes,TagTypeImportsCount); with TagTypes[TagTypeImportsCount-1] do begin IsImport:=True; ImportName:=Name; ImportModName:=ModName; if not Read(TagAttr,1) then begin InputError('Error reading import tag attribute'); exit; end; if not ReadUleb32(TagTypeIdx) then begin InputError('Error reading import tag type index'); exit; end; if TagTypeIdx>high(FFuncTypes) then begin InputError('Type index in tag import exceeds bounds of the types table'); exit; end; end; end; else begin InputError('Unknown import type: $' + HexStr(ImportType,2)); exit; end; end; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected import section size'); exit; end; Result:=true; end; function ReadFunctionSection: Boolean; var FunctionsCount: uint32; i: Integer; begin Result:=False; if FunctionSectionRead then begin InputError('Function section is duplicated'); exit; end; FunctionSectionRead:=True; if not ReadUleb32(FunctionsCount) then begin InputError('Error reading the functions count'); exit; end; SetLength(FuncTypes, FuncTypeImportsCount + FunctionsCount); for i:=0 to FunctionsCount-1 do with FuncTypes[i + FuncTypeImportsCount] do begin IsImport:=False; if not ReadUleb32(typidx) then begin InputError('Error reading type index for function'); exit; end; if typidx>high(FFuncTypes) then begin InputError('Type index in the function section exceeds bounds of the types table'); exit; end; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected function section size'); exit; end; Result:=true; end; function ReadGlobalSection: Boolean; function ParseExpr(out Init: TGlobalInitializer): Boolean; var B, B2: Byte; tmpU32: UInt32; tmpU64: UInt64; begin Result:=False; repeat if not Read(B, 1) then exit; case B of $0B: { end } ; $41: { i32.const } begin Init.typ:=wbt_i32; if not ReadSleb32(Init.init_i32) then exit; end; $42: { i64.const } begin Init.typ:=wbt_i64; if not ReadSleb(Init.init_i64) then exit; end; $43: { f32.const } begin Init.typ:=wbt_f32; if not Read(tmpU32, 4) then exit; {$ifdef FPC_BIG_ENDIAN} tmpU32:=SwapEndian(tmpU32); {$endif FPC_BIG_ENDIAN} Move(tmpU32,Init.init_f32,4); end; $44: { f64.const } begin Init.typ:=wbt_f64; if not Read(tmpU64, 8) then exit; {$ifdef FPC_BIG_ENDIAN} tmpU64:=SwapEndian(tmpU64); {$endif FPC_BIG_ENDIAN} Move(tmpU64,Init.init_f64,8); end; $D0: { ref.null } begin if not Read(B2, 1) then exit; if not decode_wasm_basic_type(B2, Init.typ) then exit; if not (Init.typ in WasmReferenceTypes) then exit; end; else begin InputError('Unsupported opcode in global initializer'); exit; end; end; until b = $0B; Result:=True; end; var GlobalsCount: uint32; i: Integer; vt: Byte; mut: Byte; begin Result:=False; if GlobalSectionRead then begin InputError('Global section is duplicated'); exit; end; GlobalSectionRead:=True; if not ReadUleb32(GlobalsCount) then begin InputError('Error reading the globals count from the global section'); exit; end; SetLength(GlobalTypes,Length(GlobalTypes)+GlobalsCount); for i:=0 to GlobalsCount-1 do with GlobalTypes[i + GlobalTypeImportsCount] do begin if not read(vt,1) then begin InputError('Error reading the type of a global from the global section'); exit; end; if not decode_wasm_basic_type(vt,valtype) then begin InputError('Unsupported type of global in the global section'); exit; end; if not read(mut,1) then begin InputError('Error reading the mutability flag of a global in the global section'); exit; end; case mut of $00: IsMutable:=False; $01: IsMutable:=True; else begin InputError('Unsupported value (' + tostr(mut) + ') for the mutability flag of a global in the global section'); exit; end; end; if not ParseExpr(GlobalInit) then begin InputError('Error parsing the global initializer expression in the global section'); exit; end; if GlobalInit.typ<>valtype then begin InputError('Initializer expression for global produces a type, which does not match the type of the global'); exit; end; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected global section size'); exit; end; Result:=True; end; function ReadExportSection: Boolean; var ExportsCount, FuncIdx, TableIdx, MemIdx, GlobalIdx, TagIdx: uint32; i: Integer; Name: ansistring; ExportType: Byte; begin Result:=False; if ExportSectionRead then begin InputError('Export section is duplicated'); exit; end; ExportSectionRead:=True; if not ReadUleb32(ExportsCount) then begin InputError('Error reading the exports count from the export section'); exit; end; for i:=0 to ExportsCount-1 do begin if not ReadName(Name) then begin InputError('Error reading an export name from the export section'); exit; end; if not Read(ExportType,1) then begin InputError('Error reading an export type from the export section'); exit; end; case ExportType of $00: { func } begin if not ReadUleb32(FuncIdx) then begin InputError('Error reading a func index from the export section'); exit; end; if FuncIdx>high(FuncTypes) then begin InputError('Func index too high in the export section'); exit; end; with FuncTypes[FuncIdx] do begin IsExported:=True; ExportName:=Name; end; end; $01: { table } begin if not ReadUleb32(TableIdx) then begin InputError('Error reading a table index from the export section'); exit; end; if TableIdx>high(TableTypes) then begin InputError('Table index too high in the export section'); exit; end; with TableTypes[TableIdx] do begin IsExported:=True; ExportName:=Name; end; end; $02: { mem } begin if not ReadUleb32(MemIdx) then begin InputError('Error reading a mem index from the export section'); exit; end; if MemIdx>high(MemTypes) then begin InputError('Mem index too high in the export section'); exit; end; with MemTypes[MemIdx] do begin IsExported:=True; ExportName:=Name; end; end; $03: { global } begin if not ReadUleb32(GlobalIdx) then begin InputError('Error reading a global index from the export section'); exit; end; if GlobalIdx>high(GlobalTypes) then begin InputError('Global index too high in the export section'); exit; end; with GlobalTypes[GlobalIdx] do begin IsExported:=True; ExportName:=Name; end; end; $04: { tag } begin if not ReadUleb32(TagIdx) then begin InputError('Error reading a tag index from the export section'); exit; end; if TagIdx>high(TagTypes) then begin InputError('Tag index too high in the export section'); exit; end; with TagTypes[TagIdx] do begin IsExported:=True; ExportName:=Name; end; end; else begin InputError('Unsupported export type in the export section: ' + tostr(ExportType)); exit; end; end; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected export section size'); exit; end; Result:=True; end; function ReadElementSection: Boolean; begin Result:=False; if ElementSectionRead then begin InputError('Element section is duplicated'); exit; end; ElementSectionRead:=True; { We skip the element section for now } { TODO: implement reading it (and linking of tables) } Result:=True; end; function ReadTagSection: Boolean; var TagCount: uint32; i: Integer; begin Result:=False; if TagSectionRead then begin InputError('Tag section is duplicated'); exit; end; TagSectionRead:=True; if not ReadUleb32(TagCount) then begin InputError('Error reading the tag count from the tag section'); exit; end; SetLength(TagTypes,Length(TagTypes)+TagCount); for i:=0 to TagCount-1 do with TagTypes[i + TagTypeImportsCount] do begin if not Read(TagAttr,1) then begin InputError('Error reading tag attribute'); exit; end; if not ReadUleb32(TagTypeIdx) then begin InputError('Error reading tag type index'); exit; end; if TagTypeIdx>high(FFuncTypes) then begin InputError('Type index in tag import exceeds bounds of the types table'); exit; end; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected tag section size'); exit; end; Result:=True; end; function ReadCodeSection: Boolean; var CodeEntriesCount: uint32; i: Integer; begin Result:=False; if CodeSectionRead then begin InputError('Code section is duplicated'); exit; end; CodeSectionRead:=True; CodeSectionIndex:=SectionIndex; if not ReadUleb32(CodeEntriesCount) then begin InputError('Error reading the code entries cound from the code section'); exit; end; if CodeEntriesCount <> (Length(FuncTypes) - FuncTypeImportsCount) then begin InputError('Code segment count in the code section does not match the function definition count in the function section'); exit; end; SetLength(CodeSegments,CodeEntriesCount); for i:=0 to CodeEntriesCount-1 do with CodeSegments[i] do begin if not ReadUleb32(CodeSize) then begin InputError('Error reading the code size of an entry in the code section'); exit; end; if (AReader.Pos+CodeSize)>(SectionStart+SectionSize) then begin InputError('Code segment exceeds the bounds of the code section'); exit; end; DataPos:=AReader.Pos; CodeSectionOffset:=AReader.Pos-SectionStart; AReader.Seek(AReader.Pos+CodeSize); end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected code section size'); exit; end; Result:=true; end; function ReadDataSection: Boolean; function ReadExpr(out ExprV: int32): Boolean; var b: Byte; begin Result:=False; if not Read(b,1) then exit; if b<>$41 then begin InputError('Only i32.const expressions supported'); exit; end; if not ReadSleb32(ExprV) then exit; if not Read(b,1) then exit; if b<>$0B then begin InputError('Only single const expressions supported'); exit; end; Result:=True; end; var DataCount: uint32; DataType: Byte; i: Integer; begin Result:=False; if DataSectionRead then begin InputError('Data section is duplicated'); exit; end; DataSectionRead:=True; DataSectionIndex:=SectionIndex; if not ReadUleb32(DataCount) then begin InputError('Error reading the data entries count from the data section'); exit; end; if DataCountSectionRead then begin if Length(DataSegments)<>DataCount then begin InputError('Data entries count in the data section do not match the number, specified in the data count section'); exit; end; end else SetLength(DataSegments,DataCount); for i:=0 to DataCount-1 do with DataSegments[i] do begin if not read(DataType, 1) then begin InputError('Error reading data type of segment from the data section'); exit; end; case DataType of 0: begin Active:=True; MemIdx:=0; if not ReadExpr(Offset) then begin InputError('Error reading memory offset of segment from the data section'); exit; end; end; 1: Active:=False; 2: begin Active:=True; if not ReadUleb32(MemIdx) then begin InputError('Error reading MemIdx of segment from the data section'); exit; end; if not ReadExpr(Offset) then begin InputError('Error reading memory offset of segment from the data section'); exit; end; end; else begin InputError('Unsupported data type of segment in the data section: ' + tostr(DataType)); exit; end; end; if MemIdx<>0 then begin InputError('Memory index other than 0 not supported (got ' + tostr(MemIdx) + ')'); exit; end; if not Active then begin InputError('Passive memory segments not supported'); exit; end; if not ReadUleb32(Len) then begin InputError('Error reading data segment length'); exit; end; if (AReader.Pos+Len)>(SectionStart+SectionSize) then begin InputError('Data segment exceeds the bounds of the data section'); exit; end; DataPos:=AReader.Pos; DataSectionOffset:=AReader.Pos-SectionStart; AReader.Seek(AReader.Pos+Len); end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected data section size'); exit; end; Result:=true; end; function ReadDataCountSection: Boolean; var DataCount: uint32; begin Result:=False; if DataCountSectionRead then begin InputError('Data count section is duplicated'); exit; end; DataCountSectionRead:=True; if DataSectionRead then begin InputError('The data count section must occur before the data section'); exit; end; if not ReadUleb32(DataCount) then begin InputError('Error reading the data count from the data count section'); exit; end; if AReader.Pos<>(SectionStart+SectionSize) then begin InputError('Unexpected data count section size'); exit; end; SetLength(DataSegments, DataCount); Result:=true; end; begin Result:=False; Inc(SectionIndex); if not AReader.read(SectionId,1) then begin InputError('Error reading section ID'); exit; end; CheckSectionBounds:=false; if not ReadUleb32(SectionSize) then begin InputError('Error reading section size'); exit; end; if (AReader.Pos+SectionSize)>AReader.size then begin InputError('Section exceeds beyond the end of file'); exit; end; SectionStart:=AReader.Pos; CheckSectionBounds:=true; case SectionId of Byte(wsiCustom): if not ReadCustomSection then begin InputError('Error encountered, while reading a custom section'); exit; end; Byte(wsiType): if not ReadTypeSection then begin InputError('Error reading the type section'); exit; end; Byte(wsiImport): if not ReadImportSection then begin InputError('Error reading the import section'); exit; end; Byte(wsiFunction): if not ReadFunctionSection then begin InputError('Error reading the function section'); exit; end; Byte(wsiGlobal): if not ReadGlobalSection then begin InputError('Error reading the global section'); exit; end; Byte(wsiExport): if not ReadExportSection then begin InputError('Error reading the export section'); exit; end; Byte(wsiElement): if not ReadElementSection then begin InputError('Error reading the element section'); exit; end; Byte(wsiTag): if not ReadTagSection then begin InputError('Error reading the tag section'); exit; end; Byte(wsiCode): if not ReadCodeSection then begin InputError('Error reading the code section'); exit; end; Byte(wsiData): if not ReadDataSection then begin InputError('Error reading the data section'); exit; end; Byte(wsiDataCount): begin if not ReadDataCountSection then begin InputError('Error reading the data count section'); exit; end; end else begin InputError('Unknown section: ' + ToStr(SectionId)); exit; end; end; if SectionSize>0 then AReader.seek(SectionStart+SectionSize); Result:=True; end; function FindCodeSegment(Ofs: uint32): Integer; var L, R, M: Integer; begin L:=Low(CodeSegments); R:=High(CodeSegments); while L<=R do begin M:=(L+R) div 2; if (CodeSegments[M].CodeSectionOffset+CodeSegments[M].CodeSize-1) < Ofs then L:=M+1 else if CodeSegments[M].CodeSectionOffset > Ofs then R:=M-1 else begin Result:=M; exit; end; end; Result:=-1; end; function FindDataSegment(Ofs: uint32): Integer; var L, R, M: Integer; begin L:=Low(DataSegments); R:=High(DataSegments); while L<=R do begin M:=(L+R) div 2; if (DataSegments[M].DataSectionOffset+DataSegments[M].Len-1) < Ofs then L:=M+1 else if DataSegments[M].DataSectionOffset > Ofs then R:=M-1 else begin Result:=M; exit; end; end; Result:=-1; end; var ModuleMagic: array [0..3] of Byte; ModuleVersion: array [0..3] of Byte; i, j, FirstCodeSegmentIdx, FirstDataSegmentIdx, SegI: Integer; CurrSec, ObjSec: TObjSection; BaseSectionOffset: UInt32; ObjReloc: TWasmObjRelocation; ds: TWasmCustomDebugSectionType; begin FReader:=AReader; InputFileName:=AReader.FileName; objdata:=CObjData.Create(InputFileName); result:=false; CodeSegments:=nil; DataSegments:=nil; SymbolTable:=nil; RelocationTable:=nil; SetLength(RelocationTable,2+(Ord(High(TWasmCustomDebugSectionType))-Ord(Low(TWasmCustomDebugSectionType))+1)); FuncTypes:=nil; FuncTypeImportsCount:=0; TableTypes:=nil; TableTypeImportsCount:=0; MemTypes:=nil; MemTypeImportsCount:=0; GlobalTypes:=nil; GlobalTypeImportsCount:=0; TagTypes:=nil; TagTypeImportsCount:=0; if not AReader.read(ModuleMagic,4) then exit; for i:=0 to 3 do if ModuleMagic[i]<>WasmModuleMagic[i] then exit; if not AReader.read(ModuleVersion,4) then exit; for i:=0 to 3 do if ModuleVersion[i]<>WasmVersion[i] then exit; while AReader.Pos0 then begin objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); objsym.bind:=AB_EXTERNAL; if (SymFlags and WASM_SYM_TLS)<>0 then begin objsym.typ:=AT_TLS; objsym.TlsGlobalSym:=TWasmObjSymbol(ObjData.CreateSymbol('GOT.mem.'+SymName)); objsym.TlsGlobalSym.TlsDataSym:=objsym; objsym.TlsGlobalSym.bind:=AB_EXTERNAL; objsym.TlsGlobalSym.typ:=AT_WASM_GLOBAL; objsym.TlsGlobalSym.objsection:=nil; objsym.TlsGlobalSym.offset:=0; objsym.TlsGlobalSym.size:=1; objsym.TlsGlobalSym.LinkingData.GlobalType:=wbt_i32; objsym.TlsGlobalSym.LinkingData.GlobalIsMutable:=true; end else objsym.typ:=AT_DATA; objsym.objsection:=nil; objsym.offset:=0; objsym.size:=0; end else begin objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); if (SymFlags and WASM_SYM_BINDING_LOCAL)<> 0 then objsym.bind:=AB_LOCAL else objsym.bind:=AB_GLOBAL; if (SymFlags and WASM_SYM_TLS)<>0 then begin objsym.typ:=AT_TLS; objsym.TlsGlobalSym:=TWasmObjSymbol(ObjData.CreateSymbol('GOT.mem.'+SymName)); objsym.TlsGlobalSym.TlsDataSym:=objsym; objsym.TlsGlobalSym.bind:=objsym.bind; objsym.TlsGlobalSym.typ:=AT_WASM_GLOBAL; objsym.TlsGlobalSym.objsection:=ObjData.createsection('.wasm_globals.n_'+objsym.TlsGlobalSym.Name,1,[oso_Data,oso_load],true); if objsym.TlsGlobalSym.objsection.Size=0 then objsym.TlsGlobalSym.objsection.WriteZeros(1); TWasmObjSection(objsym.TlsGlobalSym.objsection).MainFuncSymbol:=objsym.TlsGlobalSym; objsym.TlsGlobalSym.offset:=0; objsym.TlsGlobalSym.size:=1; objsym.TlsGlobalSym.LinkingData.GlobalType:=wbt_i32; objsym.TlsGlobalSym.LinkingData.GlobalIsMutable:=true; end else objsym.typ:=AT_DATA; objsym.objsection:=TObjSection(ObjData.ObjSectionList[FirstDataSegmentIdx+SymIndex]); objsym.offset:=SymOffset; objsym.size:=SymSize; end; SYMTAB_FPC_CUSTOM: if (SymFlags and WASM_SYM_UNDEFINED)<>0 then begin objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); objsym.bind:=AB_EXTERNAL; if (SymFlags and WASM_SYM_TLS)<>0 then internalerror(2024080702); objsym.typ:=AT_DATA; objsym.objsection:=nil; objsym.offset:=0; objsym.size:=0; end else begin objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); if (SymFlags and WASM_SYM_BINDING_LOCAL)<> 0 then objsym.bind:=AB_LOCAL else objsym.bind:=AB_GLOBAL; if (SymFlags and WASM_SYM_TLS)<>0 then internalerror(2024080703); objsym.typ:=AT_DATA; objsym.objsection:=TObjSection(ObjData.ObjSectionList.Find(WasmCustomSectionName[SymCustomSectionType])); objsym.offset:=SymOffset; objsym.size:=SymSize; end; SYMTAB_FUNCTION: begin if (SymFlags and WASM_SYM_UNDEFINED)<>0 then begin if not FuncTypes[SymIndex].IsImport then begin InputError('WASM_SYM_UNDEFINED set on a SYMTAB_FUNCTION symbol, that is not an import'); exit; end; if (SymFlags and WASM_SYM_EXPLICIT_NAME)<>0 then begin objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); objsym.bind:=AB_EXTERNAL; objsym.typ:=AT_FUNCTION; objsym.objsection:=nil; objsym.offset:=0; objsym.size:=0; objsym.LinkingData.ImportModule:=FuncTypes[SymIndex].ImportModName; objsym.LinkingData.ImportName:=FuncTypes[SymIndex].ImportName; end else begin if FuncTypes[SymIndex].ImportModName = 'env' then objsym:=TWasmObjSymbol(ObjData.CreateSymbol(FuncTypes[SymIndex].ImportName)) else objsym:=TWasmObjSymbol(ObjData.CreateSymbol(FuncTypes[SymIndex].ImportModName + '.' + FuncTypes[SymIndex].ImportName)); objsym.bind:=AB_EXTERNAL; objsym.typ:=AT_FUNCTION; objsym.objsection:=nil; objsym.offset:=0; objsym.size:=0; end; end else begin objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); objsym.bind:=AB_GLOBAL; objsym.typ:=AT_FUNCTION; objsym.objsection:=TObjSection(ObjData.ObjSectionList[FirstCodeSegmentIdx+SymIndex-FuncTypeImportsCount]); if (SymFlags and WASM_SYM_EXPLICIT_NAME)=0 then TWasmObjSection(ObjData.ObjSectionList[FirstCodeSegmentIdx+SymIndex-FuncTypeImportsCount]).MainFuncSymbol:=objsym; objsym.offset:=0; objsym.size:=objsym.objsection.Size; end; objsym.LinkingData.FuncType:=TWasmFuncType.Create(FFuncTypes[FuncTypes[SymIndex].typidx]); objsym.LinkingData.IsExported:=FuncTypes[SymIndex].IsExported; objsym.LinkingData.ExportName:=FuncTypes[SymIndex].ExportName; end; SYMTAB_GLOBAL: begin if (SymFlags and WASM_SYM_UNDEFINED)<>0 then begin if not GlobalTypes[SymIndex].IsImport then begin InputError('WASM_SYM_UNDEFINED set on a SYMTAB_GLOBAL symbol, that is not an import'); exit; end; if (SymFlags and WASM_SYM_EXPLICIT_NAME)<>0 then begin objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); objsym.bind:=AB_EXTERNAL; objsym.typ:=AT_WASM_GLOBAL; objsym.objsection:=nil; objsym.offset:=0; objsym.size:=1; objsym.LinkingData.ImportModule:=GlobalTypes[SymIndex].ImportModName; objsym.LinkingData.ImportName:=GlobalTypes[SymIndex].ImportName; end else begin if GlobalTypes[SymIndex].ImportModName = 'env' then objsym:=TWasmObjSymbol(ObjData.CreateSymbol(GlobalTypes[SymIndex].ImportName)) else objsym:=TWasmObjSymbol(ObjData.CreateSymbol(GlobalTypes[SymIndex].ImportModName + '.' + GlobalTypes[SymIndex].ImportName)); objsym.bind:=AB_EXTERNAL; objsym.typ:=AT_WASM_GLOBAL; objsym.objsection:=nil; objsym.offset:=0; objsym.size:=1; end; end else begin if GlobalTypes[SymIndex].IsImport then begin InputError('WASM_SYM_UNDEFINED not set on a SYMTAB_GLOBAL symbol, that is an import'); exit; end; objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); objsym.bind:=AB_GLOBAL; objsym.typ:=AT_WASM_GLOBAL; objsym.objsection:=ObjData.createsection('.wasm_globals.n_'+SymName,1,[oso_Data,oso_load],true); if objsym.objsection.Size=0 then objsym.objsection.WriteZeros(1); if (SymFlags and WASM_SYM_EXPLICIT_NAME)=0 then TWasmObjSection(objsym.objsection).MainFuncSymbol:=objsym; objsym.offset:=0; objsym.size:=1; objsym.LinkingData.GlobalInitializer:=GlobalTypes[SymIndex].GlobalInit; end; objsym.LinkingData.GlobalType:=GlobalTypes[SymIndex].valtype; objsym.LinkingData.GlobalIsMutable:=GlobalTypes[SymIndex].IsMutable; objsym.LinkingData.IsExported:=GlobalTypes[SymIndex].IsExported; objsym.LinkingData.ExportName:=GlobalTypes[SymIndex].ExportName; end; SYMTAB_SECTION: begin for ds:=Low(DebugSectionIndex) to High(DebugSectionIndex) do if DebugSectionIndex[ds]=TargetSection then begin ObjSec:=TWasmObjSection(ObjData.findsection(WasmCustomSectionName[ds])); break; end; if ObjSec=nil then begin InputError('SYMTAB_SECTION entry points to an unsupported section'); exit; end; end; SYMTAB_EVENT: begin if (SymFlags and WASM_SYM_UNDEFINED)<>0 then begin if not TagTypes[SymIndex].IsImport then begin InputError('WASM_SYM_UNDEFINED set on a SYMTAB_EVENT symbol, that is not an import'); exit; end; if (SymFlags and WASM_SYM_EXPLICIT_NAME)<>0 then begin objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); objsym.bind:=AB_EXTERNAL; objsym.typ:=AT_WASM_EXCEPTION_TAG; objsym.objsection:=nil; objsym.offset:=0; objsym.size:=1; objsym.LinkingData.ImportModule:=TagTypes[SymIndex].ImportModName; objsym.LinkingData.ImportName:=TagTypes[SymIndex].ImportName; end else begin if GlobalTypes[SymIndex].ImportModName = 'env' then objsym:=TWasmObjSymbol(ObjData.CreateSymbol(GlobalTypes[SymIndex].ImportName)) else objsym:=TWasmObjSymbol(ObjData.CreateSymbol(GlobalTypes[SymIndex].ImportModName + '.' + GlobalTypes[SymIndex].ImportName)); objsym.bind:=AB_EXTERNAL; objsym.typ:=AT_WASM_EXCEPTION_TAG; objsym.objsection:=nil; objsym.offset:=0; objsym.size:=1; end; end else begin if TagTypes[SymIndex].IsImport then begin InputError('WASM_SYM_UNDEFINED not set on a SYMTAB_EVENT symbol, that is an import'); exit; end; objsym:=TWasmObjSymbol(ObjData.CreateSymbol(SymName)); if (symflags and WASM_SYM_BINDING_WEAK) <> 0 then objsym.bind:=AB_WEAK_EXTERNAL else if (symflags and WASM_SYM_BINDING_LOCAL) <> 0 then objsym.bind:=AB_LOCAL else objsym.bind:=AB_GLOBAL; objsym.typ:=AT_WASM_EXCEPTION_TAG; objsym.objsection:=ObjData.createsection('.wasm_tags.n_'+SymName,1,[oso_Data,oso_load],true); if objsym.objsection.Size=0 then objsym.objsection.WriteZeros(1); if (SymFlags and WASM_SYM_EXPLICIT_NAME)=0 then TWasmObjSection(objsym.objsection).MainFuncSymbol:=objsym; objsym.offset:=0; objsym.size:=1; end; objsym.LinkingData.FuncType:=TWasmFuncType.Create(FFuncTypes[TagTypes[SymIndex].TagTypeIdx]); objsym.LinkingData.IsExported:=TagTypes[SymIndex].IsExported; objsym.LinkingData.ExportName:=TagTypes[SymIndex].ExportName; end; SYMTAB_TABLE: {TODO}; end; for j:=0 to high(RelocationTable) do for i:=0 to high(RelocationTable[j]) do with RelocationTable[j,i] do begin case j of 0: begin SegI:=FindCodeSegment(RelocOffset); if SegI=-1 then begin InputError('Relocation offset not found in code segment'); Exit; end; BaseSectionOffset:=CodeSegments[SegI].CodeSectionOffset; ObjSec:=TObjSection(ObjData.ObjSectionList[FirstCodeSegmentIdx+SegI]); end; 1: begin SegI:=FindDataSegment(RelocOffset); if SegI=-1 then begin InputError('Relocation offset not found in data segment'); Exit; end; BaseSectionOffset:=DataSegments[SegI].DataSectionOffset; ObjSec:=TObjSection(ObjData.ObjSectionList[FirstDataSegmentIdx+SegI]); end; 2..2+(Ord(High(TWasmCustomDebugSectionType))-Ord(Low(TWasmCustomDebugSectionType))): begin BaseSectionOffset:=0; ObjSec:=ObjData.findsection(WasmCustomSectionName[TWasmCustomSectionType((j-2)+Ord(Low(TWasmCustomDebugSectionType)))]); end; else internalerror(2023122801); end; case RelocType of R_WASM_FUNCTION_INDEX_LEB: ObjSec.ObjRelocations.Add(TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_FUNCTION_INDEX_LEB)); R_WASM_TABLE_INDEX_SLEB: ObjSec.ObjRelocations.Add(TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_MEMORY_ADDR_OR_TABLE_INDEX_SLEB)); R_WASM_TABLE_INDEX_I32: ObjSec.ObjRelocations.Add(TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_ABSOLUTE)); R_WASM_MEMORY_ADDR_LEB: begin ObjReloc:=TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_MEMORY_ADDR_LEB); ObjReloc.Addend:=RelocAddend; ObjSec.ObjRelocations.Add(ObjReloc); end; R_WASM_MEMORY_ADDR_SLEB: begin ObjReloc:=TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_MEMORY_ADDR_OR_TABLE_INDEX_SLEB); ObjReloc.Addend:=RelocAddend; ObjSec.ObjRelocations.Add(ObjReloc); end; R_WASM_MEMORY_ADDR_I32: begin ObjReloc:=TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_ABSOLUTE); ObjReloc.Addend:=RelocAddend; ObjSec.ObjRelocations.Add(ObjReloc); end; R_WASM_TYPE_INDEX_LEB: ObjSec.ObjRelocations.Add(TWasmObjRelocation.CreateFuncType(RelocOffset-BaseSectionOffset,FFuncTypes[RelocIndex])); R_WASM_FUNCTION_OFFSET_I32: begin ObjReloc:=TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_ABSOLUTE); ObjReloc.Addend:=RelocAddend; ObjReloc.IsFunctionOffsetI32:=True; ObjSec.ObjRelocations.Add(ObjReloc); end; R_WASM_SECTION_OFFSET_I32: begin ObjReloc:=TWasmObjRelocation.CreateSection(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSec,RELOC_ABSOLUTE); ObjReloc.Addend:=RelocAddend; ObjSec.ObjRelocations.Add(ObjReloc); end; R_WASM_GLOBAL_INDEX_LEB: begin ObjSec.ObjRelocations.Add(TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_GLOBAL_INDEX_LEB)); if Assigned(SymbolTable[RelocIndex].ObjSym.TlsGlobalSym) then ObjSec.ObjRelocations.Add(TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym.TlsGlobalSym,RELOC_GLOBAL_INDEX_LEB)); end; R_WASM_GLOBAL_INDEX_I32: begin ObjSec.ObjRelocations.Add(TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_ABSOLUTE)); if Assigned(SymbolTable[RelocIndex].ObjSym.TlsGlobalSym) then ObjSec.ObjRelocations.Add(TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym.TlsGlobalSym,RELOC_ABSOLUTE)); end; R_WASM_TAG_INDEX_LEB: ObjSec.ObjRelocations.Add(TWasmObjRelocation.CreateSymbol(RelocOffset-BaseSectionOffset,SymbolTable[RelocIndex].ObjSym,RELOC_TAG_INDEX_LEB)); else internalerror(2023122802); end; end; Result:=True; end; {**************************************************************************** TWasmExeOutput ****************************************************************************} procedure TWasmExeOutput.AddToNameMap(var nm: TCustomSectionNameMap; aidx: UInt32; const aname: string); begin SetLength(nm,Length(nm)+1); with nm[High(nm)] do begin idx:=aidx; name:=aname; end; end; procedure TWasmExeOutput.AddToFunctionNameMap(aidx: UInt32; const aname: string); begin AddToNameMap(FFunctionNameMap,aidx,aname); end; procedure TWasmExeOutput.AddToGlobalNameMap(aidx: UInt32; const aname: string); begin AddToNameMap(FGlobalNameMap,aidx,aname); end; procedure TWasmExeOutput.AddToDataNameMap(aidx: UInt32; const aname: string); begin AddToNameMap(FDataNameMap,aidx,aname); end; procedure TWasmExeOutput.AddToTagNameMap(aidx: UInt32; const aname: string); begin AddToNameMap(FTagNameMap,aidx,aname); end; procedure TWasmExeOutput.WriteWasmSection(wsid: TWasmSectionID); var b: byte; begin b:=ord(wsid); Writer.write(b,1); WriteUleb(Writer,FWasmSections[wsid].size); Writer.writearray(FWasmSections[wsid]); end; procedure TWasmExeOutput.WriteWasmSectionIfNotEmpty(wsid: TWasmSectionID); begin if FWasmSections[wsid].size>0 then WriteWasmSection(wsid); end; procedure TWasmExeOutput.WriteWasmCustomSection(wcst: TWasmCustomSectionType); var b: byte; begin b:=0; Writer.write(b,1); WriteUleb(Writer,FWasmCustomSections[wcst].size); Writer.writearray(FWasmCustomSections[wcst]); end; function TWasmExeOutput.writeData: boolean; procedure WriteImportSection; var imports_count, i: Integer; begin if assigned(exemap) then exemap.AddHeader('Import section'); imports_count:=Length(FImportedMemories)+Length(FFunctionImports); WriteUleb(FWasmSections[wsiImport],imports_count); for i:=0 to Length(FImportedMemories)-1 do with FImportedMemories[i] do begin WriteName(FWasmSections[wsiImport],ModName); WriteName(FWasmSections[wsiImport],Name); WriteByte(FWasmSections[wsiImport],$02); { mem } WriteMemoryTo(FWasmSections[wsiImport],MemType); if assigned(exemap) then exemap.Add(' Memory['+tostr(i)+'] '+Memory2String(MemType)+' <- '+ModName+'.'+Name); end; for i:=0 to Length(FFunctionImports)-1 do with FFunctionImports[i] do begin WriteName(FWasmSections[wsiImport],ModName); WriteName(FWasmSections[wsiImport],Name); WriteByte(FWasmSections[wsiImport],$00); { func } WriteUleb(FWasmSections[wsiImport],TypeIdx); if assigned(exemap) then exemap.Add(' Function['+tostr(i)+'] sig='+tostr(TypeIdx)+' <- '+ModName+'.'+Name); end; end; procedure WriteCodeSegments; var i: Integer; exesec: TExeSection; objsec: TWasmObjSection; begin exesec:=FindExeSection('.text'); if not assigned(exesec) then internalerror(2023123102); if not (oso_Data in exesec.SecOptions) then internalerror(2023123103); WriteUleb(FWasmSections[wsiFunction],exesec.ObjSectionList.Count); WriteUleb(FWasmSections[wsiCode],exesec.ObjSectionList.Count); for i:=0 to exesec.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(exesec.ObjSectionList[i]); if not (oso_data in objsec.secoptions) then internalerror(2023123104); if not assigned(objsec.data) then internalerror(2023123105); if objsec.MainFuncSymbol.LinkingData.ExeFunctionIndex<>(i+Length(FFunctionImports)) then internalerror(2024010101); WriteUleb(FWasmSections[wsiFunction],objsec.MainFuncSymbol.LinkingData.ExeTypeIndex); WriteUleb(FWasmSections[wsiCode],objsec.Data.size); objsec.Data.seek(0); CopyDynamicArray(objsec.Data,FWasmSections[wsiCode],objsec.Data.size); end; end; procedure WriteDataSegments; procedure WriteExeSection(exesec: TExeSection); var i: Integer; objsec: TObjSection; exesecdatapos: LongWord; dpos, pad: QWord; begin AddToDataNameMap(Length(FDataNameMap),exesec.Name); if ts_wasm_threads in current_settings.targetswitches then WriteByte(FWasmSections[wsiData],1) { mode passive } else begin WriteByte(FWasmSections[wsiData],0); { mode active, memory 0, offset e } WriteByte(FWasmSections[wsiData],$41); { i32.const } WriteSleb(FWasmSections[wsiData],longint(exesec.MemPos)); WriteByte(FWasmSections[wsiData],$0B); { end } end; WriteUleb(FWasmSections[wsiData],exesec.Size); exesecdatapos:=FWasmSections[wsiData].size; for i:=0 to exesec.ObjSectionList.Count-1 do begin objsec:=TObjSection(exesec.ObjSectionList[i]); if not (oso_data in objsec.secoptions) then internalerror(2024010104); if not assigned(objsec.data) then internalerror(2024010105); dpos:=objsec.MemPos-exesec.MemPos+exesecdatapos; pad:=dpos-FWasmSections[wsiData].size; { objsection must be within SecAlign bytes from the previous one } if (dpos=max(objsec.SecAlign,1)) then internalerror(2024010106); writeZeros(FWasmSections[wsiData],pad); objsec.data.seek(0); CopyDynamicArray(objsec.data,FWasmSections[wsiData],objsec.data.size); end; if (FWasmSections[wsiData].size-exesecdatapos)<>exesec.Size then internalerror(2024010107); end; var DataCount: Integer; DataSecName: string; ExeSec: TExeSection; begin DataCount:=0; for DataSecName in DataSections do begin ExeSec:=FindExeSection(DataSecName); if Assigned(ExeSec) and (ExeSec.Size>0) then Inc(DataCount); end; WriteUleb(FWasmSections[wsiDataCount],DataCount); WriteUleb(FWasmSections[wsiData],DataCount); for DataSecName in DataSections do begin ExeSec:=FindExeSection(DataSecName); if Assigned(ExeSec) and (ExeSec.Size>0) then WriteExeSection(ExeSec); end; end; procedure WriteTableAndElemSections; const TableCount=1; var i: Integer; begin { Table section } WriteUleb(FWasmSections[wsiTable],TableCount); { table 0 } { table type } WriteByte(FWasmSections[wsiTable],encode_wasm_basic_type(wbt_funcref)); { table limits } WriteByte(FWasmSections[wsiTable],$01); { has min & max } WriteUleb(FWasmSections[wsiTable],Length(FIndirectFunctionTable)); { min } WriteUleb(FWasmSections[wsiTable],Length(FIndirectFunctionTable)); { max } { Elem section } WriteUleb(FWasmSections[wsiElement],1); { 1 element segment } { element segment 0 } WriteByte(FWasmSections[wsiElement],0); { type funcref, init((ref.func y) end)*, mode active } { e:expr } WriteByte(FWasmSections[wsiElement],$41); { i32.const } WriteSleb(FWasmSections[wsiElement],1); { starting from 1 (table entry 0 is ref.null) } WriteByte(FWasmSections[wsiElement],$0B); { end } { y*:vec(funcidx) } WriteUleb(FWasmSections[wsiElement],Length(FIndirectFunctionTable)-1); for i:=1 to Length(FIndirectFunctionTable)-1 do WriteUleb(FWasmSections[wsiElement],FIndirectFunctionTable[i].FuncIdx); end; procedure WriteGlobalSection; var exesec: TExeSection; globals_count, i: Integer; objsec: TWasmObjSection; mapstr: string=''; begin if assigned(exemap) then exemap.AddHeader('Global section'); exesec:=FindExeSection('.wasm_globals'); if not assigned(exesec) then internalerror(2024010112); globals_count:=exesec.ObjSectionList.Count; if globals_count<>exesec.Size then internalerror(2024010113); WriteUleb(FWasmSections[wsiGlobal],globals_count); for i:=0 to exesec.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(exesec.ObjSectionList[i]); WriteByte(FWasmSections[wsiGlobal],encode_wasm_basic_type(objsec.MainFuncSymbol.LinkingData.GlobalType)); if objsec.MainFuncSymbol.LinkingData.GlobalIsMutable then WriteByte(FWasmSections[wsiGlobal],1) else WriteByte(FWasmSections[wsiGlobal],0); if assigned(exemap) then WriteStr(mapstr,' Global[',i,'] ',wasm_basic_type_str[objsec.MainFuncSymbol.LinkingData.GlobalType],' mutable=',objsec.MainFuncSymbol.LinkingData.GlobalIsMutable,' <',objsec.MainFuncSymbol.Name,'> - init '); { initializer expr } with objsec.MainFuncSymbol.LinkingData.GlobalInitializer do case typ of wbt_i32: begin WriteByte(FWasmSections[wsiGlobal],$41); { i32.const } WriteSleb(FWasmSections[wsiGlobal],init_i32); if assigned(exemap) then mapstr:=mapstr+'i32='+tostr(init_i32); end; wbt_i64: begin WriteByte(FWasmSections[wsiGlobal],$42); { i64.const } WriteSleb(FWasmSections[wsiGlobal],init_i64); if assigned(exemap) then mapstr:=mapstr+'i64='+tostr(init_i64); end; wbt_f32: begin WriteByte(FWasmSections[wsiGlobal],$43); { f32.const } WriteF32LE(FWasmSections[wsiGlobal],init_f32); if assigned(exemap) then WriteStr(mapstr,mapstr+'f32=',init_f32); end; wbt_f64: begin WriteByte(FWasmSections[wsiGlobal],$44); { f64.const } WriteF64LE(FWasmSections[wsiGlobal],init_f64); if assigned(exemap) then WriteStr(mapstr,mapstr+'f64=',init_f64); end; wbt_funcref, wbt_externref: begin WriteByte(FWasmSections[wsiGlobal],$D0); { ref.null } WriteByte(FWasmSections[wsiGlobal],encode_wasm_basic_type(typ)); if assigned(exemap) then mapstr:=mapstr+'ref.null '+wasm_basic_type_str[typ]; end; else internalerror(2024010114); end; WriteByte(FWasmSections[wsiGlobal],$0B); { end } { add entry for the name section } AddToGlobalNameMap(i,objsec.MainFuncSymbol.Name); if assigned(exemap) then exemap.Add(mapstr); end; end; procedure WriteTagSection; var exesec: TExeSection; tags_count, i: Integer; objsec: TWasmObjSection; begin exesec:=FindExeSection('.wasm_tags'); if not assigned(exesec) then exit; tags_count:=exesec.ObjSectionList.Count; if tags_count<>exesec.Size then internalerror(2024010702); if tags_count=0 then exit; WriteUleb(FWasmSections[wsiTag],tags_count); for i:=0 to exesec.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(exesec.ObjSectionList[i]); WriteByte(FWasmSections[wsiTag],0); WriteUleb(FWasmSections[wsiTag],objsec.MainFuncSymbol.LinkingData.ExeTypeIndex); AddToTagNameMap(i,objsec.MainFuncSymbol.Name); end; end; procedure WriteExportSection; const MemoryExportsCount=1; var FunctionExportsCount: Integer; ExportsCount: Integer; textsec: TExeSection; i: Integer; objsec: TWasmObjSection; begin if assigned(exemap) then exemap.AddHeader('Export section'); FunctionExportsCount:=0; textsec:=FindExeSection('.text'); if not assigned(textsec) then internalerror(2024010115); for i:=0 to textsec.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(textsec.ObjSectionList[i]); if objsec.MainFuncSymbol.LinkingData.IsExported then Inc(FunctionExportsCount) end; ExportsCount:=MemoryExportsCount+FunctionExportsCount; WriteUleb(FWasmSections[wsiExport],ExportsCount); { export 0 } WriteName(FWasmSections[wsiExport],'memory'); WriteByte(FWasmSections[wsiExport],$02); { mem } WriteUleb(FWasmSections[wsiExport],0); { memidx = 0 } if assigned(exemap) then exemap.Add(' Memory[0] -> "memory"'); for i:=0 to textsec.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(textsec.ObjSectionList[i]); if objsec.MainFuncSymbol.LinkingData.IsExported then begin WriteName(FWasmSections[wsiExport],objsec.MainFuncSymbol.LinkingData.ExportName); WriteByte(FWasmSections[wsiExport],$00); { func } WriteUleb(FWasmSections[wsiExport],objsec.MainFuncSymbol.LinkingData.ExeFunctionIndex); { funcidx } if assigned(exemap) then exemap.Add(' Function['+tostr(objsec.MainFuncSymbol.LinkingData.ExeFunctionIndex)+'] -> "'+objsec.MainFuncSymbol.LinkingData.ExportName+'"'); end; end; end; procedure MaybeWriteDebugSection(st: TWasmCustomDebugSectionType); var exesec: TExeSection; begin exesec:=FindExeSection(WasmCustomSectionName[st]); if assigned(exesec) then begin WriteExeSectionToDynArray(exesec,FWasmCustomSections[st]); WriteWasmCustomSection(st); end; end; procedure WriteNameMap(const nm: TCustomSectionNameMap; dest: tdynamicarray); var i: Integer; begin WriteUleb(dest,Length(nm)); for i:=low(nm) to high(nm) do with nm[i] do begin WriteUleb(dest,idx); WriteName(dest,name); end; end; procedure WriteNameSubsection(wnst: TWasmNameSubsectionType); begin if FWasmNameSubsections[wnst].size>0 then begin WriteByte(FWasmCustomSections[wcstName],Ord(wnst)); WriteUleb(FWasmCustomSections[wcstName],FWasmNameSubsections[wnst].size); FWasmNameSubsections[wnst].seek(0); CopyDynamicArray(FWasmNameSubsections[wnst],FWasmCustomSections[wcstName],FWasmNameSubsections[wnst].size); end; end; procedure WriteNameSection; begin WriteName(FWasmNameSubsections[wnstModuleName],current_module.exefilename); WriteNameSubsection(wnstModuleName); WriteNameMap(FFunctionNameMap,FWasmNameSubsections[wnstFunctionNames]); WriteNameSubsection(wnstFunctionNames); WriteNameMap(FGlobalNameMap,FWasmNameSubsections[wnstGlobalNames]); WriteNameSubsection(wnstGlobalNames); WriteNameMap(FDataNameMap,FWasmNameSubsections[wnstDataNames]); WriteNameSubsection(wnstDataNames); if Length(FTagNameMap)>0 then begin WriteNameMap(FTagNameMap,FWasmNameSubsections[wnstTagNames]); WriteNameSubsection(wnstTagNames); end; end; procedure WriteMemorySection; var i: Integer; begin if assigned(exemap) then exemap.AddHeader('Memory section'); WriteUleb(FWasmSections[wsiMemory],Length(FMemories)); for i:=low(FMemories) to high(FMemories) do begin WriteMemoryTo(FWasmSections[wsiMemory],FMemories[i]); if assigned(exemap) then exemap.Add(' Memory['+tostr(i+Length(FImportedMemories))+'] '+Memory2String(FMemories[i])); end; end; var cust_sec: TWasmCustomSectionType; begin result:=false; FMaxMemoryPages:=align(maxheapsize,WasmPageSize) div WasmPageSize; { each custom sections starts with its name } for cust_sec in TWasmCustomSectionType do WriteName(FWasmCustomSections[cust_sec],WasmCustomSectionName[cust_sec]); SetStackPointer; SetTlsSizeAlignAndBase; SetThreadVarGlobalsInitValues; GenerateCode_InitTls; GenerateCode_InitSharedMemory; if ts_wasm_threads in current_settings.targetswitches then begin SetLength(FImportedMemories,1); with FImportedMemories[0] do begin ModName:='env'; Name:='memory'; with MemType do begin Flags:=[wmfShared,wmfHasMaximumBound]; MinPages:=FMinMemoryPages; MaxPages:=Max(FMinMemoryPages,FMaxMemoryPages); end; end; end else begin SetLength(FMemories,1); with FMemories[0] do begin Flags:=[]; MinPages:=FMinMemoryPages; if FMaxMemoryPages>=FMinMemoryPages then begin Include(Flags,wmfHasMaximumBound); MaxPages:=FMaxMemoryPages; end; end; end; FFuncTypes.WriteTo(FWasmSections[wsiType]); WriteImportSection; WriteCodeSegments; WriteDataSegments; WriteTableAndElemSections; WriteGlobalSection; WriteTagSection; if Length(FMemories)>0 then WriteMemorySection; WriteExportSection; if ts_wasm_threads in current_settings.targetswitches then WriteUleb(FWasmSections[wsiStart],FInitSharedMemoryFunctionSym.LinkingData.ExeFunctionIndex); WriteNameSection; Writer.write(WasmModuleMagic,SizeOf(WasmModuleMagic)); Writer.write(WasmVersion,SizeOf(WasmVersion)); WriteWasmSection(wsiType); WriteWasmSection(wsiImport); WriteWasmSection(wsiFunction); WriteWasmSection(wsiTable); if not (ts_wasm_threads in current_settings.targetswitches) then WriteWasmSection(wsiMemory); WriteWasmSectionIfNotEmpty(wsiTag); WriteWasmSection(wsiGlobal); WriteWasmSection(wsiExport); if ts_wasm_threads in current_settings.targetswitches then WriteWasmSection(wsiStart); WriteWasmSection(wsiElement); WriteWasmSection(wsiDataCount); WriteWasmSection(wsiCode); WriteWasmSection(wsiData); MaybeWriteDebugSection(wcstDebugAbbrev); MaybeWriteDebugSection(wcstDebugInfo); MaybeWriteDebugSection(wcstDebugStr); MaybeWriteDebugSection(wcstDebugLine); MaybeWriteDebugSection(wcstDebugFrame); MaybeWriteDebugSection(wcstDebugAranges); MaybeWriteDebugSection(wcstDebugRanges); WriteWasmCustomSection(wcstName); result := true; end; procedure TWasmExeOutput.DoRelocationFixup(objsec: TObjSection); procedure writeUInt32LE(v: uint32); begin {$ifdef FPC_BIG_ENDIAN} v:=SwapEndian(v); {$endif FPC_BIG_ENDIAN} objsec.data.write(v,4); end; var i: Integer; objreloc: TWasmObjRelocation; objsym: TWasmObjSymbol; begin for i:=0 to objsec.ObjRelocations.Count-1 do begin objreloc:=TWasmObjRelocation(objsec.ObjRelocations[i]); if assigned(objreloc.symbol) then begin objsym:=TWasmObjSymbol(objreloc.symbol); case objreloc.typ of RELOC_FUNCTION_INDEX_LEB: begin if objsym.LinkingData.ExeFunctionIndex=-1 then internalerror(2024010103); if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); WriteUleb5(objsec.Data,objsym.LinkingData.ExeFunctionIndex); end; end; RELOC_ABSOLUTE: begin case objsym.typ of AT_FUNCTION: begin if objreloc.IsFunctionOffsetI32 then begin { R_WASM_FUNCTION_OFFSET_I32 } if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); writeUInt32LE(UInt32(objsym.objsection.MemPos+objreloc.Addend)); end; end else begin { R_WASM_TABLE_INDEX_I32 } if objsym.LinkingData.ExeFunctionIndex=-1 then internalerror(2024010103); case FRelocationPass of 1: if objsym.LinkingData.ExeIndirectFunctionTableIndex=-1 then objsym.LinkingData.ExeIndirectFunctionTableIndex:=AddOrGetIndirectFunctionTableIndex(objsym.LinkingData.ExeFunctionIndex); 2: begin objsec.Data.seek(objreloc.DataOffset); writeUInt32LE(UInt32(objsym.LinkingData.ExeIndirectFunctionTableIndex)); end; end; end; end; AT_DATA: begin if objreloc.IsFunctionOffsetI32 then internalerror(2024010602); if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); writeUInt32LE(UInt32((objsym.offset+objsym.objsection.MemPos)+objreloc.Addend)); end; end; AT_TLS: begin if objreloc.IsFunctionOffsetI32 then internalerror(2024010602); if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); writeUInt32LE(UInt32((objsym.offset+objsym.objsection.MemPos-objsym.objsection.ExeSection.MemPos)+objreloc.Addend)); end; end; AT_WASM_GLOBAL: begin if objreloc.IsFunctionOffsetI32 then internalerror(2024010602); if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); writeUInt32LE(UInt32(objsym.offset+objsym.objsection.MemPos)); end; end; else internalerror(2024010108); end; end; RELOC_MEMORY_ADDR_LEB: begin if objsym.typ<>AT_DATA then internalerror(2024010109); if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); WriteUleb5(objsec.Data,UInt32((objsym.offset+objsym.objsection.MemPos)+objreloc.Addend)); end; end; RELOC_MEMORY_ADDR_OR_TABLE_INDEX_SLEB: begin case objsym.typ of AT_FUNCTION: begin if objsym.LinkingData.ExeFunctionIndex=-1 then internalerror(2024010103); case FRelocationPass of 1: if objsym.LinkingData.ExeIndirectFunctionTableIndex=-1 then objsym.LinkingData.ExeIndirectFunctionTableIndex:=AddOrGetIndirectFunctionTableIndex(objsym.LinkingData.ExeFunctionIndex); 2: begin objsec.Data.seek(objreloc.DataOffset); WriteSleb5(objsec.Data,Int32(objsym.LinkingData.ExeIndirectFunctionTableIndex)); end; end; end; AT_DATA: begin if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); WriteSleb5(objsec.Data,Int32((objsym.offset+objsym.objsection.MemPos)+objreloc.Addend)); end; end; else internalerror(2024010110); end; end; RELOC_GLOBAL_INDEX_LEB: if objsym.typ=AT_WASM_GLOBAL then begin if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); WriteUleb5(objsec.Data,UInt32(objsym.offset+objsym.objsection.MemPos)); end; end else if (ts_wasm_threads in current_settings.targetswitches) and (objsym.typ=AT_TLS) then begin { Nothing to do here. A second RELOC_GLOBAL_INDEX_LEB relocation, overlaid on top of this one, pointing to an AT_WASM_GLOBAL should have already done the job. } end else internalerror(2024010111); RELOC_TAG_INDEX_LEB: begin if objsym.typ<>AT_WASM_EXCEPTION_TAG then internalerror(2024010708); if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); WriteUleb5(objsec.Data,UInt32(objsym.offset+objsym.objsection.MemPos)); end; end; else internalerror(2024010109); end; end else if assigned(objreloc.objsection) then begin if objreloc.typ<>RELOC_ABSOLUTE then internalerror(2024010601); if FRelocationPass=2 then begin objsec.Data.seek(objreloc.DataOffset); writeUInt32LE(UInt32((objreloc.objsection.MemPos)+objreloc.Addend)); end; end else if objreloc.typ=RELOC_TYPE_INDEX_LEB then begin case FRelocationPass of 1: objreloc.ExeTypeIndex:=FFuncTypes.AddOrGetFuncType(objreloc.FuncType); 2: begin objsec.Data.seek(objreloc.DataOffset); WriteUleb5(objsec.Data,objreloc.ExeTypeIndex); end; end; end else internalerror(2024010110); end; end; constructor TWasmExeOutput.create; var i: TWasmSectionID; j: TWasmCustomSectionType; k: TWasmNameSubsectionType; begin inherited create; CObjData:=TWasmObjData; SectionMemAlign:=16; MaxMemPos:=$FFFFFFFF; FFuncTypes:=TWasmFuncTypeTable.Create; for i in TWasmSectionID do FWasmSections[i] := tdynamicarray.create(SectionDataMaxGrow); for j in TWasmCustomSectionType do FWasmCustomSections[j] := tdynamicarray.create(SectionDataMaxGrow); for k:=low(FWasmNameSubsections) to high(FWasmNameSubsections) do FWasmNameSubsections[k] := tdynamicarray.create(SectionDataMaxGrow); SetLength(FIndirectFunctionTable,1); FIndirectFunctionTable[0].FuncIdx:=-1; end; destructor TWasmExeOutput.destroy; var i: TWasmSectionID; j: TWasmCustomSectionType; k: TWasmNameSubsectionType; begin for i in TWasmSectionID do FWasmSections[i].Free; for j in TWasmCustomSectionType do FWasmCustomSections[j].Free; for k:=low(FWasmNameSubsections) to high(FWasmNameSubsections) do FWasmNameSubsections[k].Free; FFuncTypes.Free; inherited destroy; end; procedure TWasmExeOutput.GenerateLibraryImports(ImportLibraryList: TFPHashObjectList); var i, j: Integer; ImportLibrary: TImportLibrary; ImportSymbol: TImportSymbol; exesym: TExeSymbol; begin { Here map import symbols to exe symbols and create necessary sections. Actual import generation is done after unused sections (and symbols) are removed. } FImports:=ImportLibraryList; for i:=0 to ImportLibraryList.Count-1 do begin ImportLibrary:=TImportLibrary(ImportLibraryList[i]); for j:=0 to ImportLibrary.ImportSymbolList.Count-1 do begin ImportSymbol:=TImportSymbol(ImportLibrary.ImportSymbolList[j]); exesym:=TExeSymbol(ExeSymbolList.Find(ImportSymbol.MangledName)); if assigned(exesym) and (exesym.State<>symstate_defined) then begin ImportSymbol.CachedExeSymbol:=exesym; exesym.State:=symstate_defined; end; end; end; PackUnresolvedExeSymbols('after module imports'); end; procedure TWasmExeOutput.AfterUnusedSectionRemoval; begin PrepareImports; PrepareFunctions; PrepareTags; if Assigned(exemap) then WriteMap_TypeSection; { we do an extra preliminary relocation pass, in order to prepare the indices for the Type section and the Table section. This is required by GenerateCode_InvokeHelper. } FRelocationPass:=1; FixupRelocations; { in pass 2, we do the actual relocation fixups. No need to call FixupRelocations here, since it'll be called in TInternalLinker.RunLinkScript, after this method finishes. We only set the FRelocationPass variable here, so DoRelocationFixup knows which pass it is. } FRelocationPass:=2; { This needs to be done before pass 2 of the relocation fixups, because it'll generate code, thus it'll move the offsets of the functions that follow it in the Code section, and we want our DWARF debug info to contain correct code offsets. } GenerateCode_InvokeHelper; if Assigned(exemap) then WriteMap_IndirectFunctionTable; end; procedure TWasmExeOutput.MemPos_ExeSection(const aname: string); const DebugPrefix = '.debug_'; var ExeSec: TExeSection; i: Integer; objsec: TObjSection; firstdatasec: string; begin { WebAssembly is a Harvard architecture. Data lives in a separate address space, so start addressing back from 0 (the LLVM leaves the first 1024 bytes in the data segment empty, so we start at 1024). } if ts_wasm_threads in current_settings.targetswitches then firstdatasec:='.tbss' else firstdatasec:='.rodata'; if aname=firstdatasec then begin CurrMemPos:=1024; inherited; end else if aname='.text' then begin CurrMemPos:=0; ExeSec:=FindExeSection(aname); if not assigned(ExeSec) then exit; exesec.MemPos:=CurrMemPos; CurrMemPos:=CurrMemPos+UlebEncodingSize(exesec.ObjSectionList.Count); { set position of object ObjSections } for i:=0 to exesec.ObjSectionList.Count-1 do begin objsec:=TObjSection(exesec.ObjSectionList[i]); CurrMemPos:=CurrMemPos+UlebEncodingSize(objsec.Size); CurrMemPos:=objsec.setmempos(CurrMemPos); end; { calculate size of the section } exesec.Size:=CurrMemPos-exesec.MemPos; end else if (aname='.wasm_globals') or (aname='.wasm_tags') or (Copy(aname,1,Length(DebugPrefix))=DebugPrefix) then begin CurrMemPos:=0; inherited; end else inherited; end; procedure TWasmExeOutput.Load_Symbol(const aname: string); begin if aname=StackPointerSymStr then begin internalObjData.createsection('*'+aname,1,[oso_Data,oso_load]); FStackPointerSym:=TWasmObjSymbol(internalObjData.SymbolDefine(aname,AB_GLOBAL,AT_WASM_GLOBAL)); FStackPointerSym.size:=1; FStackPointerSym.ObjSection.WriteZeros(1); TWasmObjSection(FStackPointerSym.ObjSection).MainFuncSymbol:=FStackPointerSym; FStackPointerSym.LinkingData.GlobalType:=wbt_i32; FStackPointerSym.LinkingData.GlobalIsMutable:=True; FStackPointerSym.LinkingData.GlobalInitializer.typ:=wbt_i32; FStackPointerSym.LinkingData.GlobalInitializer.init_i32:=0; end else if (ts_wasm_threads in current_settings.targetswitches) and (aname='__tls_base') then begin internalObjData.createsection('*'+aname,1,[oso_Data,oso_load]); FTlsBaseSym:=TWasmObjSymbol(internalObjData.SymbolDefine(aname,AB_GLOBAL,AT_WASM_GLOBAL)); FTlsBaseSym.size:=1; FTlsBaseSym.ObjSection.WriteZeros(1); TWasmObjSection(FTlsBaseSym.ObjSection).MainFuncSymbol:=FTlsBaseSym; FTlsBaseSym.LinkingData.GlobalType:=wbt_i32; FTlsBaseSym.LinkingData.GlobalIsMutable:=True; FTlsBaseSym.LinkingData.GlobalInitializer.typ:=wbt_i32; FTlsBaseSym.LinkingData.GlobalInitializer.init_i32:=0; end else if (ts_wasm_threads in current_settings.targetswitches) and (aname='__tls_size') then begin internalObjData.createsection('*'+aname,1,[oso_Data,oso_load]); FTlsSizeSym:=TWasmObjSymbol(internalObjData.SymbolDefine(aname,AB_GLOBAL,AT_WASM_GLOBAL)); FTlsSizeSym.size:=1; FTlsSizeSym.ObjSection.WriteZeros(1); TWasmObjSection(FTlsSizeSym.ObjSection).MainFuncSymbol:=FTlsSizeSym; FTlsSizeSym.LinkingData.GlobalType:=wbt_i32; FTlsSizeSym.LinkingData.GlobalIsMutable:=False; FTlsSizeSym.LinkingData.GlobalInitializer.typ:=wbt_i32; FTlsSizeSym.LinkingData.GlobalInitializer.init_i32:=0; end else if (ts_wasm_threads in current_settings.targetswitches) and (aname='__tls_align') then begin internalObjData.createsection('*'+aname,1,[oso_Data,oso_load]); FTlsAlignSym:=TWasmObjSymbol(internalObjData.SymbolDefine(aname,AB_GLOBAL,AT_WASM_GLOBAL)); FTlsAlignSym.size:=1; FTlsAlignSym.ObjSection.WriteZeros(1); TWasmObjSection(FTlsAlignSym.ObjSection).MainFuncSymbol:=FTlsAlignSym; FTlsAlignSym.LinkingData.GlobalType:=wbt_i32; FTlsAlignSym.LinkingData.GlobalIsMutable:=False; FTlsAlignSym.LinkingData.GlobalInitializer.typ:=wbt_i32; FTlsAlignSym.LinkingData.GlobalInitializer.init_i32:=0; end else if (ts_wasm_threads in current_settings.targetswitches) and (aname='__wasm_init_tls') then begin internalObjData.createsection('*'+aname,0,[]); FInitTlsFunctionSym:=TWasmObjSymbol(internalObjData.SymbolDefine(aname,AB_GLOBAL,AT_FUNCTION)); TWasmObjSection(FInitTlsFunctionSym.ObjSection).MainFuncSymbol:=FInitTlsFunctionSym; FInitTlsFunctionSym.LinkingData.FuncType:=TWasmFuncType.Create([wbt_i32],[]); end else if (ts_wasm_threads in current_settings.targetswitches) and (aname='__fpc_wasm_init_shared_memory') then begin internalObjData.createsection('*'+aname,0,[]); FInitSharedMemoryFunctionSym:=TWasmObjSymbol(internalObjData.SymbolDefine(aname,AB_GLOBAL,AT_FUNCTION)); TWasmObjSection(FInitSharedMemoryFunctionSym.ObjSection).MainFuncSymbol:=FInitSharedMemoryFunctionSym; FInitSharedMemoryFunctionSym.ObjSection.SecOptions:=FInitSharedMemoryFunctionSym.ObjSection.SecOptions+[oso_keep]; FInitSharedMemoryFunctionSym.LinkingData.FuncType:=TWasmFuncType.Create([],[]); end else inherited; end; procedure TWasmExeOutput.PrepareImports; function AddFunctionImport(const libname,symname:TCmdStr; functype: TWasmFuncType): Integer; begin SetLength(FFunctionImports,Length(FFunctionImports)+1); Result:=High(FFunctionImports); if assigned(exemap) then exemap.Add(' Importing Function[' + tostr(Result) + '] ' + symname + functype.ToString); with FFunctionImports[Result] do begin ModName:=libname; Name:=symname; TypeIdx:=FFuncTypes.AddOrGetFuncType(functype); end; end; var i, j: Integer; ImportLibrary: TImportLibrary; ImportSymbol: TImportSymbol; exesym: TExeSymbol; newdll: Boolean; fsym: TWasmObjSymbol; objdata: TObjData; begin for i:=0 to FImports.Count-1 do begin ImportLibrary:=TImportLibrary(FImports[i]); newdll:=False; for j:=0 to ImportLibrary.ImportSymbolList.Count-1 do begin ImportSymbol:=TImportSymbol(ImportLibrary.ImportSymbolList[j]); exesym:=ImportSymbol.CachedExeSymbol; if assigned(exesym) and exesym.Used then begin if (not newdll) and assigned(exemap) then begin exemap.Add(''); exemap.Add('Importing from module '+ImportLibrary.Name); end; newdll:=True; TWasmObjSymbol(exesym.ObjSymbol).LinkingData.ExeFunctionIndex:= AddFunctionImport(ImportLibrary.Name,ImportSymbol.Name,TWasmObjSymbol(exesym.ObjSymbol).LinkingData.FuncType); AddToFunctionNameMap(TWasmObjSymbol(exesym.ObjSymbol).LinkingData.ExeFunctionIndex,ImportSymbol.MangledName); end; end; end; { set ExeFunctionIndex to the alias symbols as well } for i:=0 to ObjDataList.Count-1 do begin objdata:=TObjData(ObjDataList[i]); for j:=0 to objdata.ObjSymbolList.Count-1 do begin fsym:=TWasmObjSymbol(objdata.ObjSymbolList[j]); if (fsym.LinkingData.ExeFunctionIndex=-1) and assigned(fsym.exesymbol) and (TWasmObjSymbol(fsym.exesymbol.ObjSymbol).LinkingData.ExeFunctionIndex<>-1) then fsym.LinkingData.ExeFunctionIndex:=TWasmObjSymbol(fsym.exesymbol.ObjSymbol).LinkingData.ExeFunctionIndex; end; end; end; procedure TWasmExeOutput.PrepareFunctions; var i, j: Integer; exesec: TExeSection; objsec: TWasmObjSection; fsym: TWasmObjSymbol; objdata: TObjData; begin if assigned(exemap) then begin exemap.Add(''); exemap.Add('Functions, defined in this module:'); end; exesec:=FindExeSection('.text'); if not assigned(exesec) then internalerror(2023123106); for i:=0 to exesec.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(exesec.ObjSectionList[i]); fsym:=objsec.MainFuncSymbol; if not assigned(fsym) then internalerror(2023123107); if not assigned(fsym.LinkingData.FuncType) then internalerror(2023123108); if fsym.LinkingData.ExeFunctionIndex<>-1 then internalerror(2023123109); if fsym.LinkingData.ExeTypeIndex<>-1 then internalerror(2023123109); fsym.LinkingData.ExeTypeIndex:=FFuncTypes.AddOrGetFuncType(fsym.LinkingData.FuncType); fsym.LinkingData.ExeFunctionIndex:=i+Length(FFunctionImports); if assigned(exemap) then begin exemap.Add(' Function[' + tostr(fsym.LinkingData.ExeFunctionIndex) + '] ' + fsym.Name + fsym.LinkingData.FuncType.ToString); end; AddToFunctionNameMap(fsym.LinkingData.ExeFunctionIndex,fsym.Name); end; { set ExeFunctionIndex to the alias symbols as well } for i:=0 to ObjDataList.Count-1 do begin objdata:=TObjData(ObjDataList[i]); for j:=0 to objdata.ObjSymbolList.Count-1 do begin fsym:=TWasmObjSymbol(objdata.ObjSymbolList[j]); if assigned(fsym.objsection) and fsym.objsection.USed and (fsym.typ=AT_FUNCTION) and (fsym.LinkingData.ExeFunctionIndex=-1) then begin fsym.LinkingData.ExeFunctionIndex:=TWasmObjSection(fsym.objsection).MainFuncSymbol.LinkingData.ExeFunctionIndex; if fsym.LinkingData.ExeFunctionIndex=-1 then internalerror(2024010102); end; end; end; end; procedure TWasmExeOutput.PrepareTags; var exesec: TExeSection; i, j: Integer; objsec: TWasmObjSection; fsym: TWasmObjSymbol; objdata: TObjData; begin exesec:=FindExeSection('.wasm_tags'); if not assigned(exesec) then exit; if assigned(exemap) then begin exemap.Add(''); exemap.Add('Tags, defined in this module:'); end; for i:=0 to exesec.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(exesec.ObjSectionList[i]); fsym:=objsec.MainFuncSymbol; if not assigned(fsym) then internalerror(2024010703); if not assigned(fsym.LinkingData.FuncType) then internalerror(2024010704); if fsym.LinkingData.ExeTagIndex<>-1 then internalerror(2024010705); if fsym.LinkingData.ExeTypeIndex<>-1 then internalerror(2024010706); fsym.LinkingData.ExeTypeIndex:=FFuncTypes.AddOrGetFuncType(fsym.LinkingData.FuncType); fsym.LinkingData.ExeTagIndex:=i+Length(FTagImports); if assigned(exemap) then begin exemap.Add(' Tag[' + tostr(fsym.LinkingData.ExeTagIndex) + '] ' + fsym.Name + fsym.LinkingData.FuncType.ToString); end; end; { set ExeTagIndex to the alias symbols as well } for i:=0 to ObjDataList.Count-1 do begin objdata:=TObjData(ObjDataList[i]); for j:=0 to objdata.ObjSymbolList.Count-1 do begin fsym:=TWasmObjSymbol(objdata.ObjSymbolList[j]); if assigned(fsym.objsection) and fsym.objsection.USed and (fsym.typ=AT_WASM_EXCEPTION_TAG) and (fsym.LinkingData.ExeTagIndex=-1) then begin fsym.LinkingData.ExeTagIndex:=TWasmObjSection(fsym.objsection).MainFuncSymbol.LinkingData.ExeTagIndex; if fsym.LinkingData.ExeTagIndex=-1 then internalerror(2024010707); end; end; end; end; function TWasmExeOutput.AddOrGetIndirectFunctionTableIndex(FuncIdx: Integer): integer; var i: Integer; begin for i:=1 to length(FIndirectFunctionTable)-1 do if FIndirectFunctionTable[i].FuncIdx=FuncIdx then begin Result:=i; exit; end; SetLength(FIndirectFunctionTable,Length(FIndirectFunctionTable)+1); Result:=High(FIndirectFunctionTable); FIndirectFunctionTable[Result].FuncIdx:=FuncIdx; end; procedure TWasmExeOutput.SetStackPointer; var BssSec: TExeSection; StackStart, InitialStackPtrAddr: QWord; begin BssSec:=FindExeSection('.bss'); InitialStackPtrAddr := (BssSec.MemPos+BssSec.Size+stacksize+15) and (not 15); FMinMemoryPages := Max( QWord(Align(QWord(InitialStackPtrAddr),QWord(WasmPageSize)) div WasmPageSize), QWord(Align(QWord(heapsize),QWord(WasmPageSize)) div WasmPageSize)); FStackPointerSym.LinkingData.GlobalInitializer.init_i32:=Int32(InitialStackPtrAddr); end; procedure TWasmExeOutput.SetTlsSizeAlignAndBase; var TBssSec: TExeSection; begin if not (ts_wasm_threads in current_settings.targetswitches) then exit; TBssSec:=FindExeSection('.tbss'); FTlsSizeSym.LinkingData.GlobalInitializer.init_i32:=Int32(TBssSec.Size); FTlsAlignSym.LinkingData.GlobalInitializer.init_i32:=Int32(TBssSec.SecAlign); FTlsBaseSym.LinkingData.GlobalInitializer.init_i32:=Int32(TBssSec.MemPos); end; procedure TWasmExeOutput.SetThreadVarGlobalsInitValues; var exesec: TExeSection; i: Integer; objsec: TWasmObjSection; objsym: TWasmObjSymbol; begin if not (ts_wasm_threads in current_settings.targetswitches) then exit; exesec:=FindExeSection('.wasm_globals'); if not assigned(exesec) then internalerror(2024010112); for i:=0 to exesec.ObjSectionList.Count-1 do begin objsec:=TWasmObjSection(exesec.ObjSectionList[i]); objsym:=objsec.MainFuncSymbol; if Assigned(objsym.TlsDataSym) then begin objsym.LinkingData.GlobalInitializer.typ:=wbt_i32; objsym.LinkingData.GlobalInitializer.init_i32:=objsym.TlsDataSym.offset+objsym.TlsDataSym.objsection.MemPos; end; end; end; procedure TWasmExeOutput.GenerateCode_InitTls; var Sec: TObjSection; globalexesec: TExeSection; i: Integer; globalobjsec: TWasmObjSection; globalobjsym: TWasmObjSymbol; OffsetInTls: QWord; begin if not (ts_wasm_threads in current_settings.targetswitches) then exit; globalexesec:=FindExeSection('.wasm_globals'); if not assigned(globalexesec) then internalerror(2024010112); Sec:=FInitTlsFunctionSym.objsection; Sec.SecOptions:=Sec.SecOptions+[oso_Data]; { locals } Sec.writeUInt8($00); { local.get 0 } Sec.writeUInt16BE($2000); { global.set $__tls_base } Sec.writeUInt8($24); WriteUleb(sec,FTlsBaseSym.offset+FTlsBaseSym.objsection.MemPos); for i:=0 to globalexesec.ObjSectionList.Count-1 do begin globalobjsec:=TWasmObjSection(globalexesec.ObjSectionList[i]); globalobjsym:=globalobjsec.MainFuncSymbol; if Assigned(globalobjsym.TlsDataSym) then begin OffsetInTls:=globalobjsym.TlsDataSym.offset+globalobjsym.TlsDataSym.objsection.MemPos-globalobjsym.TlsDataSym.objsection.ExeSection.MemPos; { local.get 0 } Sec.writeUInt16BE($2000); if OffsetInTls<>0 then begin { i32.const $OffsetInTls } Sec.writeUInt8($41); WriteSleb(Sec,Int32(OffsetInTls)); { i32.add } Sec.writeUInt8($6A); end; { global.set y } Sec.writeUInt8($24); WriteUleb(sec,globalobjsym.offset+globalobjsym.objsection.MemPos); end; end; Sec.writeUInt8($0B); { end } end; procedure TWasmExeOutput.GenerateCode_InitSharedMemory; const InitFlagOfs=256; var Sec: TObjSection; DataSecName: string; DataSecIdx: Integer; ExeSec: TExeSection; begin if not (ts_wasm_threads in current_settings.targetswitches) then exit; Sec:=FInitSharedMemoryFunctionSym.objsection; Sec.SecOptions:=Sec.SecOptions+[oso_Data]; { locals } Sec.writeUInt8($00); { block } Sec.writeUInt16BE($0240); { block } Sec.writeUInt16BE($0240); { block } Sec.writeUInt16BE($0240); { i32.const $InitFlag } Sec.writeUInt8($41); WriteSleb(sec,InitFlagOfs); { i32.const 0 } Sec.writeUInt16BE($4100); { i32.const 1 } Sec.writeUInt16BE($4101); { i32.atomic.rmw.cmpxchg 2 0 } Sec.writeUInt32BE($fe480200); { br_table 0 1 2 } Sec.writebytes(#$0e#$02#$00#$01#$02); { end } Sec.writeUInt8($0B); DataSecIdx:=-1; for DataSecName in DataSections do begin ExeSec:=FindExeSection(DataSecName); if Assigned(ExeSec) and (ExeSec.Size>0) then begin Inc(DataSecIdx); { i32.const $memPos } Sec.writeUInt8($41); WriteSleb(sec,Int32(ExeSec.MemPos)); { i32.const 0 } Sec.writeUInt16BE($4100); { i32.const size } Sec.writeUInt8($41); WriteSleb(sec,Int32(ExeSec.Size)); { memory.init $DataSecIdx 0 } Sec.writeUInt16BE($fc08); WriteUleb(sec,DataSecIdx); Sec.writeUInt8(0); end; end; { i32.const $InitFlag } Sec.writeUInt8($41); WriteSleb(sec,InitFlagOfs); { i32.const 2 } Sec.writeUInt16BE($4102); { i32.atomic.store 2 0 } Sec.writeUInt32BE($fe170200); { i32.const $InitFlag } Sec.writeUInt8($41); WriteSleb(sec,InitFlagOfs); { i32.const 4294967295 } Sec.writeUInt16BE($417f); { memory.atomic.notify 2 0 } Sec.writeUInt32BE($fe000200); { drop } Sec.writeUInt8($1A); { br 1 } Sec.writeUInt16BE($0C01); { end } Sec.writeUInt8($0B); { i32.const $InitFlag } Sec.writeUInt8($41); WriteSleb(sec,InitFlagOfs); { i32.const 1 } Sec.writeUInt16BE($4101); { i64.const -1 } Sec.writeUInt16BE($427f); { memory.atomic.wait32 2 0 } Sec.writeUInt32BE($fe010200); { drop } Sec.writeUInt8($1A); { end } Sec.writeUInt8($0B); DataSecIdx:=-1; for DataSecName in DataSections do begin ExeSec:=FindExeSection(DataSecName); if Assigned(ExeSec) and (ExeSec.Size>0) then begin Inc(DataSecIdx); { data.drop $DataSecIdx } Sec.writeUInt16BE($fc09); WriteUleb(sec,DataSecIdx); end; end; { end } Sec.writeUInt8($0B); end; procedure TWasmExeOutput.GenerateCode_InvokeHelper; var Sec: TObjSection; IndirectFunctionTableMap: array of Integer; procedure InvokeFuncType(typidx: Integer; islast: Boolean); var ft: TWasmFuncType; i, nextofs: Integer; begin ft:=FFuncTypes[typidx]; for i:=0 to Length(ft.results)-1 do { local.get 2 } Sec.writeUInt16BE($2002); nextofs:=0; for i:=0 to Length(ft.params)-1 do begin { local.get 1 } Sec.writeUInt16BE($2001); case ft.params[i] of wbt_i32: begin { i32.load nextofs } Sec.writeUInt16BE($2802); WriteUleb(Sec, nextofs); Inc(nextofs,4); end; wbt_i64: begin { i64.load nextofs } Sec.writeUInt16BE($2902); WriteUleb(Sec, nextofs); Inc(nextofs,8); end; wbt_f32: begin { f32.load nextofs } Sec.writeUInt16BE($2A02); WriteUleb(Sec, nextofs); Inc(nextofs,4); end; wbt_f64: begin { f64.load nextofs } Sec.writeUInt16BE($2B02); WriteUleb(Sec, nextofs); Inc(nextofs,8); end; wbt_v128: begin { v128.load nextofs } Sec.writeUInt16BE($FD00); Sec.writeUInt8($02); { align: 4 bytes } WriteUleb(Sec, nextofs); Inc(nextofs,16); end; wbt_externref, wbt_funcref: begin { unreachable } Sec.writeUInt8($00); end; else internalerror(2025012501); end; end; { local.get 0 } Sec.writeUInt16BE($2000); { call_indirect } Sec.writeUInt8($11); WriteUleb(Sec,typidx); Sec.writeUInt8($0); { table index 0 } nextofs:=0; for i:=0 to Length(ft.results)-1 do begin case ft.results[i] of wbt_i32: begin { i32.store nextofs } Sec.writeUInt16BE($3602); WriteUleb(Sec, nextofs); Inc(nextofs,4); end; wbt_i64: begin { i64.store nextofs } Sec.writeUInt16BE($3702); WriteUleb(Sec, nextofs); Inc(nextofs,8); end; wbt_f32: begin { f32.store nextofs } Sec.writeUInt16BE($3802); WriteUleb(Sec, nextofs); Inc(nextofs,4); end; wbt_f64: begin { f64.store nextofs } Sec.writeUInt16BE($3902); WriteUleb(Sec, nextofs); Inc(nextofs,8); end; wbt_v128: begin { v128.store nextofs } Sec.writeUInt16BE($FD0B); Sec.writeUInt8($02); { align: 4 bytes } WriteUleb(Sec, nextofs); Inc(nextofs,16); end; wbt_externref, wbt_funcref: begin { unreachable } Sec.writeUInt8($00); end; else internalerror(2025012501); end; end; if not islast then { return } Sec.writeUInt8($0F); end; function FuncIdx2TypeIdx(fi: Integer): Integer; var exesec: TExeSection; objsec: TWasmObjSection; fsym: TWasmObjSymbol; begin if fi0 then begin { i32.const l } Sec.writeUInt8($41); WriteSleb(sec,l); { i32.sub } Sec.writeUInt8($6B); end; { br_table } Sec.writeUInt8($0E); if h=high(IndirectFunctionTableMap) then begin WriteUleb(Sec,len); for i:=l to h do WriteUleb(Sec,IndirectFunctionTableMap[i]+addend); WriteUleb(Sec,addend); end else begin WriteUleb(Sec,len-1); for i:=l to h do WriteUleb(Sec,IndirectFunctionTableMap[i]+addend); end; end else begin m:=(l+h) div 2; { i32.const m } Sec.writeUInt8($41); WriteSleb(sec,m); { i32.lt_u } Sec.writeUInt8($49); { if } Sec.writeUInt16BE($0440); WriteBrTable(l,m-1,addend+1); { else } Sec.writeUInt8($05); WriteBrTable(m,h,addend+1); { end } Sec.writeUInt8($0B); end; end; var exesym: TExeSymbol; objsym: TObjSymbol; i, j, TypIdx: Integer; InvokableTypeIndices: array of Integer; begin exesym:=TExeSymbol(ExeSymbolList.Find('fpc_wasm_invoke_helper')); if not Assigned(exesym) then exit; SetLength(IndirectFunctionTableMap, Length(FIndirectFunctionTable)); SetLength(InvokableTypeIndices, 1); InvokableTypeIndices[0] := -1; for i:=1 to Length(FIndirectFunctionTable)-1 do begin IndirectFunctionTableMap[i]:=0; TypIdx := FuncIdx2TypeIdx(FIndirectFunctionTable[i].FuncIdx); for j := 1 to Length(InvokableTypeIndices)-1 do if InvokableTypeIndices[j]=TypIdx then begin IndirectFunctionTableMap[i]:=j; break; end; if IndirectFunctionTableMap[i]=0 then begin SetLength(InvokableTypeIndices,Length(InvokableTypeIndices)+1); InvokableTypeIndices[High(InvokableTypeIndices)]:=TypIdx; IndirectFunctionTableMap[i]:=High(InvokableTypeIndices); end; end; objsym:=exesym.ObjSymbol; Sec:=objsym.objsection; Sec.Size:=0; Sec.Data.reset; { locals } Sec.writeUInt8($00); for i:=1 to Length(InvokableTypeIndices)-1 do { block } Sec.writeUInt16BE($0240); { block } Sec.writeUInt16BE($0240); { local.get 0 + br_table } WriteBrTable(low(IndirectFunctionTableMap),high(IndirectFunctionTableMap)); { end } Sec.writeUInt8($0B); { unreachable } Sec.writeUInt8($00); for i:=1 to Length(InvokableTypeIndices)-1 do begin { end } Sec.writeUInt8($0B); InvokeFuncType(InvokableTypeIndices[i],i=(Length(InvokableTypeIndices)-1)); end; { end } Sec.writeUInt8($0B); end; procedure TWasmExeOutput.WriteExeSectionToDynArray(exesec: TExeSection; dynarr: tdynamicarray); var exesecdatapos: LongWord; i: Integer; objsec: TObjSection; dpos, pad: QWord; begin exesecdatapos:=dynarr.size; for i:=0 to exesec.ObjSectionList.Count-1 do begin objsec:=TObjSection(exesec.ObjSectionList[i]); if not (oso_data in objsec.secoptions) then internalerror(2024010104); if not assigned(objsec.data) then internalerror(2024010105); dpos:=objsec.MemPos-exesec.MemPos+exesecdatapos; pad:=dpos-dynarr.size; { objsection must be within SecAlign bytes from the previous one } if (dpos=max(objsec.SecAlign,1)) then internalerror(2024010106); writeZeros(dynarr,pad); objsec.data.seek(0); CopyDynamicArray(objsec.data,dynarr,objsec.data.size); end; if (dynarr.size-exesecdatapos)<>exesec.Size then internalerror(2024010107); end; procedure TWasmExeOutput.WriteMemoryTo(dest: tdynamicarray; const MemType: TWasmMemoryType); begin WriteByte(dest,Byte(MemType.Flags)); WriteUleb(dest,MemType.MinPages); if wmfHasMaximumBound in MemType.Flags then WriteUleb(dest,MemType.MaxPages); { todo: wmfCustomPageSize } end; function TWasmExeOutput.Memory2String(const MemType: TWasmMemoryType): string; begin Result:='index type: '; if wmfMemory64 in MemType.Flags then Result:=Result+'i64' else Result:=Result+'i32'; Result:=Result+', pages: initial='+tostr(MemType.MinPages); if wmfHasMaximumBound in MemType.Flags then Result:=Result+' max='+tostr(MemType.MaxPages); if wmfShared in MemType.Flags then Result:=Result+', shared' else Result:=Result+', unshared'; { todo: wmfCustomPageSize } end; procedure TWasmExeOutput.WriteMap_TypeSection; var i: Integer; begin exemap.AddHeader('Type section'); for i:=0 to FFuncTypes.Count-1 do exemap.Add(' Type[' + tostr(i) + '] ' + FFuncTypes.Items[i].ToString); end; procedure TWasmExeOutput.WriteMap_IndirectFunctionTable; var i: Integer; begin exemap.AddHeader('Indirect function table'); for i:=1 to High(FIndirectFunctionTable) do exemap.Add(' Elem[' + tostr(i) + '] = Function[' + tostr(FIndirectFunctionTable[i].FuncIdx) + ']'); end; {**************************************************************************** TWasmAssembler ****************************************************************************} constructor TWasmAssembler.Create(info: pasminfo; smart:boolean); begin inherited; CObjOutput:=TWasmObjOutput; end; {***************************************************************************** Initialize *****************************************************************************} {$ifdef wasm32} const as_wasm32_wasm_info : tasminfo = ( id : as_wasm32_wasm; idtxt : 'WASM'; asmbin : ''; asmcmd : ''; supported_targets : [system_wasm32_embedded,system_wasm32_wasip1]; flags : [af_outputbinary,af_smartlink_sections]; labelprefix : '..@'; labelmaxlen : -1; comment : '; '; dollarsign: '$'; ); {$endif wasm32} initialization {$ifdef wasm32} RegisterAssembler(as_wasm32_wasm_info,TWasmAssembler); {$endif wasm32} end.