mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2026-02-24 18:38:39 +01:00
337 lines
11 KiB
ObjectPascal
337 lines
11 KiB
ObjectPascal
{
|
|
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 }
|
|
globtype,
|
|
{ target }
|
|
systems,
|
|
{ assembler }
|
|
aasmbase,assemble,
|
|
{ WebAssembly module format definitions }
|
|
wasmbase,
|
|
{ output }
|
|
ogbase,
|
|
owbase;
|
|
|
|
type
|
|
|
|
{ TWasmObjSection }
|
|
|
|
TWasmObjSection = class(TObjSection)
|
|
public
|
|
SegIdx: Integer;
|
|
function IsCode: Boolean;
|
|
function IsData: Boolean;
|
|
end;
|
|
|
|
{ TWasmObjData }
|
|
|
|
TWasmObjData = class(TObjData)
|
|
private
|
|
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;
|
|
function sectionname(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string;override;
|
|
procedure writeReloc(Data:TRelocDataInt;len:aword;p:TObjSymbol;Reloctype:TObjRelocationType);override;
|
|
end;
|
|
|
|
{ TWasmObjOutput }
|
|
|
|
TWasmObjOutput = class(tObjOutput)
|
|
protected
|
|
function writeData(Data:TObjData):boolean;override;
|
|
public
|
|
constructor create(AWriter:TObjectWriter);override;
|
|
end;
|
|
|
|
{ TWasmAssembler }
|
|
|
|
TWasmAssembler = class(tinternalassembler)
|
|
constructor create(info: pasminfo; smart:boolean);override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{****************************************************************************
|
|
TWasmObjSection
|
|
****************************************************************************}
|
|
|
|
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;
|
|
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',
|
|
'.threadvar',
|
|
'.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',
|
|
'.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;
|
|
|
|
if atype=sec_threadvar then
|
|
begin
|
|
if (target_info.system in (systems_windows+systems_wince)) then
|
|
secname:='.tls'
|
|
else if (target_info.system in systems_linux) then
|
|
secname:='.tbss';
|
|
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;
|
|
end;
|
|
|
|
function TWasmObjData.sectionname(atype: TAsmSectiontype;
|
|
const aname: string; aorder: TAsmSectionOrder): string;
|
|
begin
|
|
if (atype=sec_fpc) or (atype=sec_threadvar) then
|
|
atype:=sec_data;
|
|
Result:=sectionname_gas(atype, aname, aorder);
|
|
end;
|
|
|
|
procedure TWasmObjData.writeReloc(Data: TRelocDataInt; len: aword;
|
|
p: TObjSymbol; Reloctype: TObjRelocationType);
|
|
begin
|
|
end;
|
|
|
|
{****************************************************************************
|
|
TWasmObjOutput
|
|
****************************************************************************}
|
|
|
|
function TWasmObjOutput.writeData(Data:TObjData):boolean;
|
|
var
|
|
i: Integer;
|
|
objsec: TWasmObjSection;
|
|
segment_count: Integer = 0;
|
|
begin
|
|
for i:=0 to Data.ObjSectionList.Count-1 do
|
|
begin
|
|
objsec:=TWasmObjSection(Data.ObjSectionList[i]);
|
|
if objsec.IsCode then
|
|
objsec.SegIdx:=-1
|
|
else
|
|
begin
|
|
objsec.SegIdx:=segment_count;
|
|
Inc(segment_count);
|
|
end;
|
|
end;
|
|
|
|
Writer.write(WasmModuleMagic,SizeOf(WasmModuleMagic));
|
|
Writer.write(WasmVersion,SizeOf(WasmVersion));
|
|
|
|
Writeln('ObjSectionList:');
|
|
for i:=0 to Data.ObjSectionList.Count-1 do
|
|
begin
|
|
objsec:=TWasmObjSection(Data.ObjSectionList[i]);
|
|
Writeln(objsec.Name, ' IsCode=', objsec.IsCode, ' IsData=', objsec.IsData, ' Size=', objsec.Size, ' MemPos=', objsec.MemPos, ' Data.Size=', objsec.Data.size, ' DataPos=', objsec.DataPos, ' SegIdx=', objsec.SegIdx);
|
|
end;
|
|
|
|
result:=true;
|
|
end;
|
|
|
|
constructor TWasmObjOutput.create(AWriter: TObjectWriter);
|
|
begin
|
|
inherited;
|
|
cobjdata:=TWasmObjData;
|
|
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 : 'OMF';
|
|
asmbin : '';
|
|
asmcmd : '';
|
|
supported_targets : [system_wasm32_embedded,system_wasm32_wasi];
|
|
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.
|