owomflib.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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. { TOmfLibObjectReader }
  60. TOmfLibObjectReader=class(TObjectReader)
  61. private
  62. LibSymbols : TFPHashObjectList;
  63. islib: boolean;
  64. CurrMemberPos : longint;
  65. FPageSize: Integer;
  66. procedure ReadLibrary;
  67. procedure ReadDictionary(DictionaryOffset: DWord; DictionarySizeInBlocks: Word);
  68. protected
  69. function GetPos: longint;override;
  70. function GetIsArchive: boolean;override;
  71. public
  72. constructor createAr(const Aarfn:string;allow_nonar:boolean=false);override;
  73. destructor destroy;override;
  74. function openfile(const fn:string):boolean;override;
  75. procedure closefile;override;
  76. procedure seek(len:longint);override;
  77. end;
  78. implementation
  79. uses
  80. SysUtils,
  81. cstreams,
  82. globals,
  83. verbose,
  84. omfbase;
  85. const
  86. libbufsize = 65536;
  87. objbufsize = 65536;
  88. {*****************************************************************************
  89. Helpers
  90. *****************************************************************************}
  91. function ModName2DictEntry(const modnm: string): string;
  92. begin
  93. if Copy(modnm,Length(modnm)-1,2)='.o' then
  94. Result:=Copy(modnm,1,Length(modnm)-2)+'!'
  95. else
  96. Result:=modnm;
  97. end;
  98. {*****************************************************************************
  99. TOmfLibDictionaryEntry
  100. *****************************************************************************}
  101. constructor TOmfLibDictionaryEntry.Create(HashObjectList: TFPHashObjectList; const aName: TSymStr; aPageNum: Word);
  102. begin
  103. inherited Create(HashObjectList,aName);
  104. PageNum:=aPageNum;
  105. end;
  106. {*****************************************************************************
  107. TOmfLibObjectWriter
  108. *****************************************************************************}
  109. constructor TOmfLibObjectWriter.createAr(const Aarfn: string);
  110. begin
  111. FPageSize:=512;
  112. FLibName:=Aarfn;
  113. FLibData:=TDynamicArray.Create(libbufsize);
  114. FDictionary:=TFPHashObjectList.Create;
  115. { header is at page 0, so first module starts at page 1 }
  116. FObjStartPage:=1;
  117. end;
  118. destructor TOmfLibObjectWriter.destroy;
  119. begin
  120. if Errorcount=0 then
  121. WriteLib;
  122. FLibData.Free;
  123. FObjData.Free;
  124. FDictionary.Free;
  125. inherited destroy;
  126. end;
  127. function TOmfLibObjectWriter.createfile(const fn: string): boolean;
  128. begin
  129. FObjFileName:=fn;
  130. FreeAndNil(FObjData);
  131. FObjData:=TDynamicArray.Create(objbufsize);
  132. createfile:=true;
  133. fobjsize:=0;
  134. end;
  135. procedure TOmfLibObjectWriter.closefile;
  136. var
  137. RawRec: TOmfRawRecord;
  138. ObjHeader: TOmfRecord_THEADR;
  139. begin
  140. FLibData.seek(FObjStartPage*FPageSize);
  141. FObjData.seek(0);
  142. RawRec:=TOmfRawRecord.Create;
  143. repeat
  144. RawRec.ReadFrom(FObjData);
  145. if RawRec.RecordType=RT_THEADR then
  146. begin
  147. ObjHeader:=TOmfRecord_THEADR.Create;
  148. ObjHeader.DecodeFrom(RawRec);
  149. { create a dictionary entry with the module name }
  150. TOmfLibDictionaryEntry.Create(FDictionary,ModName2DictEntry(ObjHeader.ModuleName),FObjStartPage);
  151. ObjHeader.Free;
  152. end;
  153. RawRec.WriteTo(FLibData);
  154. until RawRec.RecordType in [RT_MODEND,RT_MODEND32];
  155. RawRec.Free;
  156. { calculate start page of next module }
  157. FObjStartPage:=(FLibData.Pos+FPageSize-1) div FPageSize;
  158. fobjsize:=0;
  159. end;
  160. procedure TOmfLibObjectWriter.writesym(const sym: string);
  161. begin
  162. TOmfLibDictionaryEntry.Create(FDictionary,sym,FObjStartPage);
  163. end;
  164. procedure TOmfLibObjectWriter.write(const b; len: longword);
  165. begin
  166. inc(fobjsize,len);
  167. inc(fsize,len);
  168. FObjData.write(b,len);
  169. end;
  170. procedure TOmfLibObjectWriter.WriteHeader(DictStart: DWord; DictBlocks: Word);
  171. var
  172. Header: TOmfRecord_LIBHEAD;
  173. RawRec: TOmfRawRecord;
  174. begin
  175. { set header properties }
  176. Header:=TOmfRecord_LIBHEAD.Create;
  177. Header.PageSize:=FPageSize;
  178. Header.DictionaryOffset:=DictStart;
  179. Header.DictionarySizeInBlocks:=DictBlocks;
  180. Header.CaseSensitive:=true;
  181. { write header }
  182. RawRec:=TOmfRawRecord.Create;
  183. Header.EncodeTo(RawRec);
  184. FLibData.seek(0);
  185. RawRec.WriteTo(FLibData);
  186. Header.Free;
  187. RawRec.Free;
  188. end;
  189. procedure TOmfLibObjectWriter.WriteFooter;
  190. var
  191. Footer: TOmfRecord_LIBEND;
  192. RawRec: TOmfRawRecord;
  193. begin
  194. FLibData.seek(FObjStartPage*FPageSize);
  195. Footer:=TOmfRecord_LIBEND.Create;
  196. Footer.CalculatePaddingBytes(FLibData.Pos);
  197. RawRec:=TOmfRawRecord.Create;
  198. Footer.EncodeTo(RawRec);
  199. RawRec.WriteTo(FLibData);
  200. Footer.Free;
  201. RawRec.Free;
  202. end;
  203. procedure TOmfLibObjectWriter.WriteLib;
  204. var
  205. libf: TCCustomFileStream;
  206. DictStart: LongWord;
  207. DictBlocks: Byte;
  208. begin
  209. libf:=CFileStreamClass.Create(FLibName,fmCreate);
  210. if CStreamError<>0 then
  211. begin
  212. Message1(exec_e_cant_create_archivefile,FLibName);
  213. exit;
  214. end;
  215. WriteFooter;
  216. DictStart:=FLibData.Pos;
  217. DictBlocks:=WriteDictionary;
  218. WriteHeader(DictStart,DictBlocks);
  219. FLibData.WriteStream(libf);
  220. libf.Free;
  221. end;
  222. function TOmfLibObjectWriter.WriteDictionary: Byte;
  223. var
  224. nb: Byte;
  225. begin
  226. for nb in OmfLibDictionaryBlockCounts do
  227. if TryWriteDictionaryWithSize(nb) then
  228. exit(nb);
  229. { could not write dictionary, even with the largest number of blocks }
  230. internalerror(2015042201);
  231. end;
  232. function TOmfLibObjectWriter.TryWriteDictionaryWithSize(nblocks: Byte): Boolean;
  233. const
  234. nbuckets=37;
  235. freespace=nbuckets;
  236. type
  237. PBlock=^TBlock;
  238. TBlock=array[0..511] of byte;
  239. var
  240. blocks: array of TBlock;
  241. i: Integer;
  242. N: TSymStr;
  243. length_of_string: Integer;
  244. h: TOmfLibHash;
  245. start_block,start_bucket: Integer;
  246. space_required: Integer;
  247. pb: PBlock;
  248. success: Boolean;
  249. store_at: Integer;
  250. PageNum: Word;
  251. begin
  252. SetLength(blocks,nblocks);
  253. for i:=0 to nblocks-1 do
  254. begin
  255. FillChar(blocks[i],SizeOf(blocks[i]),0);
  256. blocks[i][freespace]:=(freespace+1) div 2;
  257. end;
  258. for i:=0 to FDictionary.Count-1 do
  259. begin
  260. N:=TOmfLibDictionaryEntry(FDictionary[i]).Name;
  261. PageNum:=TOmfLibDictionaryEntry(FDictionary[i]).PageNum;
  262. length_of_string:=Length(N);
  263. h:=compute_omf_lib_hash(N,nblocks);
  264. start_block:=h.block_x;
  265. start_bucket:=h.bucket_x;
  266. space_required:=1+length_of_string+2;
  267. if odd(space_required) then
  268. Inc(space_required);
  269. repeat
  270. pb:=@blocks[h.block_x];
  271. success:=false;
  272. repeat
  273. if pb^[h.bucket_x]=0 then
  274. begin
  275. if (512-pb^[freespace]*2)<space_required then
  276. break;
  277. pb^[h.bucket_x]:=pb^[freespace];
  278. store_at:=2*pb^[h.bucket_x];
  279. pb^[store_at]:=length_of_string;
  280. Move(N[1],pb^[store_at+1],length_of_string);
  281. pb^[store_at+1+length_of_string]:=Byte(PageNum);
  282. pb^[store_at+1+length_of_string+1]:=Byte(PageNum shr 8);
  283. Inc(pb^[freespace],space_required div 2);
  284. if pb^[freespace]=0 then
  285. pb^[freespace]:=255;
  286. success:=true;
  287. break;
  288. end;
  289. h.bucket_x:=(h.bucket_x+h.bucket_d) mod nbuckets;
  290. until h.bucket_x=start_bucket;
  291. if not success then
  292. begin
  293. h.block_x:=(h.block_x+h.block_d) mod nblocks;
  294. if h.block_x=start_block then
  295. exit(false); // not enough blocks
  296. pb^[freespace]:=255;
  297. end;
  298. until success;
  299. end;
  300. FLibData.write(blocks[0],nblocks*SizeOf(TBlock));
  301. Result:=true;
  302. end;
  303. {*****************************************************************************
  304. TOmfLibObjectReader
  305. *****************************************************************************}
  306. procedure TOmfLibObjectReader.ReadLibrary;
  307. var
  308. RawRecord: TOmfRawRecord;
  309. Header: TOmfRecord_LIBHEAD;
  310. FIsCaseSensitive: Boolean;
  311. begin
  312. RawRecord:=TOmfRawRecord.Create;
  313. RawRecord.ReadFrom(Self);
  314. Header:=TOmfRecord_LIBHEAD.Create;
  315. Header.DecodeFrom(RawRecord);
  316. FPageSize:=Header.PageSize;
  317. FIsCaseSensitive:=Header.CaseSensitive;
  318. ReadDictionary(Header.DictionaryOffset, Header.DictionarySizeInBlocks);
  319. end;
  320. procedure TOmfLibObjectReader.ReadDictionary(DictionaryOffset: DWord; DictionarySizeInBlocks: Word);
  321. const
  322. nbuckets=37;
  323. freespace=nbuckets;
  324. type
  325. PBlock=^TBlock;
  326. TBlock=array[0..511] of byte;
  327. var
  328. blocks: array of TBlock;
  329. blocknr: Integer;
  330. block: PBlock;
  331. ofs: Byte;
  332. bucket: Integer;
  333. length_of_string: Byte;
  334. name: string;
  335. PageNum: Integer;
  336. begin
  337. seek(DictionaryOffset);
  338. SetLength(blocks,DictionarySizeInBlocks);
  339. read(blocks[0],DictionarySizeInBlocks*SizeOf(TBlock));
  340. for blocknr:=0 to DictionarySizeInBlocks-1 do
  341. begin
  342. block:=@blocks[blocknr];
  343. for bucket:=0 to nbuckets-1 do
  344. if block^[bucket]<>0 then
  345. begin
  346. ofs:=block^[bucket];
  347. length_of_string:=block^[ofs];
  348. if (ofs+1+length_of_string+1)>High(TBlock) then
  349. Comment(V_Error,'OMF dictionary entry goes beyond end of block');
  350. SetLength(name,length_of_string);
  351. Move(block^[ofs+1],name[1],length_of_string);
  352. PageNum:=block^[ofs+1+length_of_string]+
  353. block^[ofs+1+length_of_string+1] shl 8;
  354. TOmfLibDictionaryEntry.create(LibSymbols,name,PageNum);
  355. end;
  356. end;
  357. end;
  358. function TOmfLibObjectReader.GetPos: longint;
  359. begin
  360. result:=inherited GetPos-CurrMemberPos;
  361. end;
  362. function TOmfLibObjectReader.GetIsArchive: boolean;
  363. begin
  364. result:=islib;
  365. end;
  366. constructor TOmfLibObjectReader.createAr(const Aarfn: string; allow_nonar: boolean);
  367. var
  368. RecType: Byte;
  369. begin
  370. inherited Create;
  371. LibSymbols:=TFPHashObjectList.Create(true);
  372. CurrMemberPos:=0;
  373. if inherited openfile(Aarfn) then
  374. begin
  375. Read(RecType,1);
  376. Seek(0);
  377. islib:=RecType=RT_LIBHEAD;
  378. if islib then
  379. ReadLibrary
  380. else if (not allow_nonar) then
  381. Comment(V_Error,'Not an OMF library file, illegal magic: '+filename);
  382. end;
  383. end;
  384. destructor TOmfLibObjectReader.destroy;
  385. begin
  386. inherited closefile;
  387. LibSymbols.Free;
  388. inherited Destroy;
  389. end;
  390. function TOmfLibObjectReader.openfile(const fn: string): boolean;
  391. var
  392. libsym: TOmfLibDictionaryEntry;
  393. begin
  394. result:=false;
  395. libsym:=TOmfLibDictionaryEntry(LibSymbols.Find(ModName2DictEntry(fn)));
  396. if not assigned(libsym) then
  397. exit;
  398. CurrMemberPos:=libsym.PageNum*FPageSize;
  399. inherited Seek(CurrMemberPos);
  400. result:=true;
  401. end;
  402. procedure TOmfLibObjectReader.closefile;
  403. begin
  404. CurrMemberPos:=0;
  405. end;
  406. procedure TOmfLibObjectReader.seek(len: longint);
  407. begin
  408. inherited Seek(CurrMemberPos+len);
  409. end;
  410. end.