link.pas 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  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. sysutils,
  23. cclasses,
  24. systems,
  25. fmodule,
  26. globtype,
  27. ogbase;
  28. Type
  29. TLinkerInfo=record
  30. ExeCmd,
  31. DllCmd,
  32. ExtDbgCmd : array[1..3] of string;
  33. ResName : string[100];
  34. ScriptName : string[100];
  35. ExtraOptions : TCmdStr;
  36. DynamicLinker : string[100];
  37. end;
  38. TLinker = class(TAbstractLinker)
  39. public
  40. HasResources,
  41. HasExports : boolean;
  42. SysInitUnit : string[20];
  43. ObjectFiles,
  44. SharedLibFiles,
  45. StaticLibFiles,
  46. FrameworkFiles : TCmdStrList;
  47. Constructor Create;virtual;
  48. Destructor Destroy;override;
  49. procedure AddModuleFiles(hp:tmodule);
  50. Procedure AddObject(const S,unitpath : TCmdStr;isunit:boolean);
  51. Procedure AddStaticLibrary(const S : TCmdStr);
  52. Procedure AddSharedLibrary(S : TCmdStr);
  53. Procedure AddStaticCLibrary(const S : TCmdStr);
  54. Procedure AddSharedCLibrary(S : TCmdStr);
  55. Procedure AddFramework(S : TCmdStr);
  56. procedure AddImportSymbol(const libname,symname:TCmdStr;OrdNr: longint;isvar:boolean);virtual;
  57. Procedure InitSysInitUnitName;virtual;
  58. Function MakeExecutable:boolean;virtual;
  59. Function MakeSharedLibrary:boolean;virtual;
  60. Function MakeStaticLibrary:boolean;virtual;
  61. procedure ExpandAndApplyOrder(var Src:TCmdStrList);
  62. procedure LoadPredefinedLibraryOrder;virtual;
  63. function ReOrderEntries : boolean;
  64. end;
  65. TExternalLinker = class(TLinker)
  66. public
  67. Info : TLinkerInfo;
  68. Constructor Create;override;
  69. Destructor Destroy;override;
  70. Function FindUtil(const s:TCmdStr):TCmdStr;
  71. Function DoExec(const command:TCmdStr; para:TCmdStr;showinfo,useshell:boolean):boolean;
  72. procedure SetDefaultInfo;virtual;
  73. Function MakeStaticLibrary:boolean;override;
  74. end;
  75. TInternalLinker = class(TLinker)
  76. private
  77. FCExeOutput : TExeOutputClass;
  78. FCObjInput : TObjInputClass;
  79. { Libraries }
  80. FStaticLibraryList : TFPHashObjectList;
  81. FImportLibraryList : TFPHashObjectList;
  82. procedure Load_ReadObject(const para:TCmdStr);
  83. procedure Load_ReadStaticLibrary(const para:TCmdStr);
  84. procedure ParseScript_Load;
  85. procedure ParseScript_Order;
  86. procedure ParseScript_MemPos;
  87. procedure ParseScript_DataPos;
  88. procedure PrintLinkerScript;
  89. function RunLinkScript(const outputname:TCmdStr):boolean;
  90. protected
  91. linkscript : TCmdStrList;
  92. property CObjInput:TObjInputClass read FCObjInput write FCObjInput;
  93. property CExeOutput:TExeOutputClass read FCExeOutput write FCExeOutput;
  94. property StaticLibraryList:TFPHashObjectList read FStaticLibraryList;
  95. property ImportLibraryList:TFPHashObjectList read FImportLibraryList;
  96. procedure DefaultLinkScript;virtual;abstract;
  97. public
  98. IsSharedLibrary : boolean;
  99. Constructor Create;override;
  100. Destructor Destroy;override;
  101. Function MakeExecutable:boolean;override;
  102. Function MakeSharedLibrary:boolean;override;
  103. procedure AddImportSymbol(const libname,symname:TCmdStr;OrdNr: longint;isvar:boolean);override;
  104. end;
  105. var
  106. Linker : TLinker;
  107. function FindObjectFile(s : TCmdStr;const unitpath:TCmdStr;isunit:boolean) : TCmdStr;
  108. function FindLibraryFile(s:TCmdStr;const prefix,ext:TCmdStr;var foundfile : TCmdStr) : boolean;
  109. function FindDLL(const s:TCmdStr;var founddll:TCmdStr):boolean;
  110. procedure InitLinker;
  111. procedure DoneLinker;
  112. Implementation
  113. uses
  114. cutils,cfileutl,cstreams,
  115. script,globals,verbose,comphook,ppu,fpccrc,
  116. aasmbase,aasmtai,aasmdata,aasmcpu,
  117. owbase,owar,ogmap;
  118. type
  119. TLinkerClass = class of Tlinker;
  120. {*****************************************************************************
  121. Helpers
  122. *****************************************************************************}
  123. function GetFileCRC(const fn:string):cardinal;
  124. var
  125. fs : TCStream;
  126. bufcount,
  127. bufsize : Integer;
  128. buf : pbyte;
  129. begin
  130. result:=0;
  131. bufsize:=64*1024;
  132. fs:=TCFileStream.Create(fn,fmOpenRead or fmShareDenyNone);
  133. if CStreamError<>0 then
  134. begin
  135. fs.Free;
  136. Comment(V_Error,'Can''t open file: '+fn);
  137. exit;
  138. end;
  139. getmem(buf,bufsize);
  140. repeat
  141. bufcount:=fs.Read(buf^,bufsize);
  142. result:=UpdateCrc32(result,buf^,bufcount);
  143. until bufcount<bufsize;
  144. freemem(buf);
  145. fs.Free;
  146. end;
  147. { searches an object file }
  148. function FindObjectFile(s:TCmdStr;const unitpath:TCmdStr;isunit:boolean) : TCmdStr;
  149. var
  150. found : boolean;
  151. foundfile : TCmdStr;
  152. begin
  153. findobjectfile:='';
  154. if s='' then
  155. exit;
  156. {When linking on target, the units has not been assembled yet,
  157. so there is no object files to look for at
  158. the host. Look for the corresponding assembler file instead,
  159. because it will be assembled to object file on the target.}
  160. if isunit and (cs_link_on_target in current_settings.globalswitches) then
  161. s:=ChangeFileExt(s,target_info.asmext);
  162. { when it does not belong to the unit then check if
  163. the specified file exists without searching any paths }
  164. if not isunit then
  165. begin
  166. if FileExists(FixFileName(s),false) then
  167. begin
  168. foundfile:=ScriptFixFileName(s);
  169. found:=true;
  170. end;
  171. end;
  172. if pos('.',s)=0 then
  173. s:=s+target_info.objext;
  174. { find object file
  175. 1. output unit path
  176. 2. output exe path
  177. 3. specified unit path (if specified)
  178. 4. cwd
  179. 5. unit search path
  180. 6. local object path
  181. 7. global object path
  182. 8. exepath (not when linking on target)
  183. for all finds don't use the directory caching }
  184. found:=false;
  185. if isunit and (OutputUnitDir<>'') then
  186. found:=FindFile(s,OutPutUnitDir,false,foundfile)
  187. else
  188. if OutputExeDir<>'' then
  189. found:=FindFile(s,OutPutExeDir,false,foundfile);
  190. if (not found) and (unitpath<>'') then
  191. found:=FindFile(s,unitpath,false,foundfile);
  192. if (not found) then
  193. found:=FindFile(s, CurDirRelPath(source_info),false,foundfile);
  194. if (not found) then
  195. found:=UnitSearchPath.FindFile(s,false,foundfile);
  196. if (not found) then
  197. found:=current_module.localobjectsearchpath.FindFile(s,false,foundfile);
  198. if (not found) then
  199. found:=objectsearchpath.FindFile(s,false,foundfile);
  200. if not(cs_link_on_target in current_settings.globalswitches) and (not found) then
  201. found:=FindFile(s,exepath,false,foundfile);
  202. if not(cs_link_nolink in current_settings.globalswitches) and (not found) then
  203. Message1(exec_w_objfile_not_found,s);
  204. {Restore file extension}
  205. if isunit and (cs_link_on_target in current_settings.globalswitches) then
  206. foundfile:= ChangeFileExt(foundfile,target_info.objext);
  207. findobjectfile:=ScriptFixFileName(foundfile);
  208. end;
  209. { searches a (windows) DLL file }
  210. function FindDLL(const s:TCmdStr;var founddll:TCmdStr):boolean;
  211. var
  212. sysdir : TCmdStr;
  213. Found : boolean;
  214. begin
  215. Found:=false;
  216. { Look for DLL in:
  217. 1. Current dir
  218. 2. Library Path
  219. 3. windir,windir/system,windir/system32 }
  220. Found:=FindFile(s,'.'+source_info.DirSep,false,founddll);
  221. if (not found) then
  222. Found:=librarysearchpath.FindFile(s,false,founddll);
  223. if (not found) then
  224. begin
  225. sysdir:=FixPath(GetEnvironmentVariable('windir'),false);
  226. Found:=FindFile(s,sysdir+';'+sysdir+'system'+source_info.DirSep+';'+sysdir+'system32'+source_info.DirSep,false,founddll);
  227. end;
  228. if (not found) then
  229. begin
  230. message1(exec_w_libfile_not_found,s);
  231. FoundDll:=s;
  232. end;
  233. FindDll:=Found;
  234. end;
  235. { searches an library file }
  236. function FindLibraryFile(s:TCmdStr;const prefix,ext:TCmdStr;var foundfile : TCmdStr) : boolean;
  237. var
  238. found : boolean;
  239. paths : TCmdStr;
  240. begin
  241. findlibraryfile:=false;
  242. foundfile:=s;
  243. if s='' then
  244. exit;
  245. { split path from filename }
  246. paths:=ExtractFilePath(s);
  247. s:=ExtractFileName(s);
  248. { add prefix 'lib' }
  249. if (prefix<>'') and (Copy(s,1,length(prefix))<>prefix) then
  250. s:=prefix+s;
  251. { add extension }
  252. if (ext<>'') and (Copy(s,length(s)-length(ext)+1,length(ext))<>ext) then
  253. s:=s+ext;
  254. { readd the split path }
  255. s:=paths+s;
  256. if FileExists(s,false) then
  257. begin
  258. foundfile:=ScriptFixFileName(s);
  259. FindLibraryFile:=true;
  260. exit;
  261. end;
  262. { find libary
  263. 1. cwd
  264. 2. local libary dir
  265. 3. global libary dir
  266. 4. exe path of the compiler (not when linking on target)
  267. for all searches don't use the directory cache }
  268. found:=FindFile(s, CurDirRelPath(source_info), false,foundfile);
  269. if (not found) and (current_module.outputpath^<>'') then
  270. found:=FindFile(s,current_module.outputpath^,false,foundfile);
  271. if (not found) then
  272. found:=current_module.locallibrarysearchpath.FindFile(s,false,foundfile);
  273. if (not found) then
  274. found:=librarysearchpath.FindFile(s,false,foundfile);
  275. if not(cs_link_on_target in current_settings.globalswitches) and (not found) then
  276. found:=FindFile(s,exepath,false,foundfile);
  277. foundfile:=ScriptFixFileName(foundfile);
  278. findlibraryfile:=found;
  279. end;
  280. {*****************************************************************************
  281. TLINKER
  282. *****************************************************************************}
  283. Constructor TLinker.Create;
  284. begin
  285. Inherited Create;
  286. ObjectFiles:=TCmdStrList.Create_no_double;
  287. SharedLibFiles:=TCmdStrList.Create_no_double;
  288. StaticLibFiles:=TCmdStrList.Create_no_double;
  289. FrameworkFiles:=TCmdStrList.Create_no_double;
  290. end;
  291. Destructor TLinker.Destroy;
  292. begin
  293. ObjectFiles.Free;
  294. SharedLibFiles.Free;
  295. StaticLibFiles.Free;
  296. FrameworkFiles.Free;
  297. end;
  298. procedure TLinker.AddModuleFiles(hp:tmodule);
  299. var
  300. mask : longint;
  301. i,j : longint;
  302. ImportLibrary : TImportLibrary;
  303. ImportSymbol : TImportSymbol;
  304. begin
  305. with hp do
  306. begin
  307. if (flags and uf_has_resourcefiles)<>0 then
  308. HasResources:=true;
  309. if (flags and uf_has_exports)<>0 then
  310. HasExports:=true;
  311. { link unit files }
  312. if (flags and uf_no_link)=0 then
  313. begin
  314. { create mask which unit files need linking }
  315. mask:=link_always;
  316. { static linking ? }
  317. if (cs_link_static in current_settings.globalswitches) then
  318. begin
  319. if (flags and uf_static_linked)=0 then
  320. begin
  321. { if smart not avail then try static linking }
  322. if (flags and uf_smart_linked)<>0 then
  323. begin
  324. Message1(exec_t_unit_not_static_linkable_switch_to_smart,modulename^);
  325. mask:=mask or link_smart;
  326. end
  327. else
  328. Message1(exec_e_unit_not_smart_or_static_linkable,modulename^);
  329. end
  330. else
  331. mask:=mask or link_static;
  332. end;
  333. { smart linking ? }
  334. if (cs_link_smart in current_settings.globalswitches) then
  335. begin
  336. if (flags and uf_smart_linked)=0 then
  337. begin
  338. { if smart not avail then try static linking }
  339. if (flags and uf_static_linked)<>0 then
  340. begin
  341. { if not create_smartlink_library, then smart linking happens using the
  342. regular object files
  343. }
  344. if create_smartlink_library then
  345. Message1(exec_t_unit_not_smart_linkable_switch_to_static,modulename^);
  346. mask:=mask or link_static;
  347. end
  348. else
  349. Message1(exec_e_unit_not_smart_or_static_linkable,modulename^);
  350. end
  351. else
  352. mask:=mask or link_smart;
  353. end;
  354. { shared linking }
  355. if (cs_link_shared in current_settings.globalswitches) then
  356. begin
  357. if (flags and uf_shared_linked)=0 then
  358. begin
  359. { if shared not avail then try static linking }
  360. if (flags and uf_static_linked)<>0 then
  361. begin
  362. Message1(exec_t_unit_not_shared_linkable_switch_to_static,modulename^);
  363. mask:=mask or link_static;
  364. end
  365. else
  366. Message1(exec_e_unit_not_shared_or_static_linkable,modulename^);
  367. end
  368. else
  369. mask:=mask or link_shared;
  370. end;
  371. { unit files }
  372. while not linkunitofiles.empty do
  373. AddObject(linkunitofiles.getusemask(mask),path^,true);
  374. while not linkunitstaticlibs.empty do
  375. AddStaticLibrary(linkunitstaticlibs.getusemask(mask));
  376. while not linkunitsharedlibs.empty do
  377. AddSharedLibrary(linkunitsharedlibs.getusemask(mask));
  378. end;
  379. { Other needed .o and libs, specified using $L,$LINKLIB,external }
  380. mask:=link_always;
  381. while not linkotherofiles.empty do
  382. AddObject(linkotherofiles.Getusemask(mask),path^,false);
  383. while not linkotherstaticlibs.empty do
  384. AddStaticCLibrary(linkotherstaticlibs.Getusemask(mask));
  385. while not linkothersharedlibs.empty do
  386. AddSharedCLibrary(linkothersharedlibs.Getusemask(mask));
  387. while not linkotherframeworks.empty do
  388. AddFramework(linkotherframeworks.Getusemask(mask));
  389. { Known Library/DLL Imports }
  390. for i:=0 to ImportLibraryList.Count-1 do
  391. begin
  392. ImportLibrary:=TImportLibrary(ImportLibraryList[i]);
  393. for j:=0 to ImportLibrary.ImportSymbolList.Count-1 do
  394. begin
  395. ImportSymbol:=TImportSymbol(ImportLibrary.ImportSymbolList[j]);
  396. AddImportSymbol(ImportLibrary.Name,ImportSymbol.Name,ImportSymbol.OrdNr,ImportSymbol.IsVar);
  397. end;
  398. end;
  399. end;
  400. end;
  401. procedure TLinker.AddImportSymbol(const libname,symname:TCmdStr;OrdNr: longint;isvar:boolean);
  402. begin
  403. end;
  404. Procedure TLinker.AddObject(const S,unitpath : TCmdStr;isunit:boolean);
  405. begin
  406. ObjectFiles.Concat(FindObjectFile(s,unitpath,isunit));
  407. end;
  408. Procedure TLinker.AddSharedLibrary(S:TCmdStr);
  409. begin
  410. if s='' then
  411. exit;
  412. { remove prefix 'lib' }
  413. if Copy(s,1,length(target_info.sharedlibprefix))=target_info.sharedlibprefix then
  414. Delete(s,1,length(target_info.sharedlibprefix));
  415. { remove extension if any }
  416. if Copy(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext))=target_info.sharedlibext then
  417. Delete(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext)+1);
  418. { ready to be added }
  419. SharedLibFiles.Concat(S);
  420. end;
  421. Procedure TLinker.AddStaticLibrary(const S:TCmdStr);
  422. var
  423. ns : TCmdStr;
  424. found : boolean;
  425. begin
  426. if s='' then
  427. exit;
  428. found:=FindLibraryFile(s,target_info.staticlibprefix,target_info.staticlibext,ns);
  429. if not(cs_link_nolink in current_settings.globalswitches) and (not found) then
  430. Message1(exec_w_libfile_not_found,s);
  431. StaticLibFiles.Concat(ns);
  432. end;
  433. Procedure TLinker.AddSharedCLibrary(S:TCmdStr);
  434. begin
  435. if s='' then
  436. exit;
  437. { remove prefix 'lib' }
  438. if Copy(s,1,length(target_info.sharedclibprefix))=target_info.sharedclibprefix then
  439. Delete(s,1,length(target_info.sharedclibprefix));
  440. { remove extension if any }
  441. if Copy(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext))=target_info.sharedclibext then
  442. Delete(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext)+1);
  443. { ready to be added }
  444. SharedLibFiles.Concat(S);
  445. end;
  446. Procedure TLinker.AddFramework(S:TCmdStr);
  447. begin
  448. if s='' then
  449. exit;
  450. { ready to be added }
  451. FrameworkFiles.Concat(S);
  452. end;
  453. Procedure TLinker.AddStaticCLibrary(const S:TCmdStr);
  454. var
  455. ns : TCmdStr;
  456. found : boolean;
  457. begin
  458. if s='' then
  459. exit;
  460. found:=FindLibraryFile(s,target_info.staticclibprefix,target_info.staticclibext,ns);
  461. if not(cs_link_nolink in current_settings.globalswitches) and (not found) then
  462. Message1(exec_w_libfile_not_found,s);
  463. StaticLibFiles.Concat(ns);
  464. end;
  465. procedure AddImportSymbol(const libname,symname:TCmdStr;OrdNr: longint;isvar:boolean);
  466. begin
  467. end;
  468. procedure TLinker.InitSysInitUnitName;
  469. begin
  470. end;
  471. function TLinker.MakeExecutable:boolean;
  472. begin
  473. MakeExecutable:=false;
  474. Message(exec_e_exe_not_supported);
  475. end;
  476. Function TLinker.MakeSharedLibrary:boolean;
  477. begin
  478. MakeSharedLibrary:=false;
  479. Message(exec_e_dll_not_supported);
  480. end;
  481. Function TLinker.MakeStaticLibrary:boolean;
  482. begin
  483. MakeStaticLibrary:=false;
  484. Message(exec_e_dll_not_supported);
  485. end;
  486. Procedure TLinker.ExpandAndApplyOrder(var Src:TCmdStrList);
  487. var
  488. p : TLinkStrMap;
  489. i : longint;
  490. begin
  491. // call Virtual TLinker method to initialize
  492. LoadPredefinedLibraryOrder;
  493. // something to do?
  494. if (LinkLibraryAliases.count=0) and (LinkLibraryOrder.Count=0) Then
  495. exit;
  496. p:=TLinkStrMap.Create;
  497. // expand libaliases, clears src
  498. LinkLibraryAliases.expand(src,p);
  499. // writeln(src.count,' ',p.count,' ',linklibraryorder.count,' ',linklibraryaliases.count);
  500. // apply order
  501. p.UpdateWeights(LinkLibraryOrder);
  502. p.SortOnWeight;
  503. // put back in src
  504. for i:=0 to p.count-1 do
  505. src.insert(p[i].Key);
  506. p.free;
  507. end;
  508. procedure TLinker.LoadPredefinedLibraryOrder;
  509. begin
  510. end;
  511. function TLinker.ReOrderEntries : boolean;
  512. begin
  513. result:=(LinkLibraryOrder.count>0) or (LinkLibraryAliases.count>0);
  514. end;
  515. {*****************************************************************************
  516. TEXTERNALLINKER
  517. *****************************************************************************}
  518. Constructor TExternalLinker.Create;
  519. begin
  520. inherited Create;
  521. { set generic defaults }
  522. FillChar(Info,sizeof(Info),0);
  523. if cs_link_on_target in current_settings.globalswitches then
  524. begin
  525. Info.ResName:=outputexedir+ChangeFileExt(inputfilename,'_link.res');
  526. Info.ScriptName:=outputexedir+ChangeFileExt(inputfilename,'_script.res');
  527. end
  528. else
  529. begin
  530. Info.ResName:='link.res';
  531. Info.ScriptName:='script.res';
  532. end;
  533. { set the linker specific defaults }
  534. SetDefaultInfo;
  535. { Allow Parameter overrides for linker info }
  536. with Info do
  537. begin
  538. if ParaLinkOptions<>'' then
  539. ExtraOptions:=ParaLinkOptions;
  540. if ParaDynamicLinker<>'' then
  541. DynamicLinker:=ParaDynamicLinker;
  542. end;
  543. end;
  544. Destructor TExternalLinker.Destroy;
  545. begin
  546. inherited destroy;
  547. end;
  548. Procedure TExternalLinker.SetDefaultInfo;
  549. begin
  550. end;
  551. Function TExternalLinker.FindUtil(const s:TCmdStr):TCmdStr;
  552. var
  553. Found : boolean;
  554. FoundBin : TCmdStr;
  555. UtilExe : TCmdStr;
  556. begin
  557. if cs_link_on_target in current_settings.globalswitches then
  558. begin
  559. { If linking on target, don't add any path PM }
  560. FindUtil:=ChangeFileExt(s,target_info.exeext);
  561. exit;
  562. end;
  563. UtilExe:=ChangeFileExt(s,source_info.exeext);
  564. FoundBin:='';
  565. Found:=false;
  566. if utilsdirectory<>'' then
  567. Found:=FindFile(utilexe,utilsdirectory,false,Foundbin);
  568. if (not Found) then
  569. Found:=FindExe(utilexe,false,Foundbin);
  570. if (not Found) and not(cs_link_nolink in current_settings.globalswitches) then
  571. begin
  572. Message1(exec_e_util_not_found,utilexe);
  573. current_settings.globalswitches:=current_settings.globalswitches+[cs_link_nolink];
  574. end;
  575. if (FoundBin<>'') then
  576. Message1(exec_t_using_util,FoundBin);
  577. FindUtil:=FoundBin;
  578. end;
  579. Function TExternalLinker.DoExec(const command:TCmdStr; para:TCmdStr;showinfo,useshell:boolean):boolean;
  580. var
  581. exitcode: longint;
  582. begin
  583. DoExec:=true;
  584. if not(cs_link_nolink in current_settings.globalswitches) then
  585. begin
  586. FlushOutput;
  587. if useshell then
  588. exitcode:=shell(maybequoted(command)+' '+para)
  589. else
  590. try
  591. exitcode:=ExecuteProcess(command,para);
  592. except on E:EOSError do
  593. begin
  594. Message(exec_e_cant_call_linker);
  595. current_settings.globalswitches:=current_settings.globalswitches+[cs_link_nolink];
  596. DoExec:=false;
  597. end;
  598. end;
  599. if (exitcode<>0) then
  600. begin
  601. Message(exec_e_error_while_linking);
  602. current_settings.globalswitches:=current_settings.globalswitches+[cs_link_nolink];
  603. DoExec:=false;
  604. end;
  605. end;
  606. { Update asmres when externmode is set }
  607. if cs_link_nolink in current_settings.globalswitches then
  608. begin
  609. if showinfo then
  610. begin
  611. if DLLsource then
  612. AsmRes.AddLinkCommand(Command,Para,current_module.sharedlibfilename^)
  613. else
  614. AsmRes.AddLinkCommand(Command,Para,current_module.exefilename^);
  615. end
  616. else
  617. AsmRes.AddLinkCommand(Command,Para,'');
  618. end;
  619. end;
  620. Function TExternalLinker.MakeStaticLibrary:boolean;
  621. function GetNextFiles(const maxCmdLength : AInt; var item : TCmdStrListItem) : TCmdStr;
  622. begin
  623. result := '';
  624. while (assigned(item) and ((length(result) + length(item.str) + 1) < maxCmdLength)) do begin
  625. result := result + ' ' + item.str;
  626. item := TCmdStrListItem(item.next);
  627. end;
  628. end;
  629. var
  630. binstr, scriptfile : TCmdStr;
  631. cmdstr, nextcmd, smartpath : TCmdStr;
  632. current : TCmdStrListItem;
  633. script: Text;
  634. scripted_ar : boolean;
  635. success : boolean;
  636. begin
  637. MakeStaticLibrary:=false;
  638. { remove the library, to be sure that it is rewritten }
  639. DeleteFile(current_module.staticlibfilename^);
  640. { Call AR }
  641. smartpath:=FixPath(ChangeFileExt(current_module.asmfilename^,target_info.smartext),false);
  642. SplitBinCmd(target_ar.arcmd,binstr,cmdstr);
  643. binstr := FindUtil(utilsprefix + binstr);
  644. scripted_ar:=target_ar.id=ar_gnu_ar_scripted;
  645. if scripted_ar then
  646. begin
  647. scriptfile := FixFileName(smartpath+'arscript.txt');
  648. Replace(cmdstr,'$SCRIPT',maybequoted(scriptfile));
  649. Assign(script, scriptfile);
  650. Rewrite(script);
  651. try
  652. writeln(script, 'CREATE ' + current_module.staticlibfilename^);
  653. current := TCmdStrListItem(SmartLinkOFiles.First);
  654. while current <> nil do
  655. begin
  656. writeln(script, 'ADDMOD ' + current.str);
  657. current := TCmdStrListItem(current.next);
  658. end;
  659. writeln(script, 'SAVE');
  660. writeln(script, 'END');
  661. finally
  662. Close(script);
  663. end;
  664. success:=DoExec(binstr,cmdstr,false,true);
  665. end
  666. else
  667. begin
  668. Replace(cmdstr,'$LIB',maybequoted(current_module.staticlibfilename^));
  669. { create AR commands }
  670. success := true;
  671. current := TCmdStrListItem(SmartLinkOFiles.First);
  672. repeat
  673. nextcmd := cmdstr;
  674. Replace(nextcmd,'$FILES',GetNextFiles(2047, current));
  675. success:=DoExec(binstr,nextcmd,false,true);
  676. until (not assigned(current)) or (not success);
  677. end;
  678. if (target_ar.arfinishcmd <> '') then
  679. begin
  680. SplitBinCmd(target_ar.arfinishcmd,binstr,cmdstr);
  681. binstr := FindUtil(utilsprefix + binstr);
  682. Replace(cmdstr,'$LIB',maybequoted(current_module.staticlibfilename^));
  683. success:=DoExec(binstr,cmdstr,false,true);
  684. end;
  685. { Clean up }
  686. if not(cs_asm_leave in current_settings.globalswitches) then
  687. if not(cs_link_nolink in current_settings.globalswitches) then
  688. begin
  689. while not SmartLinkOFiles.Empty do
  690. DeleteFile(SmartLinkOFiles.GetFirst);
  691. if scripted_ar then
  692. DeleteFile(scriptfile);
  693. RemoveDir(smartpath);
  694. end
  695. else
  696. begin
  697. AsmRes.AddDeleteCommand(FixFileName(smartpath+current_module.asmprefix^+'*'+target_info.objext));
  698. if scripted_ar then
  699. AsmRes.AddDeleteCommand(scriptfile);
  700. AsmRes.AddDeleteDirCommand(smartpath);
  701. end;
  702. MakeStaticLibrary:=success;
  703. end;
  704. {*****************************************************************************
  705. TINTERNALLINKER
  706. *****************************************************************************}
  707. Constructor TInternalLinker.Create;
  708. begin
  709. inherited Create;
  710. linkscript:=TCmdStrList.Create;
  711. FStaticLibraryList:=TFPHashObjectList.Create(true);
  712. FImportLibraryList:=TFPHashObjectList.Create(true);
  713. exemap:=nil;
  714. exeoutput:=nil;
  715. CObjInput:=TObjInput;
  716. end;
  717. Destructor TInternalLinker.Destroy;
  718. begin
  719. linkscript.free;
  720. StaticLibraryList.Free;
  721. ImportLibraryList.Free;
  722. if assigned(exeoutput) then
  723. begin
  724. exeoutput.free;
  725. exeoutput:=nil;
  726. end;
  727. if assigned(exemap) then
  728. begin
  729. exemap.free;
  730. exemap:=nil;
  731. end;
  732. inherited destroy;
  733. end;
  734. procedure TInternalLinker.AddImportSymbol(const libname,symname:TCmdStr;OrdNr: longint;isvar:boolean);
  735. var
  736. ImportLibrary : TImportLibrary;
  737. ImportSymbol : TFPHashObject;
  738. begin
  739. ImportLibrary:=TImportLibrary(ImportLibraryList.Find(libname));
  740. if not assigned(ImportLibrary) then
  741. ImportLibrary:=TImportLibrary.Create(ImportLibraryList,libname);
  742. ImportSymbol:=TFPHashObject(ImportLibrary.ImportSymbolList.Find(symname));
  743. if not assigned(ImportSymbol) then
  744. ImportSymbol:=TImportSymbol.Create(ImportLibrary.ImportSymbolList,symname,OrdNr,isvar);
  745. end;
  746. procedure TInternalLinker.Load_ReadObject(const para:TCmdStr);
  747. var
  748. objdata : TObjData;
  749. objinput : TObjinput;
  750. objreader : TObjectReader;
  751. fn : TCmdStr;
  752. begin
  753. fn:=FindObjectFile(para,'',false);
  754. Comment(V_Tried,'Reading object '+fn);
  755. objinput:=CObjInput.Create;
  756. objdata:=objinput.newObjData(para);
  757. objreader:=TObjectreader.create;
  758. if objreader.openfile(fn) then
  759. begin
  760. if objinput.ReadObjData(objreader,objdata) then
  761. exeoutput.addobjdata(objdata);
  762. end;
  763. { release input object }
  764. objinput.free;
  765. objreader.free;
  766. end;
  767. procedure TInternalLinker.Load_ReadStaticLibrary(const para:TCmdStr);
  768. var
  769. objreader : TObjectReader;
  770. begin
  771. { TODO: Cleanup ignoring of FPC generated libimp*.a files}
  772. { Don't load import libraries }
  773. if copy(ExtractFileName(para),1,6)='libimp' then
  774. exit;
  775. Comment(V_Tried,'Opening library '+para);
  776. objreader:=TArObjectreader.create(para);
  777. TStaticLibrary.Create(StaticLibraryList,para,objreader,CObjInput);
  778. end;
  779. procedure TInternalLinker.ParseScript_Load;
  780. var
  781. s,
  782. para,
  783. keyword : String;
  784. hp : TCmdStrListItem;
  785. begin
  786. exeoutput.Load_Start;
  787. hp:=TCmdStrListItem(linkscript.first);
  788. while assigned(hp) do
  789. begin
  790. s:=hp.str;
  791. if (s='') or (s[1]='#') then
  792. continue;
  793. keyword:=Upper(GetToken(s,' '));
  794. para:=GetToken(s,' ');
  795. if keyword='SYMBOL' then
  796. ExeOutput.Load_Symbol(para)
  797. else if keyword='ENTRYNAME' then
  798. ExeOutput.Load_EntryName(para)
  799. else if keyword='ISSHAREDLIBRARY' then
  800. ExeOutput.Load_IsSharedLibrary
  801. else if keyword='IMAGEBASE' then
  802. ExeOutput.Load_ImageBase(para)
  803. else if keyword='READOBJECT' then
  804. Load_ReadObject(para)
  805. else if keyword='READSTATICLIBRARY' then
  806. Load_ReadStaticLibrary(para);
  807. hp:=TCmdStrListItem(hp.next);
  808. end;
  809. end;
  810. procedure TInternalLinker.ParseScript_Order;
  811. var
  812. s,
  813. para,
  814. keyword : String;
  815. hp : TCmdStrListItem;
  816. begin
  817. exeoutput.Order_Start;
  818. hp:=TCmdStrListItem(linkscript.first);
  819. while assigned(hp) do
  820. begin
  821. s:=hp.str;
  822. if (s='') or (s[1]='#') then
  823. continue;
  824. keyword:=Upper(GetToken(s,' '));
  825. para:=GetToken(s,' ');
  826. if keyword='EXESECTION' then
  827. ExeOutput.Order_ExeSection(para)
  828. else if keyword='ENDEXESECTION' then
  829. ExeOutput.Order_EndExeSection
  830. else if keyword='OBJSECTION' then
  831. ExeOutput.Order_ObjSection(para)
  832. else if keyword='ZEROS' then
  833. ExeOutput.Order_Zeros(para)
  834. else if keyword='SYMBOL' then
  835. ExeOutput.Order_Symbol(para);
  836. hp:=TCmdStrListItem(hp.next);
  837. end;
  838. exeoutput.Order_End;
  839. end;
  840. procedure TInternalLinker.ParseScript_MemPos;
  841. var
  842. s,
  843. para,
  844. keyword : String;
  845. hp : TCmdStrListItem;
  846. begin
  847. exeoutput.MemPos_Start;
  848. hp:=TCmdStrListItem(linkscript.first);
  849. while assigned(hp) do
  850. begin
  851. s:=hp.str;
  852. if (s='') or (s[1]='#') then
  853. continue;
  854. keyword:=Upper(GetToken(s,' '));
  855. para:=GetToken(s,' ');
  856. if keyword='EXESECTION' then
  857. ExeOutput.MemPos_ExeSection(para)
  858. else if keyword='ENDEXESECTION' then
  859. ExeOutput.MemPos_EndExeSection
  860. else if keyword='HEADER' then
  861. ExeOutput.MemPos_Header;
  862. hp:=TCmdStrListItem(hp.next);
  863. end;
  864. end;
  865. procedure TInternalLinker.ParseScript_DataPos;
  866. var
  867. s,
  868. para,
  869. keyword : String;
  870. hp : TCmdStrListItem;
  871. begin
  872. exeoutput.DataPos_Start;
  873. hp:=TCmdStrListItem(linkscript.first);
  874. while assigned(hp) do
  875. begin
  876. s:=hp.str;
  877. if (s='') or (s[1]='#') then
  878. continue;
  879. keyword:=Upper(GetToken(s,' '));
  880. para:=GetToken(s,' ');
  881. if keyword='EXESECTION' then
  882. ExeOutput.DataPos_ExeSection(para)
  883. else if keyword='ENDEXESECTION' then
  884. ExeOutput.DataPos_EndExeSection
  885. else if keyword='HEADER' then
  886. ExeOutput.DataPos_Header
  887. else if keyword='SYMBOLS' then
  888. ExeOutput.DataPos_Symbols;
  889. hp:=TCmdStrListItem(hp.next);
  890. end;
  891. end;
  892. procedure TInternalLinker.PrintLinkerScript;
  893. var
  894. hp : TCmdStrListItem;
  895. begin
  896. if not assigned(exemap) then
  897. exit;
  898. exemap.Add('Used linker script');
  899. exemap.Add('');
  900. hp:=TCmdStrListItem(linkscript.first);
  901. while assigned(hp) do
  902. begin
  903. exemap.Add(hp.str);
  904. hp:=TCmdStrListItem(hp.next);
  905. end;
  906. end;
  907. function TInternalLinker.RunLinkScript(const outputname:TCmdStr):boolean;
  908. label
  909. myexit;
  910. var
  911. bsssize : aword;
  912. bsssec : TExeSection;
  913. dbgname : TCmdStr;
  914. begin
  915. result:=false;
  916. Message1(exec_i_linking,outputname);
  917. FlushOutput;
  918. { TODO: Load custom linker script}
  919. DefaultLinkScript;
  920. exeoutput:=CExeOutput.Create;
  921. if (cs_link_map in current_settings.globalswitches) then
  922. exemap:=texemap.create(current_module.mapfilename^);
  923. PrintLinkerScript;
  924. { Load .o files and resolve symbols }
  925. ParseScript_Load;
  926. exeoutput.ResolveSymbols(StaticLibraryList);
  927. { Generate symbols and code to do the importing }
  928. exeoutput.GenerateLibraryImports(ImportLibraryList);
  929. { Fill external symbols data }
  930. exeoutput.FixupSymbols;
  931. if ErrorCount>0 then
  932. goto myexit;
  933. { Create .exe sections and add .o sections }
  934. ParseScript_Order;
  935. exeoutput.RemoveUnreferencedSections;
  936. exeoutput.MergeStabs;
  937. exeoutput.RemoveEmptySections;
  938. if ErrorCount>0 then
  939. goto myexit;
  940. { Calc positions in mem }
  941. ParseScript_MemPos;
  942. exeoutput.FixupRelocations;
  943. exeoutput.PrintMemoryMap;
  944. if ErrorCount>0 then
  945. goto myexit;
  946. if cs_link_separate_dbg_file in current_settings.globalswitches then
  947. begin
  948. { create debuginfo, which is an executable without data on disk }
  949. dbgname:=ChangeFileExt(outputname,'.dbg');
  950. exeoutput.ExeWriteMode:=ewm_dbgonly;
  951. ParseScript_DataPos;
  952. exeoutput.WriteExeFile(dbgname);
  953. { create executable with link to just created debuginfo file }
  954. exeoutput.ExeWriteMode:=ewm_exeonly;
  955. exeoutput.RemoveDebugInfo;
  956. exeoutput.GenerateDebugLink(ExtractFileName(dbgname),GetFileCRC(dbgname));
  957. ParseScript_MemPos;
  958. ParseScript_DataPos;
  959. exeoutput.WriteExeFile(outputname);
  960. end
  961. else
  962. begin
  963. exeoutput.ExeWriteMode:=ewm_exefull;
  964. ParseScript_DataPos;
  965. exeoutput.WriteExeFile(outputname);
  966. end;
  967. { TODO: fixed section names}
  968. status.codesize:=exeoutput.findexesection('.text').size;
  969. status.datasize:=exeoutput.findexesection('.data').size;
  970. bsssec:=exeoutput.findexesection('.bss');
  971. if assigned(bsssec) then
  972. bsssize:=bsssec.size
  973. else
  974. bsssize:=0;
  975. { Executable info }
  976. Message1(execinfo_x_codesize,tostr(status.codesize));
  977. Message1(execinfo_x_initdatasize,tostr(status.datasize));
  978. Message1(execinfo_x_uninitdatasize,tostr(bsssize));
  979. Message1(execinfo_x_stackreserve,tostr(stacksize));
  980. myexit:
  981. { close map }
  982. if assigned(exemap) then
  983. begin
  984. exemap.free;
  985. exemap:=nil;
  986. end;
  987. { close exe }
  988. exeoutput.free;
  989. exeoutput:=nil;
  990. result:=true;
  991. end;
  992. function TInternalLinker.MakeExecutable:boolean;
  993. begin
  994. IsSharedLibrary:=false;
  995. result:=RunLinkScript(current_module.exefilename^);
  996. end;
  997. function TInternalLinker.MakeSharedLibrary:boolean;
  998. begin
  999. IsSharedLibrary:=true;
  1000. result:=RunLinkScript(current_module.sharedlibfilename^);
  1001. end;
  1002. {*****************************************************************************
  1003. Init/Done
  1004. *****************************************************************************}
  1005. procedure InitLinker;
  1006. var
  1007. lk : TlinkerClass;
  1008. begin
  1009. if (cs_link_extern in current_settings.globalswitches) and
  1010. assigned(target_info.linkextern) then
  1011. begin
  1012. lk:=TlinkerClass(target_info.linkextern);
  1013. linker:=lk.Create;
  1014. end
  1015. else
  1016. if assigned(target_info.link) then
  1017. begin
  1018. lk:=TLinkerClass(target_info.link);
  1019. linker:=lk.Create;
  1020. end
  1021. else
  1022. linker:=Tlinker.Create;
  1023. end;
  1024. procedure DoneLinker;
  1025. begin
  1026. if assigned(linker) then
  1027. Linker.Free;
  1028. end;
  1029. {*****************************************************************************
  1030. Initialize
  1031. *****************************************************************************}
  1032. const
  1033. ar_gnu_ar_info : tarinfo =
  1034. (
  1035. id : ar_gnu_ar;
  1036. arcmd : 'ar qS $LIB $FILES';
  1037. arfinishcmd : 'ar s $LIB'
  1038. );
  1039. ar_gnu_ar_scripted_info : tarinfo =
  1040. (
  1041. id : ar_gnu_ar_scripted;
  1042. arcmd : 'ar -M < $SCRIPT';
  1043. arfinishcmd : ''
  1044. );
  1045. ar_gnu_gar_info : tarinfo =
  1046. ( id : ar_gnu_gar;
  1047. arcmd : 'gar qS $LIB $FILES';
  1048. arfinishcmd : 'gar s $LIB'
  1049. );
  1050. initialization
  1051. RegisterAr(ar_gnu_ar_info);
  1052. RegisterAr(ar_gnu_ar_scripted_info);
  1053. RegisterAr(ar_gnu_gar_info);
  1054. end.