t_linux.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. {
  2. $Id$
  3. Copyright (c) 1998-2002 by Peter Vreman
  4. This unit implements support import,export,link routines
  5. for the (i386) Linux target
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. ****************************************************************************
  18. }
  19. unit t_linux;
  20. {$i fpcdefs.inc}
  21. interface
  22. uses
  23. symsym,
  24. import,export,link;
  25. type
  26. timportliblinux=class(timportlib)
  27. procedure preparelib(const s:string);override;
  28. procedure importprocedure(const func,module:string;index:longint;const name:string);override;
  29. procedure importvariable(vs:tvarsym;const name,module:string);override;
  30. procedure generatelib;override;
  31. end;
  32. texportliblinux=class(texportlib)
  33. procedure preparelib(const s : string);override;
  34. procedure exportprocedure(hp : texported_item);override;
  35. procedure exportvar(hp : texported_item);override;
  36. procedure generatelib;override;
  37. end;
  38. tlinkerlinux=class(texternallinker)
  39. private
  40. Glibc2,
  41. Glibc21 : boolean;
  42. Function WriteResponseFile(isdll:boolean) : Boolean;
  43. public
  44. constructor Create;override;
  45. procedure SetDefaultInfo;override;
  46. function MakeExecutable:boolean;override;
  47. function MakeSharedLibrary:boolean;override;
  48. end;
  49. implementation
  50. uses
  51. cutils,cclasses,
  52. verbose,systems,globtype,globals,
  53. symconst,script,
  54. fmodule
  55. {$ifdef i386}
  56. ,aasmbase,aasmtai,aasmcpu,cpubase
  57. {$endif i386}
  58. ,i_linux
  59. ;
  60. {*****************************************************************************
  61. TIMPORTLIBLINUX
  62. *****************************************************************************}
  63. procedure timportliblinux.preparelib(const s : string);
  64. begin
  65. end;
  66. procedure timportliblinux.importprocedure(const func,module : string;index : longint;const name : string);
  67. begin
  68. { insert sharedlibrary }
  69. current_module.linkothersharedlibs.add(SplitName(module),link_allways);
  70. { do nothing with the procedure, only set the mangledname }
  71. if name<>'' then
  72. begin
  73. aktprocdef.setmangledname(name);
  74. aktprocdef.has_mangledname:=true;
  75. end
  76. else
  77. message(parser_e_empty_import_name);
  78. end;
  79. procedure timportliblinux.importvariable(vs:tvarsym;const name,module:string);
  80. begin
  81. { insert sharedlibrary }
  82. current_module.linkothersharedlibs.add(SplitName(module),link_allways);
  83. { reset the mangledname and turn off the dll_var option }
  84. vs.set_mangledname(name);
  85. exclude(vs.varoptions,vo_is_dll_var);
  86. end;
  87. procedure timportliblinux.generatelib;
  88. begin
  89. end;
  90. {*****************************************************************************
  91. TEXPORTLIBLINUX
  92. *****************************************************************************}
  93. procedure texportliblinux.preparelib(const s:string);
  94. begin
  95. end;
  96. procedure texportliblinux.exportprocedure(hp : texported_item);
  97. var
  98. hp2 : texported_item;
  99. begin
  100. { first test the index value }
  101. if (hp.options and eo_index)<>0 then
  102. begin
  103. Message1(parser_e_no_export_with_index_for_target,'linux');
  104. exit;
  105. end;
  106. { now place in correct order }
  107. hp2:=texported_item(current_module._exports.first);
  108. while assigned(hp2) and
  109. (hp.name^>hp2.name^) do
  110. hp2:=texported_item(hp2.next);
  111. { insert hp there !! }
  112. if assigned(hp2) and (hp2.name^=hp.name^) then
  113. begin
  114. { this is not allowed !! }
  115. Message1(parser_e_export_name_double,hp.name^);
  116. exit;
  117. end;
  118. if hp2=texported_item(current_module._exports.first) then
  119. current_module._exports.concat(hp)
  120. else if assigned(hp2) then
  121. begin
  122. hp.next:=hp2;
  123. hp.previous:=hp2.previous;
  124. if assigned(hp2.previous) then
  125. hp2.previous.next:=hp;
  126. hp2.previous:=hp;
  127. end
  128. else
  129. current_module._exports.concat(hp);
  130. end;
  131. procedure texportliblinux.exportvar(hp : texported_item);
  132. begin
  133. hp.is_var:=true;
  134. exportprocedure(hp);
  135. end;
  136. procedure texportliblinux.generatelib;
  137. var
  138. hp2 : texported_item;
  139. begin
  140. hp2:=texported_item(current_module._exports.first);
  141. while assigned(hp2) do
  142. begin
  143. if (not hp2.is_var) and
  144. (hp2.sym.typ=procsym) then
  145. begin
  146. { the manglednames can already be the same when the procedure
  147. is declared with cdecl }
  148. if tprocsym(hp2.sym).first_procdef.mangledname<>hp2.name^ then
  149. begin
  150. {$ifdef i386}
  151. { place jump in codesegment }
  152. codesegment.concat(Tai_align.Create_op(4,$90));
  153. codeSegment.concat(Tai_symbol.Createname_global(hp2.name^,0));
  154. codeSegment.concat(Taicpu.Op_sym(A_JMP,S_NO,objectlibrary.newasmsymbol(tprocsym(hp2.sym).first_procdef.mangledname)));
  155. codeSegment.concat(Tai_symbol_end.Createname(hp2.name^));
  156. {$endif i386}
  157. end;
  158. end
  159. else
  160. Message1(parser_e_no_export_of_variables_for_target,'linux');
  161. hp2:=texported_item(hp2.next);
  162. end;
  163. end;
  164. {*****************************************************************************
  165. TLINKERLINUX
  166. *****************************************************************************}
  167. Constructor TLinkerLinux.Create;
  168. begin
  169. Inherited Create;
  170. LibrarySearchPath.AddPath('/lib;/usr/lib;/usr/X11R6/lib',true);
  171. end;
  172. procedure TLinkerLinux.SetDefaultInfo;
  173. {
  174. This will also detect which libc version will be used
  175. }
  176. {$ifdef m68k}
  177. var
  178. St : SearchRec;
  179. {$endif m68k}
  180. begin
  181. Glibc2:=false;
  182. Glibc21:=false;
  183. with Info do
  184. begin
  185. ExeCmd[1]:='ld $OPT $DYNLINK $STATIC $STRIP -L. -o $EXE $RES';
  186. DllCmd[1]:='ld $OPT $INIT $FINI $SONAME -shared -L. -o $EXE $RES';
  187. DllCmd[2]:='strip --strip-unneeded $EXE';
  188. {$ifdef m68k}
  189. Glibc2:=true;
  190. FindFirst('/lib/ld*',AnyFile,st);
  191. while DosError=0 do
  192. begin
  193. if copy(st.name,1,5)='ld-2.' then
  194. begin
  195. DynamicLinker:='/lib/'+St.name;
  196. Glibc21:=st.name[6]<>'0';
  197. break;
  198. end;
  199. FindNext(St);
  200. end;
  201. FindClose(St);
  202. {$else m68k}
  203. { first try glibc2 }
  204. DynamicLinker:='/lib/ld-linux.so.2';
  205. if FileExists(DynamicLinker) then
  206. begin
  207. Glibc2:=true;
  208. { Check for 2.0 files, else use the glibc 2.1 stub }
  209. if FileExists('/lib/ld-2.0.*') then
  210. Glibc21:=false
  211. else
  212. Glibc21:=true;
  213. end
  214. else
  215. DynamicLinker:='/lib/ld-linux.so.1';
  216. {$endif m68k}
  217. end;
  218. end;
  219. Function TLinkerLinux.WriteResponseFile(isdll:boolean) : Boolean;
  220. Var
  221. linkres : TLinkRes;
  222. i : longint;
  223. cprtobj,
  224. gprtobj,
  225. prtobj : string[80];
  226. HPath : TStringListItem;
  227. s,s1,s2 : string;
  228. found1,
  229. found2,
  230. linkdynamic,
  231. linklibc : boolean;
  232. begin
  233. WriteResponseFile:=False;
  234. { set special options for some targets }
  235. linkdynamic:=not(SharedLibFiles.empty);
  236. linklibc:=(SharedLibFiles.Find('c')<>nil);
  237. if isdll then
  238. begin
  239. prtobj:='dllprt0';
  240. cprtobj:='dllprt0';
  241. gprtobj:='dllprt0';
  242. end
  243. else
  244. begin
  245. prtobj:='prt0';
  246. cprtobj:='cprt0';
  247. gprtobj:='gprt0';
  248. if glibc21 then
  249. begin
  250. cprtobj:='cprt21';
  251. gprtobj:='gprt21';
  252. end;
  253. end;
  254. if cs_profile in aktmoduleswitches then
  255. begin
  256. prtobj:=gprtobj;
  257. if not glibc2 then
  258. AddSharedLibrary('gmon');
  259. AddSharedLibrary('c');
  260. linklibc:=true;
  261. end
  262. else
  263. begin
  264. if linklibc then
  265. prtobj:=cprtobj;
  266. end;
  267. { Open link.res file }
  268. LinkRes:=TLinkRes.Create(outputexedir+Info.ResName);
  269. { Write path to search libraries }
  270. HPath:=TStringListItem(current_module.locallibrarysearchpath.First);
  271. while assigned(HPath) do
  272. begin
  273. LinkRes.Add('SEARCH_DIR('+HPath.Str+')');
  274. HPath:=TStringListItem(HPath.Next);
  275. end;
  276. HPath:=TStringListItem(LibrarySearchPath.First);
  277. while assigned(HPath) do
  278. begin
  279. LinkRes.Add('SEARCH_DIR('+HPath.Str+')');
  280. HPath:=TStringListItem(HPath.Next);
  281. end;
  282. LinkRes.Add('INPUT(');
  283. { add objectfiles, start with prt0 always }
  284. if prtobj<>'' then
  285. LinkRes.AddFileName(FindObjectFile(prtobj,''));
  286. { try to add crti and crtbegin if linking to C }
  287. if linklibc then
  288. begin
  289. if librarysearchpath.FindFile('crtbegin.o',s) then
  290. LinkRes.AddFileName(s);
  291. if librarysearchpath.FindFile('crti.o',s) then
  292. LinkRes.AddFileName(s);
  293. end;
  294. { main objectfiles }
  295. while not ObjectFiles.Empty do
  296. begin
  297. s:=ObjectFiles.GetFirst;
  298. if s<>'' then
  299. LinkRes.AddFileName(s);
  300. end;
  301. LinkRes.Add(')');
  302. { Write staticlibraries }
  303. if not StaticLibFiles.Empty then
  304. begin
  305. LinkRes.Add('GROUP(');
  306. While not StaticLibFiles.Empty do
  307. begin
  308. S:=StaticLibFiles.GetFirst;
  309. LinkRes.AddFileName(s)
  310. end;
  311. LinkRes.Add(')');
  312. end;
  313. { Write sharedlibraries like -l<lib>, also add the needed dynamic linker
  314. here to be sure that it gets linked this is needed for glibc2 systems (PFV) }
  315. if not SharedLibFiles.Empty then
  316. begin
  317. LinkRes.Add('INPUT(');
  318. While not SharedLibFiles.Empty do
  319. begin
  320. S:=SharedLibFiles.GetFirst;
  321. if s<>'c' then
  322. begin
  323. i:=Pos(target_info.sharedlibext,S);
  324. if i>0 then
  325. Delete(S,i,255);
  326. LinkRes.Add('-l'+s);
  327. end
  328. else
  329. begin
  330. linklibc:=true;
  331. linkdynamic:=false; { libc will include the ld-linux for us }
  332. end;
  333. end;
  334. { be sure that libc is the last lib }
  335. if linklibc then
  336. LinkRes.Add('-lc');
  337. { when we have -static for the linker the we also need libgcc }
  338. if (cs_link_staticflag in aktglobalswitches) then
  339. LinkRes.Add('-lgcc');
  340. if linkdynamic and (Info.DynamicLinker<>'') then
  341. LinkRes.AddFileName(Info.DynamicLinker);
  342. LinkRes.Add(')');
  343. end;
  344. { objects which must be at the end }
  345. if linklibc then
  346. begin
  347. found1:=librarysearchpath.FindFile('crtend.o',s1);
  348. found2:=librarysearchpath.FindFile('crtn.o',s2);
  349. if found1 or found2 then
  350. begin
  351. LinkRes.Add('INPUT(');
  352. if found1 then
  353. LinkRes.AddFileName(s1);
  354. if found2 then
  355. LinkRes.AddFileName(s2);
  356. LinkRes.Add(')');
  357. end;
  358. end;
  359. { Write and Close response }
  360. linkres.writetodisk;
  361. linkres.Free;
  362. WriteResponseFile:=True;
  363. end;
  364. function TLinkerLinux.MakeExecutable:boolean;
  365. var
  366. binstr,
  367. cmdstr : string;
  368. success : boolean;
  369. DynLinkStr : string[60];
  370. StaticStr,
  371. StripStr : string[40];
  372. begin
  373. if not(cs_link_extern in aktglobalswitches) then
  374. Message1(exec_i_linking,current_module.exefilename^);
  375. { Create some replacements }
  376. StaticStr:='';
  377. StripStr:='';
  378. DynLinkStr:='';
  379. if (cs_link_staticflag in aktglobalswitches) then
  380. StaticStr:='-static';
  381. if (cs_link_strip in aktglobalswitches) then
  382. StripStr:='-s';
  383. If (cs_profile in aktmoduleswitches) or
  384. ((Info.DynamicLinker<>'') and (not SharedLibFiles.Empty)) then
  385. DynLinkStr:='-dynamic-linker='+Info.DynamicLinker;
  386. { Write used files and libraries }
  387. WriteResponseFile(false);
  388. { Call linker }
  389. SplitBinCmd(Info.ExeCmd[1],binstr,cmdstr);
  390. Replace(cmdstr,'$EXE',current_module.exefilename^);
  391. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  392. Replace(cmdstr,'$RES',outputexedir+Info.ResName);
  393. Replace(cmdstr,'$STATIC',StaticStr);
  394. Replace(cmdstr,'$STRIP',StripStr);
  395. Replace(cmdstr,'$DYNLINK',DynLinkStr);
  396. success:=DoExec(FindUtil(BinStr),CmdStr,true,false);
  397. { Remove ReponseFile }
  398. if (success) and not(cs_link_extern in aktglobalswitches) then
  399. RemoveFile(outputexedir+Info.ResName);
  400. MakeExecutable:=success; { otherwise a recursive call to link method }
  401. end;
  402. Function TLinkerLinux.MakeSharedLibrary:boolean;
  403. var
  404. InitStr,
  405. FiniStr,
  406. SoNameStr : string[80];
  407. binstr,
  408. cmdstr : string;
  409. success : boolean;
  410. begin
  411. MakeSharedLibrary:=false;
  412. if not(cs_link_extern in aktglobalswitches) then
  413. Message1(exec_i_linking,current_module.sharedlibfilename^);
  414. { Write used files and libraries }
  415. WriteResponseFile(true);
  416. { Create some replacements }
  417. InitStr:='-init FPC_LIB_START';
  418. FiniStr:='-fini FPC_LIB_EXIT';
  419. SoNameStr:='-soname '+SplitFileName(current_module.sharedlibfilename^);
  420. { Call linker }
  421. SplitBinCmd(Info.DllCmd[1],binstr,cmdstr);
  422. Replace(cmdstr,'$EXE',current_module.sharedlibfilename^);
  423. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  424. Replace(cmdstr,'$RES',outputexedir+Info.ResName);
  425. Replace(cmdstr,'$INIT',InitStr);
  426. Replace(cmdstr,'$FINI',FiniStr);
  427. Replace(cmdstr,'$SONAME',SoNameStr);
  428. success:=DoExec(FindUtil(binstr),cmdstr,true,false);
  429. { Strip the library ? }
  430. if success and (cs_link_strip in aktglobalswitches) then
  431. begin
  432. SplitBinCmd(Info.DllCmd[2],binstr,cmdstr);
  433. Replace(cmdstr,'$EXE',current_module.sharedlibfilename^);
  434. success:=DoExec(FindUtil(binstr),cmdstr,true,false);
  435. end;
  436. { Remove ReponseFile }
  437. if (success) and not(cs_link_extern in aktglobalswitches) then
  438. RemoveFile(outputexedir+Info.ResName);
  439. MakeSharedLibrary:=success; { otherwise a recursive call to link method }
  440. end;
  441. {*****************************************************************************
  442. Initialize
  443. *****************************************************************************}
  444. initialization
  445. {$ifdef i386}
  446. RegisterExternalLinker(system_i386_linux_info,TLinkerLinux);
  447. RegisterImport(system_i386_linux,timportliblinux);
  448. RegisterExport(system_i386_linux,texportliblinux);
  449. RegisterTarget(system_i386_linux_info);
  450. {$endif i386}
  451. {$ifdef m68k}
  452. RegisterExternalLinker(system_m68k_linux_info,TLinkerLinux);
  453. RegisterImport(system_m68k_linux,timportliblinux);
  454. RegisterExport(system_m68k_linux,texportliblinux);
  455. RegisterTarget(system_m68k_linux_info);
  456. {$endif m68k}
  457. {$ifdef powerpc}
  458. RegisterExternalLinker(system_powerpc_linux_info,TLinkerLinux);
  459. RegisterImport(system_powerpc_linux,timportliblinux);
  460. RegisterExport(system_powerpc_linux,texportliblinux);
  461. RegisterTarget(system_powerpc_linux_info);
  462. {$endif powerpc}
  463. {$ifdef alpha}
  464. RegisterExternalLinker(system_alpha_linux_info,TLinkerLinux);
  465. RegisterImport(system_alpha_linux,timportliblinux);
  466. RegisterExport(system_alpha_linux,texportliblinux);
  467. RegisterTarget(system_alpha_linux_info);
  468. {$endif alpha}
  469. {$ifdef x86_64}
  470. RegisterExternalLinker(system_x86_64_linux_info,TLinkerLinux);
  471. RegisterImport(system_x86_64_linux,timportliblinux);
  472. RegisterExport(system_x86_64_linux,texportliblinux);
  473. RegisterTarget(system_x86_64_linux_info);
  474. {$endif x86_64}
  475. {$ifdef SPARC}
  476. RegisterExternalLinker(system_sparc_linux_info,TLinkerLinux);
  477. RegisterImport(system_SPARC_linux,timportliblinux);
  478. RegisterExport(system_SPARC_linux,texportliblinux);
  479. RegisterTarget(system_SPARC_linux_info);
  480. {$endif SPARC}
  481. end.
  482. {
  483. $Log$
  484. Revision 1.3 2002-11-15 01:59:02 peter
  485. * merged changes from 1.0.7 up to 04-11
  486. - -V option for generating bug report tracing
  487. - more tracing for option parsing
  488. - errors for cdecl and high()
  489. - win32 import stabs
  490. - win32 records<=8 are returned in eax:edx (turned off by default)
  491. - heaptrc update
  492. - more info for temp management in .s file with EXTDEBUG
  493. Revision 1.2 2002/09/09 17:34:17 peter
  494. * tdicationary.replace added to replace and item in a dictionary. This
  495. is only allowed for the same name
  496. * varsyms are inserted in symtable before the types are parsed. This
  497. fixes the long standing "var longint : longint" bug
  498. - consume_idlist and idstringlist removed. The loops are inserted
  499. at the callers place and uses the symtable for duplicate id checking
  500. Revision 1.1 2002/09/06 15:03:51 carl
  501. * moved files to systems directory
  502. Revision 1.33 2002/09/03 16:26:28 daniel
  503. * Make Tprocdef.defs protected
  504. Revision 1.32 2002/08/12 15:08:44 carl
  505. + stab register indexes for powerpc (moved from gdb to cpubase)
  506. + tprocessor enumeration moved to cpuinfo
  507. + linker in target_info is now a class
  508. * many many updates for m68k (will soon start to compile)
  509. - removed some ifdef or correct them for correct cpu
  510. Revision 1.31 2002/08/11 14:32:32 peter
  511. * renamed current_library to objectlibrary
  512. Revision 1.30 2002/08/11 13:24:19 peter
  513. * saving of asmsymbols in ppu supported
  514. * asmsymbollist global is removed and moved into a new class
  515. tasmlibrarydata that will hold the info of a .a file which
  516. corresponds with a single module. Added librarydata to tmodule
  517. to keep the library info stored for the module. In the future the
  518. objectfiles will also be stored to the tasmlibrarydata class
  519. * all getlabel/newasmsymbol and friends are moved to the new class
  520. Revision 1.29 2002/07/26 21:15:46 florian
  521. * rewrote the system handling
  522. Revision 1.28 2002/07/04 20:43:02 florian
  523. * first x86-64 patches
  524. Revision 1.27 2002/07/01 18:46:35 peter
  525. * internal linker
  526. * reorganized aasm layer
  527. Revision 1.26 2002/05/18 13:34:26 peter
  528. * readded missing revisions
  529. Revision 1.25 2002/05/16 19:46:53 carl
  530. + defines.inc -> fpcdefs.inc to avoid conflicts if compiling by hand
  531. + try to fix temp allocation (still in ifdef)
  532. + generic constructor calls
  533. + start of tassembler / tmodulebase class cleanup
  534. Revision 1.22 2002/05/06 19:46:36 carl
  535. + added more patches from Mazen for SPARC port
  536. Revision 1.21 2002/04/22 18:19:22 carl
  537. - remove use_bound_instruction field
  538. Revision 1.20 2002/04/20 21:43:18 carl
  539. * fix stack size for some targets
  540. + add offset to parameters from frame pointer info.
  541. - remove some unused stuff
  542. Revision 1.19 2002/04/19 15:46:05 peter
  543. * mangledname rewrite, tprocdef.mangledname is now created dynamicly
  544. in most cases and not written to the ppu
  545. * add mangeledname_prefix() routine to generate the prefix of
  546. manglednames depending on the current procedure, object and module
  547. * removed static procprefix since the mangledname is now build only
  548. on demand from tprocdef.mangledname
  549. Revision 1.18 2002/04/15 19:44:23 peter
  550. * fixed stackcheck that would be called recursively when a stack
  551. error was found
  552. * generic changeregsize(reg,size) for i386 register resizing
  553. * removed some more routines from cga unit
  554. * fixed returnvalue handling
  555. * fixed default stacksize of linux and go32v2, 8kb was a bit small :-)
  556. Revision 1.17 2002/04/15 19:16:57 carl
  557. - remove size_of_pointer field
  558. Revision 1.16 2002/01/29 21:27:34 peter
  559. * default alignment changed to 4 bytes for locals and static const,var
  560. Revision 1.15 2002/01/09 07:38:37 michael
  561. + Patch from Peter for library imports
  562. }