123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- { This file is part of wasmbin - a collection of WebAssembly binary utils.
- Copyright (C) 2019, 2020 Dmitry Boyarintsev <[email protected]>
- Copyright (C) 2020 by the Free Pascal development team
- This source 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 code 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.
- A copy of the GNU General Public License is available on the World Wide Web
- at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
- to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
- Boston, MA 02110-1335, USA.
- }
- unit wasmtoolutils;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes,SysUtils, wasmbin, lebutils,
- //wasmbindebug,
- wasmlink, wasmlinkchange;
- function ChangeSymbolFlagStream(st: TStream; syms: TStrings): Boolean;
- procedure ChangeSymbolFlag(const fn, symfn: string);
- function PredictSymbolsFromLink(const wasmfn: string; weakList: TStrings; doVerbose: Boolean = false): Boolean;
- procedure MatchExportNameToSymName(const x: TExportSection; const l: TLinkingSection; dst: TStrings);
- function ExportRenameSym(var x: TExportSection; syms: TStrings): Integer;
- function ExportRenameProcess(st, dst: TStream; syms: TStrings; doVerbose: Boolean): Boolean;
- function ExportNameGather(const wasmfile: string; syms: TStrings; doVerbose: Boolean = false): Boolean;
- procedure ExportRename(const fn, symfn: string; doVerbose: Boolean);
- implementation
- function ChangeSymbolFlagStream(st: TStream; syms: TStrings): Boolean;
- var
- dw : LongWord;
- ofs : int64;
- sc : TSection;
- ps : int64;
- nm : string;
- begin
- dw := st.ReadDWord;
- Result := dw = WasmId_Int;
- if not Result then Exit;
- dw := st.ReadDWord;
- while st.Position<st.Size do begin
- ofs := st.Position;
- sc.id := st.ReadByte;
- sc.Size := ReadU(st);
- ps := st.Position+sc.size;
- if sc.id=0 then begin
- nm := GetName(st);
- if nm = SectionName_Linking then begin
- ProcessLinkingSection(st, syms);
- break;
- end;
- //DumpLinking(st, sc.size - (st.Position - ofs));
- end;
- //if sc.id= 1 then DumpTypes(st);
- if st.Position <> ps then
- begin
- //writeln('adjust stream targ=',ps,' actual: ', st.position);
- st.Position := ps;
- end;
- end;
- end;
- // Assumption is made, there's only 1 table in the file!
- // if a function is a stub function (the only code is "unreachable"), the status given
- // "weak" (it's a reference function elsewhere)
- // if a function is located in the function table, then the status given is
- // "hidden" (do not add to the final linked executable)
- // if a function is not located in the function table, the status given is:
- // "hidden"+"local" (local means the function can be used only in this object file)
- procedure MatchExportNameToSymFlag(
- const imp: TImportSection;
- const c: TCodeSection;
- const e: TElementSection;
- const x: TExportSection;
- var l: TLinkingSection;
- weakList: TStrings; // do known set of globals weak
- doVerbose: Boolean);
- type
- TFuncType = (ftImpl = 0, ftIntf, ftStub, ftExport);
- TFuncInfo = record
- hasSymbol : Boolean;
- fnType : TFuncType;
- end;
- var
- i : integer;
- j : integer;
- idx : integer;
- fn : array of TFuncInfo;
- codeofs: integer;
- begin
- idx := -1;
- for i:=0 to length(l.symbols)-1 do
- if l.symbols[i].kind = SYMTAB_FUNCTION then begin
- if l.symbols[i].symindex>idx then begin
- idx:= l.symbols[i].symindex;
- end;
- end;
- SetLength(fn, idx+1);
- for i:=0 to length(l.symbols)-1 do
- if l.symbols[i].kind = SYMTAB_FUNCTION then begin
- idx := l.symbols[i].symindex;
- fn[idx].hasSymbol:=true;
- end;
- for i:=0 to length(e.entries)-1 do
- for j:=0 to length(e.entries[i].funcs)-1 do begin
- idx := e.entries[i].funcs[j];
- fn[idx].fnType:=ftIntf;
- end;
- codeofs:=0;
- for i:=0 to length(imp.entries)-1 do
- if imp.entries[i].desc = IMPDESC_FUNC then
- inc(codeofs);
- for i:=codeofs to length(fn)-1 do begin
- if not fn[i].hasSymbol then begin
- Continue;
- end;
- if (fn[i].fnType=ftImpl) and (isUnreachable(c.entries[i-codeofs])) then begin
- fn[i].fnType:=ftStub;
- end;
- end;
- for i:=0 to length(x.entries)-1 do begin
- if x.entries[i].desc = EXPDESC_FUNC then begin
- idx := x.entries[i].index;
- if fn[idx].fnType<>ftStub then
- fn[idx].fnType:=ftExport;
- end;
- end;
- for i:=0 to length(l.symbols)-1 do begin
- if l.symbols[i].kind = SYMTAB_FUNCTION then begin
- j := l.symbols[i].symindex;
- if j>=codeofs then // not imported
- case fn[j].fnType of
- ftImpl:
- l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_VISIBILITY_HIDDEN or WASM_SYM_BINDING_LOCAL;
- ftIntf:
- l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_VISIBILITY_HIDDEN;
- ftStub:
- l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_BINDING_WEAK or WASM_SYM_VISIBILITY_HIDDEN;
- ftExport:
- //l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_VISIBILITY_HIDDEN or WASM_SYM_BINDING_WEAK;
- ;
- end;
- if DoVerbose then begin
- write('func ');
- if l.symbols[i].hasSymName then
- write(l.symbols[i].symname)
- else
- write('#',j);
- write(' ', fn[j].fnType);
- writeln;
- end;
- //if l.symbols[i].symindex>mx then mx := ;
- end else if (l.symbols[i].kind = SYMTAB_GLOBAL) and Assigned(weakList) then begin
- if l.symbols[i].hasSymName and (weakList.IndexOf(l.symbols[i].symname)>=0) then begin
- if doVerbose then
- writeln('weakining: ',l.symbols[i].symname);
- l.symbols[i].flags := l.symbols[i].flags or WASM_SYM_BINDING_WEAK or WASM_SYM_VISIBILITY_HIDDEN;
- end;
- end;
- end;
- end;
- function PredictSymbolsFromLink(const wasmfn: string; weakList: TStrings; doVerbose: Boolean = false): Boolean;
- var
- st : TFileStream;
- dw : LongWord;
- foundCode : Boolean;
- foundElement : Boolean;
- foundLink : Boolean;
- foundExport : Boolean;
- foundImport : Boolean;
- ofs : Int64;
- ps : Int64;
- sc : TSection;
- c : TCodeSection;
- imp : TImportSection;
- l : TLinkingSection;
- e : TElementSection;
- x : TExportSection;
- nm : string;
- lofs : Int64;
- lsize : Int64;
- mem : TMemoryStream;
- mem2 : TMemoryStream;
- begin
- st := TFileStream.Create(wasmfn, fmOpenReadWrite or fmShareDenyNone);
- try
- dw := st.ReadDWord;
- Result := dw = WasmId_Int;
- if not Result then Exit;
- dw := st.ReadDWord;
- foundElement := false;
- foundCode := false;
- foundLink := false;
- foundExport := false;
- foundImport := false;
- Result := false;
- while st.Position<st.Size do begin
- ofs := st.Position;
- sc.id := st.ReadByte;
- sc.Size := ReadU(st);
- ps := st.Position+sc.size;
- case sc.id of
- SECT_IMPORT: begin
- ReadImportSection(st, imp);
- foundImport := true;
- end;
- SECT_EXPORT: begin
- ReadExport(st, x);
- foundExport := true;
- end;
- SECT_ELEMENT: begin
- ReadElementSection(st, e);
- foundElement := true;
- end;
- SECT_CODE: begin
- ReadCodeSection(st, c);
- foundCode := true;
- end;
- SECT_CUSTOM: begin
- nm := ReadName(st);
- if nm = SectionName_Linking then begin
- lofs:=ofs;
- ReadLinkingSection(st, sc.size, l);
- foundLink := true;
- lsize := ps-lofs;
- end;
- end;
- end;
- if st.Position <> ps then begin
- st.Position := ps;
- end;
- Result := foundLink and foundCode and foundElement;
- if Result then break;
- end;
- if not foundExport then SetLength(x.entries,0);
- if not foundImport then SetLength(imp.entries, 0);
- if Result then begin
- if doVerbose then writeln('detecting symbols');
- MatchExportNameToSymFlag(imp, c, e, x, l, weakList, doVerbose);
- mem:=TMemoryStream.Create;
- mem2:=TMemoryStream.Create;
- try
- st.Position:=lofs+lsize;
- mem2.CopyFrom(st, st.Size - st.Position);
- st.Position:=lofs;
- WriteName(mem, SectionName_Linking);
- WriteLinkingSection(mem, l);
- st.WriteByte(SECT_CUSTOM);
- if doVerbose then writeln('section size: ', mem.Size);
- WriteU32(st, mem.Size);
- mem.Position:=0;
- if doVerbose then writeln('copying from mem');
- st.CopyFrom(mem, mem.Size);
- mem2.Position:=0;
- if doVerbose then writeln('copying from mem2');
- st.CopyFrom(mem2, mem2.Size);
- st.Size:=st.Position;
- finally
- mem.Free;
- mem2.Free;
- end;
- if doVerbose then writeln('written: ', st.Position-lofs,' bytes');
- end else
- writeln('failed. section find status. Likning: ', foundLink,'; Code: ', foundCode,'; Element: ', foundElement);
- finally
- st.Free;
- end;
- end;
- procedure ChangeSymbolFlag(const fn, symfn: string);
- var
- fs :TFileStream;
- syms: TStringList;
- begin
- syms:=TStringList.Create;
- fs := TFileStream.Create(fn, fmOpenReadWrite or fmShareDenyNone);
- try
- if (symfn<>'') then begin
- ReadSymbolsConf(symfn, syms);
- ChangeSymbolFlagStream(fs, syms);
- end;
- finally
- fs.Free;
- syms.Free;
- end;
- end;
- function ExportRenameSym(var x: TExportSection; syms: TStrings): integer;
- var
- i : integer;
- v : string;
- begin
- Result := 0;
- for i:=0 to length(x.entries)-1 do begin
- v := syms.Values[x.entries[i].name];
- if v <> '' then begin
- x.entries[i].name := v;
- inc(Result);
- end;
- end;
- end;
- function ExportRenameProcess(st, dst: TStream; syms: TStrings; doVerbose: Boolean): Boolean;
- var
- dw : LongWord;
- ofs : int64;
- sc : TSection;
- ps : int64;
- x : TExportSection;
- mem : TMemoryStream;
- cnt : integer;
- begin
- dw := st.ReadDWord;
- Result := dw = WasmId_Int;
- if not Result then begin
- Exit;
- end;
- dw := st.ReadDWord;
- while st.Position<st.Size do begin
- ofs := st.Position;
- sc.id := st.ReadByte;
- sc.Size := ReadU(st);
- ps := st.Position+sc.size;
- if sc.id = SECT_EXPORT then begin
- if doVerbose then writeln(' export section found');
- ReadExport(st, x);
- cnt := ExportRenameSym(x, syms);
- st.Position:=0;
- dst.CopyFrom(st, ofs);
- st.Position:=ps;
- mem := TMemoryStream.Create;
- WriteExport(x, mem);
- mem.Position:=0;
- dst.WriteByte(SECT_EXPORT);
- WriteU32(dst, mem.Size);
- dst.CopyFrom(mem, mem.Size);
- dst.CopyFrom(st, st.Size-st.Position);
- break;
- end;
- if st.Position <> ps then
- st.Position := ps;
- end;
- end;
- // match between exported function names and symbol names
- procedure MatchExportNameToSymName(const x: TExportSection; const l: TLinkingSection; dst: TStrings);
- var
- expname : string;
- i,j : integer;
- begin
- for i:=0 to length(x.entries)-1 do begin
- // gathering only function names for now
- if x.entries[i].desc <> EXPDESC_FUNC then continue;
- expname := x.entries[i].name;
- for j:=0 to length(l.symbols)-1 do begin
- if (l.symbols[j].kind = SYMTAB_FUNCTION)
- and (l.symbols[j].symindex = x.entries[i].index)
- and (l.symbols[j].hasSymName)
- then
- dst.Values[ l.symbols[j].symname ] := expname;
- end;
- end;
- end;
- function ExportNameGather(const wasmfile: string; syms: TStrings; doVerbose: Boolean = false): Boolean;
- var
- dw : LongWord;
- ofs : int64;
- sc : TSection;
- ps : int64;
- mem : TMemoryStream;
- cnt : integer;
- st : TFileStream;
- nm : string;
- x : TExportSection;
- foundExport: Boolean;
- l : TLinkingSection;
- foundLink: Boolean;
- begin
- st := TFileStream.Create(wasmfile, fmOpenRead or fmShareDenyNone);
- try
- dw := st.ReadDWord;
- Result := dw = WasmId_Int;
- if not Result then begin
- Exit;
- end;
- dw := st.ReadDWord;
- foundExport:=false;
- foundLink:=false;
- while st.Position<st.Size do begin
- ofs := st.Position;
- sc.id := st.ReadByte;
- sc.Size := ReadU(st);
- ps := st.Position+sc.size;
- if sc.id = SECT_EXPORT then begin
- if doVerbose then writeln(' export section found');
- ReadExport(st, x);
- cnt := ExportRenameSym(x, syms);
- foundExport:=true;
- end else if sc.id = SECT_CUSTOM then begin
- nm := ReadName(st);
- if nm = SectionName_Linking then begin
- foundLink := true;
- ReadLinkingSection(st, sc.size, l);
- end;
- end;
- if st.Position <> ps then
- st.Position := ps;
- end;
- Result := foundLink and foundExport;
- if Result then
- MatchExportNameToSymName(x, l, syms);
- finally
- st.Free;
- end;
- end;
- procedure ExportRename(const fn, symfn: string; doVerbose: Boolean);
- var
- fs : TFileStream;
- syms : TStringList;
- dst : TMemoryStream;
- begin
- if doVerbose then writeln('Export symbols renaming');
- syms:=TStringList.Create;
- fs := TFileStream.Create(fn, fmOpenReadWrite or fmShareDenyNone);
- dst := TMemoryStream.Create;
- try
- if (symfn <> '') and fileExists(symfn) then
- begin
- if doVerbose then writeln('reading symbols: ', symfn);
- if isWasmFile(symfn) then
- ExportNameGather(symfn, syms, doVerbose)
- else
- syms.LoadFromFile(symfn);
- if doVerbose then write(syms.Text);
- end;
- ExportRenameProcess(fs, dst, syms, doVerbose);
- fs.Position:=0;
- dst.Position:=0;
- fs.CopyFrom(dst, dst.Size);
- fs.Size:=dst.Size;
- finally
- dst.Free;
- fs.Free;
- syms.Free;
- end;
- end;
- end.
|