owomflib.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  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. { TOmfLibObjectWriter }
  29. TOmfLibObjectWriter=class(TObjectWriter)
  30. strict private
  31. type
  32. { TOmfLibObjectModule }
  33. TOmfLibObjectModule=class
  34. strict private
  35. FObjFileName: string;
  36. FObjData: TDynamicArray;
  37. FPageNum: Word;
  38. public
  39. constructor Create(const fn:string);
  40. destructor Destroy; override;
  41. property ObjData: TDynamicArray read FObjData;
  42. property PageNum: Word read FPageNum write FPageNum;
  43. end;
  44. { TOmfLibDictionaryEntry }
  45. TOmfLibDictionaryEntry=class(TFPHashObject)
  46. strict private
  47. FModuleIndex: Integer;
  48. public
  49. constructor Create(HashObjectList:TFPHashObjectList;const aName:TSymStr;aModuleIndex:Integer);
  50. property ModuleIndex: Integer read FModuleIndex write FModuleIndex;
  51. end;
  52. strict private
  53. FPageSize: Integer;
  54. FLibName: string;
  55. FLibData: TDynamicArray;
  56. FFooterPos: LongWord;
  57. FDictionary: TFPHashObjectList;
  58. FObjectModules: TFPObjectList;
  59. FCurrentModule: TOmfLibObjectModule;
  60. FCurrentModuleIndex: Integer;
  61. procedure WriteHeader(DictStart: DWord; DictBlocks: Word);
  62. procedure WriteFooter;
  63. function TryPageSize(aPageSize: Integer): Boolean;
  64. procedure DeterminePageSize;
  65. procedure WriteLib;
  66. function WriteDictionary: Word;
  67. function TryWriteDictionaryWithSize(nblocks: Word): Boolean;
  68. public
  69. constructor createAr(const Aarfn:string);override;
  70. constructor createAr(const Aarfn:string;PageSize:Integer);
  71. destructor destroy;override;
  72. function createfile(const fn:string):boolean;override;
  73. procedure closefile;override;
  74. procedure writesym(const sym:string);override;
  75. procedure write(const b;len:longword);override;
  76. end;
  77. { TOmfLibObjectReader }
  78. TOmfLibObjectReader=class(TObjectReader)
  79. strict private
  80. type
  81. { TOmfLibDictionaryEntry }
  82. TOmfLibDictionaryEntry=class(TFPHashObject)
  83. strict private
  84. FPageNum: Word;
  85. public
  86. constructor Create(HashObjectList:TFPHashObjectList;const aName:TSymStr;aPageNum:Word);
  87. property PageNum: Word read FPageNum write FPageNum;
  88. end;
  89. strict private
  90. LibSymbols : TFPHashObjectList;
  91. islib: boolean;
  92. CurrMemberPos : longint;
  93. CurrMemberName : string;
  94. FPageSize: Integer;
  95. FIsCaseSensitive: Boolean;
  96. procedure ReadLibrary;
  97. procedure ReadDictionary(DictionaryOffset: DWord; DictionarySizeInBlocks: Word);
  98. protected
  99. function getfilename:string;override;
  100. function GetPos: longint;override;
  101. function GetIsArchive: boolean;override;
  102. public
  103. constructor createAr(const Aarfn:string;allow_nonar:boolean=false);override;
  104. destructor destroy;override;
  105. function openfile(const fn:string):boolean;override;
  106. procedure closefile;override;
  107. procedure seek(len:longint);override;
  108. property IsCaseSensitive: Boolean read FIsCaseSensitive;
  109. end;
  110. implementation
  111. uses
  112. SysUtils,
  113. cstreams,cutils,
  114. verbose,
  115. omfbase;
  116. const
  117. libbufsize = 65536;
  118. objbufsize = 65536;
  119. {*****************************************************************************
  120. Helpers
  121. *****************************************************************************}
  122. function ModName2DictEntry(const modnm: string): string;
  123. begin
  124. if Copy(modnm,Length(modnm)-1,2)='.o' then
  125. Result:=Copy(modnm,1,Length(modnm)-2)+'!'
  126. else
  127. Result:=modnm;
  128. end;
  129. {*****************************************************************************
  130. TOmfLibObjectWriter.TOmfLibObjectModule
  131. *****************************************************************************}
  132. constructor TOmfLibObjectWriter.TOmfLibObjectModule.Create(const fn: string);
  133. begin
  134. FObjFileName:=fn;
  135. FObjData:=TDynamicArray.Create(objbufsize);
  136. end;
  137. destructor TOmfLibObjectWriter.TOmfLibObjectModule.Destroy;
  138. begin
  139. FObjData.Free;
  140. inherited Destroy;
  141. end;
  142. {*****************************************************************************
  143. TOmfLibObjectWriter.TOmfLibDictionaryEntry
  144. *****************************************************************************}
  145. constructor TOmfLibObjectWriter.TOmfLibDictionaryEntry.Create(
  146. HashObjectList: TFPHashObjectList; const aName: TSymStr; aModuleIndex:Integer);
  147. begin
  148. inherited Create(HashObjectList,aName);
  149. ModuleIndex:=aModuleIndex;
  150. end;
  151. {*****************************************************************************
  152. TOmfLibObjectWriter
  153. *****************************************************************************}
  154. constructor TOmfLibObjectWriter.createAr(const Aarfn: string);
  155. begin
  156. createAr(Aarfn,-1);
  157. end;
  158. constructor TOmfLibObjectWriter.createAr(const Aarfn: string;PageSize: Integer);
  159. begin
  160. FPageSize:=PageSize;
  161. FLibName:=Aarfn;
  162. FLibData:=TDynamicArray.Create(libbufsize);
  163. FDictionary:=TFPHashObjectList.Create;
  164. FObjectModules:=TFPObjectList.Create(True);
  165. FCurrentModule:=nil;
  166. end;
  167. destructor TOmfLibObjectWriter.destroy;
  168. begin
  169. if Errorcount=0 then
  170. WriteLib;
  171. FLibData.Free;
  172. FObjectModules.Free;
  173. FDictionary.Free;
  174. inherited destroy;
  175. end;
  176. function TOmfLibObjectWriter.createfile(const fn: string): boolean;
  177. begin
  178. FCurrentModule:=TOmfLibObjectModule.Create(fn);
  179. FCurrentModuleIndex:=FObjectModules.Add(FCurrentModule);
  180. createfile:=true;
  181. fobjsize:=0;
  182. end;
  183. procedure TOmfLibObjectWriter.closefile;
  184. var
  185. RawRec: TOmfRawRecord;
  186. ObjHeader: TOmfRecord_THEADR;
  187. begin
  188. FCurrentModule.ObjData.seek(0);
  189. RawRec:=TOmfRawRecord.Create;
  190. RawRec.ReadFrom(FCurrentModule.ObjData);
  191. if RawRec.RecordType<>RT_THEADR then
  192. begin
  193. RawRec.Free;
  194. InternalError(2018060801);
  195. end;
  196. ObjHeader:=TOmfRecord_THEADR.Create;
  197. ObjHeader.DecodeFrom(RawRec);
  198. { create a dictionary entry with the module name }
  199. TOmfLibDictionaryEntry.Create(FDictionary,ModName2DictEntry(ObjHeader.ModuleName),FCurrentModuleIndex);
  200. ObjHeader.Free;
  201. RawRec.Free;
  202. fobjsize:=0;
  203. end;
  204. procedure TOmfLibObjectWriter.writesym(const sym: string);
  205. begin
  206. TOmfLibDictionaryEntry.Create(FDictionary,sym,FCurrentModuleIndex);
  207. end;
  208. procedure TOmfLibObjectWriter.write(const b; len: longword);
  209. begin
  210. inc(fobjsize,len);
  211. inc(fsize,len);
  212. FCurrentModule.ObjData.write(b,len);
  213. end;
  214. procedure TOmfLibObjectWriter.WriteHeader(DictStart: DWord; DictBlocks: Word);
  215. var
  216. Header: TOmfRecord_LIBHEAD;
  217. RawRec: TOmfRawRecord;
  218. begin
  219. { set header properties }
  220. Header:=TOmfRecord_LIBHEAD.Create;
  221. Header.PageSize:=FPageSize;
  222. Header.DictionaryOffset:=DictStart;
  223. Header.DictionarySizeInBlocks:=DictBlocks;
  224. Header.CaseSensitive:=true;
  225. { write header }
  226. RawRec:=TOmfRawRecord.Create;
  227. Header.EncodeTo(RawRec);
  228. FLibData.seek(0);
  229. RawRec.WriteTo(FLibData);
  230. Header.Free;
  231. RawRec.Free;
  232. end;
  233. procedure TOmfLibObjectWriter.WriteFooter;
  234. var
  235. Footer: TOmfRecord_LIBEND;
  236. RawRec: TOmfRawRecord;
  237. begin
  238. FLibData.seek(FFooterPos);
  239. Footer:=TOmfRecord_LIBEND.Create;
  240. Footer.CalculatePaddingBytes(FLibData.Pos);
  241. RawRec:=TOmfRawRecord.Create;
  242. Footer.EncodeTo(RawRec);
  243. RawRec.WriteTo(FLibData);
  244. Footer.Free;
  245. RawRec.Free;
  246. end;
  247. function TOmfLibObjectWriter.TryPageSize(aPageSize: Integer): Boolean;
  248. var
  249. I: Integer;
  250. CurrentPage: Integer;
  251. CurrentPos: LongWord;
  252. pow: longint;
  253. begin
  254. if not IsPowerOf2(aPageSize,pow) then
  255. internalerror(2018060701);
  256. if (pow<4) or (pow>15) then
  257. internalerror(2018060702);
  258. FPageSize:=aPageSize;
  259. { header is at page 0, so first module starts at page 1 }
  260. CurrentPage:=1;
  261. for I:=0 to FObjectModules.Count-1 do
  262. with TOmfLibObjectModule(FObjectModules[I]) do
  263. begin
  264. if CurrentPage>high(word) then
  265. exit(False);
  266. PageNum:=CurrentPage;
  267. { calculate next page }
  268. CurrentPos:=CurrentPage*FPageSize+ObjData.Size;
  269. CurrentPage:=(CurrentPos+FPageSize-1) div FPageSize;
  270. end;
  271. FFooterPos:=CurrentPage*FPageSize;
  272. Result:=True;
  273. end;
  274. procedure TOmfLibObjectWriter.DeterminePageSize;
  275. var
  276. I: Integer;
  277. begin
  278. if (FPageSize<>-1) and TryPageSize(FPageSize) then
  279. { success }
  280. exit;
  281. for I:=4 to 15 do
  282. if TryPageSize(1 shl I) then
  283. exit;
  284. internalerror(2018060703);
  285. end;
  286. procedure TOmfLibObjectWriter.WriteLib;
  287. var
  288. libf: TCCustomFileStream;
  289. DictStart, bytes: LongWord;
  290. DictBlocks: Word;
  291. I: Integer;
  292. buf: array [0..1023] of Byte;
  293. begin
  294. DeterminePageSize;
  295. libf:=CFileStreamClass.Create(FLibName,fmCreate);
  296. if CStreamError<>0 then
  297. begin
  298. Message1(exec_e_cant_create_archivefile,FLibName);
  299. exit;
  300. end;
  301. for I:=0 to FObjectModules.Count-1 do
  302. with TOmfLibObjectModule(FObjectModules[I]) do
  303. begin
  304. FLibData.seek(PageNum*FPageSize);
  305. ObjData.seek(0);
  306. while ObjData.Pos<ObjData.size do
  307. begin
  308. bytes:=ObjData.read(buf,Min(SizeOf(buf),ObjData.size-ObjData.Pos));
  309. FLibData.write(buf,bytes);
  310. end;
  311. end;
  312. WriteFooter;
  313. DictStart:=FLibData.Pos;
  314. DictBlocks:=WriteDictionary;
  315. WriteHeader(DictStart,DictBlocks);
  316. FLibData.WriteStream(libf);
  317. libf.Free;
  318. end;
  319. function TOmfLibObjectWriter.WriteDictionary: Word;
  320. var
  321. nb: Word;
  322. begin
  323. for nb in OmfLibDictionaryBlockCounts do
  324. if TryWriteDictionaryWithSize(nb) then
  325. exit(nb);
  326. { could not write dictionary, even with the largest number of blocks }
  327. internalerror(2015042202);
  328. end;
  329. function TOmfLibObjectWriter.TryWriteDictionaryWithSize(nblocks: Word
  330. ): Boolean;
  331. const
  332. nbuckets=37;
  333. freespace=nbuckets;
  334. type
  335. PBlock=^TBlock;
  336. TBlock=array[0..511] of byte;
  337. var
  338. blocks: array of TBlock;
  339. i: Integer;
  340. N: TSymStr;
  341. length_of_string: Integer;
  342. h: TOmfLibHash;
  343. start_block,start_bucket: Integer;
  344. space_required: Integer;
  345. pb: PBlock;
  346. success: Boolean;
  347. store_at: Integer;
  348. PageNum: Word;
  349. begin
  350. blocks:=nil;
  351. SetLength(blocks,nblocks);
  352. for i:=0 to nblocks-1 do
  353. begin
  354. FillChar(blocks[i],SizeOf(blocks[i]),0);
  355. blocks[i][freespace]:=(freespace+1) div 2;
  356. end;
  357. for i:=0 to FDictionary.Count-1 do
  358. begin
  359. N:=TOmfLibDictionaryEntry(FDictionary[i]).Name;
  360. PageNum:=TOmfLibObjectModule(FObjectModules[TOmfLibDictionaryEntry(FDictionary[i]).ModuleIndex]).PageNum;
  361. length_of_string:=Length(N);
  362. h:=compute_omf_lib_hash(N,nblocks);
  363. start_block:=h.block_x;
  364. start_bucket:=h.bucket_x;
  365. space_required:=1+length_of_string+2;
  366. if odd(space_required) then
  367. Inc(space_required);
  368. repeat
  369. pb:=@blocks[h.block_x];
  370. success:=false;
  371. {$push}
  372. { Disable range check in that part of code }
  373. {$R-}
  374. repeat
  375. if pb^[h.bucket_x]=0 then
  376. begin
  377. if (512-pb^[freespace]*2)<space_required then
  378. break;
  379. pb^[h.bucket_x]:=pb^[freespace];
  380. store_at:=2*pb^[h.bucket_x];
  381. pb^[store_at]:=length_of_string;
  382. Move(N[1],pb^[store_at+1],length_of_string);
  383. pb^[store_at+1+length_of_string]:=Byte(PageNum);
  384. pb^[store_at+1+length_of_string+1]:=Byte(PageNum shr 8);
  385. Inc(pb^[freespace],space_required div 2);
  386. if pb^[freespace]=0 then
  387. pb^[freespace]:=255;
  388. success:=true;
  389. break;
  390. end;
  391. h.bucket_x:=(h.bucket_x+h.bucket_d) mod nbuckets;
  392. until h.bucket_x=start_bucket;
  393. {$pop}
  394. if not success then
  395. begin
  396. h.block_x:=(h.block_x+h.block_d) mod nblocks;
  397. if h.block_x=start_block then
  398. exit(false); // not enough blocks
  399. pb^[freespace]:=255;
  400. end;
  401. until success;
  402. end;
  403. FLibData.write(blocks[0],nblocks*SizeOf(TBlock));
  404. Result:=true;
  405. end;
  406. {*****************************************************************************
  407. TOmfLibObjectReader.TOmfLibDictionaryEntry
  408. *****************************************************************************}
  409. constructor TOmfLibObjectReader.TOmfLibDictionaryEntry.Create(
  410. HashObjectList: TFPHashObjectList; const aName: TSymStr; aPageNum: Word);
  411. begin
  412. inherited Create(HashObjectList,aName);
  413. PageNum:=aPageNum;
  414. end;
  415. {*****************************************************************************
  416. TOmfLibObjectReader
  417. *****************************************************************************}
  418. procedure TOmfLibObjectReader.ReadLibrary;
  419. var
  420. RawRecord: TOmfRawRecord;
  421. Header: TOmfRecord_LIBHEAD;
  422. begin
  423. RawRecord:=TOmfRawRecord.Create;
  424. RawRecord.ReadFrom(Self);
  425. Header:=TOmfRecord_LIBHEAD.Create;
  426. Header.DecodeFrom(RawRecord);
  427. FPageSize:=Header.PageSize;
  428. FIsCaseSensitive:=Header.CaseSensitive;
  429. ReadDictionary(Header.DictionaryOffset, Header.DictionarySizeInBlocks);
  430. Header.Free;
  431. RawRecord.Free;
  432. end;
  433. procedure TOmfLibObjectReader.ReadDictionary(DictionaryOffset: DWord; DictionarySizeInBlocks: Word);
  434. const
  435. nbuckets=37;
  436. freespace=nbuckets;
  437. type
  438. PBlock=^TBlock;
  439. TBlock=array[0..511] of byte;
  440. var
  441. blocks: array of TBlock;
  442. blocknr: Integer;
  443. block: PBlock;
  444. ofs: Integer;
  445. bucket: Integer;
  446. length_of_string: Byte;
  447. name: string;
  448. PageNum: Integer;
  449. begin
  450. blocks:=nil;
  451. name:='';
  452. seek(DictionaryOffset);
  453. SetLength(blocks,DictionarySizeInBlocks);
  454. read(blocks[0],DictionarySizeInBlocks*SizeOf(TBlock));
  455. for blocknr:=0 to DictionarySizeInBlocks-1 do
  456. begin
  457. block:=@(blocks[blocknr]);
  458. for bucket:=0 to nbuckets-1 do
  459. if block^[bucket]<>0 then
  460. begin
  461. ofs:=2*block^[bucket];
  462. length_of_string:=block^[ofs];
  463. if (ofs+1+length_of_string+1)>High(TBlock) then
  464. begin
  465. Comment(V_Error,'OMF dictionary entry goes beyond end of block');
  466. continue;
  467. end;
  468. SetLength(name,length_of_string);
  469. Move(block^[ofs+1],name[1],length_of_string);
  470. PageNum:=block^[ofs+1+length_of_string]+
  471. block^[ofs+1+length_of_string+1] shl 8;
  472. TOmfLibDictionaryEntry.create(LibSymbols,name,PageNum);
  473. end;
  474. end;
  475. end;
  476. function TOmfLibObjectReader.getfilename: string;
  477. begin
  478. Result:=inherited getfilename;
  479. if CurrMemberName<>'' then
  480. result:=result+'('+CurrMemberName+')';
  481. end;
  482. function TOmfLibObjectReader.GetPos: longint;
  483. begin
  484. result:=inherited GetPos-CurrMemberPos;
  485. end;
  486. function TOmfLibObjectReader.GetIsArchive: boolean;
  487. begin
  488. result:=islib;
  489. end;
  490. constructor TOmfLibObjectReader.createAr(const Aarfn: string; allow_nonar: boolean);
  491. var
  492. RecType: Byte;
  493. begin
  494. inherited Create;
  495. LibSymbols:=TFPHashObjectList.Create(true);
  496. CurrMemberPos:=0;
  497. CurrMemberName:='';
  498. if inherited openfile(Aarfn) then
  499. begin
  500. Read(RecType,1);
  501. Seek(0);
  502. islib:=RecType=RT_LIBHEAD;
  503. if islib then
  504. ReadLibrary
  505. else if (not allow_nonar) then
  506. Comment(V_Error,'Not an OMF library file, illegal magic: '+filename);
  507. end;
  508. end;
  509. destructor TOmfLibObjectReader.destroy;
  510. begin
  511. inherited closefile;
  512. LibSymbols.Free;
  513. inherited Destroy;
  514. end;
  515. function TOmfLibObjectReader.openfile(const fn: string): boolean;
  516. var
  517. libsym: TOmfLibDictionaryEntry;
  518. RawRec: TOmfRawRecord;
  519. Header: TOmfRecord_THEADR;
  520. begin
  521. result:=false;
  522. libsym:=TOmfLibDictionaryEntry(LibSymbols.Find(ModName2DictEntry(fn)));
  523. if not assigned(libsym) then
  524. exit;
  525. CurrMemberPos:=libsym.PageNum*FPageSize;
  526. inherited Seek(CurrMemberPos);
  527. { read the header, to obtain the module name }
  528. RawRec:=TOmfRawRecord.Create;
  529. RawRec.ReadFrom(self);
  530. Header:=TOmfRecord_THEADR.Create;
  531. Header.DecodeFrom(RawRec);
  532. CurrMemberName:=Header.ModuleName;
  533. Header.Free;
  534. RawRec.Free;
  535. { go back to the beginning of the file }
  536. inherited Seek(CurrMemberPos);
  537. result:=true;
  538. end;
  539. procedure TOmfLibObjectReader.closefile;
  540. begin
  541. CurrMemberPos:=0;
  542. CurrMemberName:='';
  543. end;
  544. procedure TOmfLibObjectReader.seek(len: longint);
  545. begin
  546. inherited Seek(CurrMemberPos+len);
  547. end;
  548. end.