owomflib.pas 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. {
  2. Copyright (c) 2015 by Nikolay Nikolov
  3. Contains the stuff for writing Relocatable Object Module Format (OMF)
  4. libraries directly. This is the object format used on the i8086-msdos
  5. platform (also known as .lib files in the dos world, even though Free
  6. Pascal uses the extension .a).
  7. This program is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2 of the License, or
  10. (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. ****************************************************************************
  19. }
  20. unit owomflib;
  21. {$i fpcdefs.inc}
  22. interface
  23. uses
  24. cclasses,
  25. globtype,
  26. owbase;
  27. type
  28. { TOmfLibDictionaryEntry }
  29. TOmfLibDictionaryEntry=class(TFPHashObject)
  30. private
  31. FPageNum: Word;
  32. public
  33. constructor Create(HashObjectList:TFPHashObjectList;const aName:TSymStr;aPageNum:Word);
  34. property PageNum: Word read FPageNum write FPageNum;
  35. end;
  36. { TOmfLibObjectWriter }
  37. TOmfLibObjectWriter=class(TObjectWriter)
  38. private
  39. FPageSize: Integer;
  40. FLibName: string;
  41. FLibData: TDynamicArray;
  42. FObjFileName: string;
  43. FObjData: TDynamicArray;
  44. FObjStartPage: Word;
  45. FDictionary: TFPHashObjectList;
  46. procedure WriteHeader(DictStart: DWord; DictBlocks: Word);
  47. procedure WriteFooter;
  48. procedure WriteLib;
  49. function WriteDictionary: byte;
  50. function TryWriteDictionaryWithSize(nblocks: Byte): Boolean;
  51. public
  52. constructor createAr(const Aarfn:string);override;
  53. destructor destroy;override;
  54. function createfile(const fn:string):boolean;override;
  55. procedure closefile;override;
  56. procedure writesym(const sym:string);override;
  57. procedure write(const b;len:longword);override;
  58. end;
  59. implementation
  60. uses
  61. SysUtils,
  62. cstreams,
  63. globals,
  64. verbose,
  65. omfbase;
  66. const
  67. libbufsize = 65536;
  68. objbufsize = 65536;
  69. {*****************************************************************************
  70. Helpers
  71. *****************************************************************************}
  72. function ModName2DictEntry(const modnm: string): string;
  73. begin
  74. if Copy(modnm,Length(modnm)-1,2)='.o' then
  75. Result:=Copy(modnm,1,Length(modnm)-2)+'!'
  76. else
  77. Result:=modnm;
  78. end;
  79. {*****************************************************************************
  80. TOmfLibDictionaryEntry
  81. *****************************************************************************}
  82. constructor TOmfLibDictionaryEntry.Create(HashObjectList: TFPHashObjectList; const aName: TSymStr; aPageNum: Word);
  83. begin
  84. inherited Create(HashObjectList,aName);
  85. PageNum:=aPageNum;
  86. end;
  87. {*****************************************************************************
  88. TOmfLibObjectWriter
  89. *****************************************************************************}
  90. constructor TOmfLibObjectWriter.createAr(const Aarfn: string);
  91. begin
  92. FPageSize:=512;
  93. FLibName:=Aarfn;
  94. FLibData:=TDynamicArray.Create(libbufsize);
  95. FDictionary:=TFPHashObjectList.Create;
  96. { header is at page 0, so first module starts at page 1 }
  97. FObjStartPage:=1;
  98. end;
  99. destructor TOmfLibObjectWriter.destroy;
  100. begin
  101. if Errorcount=0 then
  102. WriteLib;
  103. FLibData.Free;
  104. FObjData.Free;
  105. FDictionary.Free;
  106. inherited destroy;
  107. end;
  108. function TOmfLibObjectWriter.createfile(const fn: string): boolean;
  109. begin
  110. FObjFileName:=fn;
  111. FreeAndNil(FObjData);
  112. FObjData:=TDynamicArray.Create(objbufsize);
  113. createfile:=true;
  114. fobjsize:=0;
  115. end;
  116. procedure TOmfLibObjectWriter.closefile;
  117. var
  118. RawRec: TOmfRawRecord;
  119. ObjHeader: TOmfRecord_THEADR;
  120. begin
  121. FLibData.seek(FObjStartPage*FPageSize);
  122. FObjData.seek(0);
  123. RawRec:=TOmfRawRecord.Create;
  124. repeat
  125. RawRec.ReadFrom(FObjData);
  126. if RawRec.RecordType=RT_THEADR then
  127. begin
  128. ObjHeader:=TOmfRecord_THEADR.Create;
  129. ObjHeader.DecodeFrom(RawRec);
  130. { create a dictionary entry with the module name }
  131. TOmfLibDictionaryEntry.Create(FDictionary,ModName2DictEntry(ObjHeader.ModuleName),FObjStartPage);
  132. ObjHeader.Free;
  133. end;
  134. RawRec.WriteTo(FLibData);
  135. until RawRec.RecordType in [RT_MODEND,RT_MODEND32];
  136. RawRec.Free;
  137. { calculate start page of next module }
  138. FObjStartPage:=(FLibData.Pos+FPageSize-1) div FPageSize;
  139. fobjsize:=0;
  140. end;
  141. procedure TOmfLibObjectWriter.writesym(const sym: string);
  142. begin
  143. TOmfLibDictionaryEntry.Create(FDictionary,sym,FObjStartPage);
  144. end;
  145. procedure TOmfLibObjectWriter.write(const b; len: longword);
  146. begin
  147. inc(fobjsize,len);
  148. inc(fsize,len);
  149. FObjData.write(b,len);
  150. end;
  151. procedure TOmfLibObjectWriter.WriteHeader(DictStart: DWord; DictBlocks: Word);
  152. var
  153. Header: TOmfRecord_LIBHEAD;
  154. RawRec: TOmfRawRecord;
  155. begin
  156. { set header properties }
  157. Header:=TOmfRecord_LIBHEAD.Create;
  158. Header.PageSize:=FPageSize;
  159. Header.DictionaryOffset:=DictStart;
  160. Header.DictionarySizeInBlocks:=DictBlocks;
  161. Header.CaseSensitive:=true;
  162. { write header }
  163. RawRec:=TOmfRawRecord.Create;
  164. Header.EncodeTo(RawRec);
  165. FLibData.seek(0);
  166. RawRec.WriteTo(FLibData);
  167. Header.Free;
  168. RawRec.Free;
  169. end;
  170. procedure TOmfLibObjectWriter.WriteFooter;
  171. var
  172. Footer: TOmfRecord_LIBEND;
  173. RawRec: TOmfRawRecord;
  174. begin
  175. FLibData.seek(FObjStartPage*FPageSize);
  176. Footer:=TOmfRecord_LIBEND.Create;
  177. Footer.CalculatePaddingBytes(FLibData.Pos);
  178. RawRec:=TOmfRawRecord.Create;
  179. Footer.EncodeTo(RawRec);
  180. RawRec.WriteTo(FLibData);
  181. Footer.Free;
  182. RawRec.Free;
  183. end;
  184. procedure TOmfLibObjectWriter.WriteLib;
  185. var
  186. libf: TCCustomFileStream;
  187. DictStart: LongWord;
  188. DictBlocks: Byte;
  189. begin
  190. libf:=CFileStreamClass.Create(FLibName,fmCreate);
  191. if CStreamError<>0 then
  192. begin
  193. Message1(exec_e_cant_create_archivefile,FLibName);
  194. exit;
  195. end;
  196. WriteFooter;
  197. DictStart:=FLibData.Pos;
  198. DictBlocks:=WriteDictionary;
  199. WriteHeader(DictStart,DictBlocks);
  200. FLibData.WriteStream(libf);
  201. libf.Free;
  202. end;
  203. function TOmfLibObjectWriter.WriteDictionary: Byte;
  204. var
  205. nb: Byte;
  206. begin
  207. for nb in OmfLibDictionaryBlockCounts do
  208. if TryWriteDictionaryWithSize(nb) then
  209. exit(nb);
  210. { could not write dictionary, even with the largest number of blocks }
  211. internalerror(2015042201);
  212. end;
  213. function TOmfLibObjectWriter.TryWriteDictionaryWithSize(nblocks: Byte): Boolean;
  214. const
  215. nbuckets=37;
  216. freespace=nbuckets;
  217. type
  218. PBlock=^TBlock;
  219. TBlock=array[0..511] of byte;
  220. var
  221. blocks: array of TBlock;
  222. i: Integer;
  223. N: TSymStr;
  224. length_of_string: Integer;
  225. h: TOmfLibHash;
  226. start_block,start_bucket: Integer;
  227. space_required: Integer;
  228. pb: PBlock;
  229. success: Boolean;
  230. store_at: Integer;
  231. PageNum: Word;
  232. begin
  233. SetLength(blocks,nblocks);
  234. for i:=0 to nblocks-1 do
  235. begin
  236. FillChar(blocks[i],SizeOf(blocks[i]),0);
  237. blocks[i][freespace]:=(freespace+1) div 2;
  238. end;
  239. for i:=0 to FDictionary.Count-1 do
  240. begin
  241. N:=TOmfLibDictionaryEntry(FDictionary[i]).Name;
  242. PageNum:=TOmfLibDictionaryEntry(FDictionary[i]).PageNum;
  243. length_of_string:=Length(N);
  244. h:=compute_omf_lib_hash(N,nblocks);
  245. start_block:=h.block_x;
  246. start_bucket:=h.bucket_x;
  247. space_required:=1+length_of_string+2;
  248. if odd(space_required) then
  249. Inc(space_required);
  250. repeat
  251. pb:=@blocks[h.block_x];
  252. success:=false;
  253. repeat
  254. if pb^[h.bucket_x]=0 then
  255. begin
  256. if (512-pb^[freespace]*2)<space_required then
  257. break;
  258. pb^[h.bucket_x]:=pb^[freespace];
  259. store_at:=2*pb^[h.bucket_x];
  260. pb^[store_at]:=length_of_string;
  261. Move(N[1],pb^[store_at+1],length_of_string);
  262. pb^[store_at+1+length_of_string]:=Byte(PageNum);
  263. pb^[store_at+1+length_of_string+1]:=Byte(PageNum shr 8);
  264. Inc(pb^[freespace],space_required div 2);
  265. if pb^[freespace]=0 then
  266. pb^[freespace]:=255;
  267. success:=true;
  268. break;
  269. end;
  270. h.bucket_x:=(h.bucket_x+h.bucket_d) mod nbuckets;
  271. until h.bucket_x=start_bucket;
  272. if not success then
  273. begin
  274. h.block_x:=(h.block_x+h.block_d) mod nblocks;
  275. if h.block_x=start_block then
  276. exit(false); // not enough blocks
  277. pb^[freespace]:=255;
  278. end;
  279. until success;
  280. end;
  281. FLibData.write(blocks[0],nblocks*SizeOf(TBlock));
  282. Result:=true;
  283. end;
  284. end.