mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-11-30 00:17:26 +01:00
longer copy the entire file there, so we parse the file only to get the module name, and the THEADR record should always be the first record in the OMF file. git-svn-id: trunk@39198 -
611 lines
19 KiB
ObjectPascal
611 lines
19 KiB
ObjectPascal
{
|
|
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.Pos<ObjData.size do
|
|
begin
|
|
bytes:=ObjData.read(buf,Min(SizeOf(buf),ObjData.size-ObjData.Pos));
|
|
FLibData.write(buf,bytes);
|
|
end;
|
|
end;
|
|
WriteFooter;
|
|
DictStart:=FLibData.Pos;
|
|
DictBlocks:=WriteDictionary;
|
|
WriteHeader(DictStart,DictBlocks);
|
|
FLibData.WriteStream(libf);
|
|
libf.Free;
|
|
end;
|
|
|
|
function TOmfLibObjectWriter.WriteDictionary: Word;
|
|
var
|
|
nb: Word;
|
|
begin
|
|
for nb in OmfLibDictionaryBlockCounts do
|
|
if TryWriteDictionaryWithSize(nb) then
|
|
exit(nb);
|
|
{ could not write dictionary, even with the largest number of blocks }
|
|
internalerror(2015042202);
|
|
end;
|
|
|
|
function TOmfLibObjectWriter.TryWriteDictionaryWithSize(nblocks: Word
|
|
): Boolean;
|
|
const
|
|
nbuckets=37;
|
|
freespace=nbuckets;
|
|
type
|
|
PBlock=^TBlock;
|
|
TBlock=array[0..511] of byte;
|
|
var
|
|
blocks: array of TBlock;
|
|
i: Integer;
|
|
N: TSymStr;
|
|
length_of_string: Integer;
|
|
h: TOmfLibHash;
|
|
start_block,start_bucket: Integer;
|
|
space_required: Integer;
|
|
pb: PBlock;
|
|
success: Boolean;
|
|
store_at: Integer;
|
|
PageNum: Word;
|
|
begin
|
|
SetLength(blocks,nblocks);
|
|
for i:=0 to nblocks-1 do
|
|
begin
|
|
FillChar(blocks[i],SizeOf(blocks[i]),0);
|
|
blocks[i][freespace]:=(freespace+1) div 2;
|
|
end;
|
|
|
|
for i:=0 to FDictionary.Count-1 do
|
|
begin
|
|
N:=TOmfLibDictionaryEntry(FDictionary[i]).Name;
|
|
PageNum:=TOmfLibObjectModule(FObjectModules[TOmfLibDictionaryEntry(FDictionary[i]).ModuleIndex]).PageNum;
|
|
length_of_string:=Length(N);
|
|
h:=compute_omf_lib_hash(N,nblocks);
|
|
start_block:=h.block_x;
|
|
start_bucket:=h.bucket_x;
|
|
space_required:=1+length_of_string+2;
|
|
if odd(space_required) then
|
|
Inc(space_required);
|
|
repeat
|
|
pb:=@blocks[h.block_x];
|
|
success:=false;
|
|
repeat
|
|
if pb^[h.bucket_x]=0 then
|
|
begin
|
|
if (512-pb^[freespace]*2)<space_required then
|
|
break;
|
|
pb^[h.bucket_x]:=pb^[freespace];
|
|
store_at:=2*pb^[h.bucket_x];
|
|
pb^[store_at]:=length_of_string;
|
|
Move(N[1],pb^[store_at+1],length_of_string);
|
|
pb^[store_at+1+length_of_string]:=Byte(PageNum);
|
|
pb^[store_at+1+length_of_string+1]:=Byte(PageNum shr 8);
|
|
Inc(pb^[freespace],space_required div 2);
|
|
if pb^[freespace]=0 then
|
|
pb^[freespace]:=255;
|
|
success:=true;
|
|
break;
|
|
end;
|
|
h.bucket_x:=(h.bucket_x+h.bucket_d) mod nbuckets;
|
|
until h.bucket_x=start_bucket;
|
|
if not success then
|
|
begin
|
|
h.block_x:=(h.block_x+h.block_d) mod nblocks;
|
|
if h.block_x=start_block then
|
|
exit(false); // not enough blocks
|
|
pb^[freespace]:=255;
|
|
end;
|
|
until success;
|
|
end;
|
|
FLibData.write(blocks[0],nblocks*SizeOf(TBlock));
|
|
Result:=true;
|
|
end;
|
|
|
|
{*****************************************************************************
|
|
TOmfLibObjectReader.TOmfLibDictionaryEntry
|
|
*****************************************************************************}
|
|
|
|
constructor TOmfLibObjectReader.TOmfLibDictionaryEntry.Create(
|
|
HashObjectList: TFPHashObjectList; const aName: TSymStr; aPageNum: Word);
|
|
begin
|
|
inherited Create(HashObjectList,aName);
|
|
PageNum:=aPageNum;
|
|
end;
|
|
|
|
{*****************************************************************************
|
|
TOmfLibObjectReader
|
|
*****************************************************************************}
|
|
|
|
procedure TOmfLibObjectReader.ReadLibrary;
|
|
var
|
|
RawRecord: TOmfRawRecord;
|
|
Header: TOmfRecord_LIBHEAD;
|
|
begin
|
|
RawRecord:=TOmfRawRecord.Create;
|
|
RawRecord.ReadFrom(Self);
|
|
Header:=TOmfRecord_LIBHEAD.Create;
|
|
Header.DecodeFrom(RawRecord);
|
|
FPageSize:=Header.PageSize;
|
|
FIsCaseSensitive:=Header.CaseSensitive;
|
|
ReadDictionary(Header.DictionaryOffset, Header.DictionarySizeInBlocks);
|
|
Header.Free;
|
|
RawRecord.Free;
|
|
end;
|
|
|
|
procedure TOmfLibObjectReader.ReadDictionary(DictionaryOffset: DWord; DictionarySizeInBlocks: Word);
|
|
const
|
|
nbuckets=37;
|
|
freespace=nbuckets;
|
|
type
|
|
PBlock=^TBlock;
|
|
TBlock=array[0..511] of byte;
|
|
var
|
|
blocks: array of TBlock;
|
|
blocknr: Integer;
|
|
block: PBlock;
|
|
ofs: Integer;
|
|
bucket: Integer;
|
|
length_of_string: Byte;
|
|
name: string;
|
|
PageNum: Integer;
|
|
begin
|
|
seek(DictionaryOffset);
|
|
SetLength(blocks,DictionarySizeInBlocks);
|
|
read(blocks[0],DictionarySizeInBlocks*SizeOf(TBlock));
|
|
for blocknr:=0 to DictionarySizeInBlocks-1 do
|
|
begin
|
|
block:=@(blocks[blocknr]);
|
|
for bucket:=0 to nbuckets-1 do
|
|
if block^[bucket]<>0 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.
|