owomflib.pas 16 KB

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