link.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. {
  2. Copyright (c) 1998-2002 by Peter Vreman
  3. This unit handles the linker and binder calls for programs and
  4. libraries
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit link;
  19. {$i fpcdefs.inc}
  20. interface
  21. uses
  22. cclasses,
  23. systems,
  24. fmodule,
  25. globtype;
  26. Type
  27. TLinkerInfo=record
  28. ExeCmd,
  29. DllCmd : array[1..3] of string;
  30. ResName : string[100];
  31. ScriptName : string[100];
  32. ExtraOptions : string;
  33. DynamicLinker : string[100];
  34. end;
  35. TLinker = class(TAbstractLinker)
  36. public
  37. ObjectFiles,
  38. SharedLibFiles,
  39. StaticLibFiles : TStringList;
  40. Constructor Create;virtual;
  41. Destructor Destroy;override;
  42. procedure AddModuleFiles(hp:tmodule);
  43. Procedure AddObject(const S,unitpath : String;isunit:boolean);
  44. Procedure AddStaticLibrary(const S : String);
  45. Procedure AddSharedLibrary(S : String);
  46. Procedure AddStaticCLibrary(const S : String);
  47. Procedure AddSharedCLibrary(S : String);
  48. Function MakeExecutable:boolean;virtual;
  49. Function MakeSharedLibrary:boolean;virtual;
  50. Function MakeStaticLibrary:boolean;virtual;
  51. end;
  52. TExternalLinker = class(TLinker)
  53. public
  54. Info : TLinkerInfo;
  55. Constructor Create;override;
  56. Destructor Destroy;override;
  57. Function FindUtil(const s:string):String;
  58. Function DoExec(const command:string; para:TCmdStr;showinfo,useshell:boolean):boolean;
  59. procedure SetDefaultInfo;virtual;
  60. Function MakeStaticLibrary:boolean;override;
  61. end;
  62. TInternalLinker = class(TLinker)
  63. private
  64. procedure readobj(const fn:string);
  65. public
  66. Constructor Create;override;
  67. Destructor Destroy;override;
  68. Function MakeExecutable:boolean;override;
  69. end;
  70. var
  71. Linker : TLinker;
  72. function FindObjectFile(s : string;const unitpath:string;isunit:boolean) : string;
  73. function FindLibraryFile(s:string;const prefix,ext:string;var foundfile : string) : boolean;
  74. procedure InitLinker;
  75. procedure DoneLinker;
  76. Implementation
  77. uses
  78. {$IFDEF USE_SYSUTILS}
  79. SysUtils,
  80. {$ELSE USE_SYSUTILS}
  81. dos,
  82. {$ENDIF USE_SYSUTILS}
  83. cutils,
  84. script,globals,verbose,ppu,
  85. aasmbase,aasmtai,aasmcpu,
  86. ogbase,ogmap;
  87. type
  88. TLinkerClass = class of Tlinker;
  89. {*****************************************************************************
  90. Helpers
  91. *****************************************************************************}
  92. { searches an object file }
  93. function FindObjectFile(s:string;const unitpath:string;isunit:boolean) : string;
  94. var
  95. found : boolean;
  96. foundfile : string;
  97. begin
  98. findobjectfile:='';
  99. if s='' then
  100. exit;
  101. {When linking on target, the units has not been assembled yet,
  102. so there is no object files to look for at
  103. the host. Look for the corresponding assembler file instead,
  104. because it will be assembled to object file on the target.}
  105. if isunit and (cs_link_on_target in aktglobalswitches) then
  106. s:= ForceExtension(s,target_info.asmext);
  107. { when it does not belong to the unit then check if
  108. the specified file exists without searching any paths }
  109. if not isunit then
  110. begin
  111. if FileExists(FixFileName(s)) then
  112. begin
  113. foundfile:=ScriptFixFileName(s);
  114. found:=true;
  115. end;
  116. end;
  117. if pos('.',s)=0 then
  118. s:=s+target_info.objext;
  119. { find object file
  120. 1. output unit path
  121. 2. output exe path
  122. 3. specified unit path (if specified)
  123. 4. cwd
  124. 5. unit search path
  125. 6. local object path
  126. 7. global object path
  127. 8. exepath (not when linking on target) }
  128. found:=false;
  129. if isunit and (OutputUnitDir<>'') then
  130. found:=FindFile(s,OutPutUnitDir,foundfile)
  131. else
  132. if OutputExeDir<>'' then
  133. found:=FindFile(s,OutPutExeDir,foundfile);
  134. if (not found) and (unitpath<>'') then
  135. found:=FindFile(s,unitpath,foundfile);
  136. if (not found) then
  137. found:=FindFile(s, CurDirRelPath(source_info), foundfile);
  138. if (not found) then
  139. found:=UnitSearchPath.FindFile(s,foundfile);
  140. if (not found) then
  141. found:=current_module.localobjectsearchpath.FindFile(s,foundfile);
  142. if (not found) then
  143. found:=objectsearchpath.FindFile(s,foundfile);
  144. if not(cs_link_on_target in aktglobalswitches) and (not found) then
  145. found:=FindFile(s,exepath,foundfile);
  146. if not(cs_link_extern in aktglobalswitches) and (not found) then
  147. Message1(exec_w_objfile_not_found,s);
  148. {Restore file extension}
  149. if isunit and (cs_link_on_target in aktglobalswitches) then
  150. foundfile:= ForceExtension(foundfile,target_info.objext);
  151. findobjectfile:=ScriptFixFileName(foundfile);
  152. end;
  153. { searches an library file }
  154. function FindLibraryFile(s:string;const prefix,ext:string;var foundfile : string) : boolean;
  155. var
  156. found : boolean;
  157. paths : string;
  158. begin
  159. findlibraryfile:=false;
  160. foundfile:=s;
  161. if s='' then
  162. exit;
  163. { split path from filename }
  164. paths:=SplitPath(s);
  165. s:=SplitFileName(s);
  166. { add prefix 'lib' }
  167. if (prefix<>'') and (Copy(s,1,length(prefix))<>prefix) then
  168. s:=prefix+s;
  169. { add extension }
  170. if (ext<>'') and (Copy(s,length(s)-length(ext)+1,length(ext))<>ext) then
  171. s:=s+ext;
  172. { readd the split path }
  173. s:=paths+s;
  174. if FileExists(s) then
  175. begin
  176. foundfile:=ScriptFixFileName(s);
  177. FindLibraryFile:=true;
  178. exit;
  179. end;
  180. { find libary
  181. 1. cwd
  182. 2. local libary dir
  183. 3. global libary dir
  184. 4. exe path of the compiler (not when linking on target) }
  185. found:=FindFile(s, CurDirRelPath(source_info), foundfile);
  186. if (not found) and (current_module.outputpath^<>'') then
  187. found:=FindFile(s,current_module.outputpath^,foundfile);
  188. if (not found) then
  189. found:=current_module.locallibrarysearchpath.FindFile(s,foundfile);
  190. if (not found) then
  191. found:=librarysearchpath.FindFile(s,foundfile);
  192. if not(cs_link_on_target in aktglobalswitches) and (not found) then
  193. found:=FindFile(s,exepath,foundfile);
  194. foundfile:=ScriptFixFileName(foundfile);
  195. findlibraryfile:=found;
  196. end;
  197. {*****************************************************************************
  198. TLINKER
  199. *****************************************************************************}
  200. Constructor TLinker.Create;
  201. begin
  202. Inherited Create;
  203. ObjectFiles:=TStringList.Create_no_double;
  204. SharedLibFiles:=TStringList.Create_no_double;
  205. StaticLibFiles:=TStringList.Create_no_double;
  206. end;
  207. Destructor TLinker.Destroy;
  208. begin
  209. ObjectFiles.Free;
  210. SharedLibFiles.Free;
  211. StaticLibFiles.Free;
  212. end;
  213. procedure TLinker.AddModuleFiles(hp:tmodule);
  214. var
  215. mask : longint;
  216. begin
  217. with hp do
  218. begin
  219. { link unit files }
  220. if (flags and uf_no_link)=0 then
  221. begin
  222. { create mask which unit files need linking }
  223. mask:=link_allways;
  224. { static linking ? }
  225. if (cs_link_static in aktglobalswitches) then
  226. begin
  227. if (flags and uf_static_linked)=0 then
  228. begin
  229. { if smart not avail then try static linking }
  230. if (flags and uf_smart_linked)<>0 then
  231. begin
  232. Message1(exec_t_unit_not_static_linkable_switch_to_smart,modulename^);
  233. mask:=mask or link_smart;
  234. end
  235. else
  236. Message1(exec_e_unit_not_smart_or_static_linkable,modulename^);
  237. end
  238. else
  239. mask:=mask or link_static;
  240. end;
  241. { smart linking ? }
  242. if (cs_link_smart in aktglobalswitches) then
  243. begin
  244. if (flags and uf_smart_linked)=0 then
  245. begin
  246. { if smart not avail then try static linking }
  247. if (flags and uf_static_linked)<>0 then
  248. begin
  249. Message1(exec_t_unit_not_smart_linkable_switch_to_static,modulename^);
  250. mask:=mask or link_static;
  251. end
  252. else
  253. Message1(exec_e_unit_not_smart_or_static_linkable,modulename^);
  254. end
  255. else
  256. mask:=mask or link_smart;
  257. end;
  258. { shared linking }
  259. if (cs_link_shared in aktglobalswitches) then
  260. begin
  261. if (flags and uf_shared_linked)=0 then
  262. begin
  263. { if shared not avail then try static linking }
  264. if (flags and uf_static_linked)<>0 then
  265. begin
  266. Message1(exec_t_unit_not_shared_linkable_switch_to_static,modulename^);
  267. mask:=mask or link_static;
  268. end
  269. else
  270. Message1(exec_e_unit_not_shared_or_static_linkable,modulename^);
  271. end
  272. else
  273. mask:=mask or link_shared;
  274. end;
  275. { unit files }
  276. while not linkunitofiles.empty do
  277. begin
  278. AddObject(linkunitofiles.getusemask(mask),path^,true);
  279. end;
  280. while not linkunitstaticlibs.empty do
  281. AddStaticLibrary(linkunitstaticlibs.getusemask(mask));
  282. while not linkunitsharedlibs.empty do
  283. AddSharedLibrary(linkunitsharedlibs.getusemask(mask));
  284. end;
  285. { Other needed .o and libs, specified using $L,$LINKLIB,external }
  286. mask:=link_allways;
  287. while not linkotherofiles.empty do
  288. AddObject(linkotherofiles.Getusemask(mask),path^,false);
  289. while not linkotherstaticlibs.empty do
  290. AddStaticCLibrary(linkotherstaticlibs.Getusemask(mask));
  291. while not linkothersharedlibs.empty do
  292. AddSharedCLibrary(linkothersharedlibs.Getusemask(mask));
  293. end;
  294. end;
  295. Procedure TLinker.AddObject(const S,unitpath : String;isunit:boolean);
  296. begin
  297. ObjectFiles.Concat(FindObjectFile(s,unitpath,isunit));
  298. end;
  299. Procedure TLinker.AddSharedLibrary(S:String);
  300. begin
  301. if s='' then
  302. exit;
  303. { remove prefix 'lib' }
  304. if Copy(s,1,length(target_info.sharedlibprefix))=target_info.sharedlibprefix then
  305. Delete(s,1,length(target_info.sharedlibprefix));
  306. { remove extension if any }
  307. if Copy(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext))=target_info.sharedlibext then
  308. Delete(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext)+1);
  309. { ready to be added }
  310. SharedLibFiles.Concat(S);
  311. end;
  312. Procedure TLinker.AddStaticLibrary(const S:String);
  313. var
  314. ns : string;
  315. found : boolean;
  316. begin
  317. if s='' then
  318. exit;
  319. found:=FindLibraryFile(s,target_info.staticlibprefix,target_info.staticlibext,ns);
  320. if not(cs_link_extern in aktglobalswitches) and (not found) then
  321. Message1(exec_w_libfile_not_found,s);
  322. StaticLibFiles.Concat(ns);
  323. end;
  324. Procedure TLinker.AddSharedCLibrary(S:String);
  325. begin
  326. if s='' then
  327. exit;
  328. { remove prefix 'lib' }
  329. if Copy(s,1,length(target_info.sharedclibprefix))=target_info.sharedclibprefix then
  330. Delete(s,1,length(target_info.sharedclibprefix));
  331. { remove extension if any }
  332. if Copy(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext))=target_info.sharedclibext then
  333. Delete(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext)+1);
  334. { ready to be added }
  335. SharedLibFiles.Concat(S);
  336. end;
  337. Procedure TLinker.AddStaticCLibrary(const S:String);
  338. var
  339. ns : string;
  340. found : boolean;
  341. begin
  342. if s='' then
  343. exit;
  344. found:=FindLibraryFile(s,target_info.staticclibprefix,target_info.staticclibext,ns);
  345. if not(cs_link_extern in aktglobalswitches) and (not found) then
  346. Message1(exec_w_libfile_not_found,s);
  347. StaticLibFiles.Concat(ns);
  348. end;
  349. function TLinker.MakeExecutable:boolean;
  350. begin
  351. MakeExecutable:=false;
  352. Message(exec_e_exe_not_supported);
  353. end;
  354. Function TLinker.MakeSharedLibrary:boolean;
  355. begin
  356. MakeSharedLibrary:=false;
  357. Message(exec_e_dll_not_supported);
  358. end;
  359. Function TLinker.MakeStaticLibrary:boolean;
  360. begin
  361. MakeStaticLibrary:=false;
  362. Message(exec_e_dll_not_supported);
  363. end;
  364. {*****************************************************************************
  365. TEXTERNALLINKER
  366. *****************************************************************************}
  367. Constructor TExternalLinker.Create;
  368. begin
  369. inherited Create;
  370. { set generic defaults }
  371. FillChar(Info,sizeof(Info),0);
  372. if cs_link_on_target in aktglobalswitches then
  373. begin
  374. Info.ResName:=outputexedir+inputfile+'_link.res';
  375. Info.ScriptName:=outputexedir+inputfile+'_script.res';
  376. end
  377. else
  378. begin
  379. Info.ResName:='link.res';
  380. Info.ScriptName:='script.res';
  381. end;
  382. { set the linker specific defaults }
  383. SetDefaultInfo;
  384. { Allow Parameter overrides for linker info }
  385. with Info do
  386. begin
  387. if ParaLinkOptions<>'' then
  388. ExtraOptions:=ParaLinkOptions;
  389. if ParaDynamicLinker<>'' then
  390. DynamicLinker:=ParaDynamicLinker;
  391. end;
  392. end;
  393. Destructor TExternalLinker.Destroy;
  394. begin
  395. inherited destroy;
  396. end;
  397. Procedure TExternalLinker.SetDefaultInfo;
  398. begin
  399. end;
  400. Function TExternalLinker.FindUtil(const s:string):string;
  401. var
  402. Found : boolean;
  403. FoundBin : string;
  404. UtilExe : string;
  405. begin
  406. if cs_link_on_target in aktglobalswitches then
  407. begin
  408. { If linking on target, don't add any path PM }
  409. FindUtil:=AddExtension(s,target_info.exeext);
  410. exit;
  411. end;
  412. UtilExe:=AddExtension(s,source_info.exeext);
  413. FoundBin:='';
  414. Found:=false;
  415. if utilsdirectory<>'' then
  416. Found:=FindFile(utilexe,utilsdirectory,Foundbin);
  417. if (not Found) then
  418. Found:=FindExe(utilexe,Foundbin);
  419. if (not Found) and not(cs_link_extern in aktglobalswitches) then
  420. begin
  421. Message1(exec_e_util_not_found,utilexe);
  422. aktglobalswitches:=aktglobalswitches+[cs_link_extern];
  423. end;
  424. if (FoundBin<>'') then
  425. Message1(exec_t_using_util,FoundBin);
  426. FindUtil:=FoundBin;
  427. end;
  428. Function TExternalLinker.DoExec(const command:string; para:TCmdStr;showinfo,useshell:boolean):boolean;
  429. var
  430. exitcode: longint;
  431. begin
  432. DoExec:=true;
  433. if not(cs_link_extern in aktglobalswitches) then
  434. begin
  435. if useshell then
  436. exitcode := shell(maybequoted(command)+' '+para)
  437. else
  438. {$IFDEF USE_SYSUTILS}
  439. try
  440. if ExecuteProcess(command,para) <> 0
  441. then begin
  442. Message(exec_e_error_while_linking);
  443. aktglobalswitches:=aktglobalswitches+[cs_link_extern];
  444. DoExec:=false;
  445. end;
  446. except on E:EOSError do
  447. begin
  448. Message(exec_e_cant_call_linker);
  449. aktglobalswitches:=aktglobalswitches+[cs_link_extern];
  450. DoExec:=false;
  451. end;
  452. end
  453. end;
  454. {$ELSE USE_SYSUTILS}
  455. begin
  456. swapvectors;
  457. exec(command,para);
  458. swapvectors;
  459. exitcode := dosexitcode;
  460. end;
  461. if (doserror<>0) then
  462. begin
  463. Message(exec_e_cant_call_linker);
  464. aktglobalswitches:=aktglobalswitches+[cs_link_extern];
  465. DoExec:=false;
  466. end
  467. else
  468. if (exitcode<>0) then
  469. begin
  470. Message(exec_e_error_while_linking);
  471. aktglobalswitches:=aktglobalswitches+[cs_link_extern];
  472. DoExec:=false;
  473. end;
  474. end;
  475. {$ENDIF USE_SYSUTILS}
  476. { Update asmres when externmode is set }
  477. if cs_link_extern in aktglobalswitches then
  478. begin
  479. if showinfo then
  480. begin
  481. if DLLsource then
  482. AsmRes.AddLinkCommand(Command,Para,current_module.sharedlibfilename^)
  483. else
  484. AsmRes.AddLinkCommand(Command,Para,current_module.exefilename^);
  485. end
  486. else
  487. AsmRes.AddLinkCommand(Command,Para,'');
  488. end;
  489. end;
  490. Function TExternalLinker.MakeStaticLibrary:boolean;
  491. var
  492. smartpath,
  493. cmdstr : TCmdStr;
  494. binstr : string;
  495. success : boolean;
  496. begin
  497. MakeStaticLibrary:=false;
  498. { remove the library, to be sure that it is rewritten }
  499. RemoveFile(current_module.staticlibfilename^);
  500. { Call AR }
  501. smartpath:=current_module.outputpath^+FixPath(lower(current_module.modulename^)+target_info.smartext,false);
  502. SplitBinCmd(target_ar.arcmd,binstr,cmdstr);
  503. Replace(cmdstr,'$LIB',maybequoted(current_module.staticlibfilename^));
  504. Replace(cmdstr,'$FILES',FixFileName(smartpath+current_module.asmprefix^+'*'+target_info.objext));
  505. success:=DoExec(FindUtil(binstr),cmdstr,false,true);
  506. { Clean up }
  507. if not(cs_asm_leave in aktglobalswitches) then
  508. if not(cs_link_extern in aktglobalswitches) then
  509. begin
  510. while not SmartLinkOFiles.Empty do
  511. RemoveFile(SmartLinkOFiles.GetFirst);
  512. RemoveDir(smartpath);
  513. end
  514. else
  515. begin
  516. AsmRes.AddDeleteCommand(FixFileName(smartpath+current_module.asmprefix^+'*'+target_info.objext));
  517. AsmRes.Add('rmdir '+smartpath);
  518. end;
  519. MakeStaticLibrary:=success;
  520. end;
  521. {*****************************************************************************
  522. TINTERNALLINKER
  523. *****************************************************************************}
  524. Constructor TInternalLinker.Create;
  525. begin
  526. inherited Create;
  527. exemap:=nil;
  528. exeoutput:=nil;
  529. end;
  530. Destructor TInternalLinker.Destroy;
  531. begin
  532. exeoutput.free;
  533. exeoutput:=nil;
  534. inherited destroy;
  535. end;
  536. procedure TInternalLinker.readobj(const fn:string);
  537. var
  538. objdata : TAsmObjectData;
  539. objinput : tobjectinput;
  540. begin
  541. Comment(V_Info,'Reading object '+fn);
  542. objinput:=exeoutput.newobjectinput;
  543. objdata:=objinput.newobjectdata(fn);
  544. if objinput.readobjectfile(fn,objdata) then
  545. exeoutput.addobjdata(objdata);
  546. { release input object }
  547. objinput.free;
  548. end;
  549. function TInternalLinker.MakeExecutable:boolean;
  550. var
  551. s : string;
  552. begin
  553. MakeExecutable:=false;
  554. { no support yet for libraries }
  555. if (not StaticLibFiles.Empty) or
  556. (not SharedLibFiles.Empty) then
  557. internalerror(123456789);
  558. if (cs_link_map in aktglobalswitches) then
  559. exemap:=texemap.create(current_module.mapfilename^);
  560. { read objects }
  561. readobj(FindObjectFile('prt0','',false));
  562. while not ObjectFiles.Empty do
  563. begin
  564. s:=ObjectFiles.GetFirst;
  565. if s<>'' then
  566. readobj(s);
  567. end;
  568. { generate executable }
  569. exeoutput.GenerateExecutable(current_module.exefilename^);
  570. { close map }
  571. if assigned(exemap) then
  572. begin
  573. exemap.free;
  574. exemap:=nil;
  575. end;
  576. MakeExecutable:=true;
  577. end;
  578. {*****************************************************************************
  579. Init/Done
  580. *****************************************************************************}
  581. procedure InitLinker;
  582. var
  583. lk : TlinkerClass;
  584. begin
  585. if (cs_link_internal in aktglobalswitches) and
  586. assigned(target_info.link) then
  587. begin
  588. lk:=TLinkerClass(target_info.link);
  589. linker:=lk.Create;
  590. end
  591. else if assigned(target_info.linkextern) then
  592. begin
  593. lk:=TlinkerClass(target_info.linkextern);
  594. linker:=lk.Create;
  595. end
  596. else
  597. begin
  598. linker:=Tlinker.Create;
  599. end;
  600. end;
  601. procedure DoneLinker;
  602. begin
  603. if assigned(linker) then
  604. Linker.Free;
  605. end;
  606. {*****************************************************************************
  607. Initialize
  608. *****************************************************************************}
  609. const
  610. ar_gnu_ar_info : tarinfo =
  611. (
  612. id : ar_gnu_ar;
  613. arcmd : 'ar rs $LIB $FILES'
  614. );
  615. initialization
  616. RegisterAr(ar_gnu_ar_info);
  617. end.