owomflib.pas 15 KB

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