owomflib.pas 19 KB

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