link.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. {
  2. $Id$
  3. Copyright (c) 1998-2002 by Peter Vreman
  4. This unit handles the linker and binder calls for programs and
  5. libraries
  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 link;
  20. {$i fpcdefs.inc}
  21. interface
  22. uses
  23. cclasses,
  24. systems,
  25. fmodule;
  26. Type
  27. TLinkerInfo=record
  28. ExeCmd,
  29. DllCmd : array[1..3] of string[100];
  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,para:string;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 Delphi}
  79. dmisc,
  80. {$else Delphi}
  81. dos,
  82. {$endif Delphi}
  83. cutils,globtype,
  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. specified unit path (if specified)
  121. 2. cwd
  122. 3. unit search path
  123. 4. local object path
  124. 5. global object path
  125. 6. exepath (not when linking on target) }
  126. found:=false;
  127. if unitpath<>'' then
  128. found:=FindFile(s,unitpath,foundfile);
  129. if (not found) then
  130. found:=FindFile(s, CurDirRelPath(source_info), foundfile);
  131. if (not found) then
  132. found:=UnitSearchPath.FindFile(s,foundfile);
  133. if (not found) then
  134. found:=current_module.localobjectsearchpath.FindFile(s,foundfile);
  135. if (not found) then
  136. found:=objectsearchpath.FindFile(s,foundfile);
  137. if not(cs_link_on_target in aktglobalswitches) and (not found) then
  138. found:=FindFile(s,exepath,foundfile);
  139. if not(cs_link_extern in aktglobalswitches) and (not found) then
  140. Message1(exec_w_objfile_not_found,s);
  141. {Restore file extension}
  142. if isunit and (cs_link_on_target in aktglobalswitches) then
  143. foundfile:= ForceExtension(foundfile,target_info.objext);
  144. findobjectfile:=ScriptFixFileName(foundfile);
  145. end;
  146. { searches an library file }
  147. function FindLibraryFile(s:string;const prefix,ext:string;var foundfile : string) : boolean;
  148. var
  149. found : boolean;
  150. paths : string;
  151. begin
  152. findlibraryfile:=false;
  153. foundfile:=s;
  154. if s='' then
  155. exit;
  156. { split path from filename }
  157. paths:=SplitPath(s);
  158. s:=SplitFileName(s);
  159. { add prefix 'lib' }
  160. if (prefix<>'') and (Copy(s,1,length(prefix))<>prefix) then
  161. s:=prefix+s;
  162. { add extension }
  163. if (ext<>'') and (Copy(s,length(s)-length(ext)+1,length(ext))<>ext) then
  164. s:=s+ext;
  165. { readd the split path }
  166. s:=paths+s;
  167. if FileExists(s) then
  168. begin
  169. foundfile:=ScriptFixFileName(s);
  170. FindLibraryFile:=true;
  171. exit;
  172. end;
  173. { find libary
  174. 1. cwd
  175. 2. local libary dir
  176. 3. global libary dir
  177. 4. exe path of the compiler (not when linking on target) }
  178. found:=FindFile(s, CurDirRelPath(source_info), foundfile);
  179. if (not found) and (current_module.outputpath^<>'') then
  180. found:=FindFile(s,current_module.outputpath^,foundfile);
  181. if (not found) then
  182. found:=current_module.locallibrarysearchpath.FindFile(s,foundfile);
  183. if (not found) then
  184. found:=librarysearchpath.FindFile(s,foundfile);
  185. if not(cs_link_on_target in aktglobalswitches) and (not found) then
  186. found:=FindFile(s,exepath,foundfile);
  187. foundfile:=ScriptFixFileName(foundfile);
  188. findlibraryfile:=found;
  189. end;
  190. {*****************************************************************************
  191. TLINKER
  192. *****************************************************************************}
  193. Constructor TLinker.Create;
  194. begin
  195. Inherited Create;
  196. ObjectFiles:=TStringList.Create_no_double;
  197. SharedLibFiles:=TStringList.Create_no_double;
  198. StaticLibFiles:=TStringList.Create_no_double;
  199. end;
  200. Destructor TLinker.Destroy;
  201. begin
  202. ObjectFiles.Free;
  203. SharedLibFiles.Free;
  204. StaticLibFiles.Free;
  205. end;
  206. procedure TLinker.AddModuleFiles(hp:tmodule);
  207. var
  208. mask : longint;
  209. begin
  210. with hp do
  211. begin
  212. { link unit files }
  213. if (flags and uf_no_link)=0 then
  214. begin
  215. { create mask which unit files need linking }
  216. mask:=link_allways;
  217. { static linking ? }
  218. if (cs_link_static in aktglobalswitches) then
  219. begin
  220. if (flags and uf_static_linked)=0 then
  221. begin
  222. { if smart not avail then try static linking }
  223. if (flags and uf_smart_linked)<>0 then
  224. begin
  225. Message1(exec_t_unit_not_static_linkable_switch_to_smart,modulename^);
  226. mask:=mask or link_smart;
  227. end
  228. else
  229. Message1(exec_e_unit_not_smart_or_static_linkable,modulename^);
  230. end
  231. else
  232. mask:=mask or link_static;
  233. end;
  234. { smart linking ? }
  235. if (cs_link_smart in aktglobalswitches) then
  236. begin
  237. if (flags and uf_smart_linked)=0 then
  238. begin
  239. { if smart not avail then try static linking }
  240. if (flags and uf_static_linked)<>0 then
  241. begin
  242. Message1(exec_t_unit_not_smart_linkable_switch_to_static,modulename^);
  243. mask:=mask or link_static;
  244. end
  245. else
  246. Message1(exec_e_unit_not_smart_or_static_linkable,modulename^);
  247. end
  248. else
  249. mask:=mask or link_smart;
  250. end;
  251. { shared linking }
  252. if (cs_link_shared in aktglobalswitches) then
  253. begin
  254. if (flags and uf_shared_linked)=0 then
  255. begin
  256. { if shared not avail then try static linking }
  257. if (flags and uf_static_linked)<>0 then
  258. begin
  259. Message1(exec_t_unit_not_shared_linkable_switch_to_static,modulename^);
  260. mask:=mask or link_static;
  261. end
  262. else
  263. Message1(exec_e_unit_not_shared_or_static_linkable,modulename^);
  264. end
  265. else
  266. mask:=mask or link_shared;
  267. end;
  268. { unit files }
  269. while not linkunitofiles.empty do
  270. begin
  271. AddObject(linkunitofiles.getusemask(mask),path^,true);
  272. end;
  273. while not linkunitstaticlibs.empty do
  274. AddStaticLibrary(linkunitstaticlibs.getusemask(mask));
  275. while not linkunitsharedlibs.empty do
  276. AddSharedLibrary(linkunitsharedlibs.getusemask(mask));
  277. end;
  278. { Other needed .o and libs, specified using $L,$LINKLIB,external }
  279. mask:=link_allways;
  280. while not linkotherofiles.empty do
  281. AddObject(linkotherofiles.Getusemask(mask),path^,false);
  282. while not linkotherstaticlibs.empty do
  283. AddStaticCLibrary(linkotherstaticlibs.Getusemask(mask));
  284. while not linkothersharedlibs.empty do
  285. AddSharedCLibrary(linkothersharedlibs.Getusemask(mask));
  286. end;
  287. end;
  288. Procedure TLinker.AddObject(const S,unitpath : String;isunit:boolean);
  289. begin
  290. ObjectFiles.Concat(FindObjectFile(s,unitpath,isunit));
  291. end;
  292. Procedure TLinker.AddSharedLibrary(S:String);
  293. begin
  294. if s='' then
  295. exit;
  296. { remove prefix 'lib' }
  297. if Copy(s,1,length(target_info.sharedlibprefix))=target_info.sharedlibprefix then
  298. Delete(s,1,length(target_info.sharedlibprefix));
  299. { remove extension if any }
  300. if Copy(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext))=target_info.sharedlibext then
  301. Delete(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext)+1);
  302. { ready to be added }
  303. SharedLibFiles.Concat(S);
  304. end;
  305. Procedure TLinker.AddStaticLibrary(const S:String);
  306. var
  307. ns : string;
  308. found : boolean;
  309. begin
  310. if s='' then
  311. exit;
  312. found:=FindLibraryFile(s,target_info.staticlibprefix,target_info.staticlibext,ns);
  313. if not(cs_link_extern in aktglobalswitches) and (not found) then
  314. Message1(exec_w_libfile_not_found,s);
  315. StaticLibFiles.Concat(ns);
  316. end;
  317. Procedure TLinker.AddSharedCLibrary(S:String);
  318. begin
  319. if s='' then
  320. exit;
  321. { remove prefix 'lib' }
  322. if Copy(s,1,length(target_info.sharedclibprefix))=target_info.sharedclibprefix then
  323. Delete(s,1,length(target_info.sharedclibprefix));
  324. { remove extension if any }
  325. if Copy(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext))=target_info.sharedclibext then
  326. Delete(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext)+1);
  327. { ready to be added }
  328. SharedLibFiles.Concat(S);
  329. end;
  330. Procedure TLinker.AddStaticCLibrary(const S:String);
  331. var
  332. ns : string;
  333. found : boolean;
  334. begin
  335. if s='' then
  336. exit;
  337. found:=FindLibraryFile(s,target_info.staticclibprefix,target_info.staticclibext,ns);
  338. if not(cs_link_extern in aktglobalswitches) and (not found) then
  339. Message1(exec_w_libfile_not_found,s);
  340. StaticLibFiles.Concat(ns);
  341. end;
  342. function TLinker.MakeExecutable:boolean;
  343. begin
  344. MakeExecutable:=false;
  345. Message(exec_e_exe_not_supported);
  346. end;
  347. Function TLinker.MakeSharedLibrary:boolean;
  348. begin
  349. MakeSharedLibrary:=false;
  350. Message(exec_e_dll_not_supported);
  351. end;
  352. Function TLinker.MakeStaticLibrary:boolean;
  353. begin
  354. MakeStaticLibrary:=false;
  355. Message(exec_e_dll_not_supported);
  356. end;
  357. {*****************************************************************************
  358. TEXTERNALLINKER
  359. *****************************************************************************}
  360. Constructor TExternalLinker.Create;
  361. begin
  362. inherited Create;
  363. { set generic defaults }
  364. FillChar(Info,sizeof(Info),0);
  365. if cs_link_on_target in aktglobalswitches then
  366. begin
  367. Info.ResName:=outputexedir+inputfile+'_link.res';
  368. Info.ScriptName:=outputexedir+inputfile+'_script.res';
  369. end
  370. else
  371. begin
  372. Info.ResName:='link.res';
  373. Info.ScriptName:='script.res';
  374. end;
  375. { set the linker specific defaults }
  376. SetDefaultInfo;
  377. { Allow Parameter overrides for linker info }
  378. with Info do
  379. begin
  380. if ParaLinkOptions<>'' then
  381. ExtraOptions:=ParaLinkOptions;
  382. if ParaDynamicLinker<>'' then
  383. DynamicLinker:=ParaDynamicLinker;
  384. end;
  385. end;
  386. Destructor TExternalLinker.Destroy;
  387. begin
  388. inherited destroy;
  389. end;
  390. Procedure TExternalLinker.SetDefaultInfo;
  391. begin
  392. end;
  393. Function TExternalLinker.FindUtil(const s:string):string;
  394. var
  395. Found : boolean;
  396. FoundBin : string;
  397. UtilExe : string;
  398. begin
  399. if cs_link_on_target in aktglobalswitches then
  400. begin
  401. { If linking on target, don't add any path PM }
  402. FindUtil:=AddExtension(s,target_info.exeext);
  403. exit;
  404. end;
  405. UtilExe:=AddExtension(s,source_info.exeext);
  406. FoundBin:='';
  407. Found:=false;
  408. if utilsdirectory<>'' then
  409. Found:=FindFile(utilexe,utilsdirectory,Foundbin);
  410. if (not Found) then
  411. Found:=FindExe(utilexe,Foundbin);
  412. if (not Found) and not(cs_link_extern in aktglobalswitches) then
  413. begin
  414. Message1(exec_e_util_not_found,utilexe);
  415. aktglobalswitches:=aktglobalswitches+[cs_link_extern];
  416. end;
  417. if (FoundBin<>'') then
  418. Message1(exec_t_using_util,FoundBin);
  419. FindUtil:=FoundBin;
  420. end;
  421. Function TExternalLinker.DoExec(const command,para:string;showinfo,useshell:boolean):boolean;
  422. var
  423. exitcode: longint;
  424. begin
  425. DoExec:=true;
  426. if not(cs_link_extern in aktglobalswitches) then
  427. begin
  428. if useshell then
  429. exitcode := shell(maybequoted(command)+' '+para)
  430. else
  431. begin
  432. swapvectors;
  433. exec(command,para);
  434. swapvectors;
  435. exitcode := dosexitcode;
  436. end;
  437. if (doserror<>0) then
  438. begin
  439. Message(exec_e_cant_call_linker);
  440. aktglobalswitches:=aktglobalswitches+[cs_link_extern];
  441. DoExec:=false;
  442. end
  443. else
  444. if (exitcode<>0) then
  445. begin
  446. Message(exec_e_error_while_linking);
  447. aktglobalswitches:=aktglobalswitches+[cs_link_extern];
  448. DoExec:=false;
  449. end;
  450. end;
  451. { Update asmres when externmode is set }
  452. if cs_link_extern in aktglobalswitches then
  453. begin
  454. if showinfo then
  455. begin
  456. if DLLsource then
  457. AsmRes.AddLinkCommand(Command,Para,current_module.sharedlibfilename^)
  458. else
  459. AsmRes.AddLinkCommand(Command,Para,current_module.exefilename^);
  460. end
  461. else
  462. AsmRes.AddLinkCommand(Command,Para,'');
  463. end;
  464. end;
  465. Function TExternalLinker.MakeStaticLibrary:boolean;
  466. var
  467. smartpath,
  468. cmdstr,
  469. binstr : string;
  470. success : boolean;
  471. begin
  472. MakeStaticLibrary:=false;
  473. { remove the library, to be sure that it is rewritten }
  474. RemoveFile(current_module.staticlibfilename^);
  475. { Call AR }
  476. smartpath:=current_module.outputpath^+FixPath(lower(current_module.modulename^)+target_info.smartext,false);
  477. SplitBinCmd(target_ar.arcmd,binstr,cmdstr);
  478. Replace(cmdstr,'$LIB',current_module.staticlibfilename^);
  479. Replace(cmdstr,'$FILES',FixFileName(smartpath+current_module.asmprefix^+'*'+target_info.objext));
  480. success:=DoExec(FindUtil(binstr),cmdstr,false,true);
  481. { Clean up }
  482. if not(cs_asm_leave in aktglobalswitches) then
  483. if not(cs_link_extern in aktglobalswitches) then
  484. begin
  485. while not SmartLinkOFiles.Empty do
  486. RemoveFile(SmartLinkOFiles.GetFirst);
  487. RemoveDir(smartpath);
  488. end
  489. else
  490. begin
  491. AsmRes.AddDeleteCommand(FixFileName(smartpath+current_module.asmprefix^+'*'+target_info.objext));
  492. AsmRes.Add('rmdir '+smartpath);
  493. end;
  494. MakeStaticLibrary:=success;
  495. end;
  496. {*****************************************************************************
  497. TINTERNALLINKER
  498. *****************************************************************************}
  499. Constructor TInternalLinker.Create;
  500. begin
  501. inherited Create;
  502. exemap:=nil;
  503. exeoutput:=nil;
  504. end;
  505. Destructor TInternalLinker.Destroy;
  506. begin
  507. exeoutput.free;
  508. exeoutput:=nil;
  509. inherited destroy;
  510. end;
  511. procedure TInternalLinker.readobj(const fn:string);
  512. var
  513. objdata : TAsmObjectData;
  514. objinput : tobjectinput;
  515. begin
  516. Comment(V_Info,'Reading object '+fn);
  517. objinput:=exeoutput.newobjectinput;
  518. objdata:=objinput.newobjectdata(fn);
  519. if objinput.readobjectfile(fn,objdata) then
  520. exeoutput.addobjdata(objdata);
  521. { release input object }
  522. objinput.free;
  523. end;
  524. function TInternalLinker.MakeExecutable:boolean;
  525. var
  526. s : string;
  527. begin
  528. MakeExecutable:=false;
  529. { no support yet for libraries }
  530. if (not StaticLibFiles.Empty) or
  531. (not SharedLibFiles.Empty) then
  532. internalerror(123456789);
  533. if (cs_link_map in aktglobalswitches) then
  534. exemap:=texemap.create(current_module.mapfilename^);
  535. { read objects }
  536. readobj(FindObjectFile('prt0','',false));
  537. while not ObjectFiles.Empty do
  538. begin
  539. s:=ObjectFiles.GetFirst;
  540. if s<>'' then
  541. readobj(s);
  542. end;
  543. { generate executable }
  544. exeoutput.GenerateExecutable(current_module.exefilename^);
  545. { close map }
  546. if assigned(exemap) then
  547. begin
  548. exemap.free;
  549. exemap:=nil;
  550. end;
  551. MakeExecutable:=true;
  552. end;
  553. {*****************************************************************************
  554. Init/Done
  555. *****************************************************************************}
  556. procedure InitLinker;
  557. var
  558. lk : TlinkerClass;
  559. begin
  560. if (cs_link_internal in aktglobalswitches) and
  561. assigned(target_info.link) then
  562. begin
  563. lk:=TLinkerClass(target_info.link);
  564. linker:=lk.Create;
  565. end
  566. else if assigned(target_info.linkextern) then
  567. begin
  568. lk:=TlinkerClass(target_info.linkextern);
  569. linker:=lk.Create;
  570. end
  571. else
  572. begin
  573. linker:=Tlinker.Create;
  574. end;
  575. end;
  576. procedure DoneLinker;
  577. begin
  578. if assigned(linker) then
  579. Linker.Free;
  580. end;
  581. {*****************************************************************************
  582. Initialize
  583. *****************************************************************************}
  584. const
  585. ar_gnu_ar_info : tarinfo =
  586. (
  587. id : ar_gnu_ar;
  588. arcmd : 'ar rs $LIB $FILES'
  589. );
  590. initialization
  591. RegisterAr(ar_gnu_ar_info);
  592. end.
  593. {
  594. $Log$
  595. Revision 1.44 2004-10-09 11:37:09 olle
  596. * Exchanged hardcoded "./" to CurDirRelPath
  597. * In FindObjectFile, when link on target, special handling is now
  598. only done for units.
  599. Revision 1.43 2004/07/17 15:51:57 jonas
  600. * shell now returns an exitcode
  601. * print an error if linking failed when linking was done using a script
  602. Revision 1.42 2004/06/20 08:55:29 florian
  603. * logs truncated
  604. Revision 1.41 2004/02/24 00:53:49 olle
  605. * increased maxsize of link.res file name
  606. * fixed a 255-limit in TScript.WriteToDisk
  607. Revision 1.40 2004/02/19 20:40:15 olle
  608. + Support for Link on target especially for MacOS
  609. + TLinkerMPW
  610. + TAsmScriptMPW
  611. }