{ Copyright (c) 2015 by Nikolay Nikolov Contains the stuff for writing Relocatable Object Module Format (OMF) libraries directly. This is the object format used on the i8086-msdos platform (also known as .lib files in the dos world, even though Free Pascal uses the extension .a). 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 owomflib; {$i fpcdefs.inc} interface uses cclasses, globtype, owbase; type { TOmfLibObjectWriter } TOmfLibObjectWriter=class(TObjectWriter) strict private type { TOmfLibObjectModule } TOmfLibObjectModule=class strict private FObjFileName: string; FObjData: TDynamicArray; FPageNum: Word; public constructor Create(const fn:string); destructor Destroy; override; property ObjData: TDynamicArray read FObjData; property PageNum: Word read FPageNum write FPageNum; end; { TOmfLibDictionaryEntry } TOmfLibDictionaryEntry=class(TFPHashObject) strict private FModuleIndex: Integer; public constructor Create(HashObjectList:TFPHashObjectList;const aName:TSymStr;aModuleIndex:Integer); property ModuleIndex: Integer read FModuleIndex write FModuleIndex; end; strict private FPageSize: Integer; FLibName: string; FLibData: TDynamicArray; FFooterPos: LongWord; FDictionary: TFPHashObjectList; FObjectModules: TFPObjectList; FCurrentModule: TOmfLibObjectModule; FCurrentModuleIndex: Integer; procedure WriteHeader(DictStart: DWord; DictBlocks: Word); procedure WriteFooter; function TryPageSize(aPageSize: Integer): Boolean; procedure DeterminePageSize; procedure WriteLib; function WriteDictionary: Word; function TryWriteDictionaryWithSize(nblocks: Word): Boolean; public constructor createAr(const Aarfn:string);override; constructor createAr(const Aarfn:string;PageSize:Integer); destructor destroy;override; function createfile(const fn:string):boolean;override; procedure closefile;override; procedure writesym(const sym:string);override; procedure write(const b;len:longword);override; end; { TOmfLibObjectReader } TOmfLibObjectReader=class(TObjectReader) strict private type { TOmfLibDictionaryEntry } TOmfLibDictionaryEntry=class(TFPHashObject) strict private FPageNum: Word; public constructor Create(HashObjectList:TFPHashObjectList;const aName:TSymStr;aPageNum:Word); property PageNum: Word read FPageNum write FPageNum; end; strict private LibSymbols : TFPHashObjectList; islib: boolean; CurrMemberPos : longint; CurrMemberName : string; FPageSize: Integer; FIsCaseSensitive: Boolean; procedure ReadLibrary; procedure ReadDictionary(DictionaryOffset: DWord; DictionarySizeInBlocks: Word); protected function getfilename:string;override; function GetPos: longint;override; function GetIsArchive: boolean;override; public constructor createAr(const Aarfn:string;allow_nonar:boolean=false);override; destructor destroy;override; function openfile(const fn:string):boolean;override; procedure closefile;override; procedure seek(len:longint);override; property IsCaseSensitive: Boolean read FIsCaseSensitive; end; implementation uses SysUtils, cstreams,cutils, verbose, omfbase; const libbufsize = 65536; objbufsize = 65536; {***************************************************************************** Helpers *****************************************************************************} function ModName2DictEntry(const modnm: string): string; begin if Copy(modnm,Length(modnm)-1,2)='.o' then Result:=Copy(modnm,1,Length(modnm)-2)+'!' else Result:=modnm; end; {***************************************************************************** TOmfLibObjectWriter.TOmfLibObjectModule *****************************************************************************} constructor TOmfLibObjectWriter.TOmfLibObjectModule.Create(const fn: string); begin FObjFileName:=fn; FObjData:=TDynamicArray.Create(objbufsize); end; destructor TOmfLibObjectWriter.TOmfLibObjectModule.Destroy; begin FObjData.Free; inherited Destroy; end; {***************************************************************************** TOmfLibObjectWriter.TOmfLibDictionaryEntry *****************************************************************************} constructor TOmfLibObjectWriter.TOmfLibDictionaryEntry.Create( HashObjectList: TFPHashObjectList; const aName: TSymStr; aModuleIndex:Integer); begin inherited Create(HashObjectList,aName); ModuleIndex:=aModuleIndex; end; {***************************************************************************** TOmfLibObjectWriter *****************************************************************************} constructor TOmfLibObjectWriter.createAr(const Aarfn: string); begin createAr(Aarfn,-1); end; constructor TOmfLibObjectWriter.createAr(const Aarfn: string;PageSize: Integer); begin FPageSize:=PageSize; FLibName:=Aarfn; FLibData:=TDynamicArray.Create(libbufsize); FDictionary:=TFPHashObjectList.Create; FObjectModules:=TFPObjectList.Create(True); FCurrentModule:=nil; end; destructor TOmfLibObjectWriter.destroy; begin if Errorcount=0 then WriteLib; FLibData.Free; FObjectModules.Free; FDictionary.Free; inherited destroy; end; function TOmfLibObjectWriter.createfile(const fn: string): boolean; begin FCurrentModule:=TOmfLibObjectModule.Create(fn); FCurrentModuleIndex:=FObjectModules.Add(FCurrentModule); createfile:=true; fobjsize:=0; end; procedure TOmfLibObjectWriter.closefile; var RawRec: TOmfRawRecord; ObjHeader: TOmfRecord_THEADR; begin FCurrentModule.ObjData.seek(0); RawRec:=TOmfRawRecord.Create; RawRec.ReadFrom(FCurrentModule.ObjData); if RawRec.RecordType<>RT_THEADR then begin RawRec.Free; InternalError(2018060801); end; ObjHeader:=TOmfRecord_THEADR.Create; ObjHeader.DecodeFrom(RawRec); { create a dictionary entry with the module name } TOmfLibDictionaryEntry.Create(FDictionary,ModName2DictEntry(ObjHeader.ModuleName),FCurrentModuleIndex); ObjHeader.Free; RawRec.Free; fobjsize:=0; end; procedure TOmfLibObjectWriter.writesym(const sym: string); begin TOmfLibDictionaryEntry.Create(FDictionary,sym,FCurrentModuleIndex); end; procedure TOmfLibObjectWriter.write(const b; len: longword); begin inc(fobjsize,len); inc(fsize,len); FCurrentModule.ObjData.write(b,len); end; procedure TOmfLibObjectWriter.WriteHeader(DictStart: DWord; DictBlocks: Word); var Header: TOmfRecord_LIBHEAD; RawRec: TOmfRawRecord; begin { set header properties } Header:=TOmfRecord_LIBHEAD.Create; Header.PageSize:=FPageSize; Header.DictionaryOffset:=DictStart; Header.DictionarySizeInBlocks:=DictBlocks; Header.CaseSensitive:=true; { write header } RawRec:=TOmfRawRecord.Create; Header.EncodeTo(RawRec); FLibData.seek(0); RawRec.WriteTo(FLibData); Header.Free; RawRec.Free; end; procedure TOmfLibObjectWriter.WriteFooter; var Footer: TOmfRecord_LIBEND; RawRec: TOmfRawRecord; begin FLibData.seek(FFooterPos); Footer:=TOmfRecord_LIBEND.Create; Footer.CalculatePaddingBytes(FLibData.Pos); RawRec:=TOmfRawRecord.Create; Footer.EncodeTo(RawRec); RawRec.WriteTo(FLibData); Footer.Free; RawRec.Free; end; function TOmfLibObjectWriter.TryPageSize(aPageSize: Integer): Boolean; var I: Integer; CurrentPage: Integer; CurrentPos: LongWord; pow: longint; begin if not IsPowerOf2(aPageSize,pow) then internalerror(2018060701); if (pow<4) or (pow>15) then internalerror(2018060702); FPageSize:=aPageSize; { header is at page 0, so first module starts at page 1 } CurrentPage:=1; for I:=0 to FObjectModules.Count-1 do with TOmfLibObjectModule(FObjectModules[I]) do begin if CurrentPage>high(word) then exit(False); PageNum:=CurrentPage; { calculate next page } CurrentPos:=CurrentPage*FPageSize+ObjData.Size; CurrentPage:=(CurrentPos+FPageSize-1) div FPageSize; end; FFooterPos:=CurrentPage*FPageSize; Result:=True; end; procedure TOmfLibObjectWriter.DeterminePageSize; var I: Integer; begin if (FPageSize<>-1) and TryPageSize(FPageSize) then { success } exit; for I:=4 to 15 do if TryPageSize(1 shl I) then exit; internalerror(2018060703); end; procedure TOmfLibObjectWriter.WriteLib; var libf: TCCustomFileStream; DictStart, bytes: LongWord; DictBlocks: Word; I: Integer; buf: array [0..1023] of Byte; begin DeterminePageSize; libf:=CFileStreamClass.Create(FLibName,fmCreate); if CStreamError<>0 then begin Message1(exec_e_cant_create_archivefile,FLibName); exit; end; for I:=0 to FObjectModules.Count-1 do with TOmfLibObjectModule(FObjectModules[I]) do begin FLibData.seek(PageNum*FPageSize); ObjData.seek(0); while ObjData.Pos0 then begin ofs:=2*block^[bucket]; length_of_string:=block^[ofs]; if (ofs+1+length_of_string+1)>High(TBlock) then begin Comment(V_Error,'OMF dictionary entry goes beyond end of block'); continue; end; SetLength(name,length_of_string); Move(block^[ofs+1],name[1],length_of_string); PageNum:=block^[ofs+1+length_of_string]+ block^[ofs+1+length_of_string+1] shl 8; TOmfLibDictionaryEntry.create(LibSymbols,name,PageNum); end; end; end; function TOmfLibObjectReader.getfilename: string; begin Result:=inherited getfilename; if CurrMemberName<>'' then result:=result+'('+CurrMemberName+')'; end; function TOmfLibObjectReader.GetPos: longint; begin result:=inherited GetPos-CurrMemberPos; end; function TOmfLibObjectReader.GetIsArchive: boolean; begin result:=islib; end; constructor TOmfLibObjectReader.createAr(const Aarfn: string; allow_nonar: boolean); var RecType: Byte; begin inherited Create; LibSymbols:=TFPHashObjectList.Create(true); CurrMemberPos:=0; CurrMemberName:=''; if inherited openfile(Aarfn) then begin Read(RecType,1); Seek(0); islib:=RecType=RT_LIBHEAD; if islib then ReadLibrary else if (not allow_nonar) then Comment(V_Error,'Not an OMF library file, illegal magic: '+filename); end; end; destructor TOmfLibObjectReader.destroy; begin inherited closefile; LibSymbols.Free; inherited Destroy; end; function TOmfLibObjectReader.openfile(const fn: string): boolean; var libsym: TOmfLibDictionaryEntry; RawRec: TOmfRawRecord; Header: TOmfRecord_THEADR; begin result:=false; libsym:=TOmfLibDictionaryEntry(LibSymbols.Find(ModName2DictEntry(fn))); if not assigned(libsym) then exit; CurrMemberPos:=libsym.PageNum*FPageSize; inherited Seek(CurrMemberPos); { read the header, to obtain the module name } RawRec:=TOmfRawRecord.Create; RawRec.ReadFrom(self); Header:=TOmfRecord_THEADR.Create; Header.DecodeFrom(RawRec); CurrMemberName:=Header.ModuleName; Header.Free; RawRec.Free; { go back to the beginning of the file } inherited Seek(CurrMemberPos); result:=true; end; procedure TOmfLibObjectReader.closefile; begin CurrMemberPos:=0; CurrMemberName:=''; end; procedure TOmfLibObjectReader.seek(len: longint); begin inherited Seek(CurrMemberPos+len); end; end.