owomflib.pas 19 KB

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