fpc/compiler/systems/t_wasi.pas

411 lines
11 KiB
ObjectPascal

{
Copyright (c) 2019 by Dmitry Boyarintsev
This unit implements support import,export,link routines
for the WASI target
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 t_wasi;
{$i fpcdefs.inc}
interface
uses
systems,
globtype, globals,
aasmbase,
cfileutl, cutils, cclasses,
import, export, aasmdata, aasmcpu,
fmodule, ogbase, ogwasm,
symconst, symsym, symdef, symcpu,
link,
i_wasi, tgcpu;
type
{ texportlibwasi }
texportlibwasi=class(texportlib)
procedure preparelib(const s : string);override;
procedure exportprocedure(hp : texported_item);override;
procedure exportvar(hp : texported_item);override;
procedure generatelib;override;
end;
{ timportlibwasi }
timportlibwasi = class(timportlib)
procedure generatelib;override;
end;
{ tlinkerwasi }
tlinkerwasi=class(texternallinker)
public
constructor Create;override;
procedure SetDefaultInfo;override;
procedure InitSysInitUnitName;override;
function MakeExecutable:boolean;override;
function MakeSharedLibrary:boolean;override;
end;
{ TInternalLinkerWasi }
TInternalLinkerWasi=class(tinternallinker)
private
function GetExeSectionSize(aExeOutput: TExeOutput; const aname:string): QWord;
protected
procedure DefaultLinkScript;override;
function GetDataSize(aExeOutput: TExeOutput): QWord;override;
function GetBssSize(aExeOutput: TExeOutput): QWord;override;
public
constructor create;override;
procedure InitSysInitUnitName;override;
end;
implementation
uses
SysUtils,
verbose,
comprsrc,rescmn;
{ timportlibwasi }
procedure timportlibwasi.generatelib;
begin
end;
{ tlinkerwasi }
constructor tlinkerwasi.Create;
begin
inherited Create;
end;
procedure tlinkerwasi.SetDefaultInfo;
begin
with Info do
begin
ExeCmd[1] := 'wasm-ld -m wasm32 $SONAME $GCSECTIONS $MAP -z stack-size=$STACKSIZE $OPT -o $EXE';
DllCmd[1] := 'wasm-ld -m wasm32 $SONAME $GCSECTIONS $MAP -z stack-size=$STACKSIZE $OPT -o $EXE';
end;
end;
procedure tlinkerwasi.InitSysInitUnitName;
begin
if current_module.islibrary then
sysinitunit:='si_dll'
else
sysinitunit:='si_prc';
end;
function tlinkerwasi.MakeExecutable:boolean;
const
PageSize = 65536;
var
GCSectionsStr : ansistring;
binstr, cmdstr : Tcmdstr;
InitStr,
FiniStr,
SoNameStr : string[80];
mapstr,ltostr : TCmdStr;
success : Boolean;
tmp : TCmdStrListItem;
tempFileName : ansistring;
initialmem,
maxmem : longint;
begin
if not(cs_link_nolink in current_settings.globalswitches) then
Message1(exec_i_linking,current_module.exefilename);
{ Create some replacements }
mapstr:='';
if (cs_link_map in current_settings.globalswitches) then
mapstr:='-Map '+maybequoted(ChangeFileExt(current_module.exefilename,'.map'));
if (cs_link_smart in current_settings.globalswitches) and
create_smartlink_sections then
GCSectionsStr:='--gc-sections'
else
GCSectionsStr:='';
SoNameStr:='';
SplitBinCmd(Info.ExeCmd[1],binstr,cmdstr);
Replace(cmdstr,'$EXE',maybequoted(current_module.exefilename));
tmp := TCmdStrListItem(ObjectFiles.First);
while Assigned(tmp) do begin
cmdstr := tmp.Str+ ' ' + cmdstr;
tmp := TCmdStrListItem(tmp.Next);
end;
// if HasExports then
// cmdstr := cmdstr + ' --export-dynamic'; //' --export-dynamic';
cmdstr := cmdstr + ' --no-entry';
initialmem:=align(heapsize,PageSize);
maxmem:=align(maxheapsize,PageSize);
if ts_wasm_threads in current_settings.targetswitches then
begin
cmdstr := cmdstr + ' --import-memory --shared-memory --global-base=1024';
end;
if initialmem>0 then
cmdstr := cmdstr + ' --initial-memory=' + tostr(initialmem);
if maxmem>0 then
cmdstr := cmdstr + ' --max-memory=' + tostr(maxmem);
if (cs_link_strip in current_settings.globalswitches) then
begin
{ only remove non global symbols and debugging info for a library }
cmdstr := cmdstr + ' --strip-all';
end;
Replace(cmdstr,'$OPT',Info.ExtraOptions);
//Replace(cmdstr,'$RES',maybequoted(outputexedir+Info.ResName));
//Replace(cmdstr,'$INIT',InitStr);
//Replace(cmdstr,'$FINI',FiniStr);
Replace(cmdstr,'$STACKSIZE',tostr(stacksize));
Replace(cmdstr,'$SONAME',SoNameStr);
Replace(cmdstr,'$MAP',mapstr);
//Replace(cmdstr,'$LTO',ltostr);
Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
success:=DoExec(FindUtil(utilsprefix+binstr),cmdstr,true,false);
MakeExecutable:=success;
end;
function tlinkerwasi.MakeSharedLibrary: boolean;
var
GCSectionsStr : ansistring;
binstr, cmdstr : Tcmdstr;
InitStr,
FiniStr,
SoNameStr : string[80];
mapstr,ltostr : TCmdStr;
success : Boolean;
tmp : TCmdStrListItem;
tempFileName : ansistring;
begin
Result:=false;
if not(cs_link_nolink in current_settings.globalswitches) then
Message1(exec_i_linking,current_module.sharedlibfilename);
{ Create some replacements }
mapstr:='';
if (cs_link_map in current_settings.globalswitches) then
mapstr:='-Map '+maybequoted(ChangeFileExt(current_module.sharedlibfilename,'.map'));
if (cs_link_smart in current_settings.globalswitches) and
create_smartlink_sections then
GCSectionsStr:='--gc-sections'
else
GCSectionsStr:='';
SoNameStr:='';
SplitBinCmd(Info.DllCmd[1],binstr,cmdstr);
Replace(cmdstr,'$EXE',maybequoted(current_module.sharedlibfilename));
tmp := TCmdStrListItem(ObjectFiles.First);
while Assigned(tmp) do begin
cmdstr := tmp.Str+ ' ' + cmdstr;
tmp := TCmdStrListItem(tmp.Next);
end;
// if HasExports then
// cmdstr := cmdstr + ' --export-dynamic'; //' --export-dynamic';
cmdstr := cmdstr + ' --no-entry';
if (cs_link_strip in current_settings.globalswitches) then
begin
{ only remove non global symbols and debugging info for a library }
cmdstr := cmdstr + ' --strip-all';
end;
Replace(cmdstr,'$OPT',Info.ExtraOptions);
//Replace(cmdstr,'$RES',maybequoted(outputexedir+Info.ResName));
//Replace(cmdstr,'$INIT',InitStr);
//Replace(cmdstr,'$FINI',FiniStr);
Replace(cmdstr,'$STACKSIZE',tostr(stacksize));
Replace(cmdstr,'$SONAME',SoNameStr);
Replace(cmdstr,'$MAP',mapstr);
//Replace(cmdstr,'$LTO',ltostr);
Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
success:=DoExec(FindUtil(utilsprefix+binstr),cmdstr,true,false);
MakeSharedLibrary:=success;
end;
{ texportlibwasi }
procedure texportlibwasi.preparelib(const s: string);
begin
//nothing to happen. wasm files are modules
end;
procedure texportlibwasi.exportprocedure(hp: texported_item);
var
nm : TSymStr;
pd: tcpuprocdef;
begin
pd:=tcpuprocdef(tprocsym(hp.sym).ProcdefList[0]);
if eo_promising_first in hp.options then
pd.add_promising_export(hp.name^,false)
else if eo_promising_last in hp.options then
pd.add_promising_export(hp.name^,true)
else
begin
nm := pd.mangledname;
current_asmdata.asmlists[al_exports].Concat(tai_export_name.create(hp.name^, nm, ie_Func));
end;
end;
procedure texportlibwasi.exportvar(hp: texported_item);
begin
//inherited exportvar(hp);
end;
procedure texportlibwasi.generatelib;
begin
//inherited generatelib;
end;
{ TInternalLinkerWasi }
function TInternalLinkerWasi.GetExeSectionSize(aExeOutput: TExeOutput;
const aname: string): QWord;
var
sec: TExeSection;
begin
sec:=aExeOutput.findexesection(aname);
if assigned(sec) then
Result:=sec.size
else
Result:=0;
end;
procedure TInternalLinkerWasi.DefaultLinkScript;
var
s: TCmdStr;
begin
while not ObjectFiles.Empty do
begin
s:=ObjectFiles.GetFirst;
if s<>'' then
LinkScript.Concat('READOBJECT ' + maybequoted(s));
end;
LinkScript.Concat('EXESECTION .wasm_globals');
LinkScript.Concat(' SYMBOL __stack_pointer');
if ts_wasm_threads in current_settings.targetswitches then
begin
LinkScript.Concat(' SYMBOL __tls_base');
LinkScript.Concat(' SYMBOL __tls_size');
LinkScript.Concat(' SYMBOL __tls_align');
end;
LinkScript.Concat(' OBJSECTION .wasm_globals.*');
LinkScript.Concat('ENDEXESECTION');
{ WebAssembly is a Harvard architecture, with multiple address spaces, so it
is important to keep the sections grouped, and keep the first section in
each group intact (otherwise, TWasmExeOutput.MemPos_ExeSection in ogwasm.pas
needs to be updated) }
{ tags (used by WebAssembly native exceptions) }
ScriptAddGenericSections('.wasm_tags');
{ code }
if ts_wasm_threads in current_settings.targetswitches then
begin
linkscript.Concat('EXESECTION .text');
linkscript.Concat(' OBJSECTION .text*');
{ functions, generated by the linker: }
linkscript.Concat(' SYMBOL __wasm_init_tls');
linkscript.Concat(' SYMBOL __fpc_wasm_init_shared_memory');
linkscript.Concat('ENDEXESECTION');
end
else
ScriptAddGenericSections('.text');
if ts_wasm_threads in current_settings.targetswitches then
ScriptAddGenericSections('.tbss');
ScriptAddGenericSections(
{ data (initialized data first, uninitialized data later) }
'.rodata,.data,fpc.resources,fpc.reshandles,.bss,'+
{ debug info }
'.debug_frame,.debug_info,.debug_line,.debug_abbrev,.debug_aranges,.debug_ranges,.debug_str');
end;
function TInternalLinkerWasi.GetDataSize(aExeOutput: TExeOutput): QWord;
begin
Result:=GetExeSectionSize(aExeOutput,'.rodata') +
GetExeSectionSize(aExeOutput,'.data') +
GetExeSectionSize(aExeOutput,'fpc.resources');
end;
function TInternalLinkerWasi.GetBssSize(aExeOutput: TExeOutput): QWord;
begin
Result:=GetExeSectionSize(aExeOutput,'.bss') +
GetExeSectionSize(aExeOutput,'.tbss') +
GetExeSectionSize(aExeOutput,'fpc.reshandles');
end;
constructor TInternalLinkerWasi.create;
begin
inherited create;
CExeOutput:=TWasmExeOutput;
CObjInput:=TWasmObjInput;
end;
procedure TInternalLinkerWasi.InitSysInitUnitName;
begin
if current_module.islibrary then
sysinitunit:='si_dll'
else
sysinitunit:='si_prc';
end;
initialization
RegisterTarget(system_wasm32_wasip1_info);
RegisterImport(system_wasm32_wasip1, timportlibwasi);
RegisterExport(system_wasm32_wasip1, texportlibwasi);
RegisterTarget(system_wasm32_wasip1threads_info);
RegisterImport(system_wasm32_wasip1threads, timportlibwasi);
RegisterExport(system_wasm32_wasip1threads, texportlibwasi);
RegisterTarget(system_wasm32_wasip2_info);
RegisterImport(system_wasm32_wasip2, timportlibwasi);
RegisterExport(system_wasm32_wasip2, texportlibwasi);
RegisterLinker(ld_int_wasi,TInternalLinkerWasi);
RegisterLinker(ld_wasi, tlinkerwasi);
RegisterRes(res_wasm_info,TWinLikeResourceFile);
end.