2
0

owomflib.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  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. 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. unaligned(PUint16(@pb^[store_at+1+length_of_string{..store_at+1+length_of_string+1}])^):=NtoLE(uint16(PageNum));
  384. Inc(pb^[freespace],space_required div 2);
  385. if pb^[freespace]=0 then
  386. pb^[freespace]:=255;
  387. success:=true;
  388. break;
  389. end;
  390. h.bucket_x:=(h.bucket_x+h.bucket_d) mod nbuckets;
  391. until h.bucket_x=start_bucket;
  392. {$pop}
  393. if not success then
  394. begin
  395. h.block_x:=(h.block_x+h.block_d) mod nblocks;
  396. if h.block_x=start_block then
  397. exit(false); // not enough blocks
  398. pb^[freespace]:=255;
  399. end;
  400. until success;
  401. end;
  402. FLibData.write(blocks[0],nblocks*SizeOf(TBlock));
  403. Result:=true;
  404. end;
  405. {*****************************************************************************
  406. TOmfLibObjectReader.TOmfLibDictionaryEntry
  407. *****************************************************************************}
  408. constructor TOmfLibObjectReader.TOmfLibDictionaryEntry.Create(
  409. HashObjectList: TFPHashObjectList; const aName: TSymStr; aPageNum: Word);
  410. begin
  411. inherited Create(HashObjectList,aName);
  412. PageNum:=aPageNum;
  413. end;
  414. {*****************************************************************************
  415. TOmfLibObjectReader
  416. *****************************************************************************}
  417. procedure TOmfLibObjectReader.ReadLibrary;
  418. var
  419. RawRecord: TOmfRawRecord;
  420. Header: TOmfRecord_LIBHEAD;
  421. begin
  422. RawRecord:=TOmfRawRecord.Create;
  423. RawRecord.ReadFrom(Self);
  424. Header:=TOmfRecord_LIBHEAD.Create;
  425. Header.DecodeFrom(RawRecord);
  426. FPageSize:=Header.PageSize;
  427. FIsCaseSensitive:=Header.CaseSensitive;
  428. ReadDictionary(Header.DictionaryOffset, Header.DictionarySizeInBlocks);
  429. Header.Free;
  430. RawRecord.Free;
  431. end;
  432. procedure TOmfLibObjectReader.ReadDictionary(DictionaryOffset: DWord; DictionarySizeInBlocks: Word);
  433. const
  434. nbuckets=37;
  435. freespace=nbuckets;
  436. type
  437. PBlock=^TBlock;
  438. TBlock=array[0..511] of byte;
  439. var
  440. blocks: array of TBlock;
  441. blocknr: Integer;
  442. block: PBlock;
  443. ofs: Integer;
  444. bucket: Integer;
  445. length_of_string: Byte;
  446. name: string;
  447. PageNum: Integer;
  448. begin
  449. blocks:=nil;
  450. name:='';
  451. seek(DictionaryOffset);
  452. SetLength(blocks,DictionarySizeInBlocks);
  453. read(blocks[0],DictionarySizeInBlocks*SizeOf(TBlock));
  454. for blocknr:=0 to DictionarySizeInBlocks-1 do
  455. begin
  456. block:=@(blocks[blocknr]);
  457. for bucket:=0 to nbuckets-1 do
  458. if block^[bucket]<>0 then
  459. begin
  460. ofs:=2*block^[bucket];
  461. length_of_string:=block^[ofs];
  462. if (ofs+1+length_of_string+1)>High(TBlock) then
  463. begin
  464. Comment(V_Error,'OMF dictionary entry goes beyond end of block');
  465. continue;
  466. end;
  467. SetLength(name,length_of_string);
  468. Move(block^[ofs+1],name[1],length_of_string);
  469. PageNum:=LEtoN(unaligned(PUint16(@block^[ofs+1+length_of_string{..ofs+1+length_of_string+1}])^));
  470. TOmfLibDictionaryEntry.create(LibSymbols,name,PageNum);
  471. end;
  472. end;
  473. end;
  474. function TOmfLibObjectReader.getfilename: string;
  475. begin
  476. Result:=inherited getfilename;
  477. if CurrMemberName<>'' then
  478. result:=result+'('+CurrMemberName+')';
  479. end;
  480. function TOmfLibObjectReader.GetPos: longint;
  481. begin
  482. result:=inherited GetPos-CurrMemberPos;
  483. end;
  484. function TOmfLibObjectReader.GetIsArchive: boolean;
  485. begin
  486. result:=islib;
  487. end;
  488. constructor TOmfLibObjectReader.createAr(const Aarfn: string; allow_nonar: boolean);
  489. var
  490. RecType: Byte;
  491. begin
  492. inherited Create;
  493. LibSymbols:=TFPHashObjectList.Create(true);
  494. CurrMemberPos:=0;
  495. CurrMemberName:='';
  496. if inherited openfile(Aarfn) then
  497. begin
  498. Read(RecType,1);
  499. Seek(0);
  500. islib:=RecType=RT_LIBHEAD;
  501. if islib then
  502. ReadLibrary
  503. else if (not allow_nonar) then
  504. Comment(V_Error,'Not an OMF library file, illegal magic: '+filename);
  505. end;
  506. end;
  507. destructor TOmfLibObjectReader.destroy;
  508. begin
  509. inherited closefile;
  510. LibSymbols.Free;
  511. inherited Destroy;
  512. end;
  513. function TOmfLibObjectReader.openfile(const fn: string): boolean;
  514. var
  515. libsym: TOmfLibDictionaryEntry;
  516. RawRec: TOmfRawRecord;
  517. Header: TOmfRecord_THEADR;
  518. begin
  519. result:=false;
  520. libsym:=TOmfLibDictionaryEntry(LibSymbols.Find(ModName2DictEntry(fn)));
  521. if not assigned(libsym) then
  522. exit;
  523. CurrMemberPos:=libsym.PageNum*FPageSize;
  524. inherited Seek(CurrMemberPos);
  525. { read the header, to obtain the module name }
  526. RawRec:=TOmfRawRecord.Create;
  527. RawRec.ReadFrom(self);
  528. Header:=TOmfRecord_THEADR.Create;
  529. Header.DecodeFrom(RawRec);
  530. CurrMemberName:=Header.ModuleName;
  531. Header.Free;
  532. RawRec.Free;
  533. { go back to the beginning of the file }
  534. inherited Seek(CurrMemberPos);
  535. result:=true;
  536. end;
  537. procedure TOmfLibObjectReader.closefile;
  538. begin
  539. CurrMemberPos:=0;
  540. CurrMemberName:='';
  541. end;
  542. procedure TOmfLibObjectReader.seek(len: longint);
  543. begin
  544. inherited Seek(CurrMemberPos+len);
  545. end;
  546. end.