link.pas 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  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. property CObjInput:TObjInputClass read FCObjInput write FCObjInput;
  92. property CExeOutput:TExeOutputClass read FCExeOutput write FCExeOutput;
  93. property StaticLibraryList:TFPHashObjectList read FStaticLibraryList;
  94. property ImportLibraryList:TFPHashObjectList read FImportLibraryList;
  95. procedure DefaultLinkScript;virtual;abstract;
  96. linkscript : TCmdStrList;
  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. Message1(exec_t_unit_not_smart_linkable_switch_to_static,modulename^);
  342. mask:=mask or link_static;
  343. end
  344. else
  345. Message1(exec_e_unit_not_smart_or_static_linkable,modulename^);
  346. end
  347. else
  348. mask:=mask or link_smart;
  349. end;
  350. { shared linking }
  351. if (cs_link_shared in current_settings.globalswitches) then
  352. begin
  353. if (flags and uf_shared_linked)=0 then
  354. begin
  355. { if shared not avail then try static linking }
  356. if (flags and uf_static_linked)<>0 then
  357. begin
  358. Message1(exec_t_unit_not_shared_linkable_switch_to_static,modulename^);
  359. mask:=mask or link_static;
  360. end
  361. else
  362. Message1(exec_e_unit_not_shared_or_static_linkable,modulename^);
  363. end
  364. else
  365. mask:=mask or link_shared;
  366. end;
  367. { unit files }
  368. while not linkunitofiles.empty do
  369. AddObject(linkunitofiles.getusemask(mask),path^,true);
  370. while not linkunitstaticlibs.empty do
  371. AddStaticLibrary(linkunitstaticlibs.getusemask(mask));
  372. while not linkunitsharedlibs.empty do
  373. AddSharedLibrary(linkunitsharedlibs.getusemask(mask));
  374. end;
  375. { Other needed .o and libs, specified using $L,$LINKLIB,external }
  376. mask:=link_always;
  377. while not linkotherofiles.empty do
  378. AddObject(linkotherofiles.Getusemask(mask),path^,false);
  379. while not linkotherstaticlibs.empty do
  380. AddStaticCLibrary(linkotherstaticlibs.Getusemask(mask));
  381. while not linkothersharedlibs.empty do
  382. AddSharedCLibrary(linkothersharedlibs.Getusemask(mask));
  383. while not linkotherframeworks.empty do
  384. AddFramework(linkotherframeworks.Getusemask(mask));
  385. { Known Library/DLL Imports }
  386. for i:=0 to ImportLibraryList.Count-1 do
  387. begin
  388. ImportLibrary:=TImportLibrary(ImportLibraryList[i]);
  389. for j:=0 to ImportLibrary.ImportSymbolList.Count-1 do
  390. begin
  391. ImportSymbol:=TImportSymbol(ImportLibrary.ImportSymbolList[j]);
  392. AddImportSymbol(ImportLibrary.Name,ImportSymbol.Name,ImportSymbol.OrdNr,ImportSymbol.IsVar);
  393. end;
  394. end;
  395. end;
  396. end;
  397. procedure TLinker.AddImportSymbol(const libname,symname:TCmdStr;OrdNr: longint;isvar:boolean);
  398. begin
  399. end;
  400. Procedure TLinker.AddObject(const S,unitpath : TCmdStr;isunit:boolean);
  401. begin
  402. ObjectFiles.Concat(FindObjectFile(s,unitpath,isunit));
  403. end;
  404. Procedure TLinker.AddSharedLibrary(S:TCmdStr);
  405. begin
  406. if s='' then
  407. exit;
  408. { remove prefix 'lib' }
  409. if Copy(s,1,length(target_info.sharedlibprefix))=target_info.sharedlibprefix then
  410. Delete(s,1,length(target_info.sharedlibprefix));
  411. { remove extension if any }
  412. if Copy(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext))=target_info.sharedlibext then
  413. Delete(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext)+1);
  414. { ready to be added }
  415. SharedLibFiles.Concat(S);
  416. end;
  417. Procedure TLinker.AddStaticLibrary(const S:TCmdStr);
  418. var
  419. ns : TCmdStr;
  420. found : boolean;
  421. begin
  422. if s='' then
  423. exit;
  424. found:=FindLibraryFile(s,target_info.staticlibprefix,target_info.staticlibext,ns);
  425. if not(cs_link_nolink in current_settings.globalswitches) and (not found) then
  426. Message1(exec_w_libfile_not_found,s);
  427. StaticLibFiles.Concat(ns);
  428. end;
  429. Procedure TLinker.AddSharedCLibrary(S:TCmdStr);
  430. begin
  431. if s='' then
  432. exit;
  433. { remove prefix 'lib' }
  434. if Copy(s,1,length(target_info.sharedclibprefix))=target_info.sharedclibprefix then
  435. Delete(s,1,length(target_info.sharedclibprefix));
  436. { remove extension if any }
  437. if Copy(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext))=target_info.sharedclibext then
  438. Delete(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext)+1);
  439. { ready to be added }
  440. SharedLibFiles.Concat(S);
  441. end;
  442. Procedure TLinker.AddFramework(S:TCmdStr);
  443. begin
  444. if s='' then
  445. exit;
  446. { ready to be added }
  447. FrameworkFiles.Concat(S);
  448. end;
  449. Procedure TLinker.AddStaticCLibrary(const S:TCmdStr);
  450. var
  451. ns : TCmdStr;
  452. found : boolean;
  453. begin
  454. if s='' then
  455. exit;
  456. found:=FindLibraryFile(s,target_info.staticclibprefix,target_info.staticclibext,ns);
  457. if not(cs_link_nolink in current_settings.globalswitches) and (not found) then
  458. Message1(exec_w_libfile_not_found,s);
  459. StaticLibFiles.Concat(ns);
  460. end;
  461. procedure AddImportSymbol(const libname,symname:TCmdStr;OrdNr: longint;isvar:boolean);
  462. begin
  463. end;
  464. procedure TLinker.InitSysInitUnitName;
  465. begin
  466. end;
  467. function TLinker.MakeExecutable:boolean;
  468. begin
  469. MakeExecutable:=false;
  470. Message(exec_e_exe_not_supported);
  471. end;
  472. Function TLinker.MakeSharedLibrary:boolean;
  473. begin
  474. MakeSharedLibrary:=false;
  475. Message(exec_e_dll_not_supported);
  476. end;
  477. Function TLinker.MakeStaticLibrary:boolean;
  478. begin
  479. MakeStaticLibrary:=false;
  480. Message(exec_e_dll_not_supported);
  481. end;
  482. Procedure TLinker.ExpandAndApplyOrder(var Src:TCmdStrList);
  483. var
  484. p : TLinkStrMap;
  485. i : longint;
  486. begin
  487. // call Virtual TLinker method to initialize
  488. LoadPredefinedLibraryOrder;
  489. // something to do?
  490. if (LinkLibraryAliases.count=0) and (LinkLibraryOrder.Count=0) Then
  491. exit;
  492. p:=TLinkStrMap.Create;
  493. // expand libaliases, clears src
  494. LinkLibraryAliases.expand(src,p);
  495. // writeln(src.count,' ',p.count,' ',linklibraryorder.count,' ',linklibraryaliases.count);
  496. // apply order
  497. p.UpdateWeights(LinkLibraryOrder);
  498. p.SortOnWeight;
  499. // put back in src
  500. for i:=0 to p.count-1 do
  501. src.insert(p[i].Key);
  502. p.free;
  503. end;
  504. procedure TLinker.LoadPredefinedLibraryOrder;
  505. begin
  506. end;
  507. function TLinker.ReOrderEntries : boolean;
  508. begin
  509. result:=(LinkLibraryOrder.count>0) or (LinkLibraryAliases.count>0);
  510. end;
  511. {*****************************************************************************
  512. TEXTERNALLINKER
  513. *****************************************************************************}
  514. Constructor TExternalLinker.Create;
  515. begin
  516. inherited Create;
  517. { set generic defaults }
  518. FillChar(Info,sizeof(Info),0);
  519. if cs_link_on_target in current_settings.globalswitches then
  520. begin
  521. Info.ResName:=outputexedir+ChangeFileExt(inputfilename,'_link.res');
  522. Info.ScriptName:=outputexedir+ChangeFileExt(inputfilename,'_script.res');
  523. end
  524. else
  525. begin
  526. Info.ResName:='link.res';
  527. Info.ScriptName:='script.res';
  528. end;
  529. { set the linker specific defaults }
  530. SetDefaultInfo;
  531. { Allow Parameter overrides for linker info }
  532. with Info do
  533. begin
  534. if ParaLinkOptions<>'' then
  535. ExtraOptions:=ParaLinkOptions;
  536. if ParaDynamicLinker<>'' then
  537. DynamicLinker:=ParaDynamicLinker;
  538. end;
  539. end;
  540. Destructor TExternalLinker.Destroy;
  541. begin
  542. inherited destroy;
  543. end;
  544. Procedure TExternalLinker.SetDefaultInfo;
  545. begin
  546. end;
  547. Function TExternalLinker.FindUtil(const s:TCmdStr):TCmdStr;
  548. var
  549. Found : boolean;
  550. FoundBin : TCmdStr;
  551. UtilExe : TCmdStr;
  552. begin
  553. if cs_link_on_target in current_settings.globalswitches then
  554. begin
  555. { If linking on target, don't add any path PM }
  556. FindUtil:=ChangeFileExt(s,target_info.exeext);
  557. exit;
  558. end;
  559. UtilExe:=ChangeFileExt(s,source_info.exeext);
  560. FoundBin:='';
  561. Found:=false;
  562. if utilsdirectory<>'' then
  563. Found:=FindFile(utilexe,utilsdirectory,false,Foundbin);
  564. if (not Found) then
  565. Found:=FindExe(utilexe,false,Foundbin);
  566. if (not Found) and not(cs_link_nolink in current_settings.globalswitches) then
  567. begin
  568. Message1(exec_e_util_not_found,utilexe);
  569. current_settings.globalswitches:=current_settings.globalswitches+[cs_link_nolink];
  570. end;
  571. if (FoundBin<>'') then
  572. Message1(exec_t_using_util,FoundBin);
  573. FindUtil:=FoundBin;
  574. end;
  575. Function TExternalLinker.DoExec(const command:TCmdStr; para:TCmdStr;showinfo,useshell:boolean):boolean;
  576. var
  577. exitcode: longint;
  578. begin
  579. DoExec:=true;
  580. if not(cs_link_nolink in current_settings.globalswitches) then
  581. begin
  582. FlushOutput;
  583. if useshell then
  584. exitcode:=shell(maybequoted(command)+' '+para)
  585. else
  586. try
  587. exitcode:=ExecuteProcess(command,para);
  588. except on E:EOSError do
  589. begin
  590. Message(exec_e_cant_call_linker);
  591. current_settings.globalswitches:=current_settings.globalswitches+[cs_link_nolink];
  592. DoExec:=false;
  593. end;
  594. end;
  595. if (exitcode<>0) then
  596. begin
  597. Message(exec_e_error_while_linking);
  598. current_settings.globalswitches:=current_settings.globalswitches+[cs_link_nolink];
  599. DoExec:=false;
  600. end;
  601. end;
  602. { Update asmres when externmode is set }
  603. if cs_link_nolink in current_settings.globalswitches then
  604. begin
  605. if showinfo then
  606. begin
  607. if DLLsource then
  608. AsmRes.AddLinkCommand(Command,Para,current_module.sharedlibfilename^)
  609. else
  610. AsmRes.AddLinkCommand(Command,Para,current_module.exefilename^);
  611. end
  612. else
  613. AsmRes.AddLinkCommand(Command,Para,'');
  614. end;
  615. end;
  616. Function TExternalLinker.MakeStaticLibrary:boolean;
  617. function GetNextFiles(const maxCmdLength : AInt; var item : TCmdStrListItem) : TCmdStr;
  618. begin
  619. result := '';
  620. while (assigned(item) and ((length(result) + length(item.str) + 1) < maxCmdLength)) do begin
  621. result := result + ' ' + item.str;
  622. item := TCmdStrListItem(item.next);
  623. end;
  624. end;
  625. var
  626. binstr, scriptfile : TCmdStr;
  627. cmdstr, nextcmd, smartpath : TCmdStr;
  628. current : TCmdStrListItem;
  629. script: Text;
  630. scripted_ar : boolean;
  631. success : boolean;
  632. begin
  633. MakeStaticLibrary:=false;
  634. { remove the library, to be sure that it is rewritten }
  635. DeleteFile(current_module.staticlibfilename^);
  636. { Call AR }
  637. smartpath:=FixPath(ChangeFileExt(current_module.asmfilename^,target_info.smartext),false);
  638. SplitBinCmd(target_ar.arcmd,binstr,cmdstr);
  639. binstr := FindUtil(utilsprefix + binstr);
  640. scripted_ar:=target_ar.id=ar_gnu_ar_scripted;
  641. if scripted_ar then
  642. begin
  643. scriptfile := FixFileName(smartpath+'arscript.txt');
  644. Replace(cmdstr,'$SCRIPT',maybequoted(scriptfile));
  645. Assign(script, scriptfile);
  646. Rewrite(script);
  647. try
  648. writeln(script, 'CREATE ' + current_module.staticlibfilename^);
  649. current := TCmdStrListItem(SmartLinkOFiles.First);
  650. while current <> nil do
  651. begin
  652. writeln(script, 'ADDMOD ' + current.str);
  653. current := TCmdStrListItem(current.next);
  654. end;
  655. writeln(script, 'SAVE');
  656. writeln(script, 'END');
  657. finally
  658. Close(script);
  659. end;
  660. success:=DoExec(binstr,cmdstr,false,true);
  661. end
  662. else
  663. begin
  664. Replace(cmdstr,'$LIB',maybequoted(current_module.staticlibfilename^));
  665. { create AR commands }
  666. success := true;
  667. current := TCmdStrListItem(SmartLinkOFiles.First);
  668. repeat
  669. nextcmd := cmdstr;
  670. Replace(nextcmd,'$FILES',GetNextFiles(2047, current));
  671. success:=DoExec(binstr,nextcmd,false,true);
  672. until (not assigned(current)) or (not success);
  673. end;
  674. if (target_ar.arfinishcmd <> '') then
  675. begin
  676. SplitBinCmd(target_ar.arfinishcmd,binstr,cmdstr);
  677. binstr := FindUtil(utilsprefix + binstr);
  678. Replace(cmdstr,'$LIB',maybequoted(current_module.staticlibfilename^));
  679. success:=DoExec(binstr,cmdstr,false,true);
  680. end;
  681. { Clean up }
  682. if not(cs_asm_leave in current_settings.globalswitches) then
  683. if not(cs_link_nolink in current_settings.globalswitches) then
  684. begin
  685. while not SmartLinkOFiles.Empty do
  686. DeleteFile(SmartLinkOFiles.GetFirst);
  687. if scripted_ar then
  688. DeleteFile(scriptfile);
  689. RemoveDir(smartpath);
  690. end
  691. else
  692. begin
  693. AsmRes.AddDeleteCommand(FixFileName(smartpath+current_module.asmprefix^+'*'+target_info.objext));
  694. if scripted_ar then
  695. AsmRes.AddDeleteCommand(scriptfile);
  696. AsmRes.AddDeleteDirCommand(smartpath);
  697. end;
  698. MakeStaticLibrary:=success;
  699. end;
  700. {*****************************************************************************
  701. TINTERNALLINKER
  702. *****************************************************************************}
  703. Constructor TInternalLinker.Create;
  704. begin
  705. inherited Create;
  706. linkscript:=TCmdStrList.Create;
  707. FStaticLibraryList:=TFPHashObjectList.Create(true);
  708. FImportLibraryList:=TFPHashObjectList.Create(true);
  709. exemap:=nil;
  710. exeoutput:=nil;
  711. CObjInput:=TObjInput;
  712. end;
  713. Destructor TInternalLinker.Destroy;
  714. begin
  715. linkscript.free;
  716. StaticLibraryList.Free;
  717. ImportLibraryList.Free;
  718. if assigned(exeoutput) then
  719. begin
  720. exeoutput.free;
  721. exeoutput:=nil;
  722. end;
  723. if assigned(exemap) then
  724. begin
  725. exemap.free;
  726. exemap:=nil;
  727. end;
  728. inherited destroy;
  729. end;
  730. procedure TInternalLinker.AddImportSymbol(const libname,symname:TCmdStr;OrdNr: longint;isvar:boolean);
  731. var
  732. ImportLibrary : TImportLibrary;
  733. ImportSymbol : TFPHashObject;
  734. begin
  735. ImportLibrary:=TImportLibrary(ImportLibraryList.Find(libname));
  736. if not assigned(ImportLibrary) then
  737. ImportLibrary:=TImportLibrary.Create(ImportLibraryList,libname);
  738. ImportSymbol:=TFPHashObject(ImportLibrary.ImportSymbolList.Find(symname));
  739. if not assigned(ImportSymbol) then
  740. ImportSymbol:=TImportSymbol.Create(ImportLibrary.ImportSymbolList,symname,OrdNr,isvar);
  741. end;
  742. procedure TInternalLinker.Load_ReadObject(const para:TCmdStr);
  743. var
  744. objdata : TObjData;
  745. objinput : TObjinput;
  746. objreader : TObjectReader;
  747. fn : TCmdStr;
  748. begin
  749. fn:=FindObjectFile(para,'',false);
  750. Comment(V_Tried,'Reading object '+fn);
  751. objinput:=CObjInput.Create;
  752. objdata:=objinput.newObjData(para);
  753. objreader:=TObjectreader.create;
  754. if objreader.openfile(fn) then
  755. begin
  756. if objinput.ReadObjData(objreader,objdata) then
  757. exeoutput.addobjdata(objdata);
  758. end;
  759. { release input object }
  760. objinput.free;
  761. objreader.free;
  762. end;
  763. procedure TInternalLinker.Load_ReadStaticLibrary(const para:TCmdStr);
  764. var
  765. objreader : TObjectReader;
  766. begin
  767. { TODO: Cleanup ignoring of FPC generated libimp*.a files}
  768. { Don't load import libraries }
  769. if copy(ExtractFileName(para),1,6)='libimp' then
  770. exit;
  771. Comment(V_Tried,'Opening library '+para);
  772. objreader:=TArObjectreader.create(para);
  773. TStaticLibrary.Create(StaticLibraryList,para,objreader,CObjInput);
  774. end;
  775. procedure TInternalLinker.ParseScript_Load;
  776. var
  777. s,
  778. para,
  779. keyword : String;
  780. hp : TCmdStrListItem;
  781. begin
  782. exeoutput.Load_Start;
  783. hp:=TCmdStrListItem(linkscript.first);
  784. while assigned(hp) do
  785. begin
  786. s:=hp.str;
  787. if (s='') or (s[1]='#') then
  788. continue;
  789. keyword:=Upper(GetToken(s,' '));
  790. para:=GetToken(s,' ');
  791. if keyword='SYMBOL' then
  792. ExeOutput.Load_Symbol(para)
  793. else if keyword='ENTRYNAME' then
  794. ExeOutput.Load_EntryName(para)
  795. else if keyword='ISSHAREDLIBRARY' then
  796. ExeOutput.Load_IsSharedLibrary
  797. else if keyword='IMAGEBASE' then
  798. ExeOutput.Load_ImageBase(para)
  799. else if keyword='READOBJECT' then
  800. Load_ReadObject(para)
  801. else if keyword='READSTATICLIBRARY' then
  802. Load_ReadStaticLibrary(para);
  803. hp:=TCmdStrListItem(hp.next);
  804. end;
  805. end;
  806. procedure TInternalLinker.ParseScript_Order;
  807. var
  808. s,
  809. para,
  810. keyword : String;
  811. hp : TCmdStrListItem;
  812. begin
  813. exeoutput.Order_Start;
  814. hp:=TCmdStrListItem(linkscript.first);
  815. while assigned(hp) do
  816. begin
  817. s:=hp.str;
  818. if (s='') or (s[1]='#') then
  819. continue;
  820. keyword:=Upper(GetToken(s,' '));
  821. para:=GetToken(s,' ');
  822. if keyword='EXESECTION' then
  823. ExeOutput.Order_ExeSection(para)
  824. else if keyword='ENDEXESECTION' then
  825. ExeOutput.Order_EndExeSection
  826. else if keyword='OBJSECTION' then
  827. ExeOutput.Order_ObjSection(para)
  828. else if keyword='ZEROS' then
  829. ExeOutput.Order_Zeros(para)
  830. else if keyword='SYMBOL' then
  831. ExeOutput.Order_Symbol(para);
  832. hp:=TCmdStrListItem(hp.next);
  833. end;
  834. exeoutput.Order_End;
  835. end;
  836. procedure TInternalLinker.ParseScript_MemPos;
  837. var
  838. s,
  839. para,
  840. keyword : String;
  841. hp : TCmdStrListItem;
  842. begin
  843. exeoutput.MemPos_Start;
  844. hp:=TCmdStrListItem(linkscript.first);
  845. while assigned(hp) do
  846. begin
  847. s:=hp.str;
  848. if (s='') or (s[1]='#') then
  849. continue;
  850. keyword:=Upper(GetToken(s,' '));
  851. para:=GetToken(s,' ');
  852. if keyword='EXESECTION' then
  853. ExeOutput.MemPos_ExeSection(para)
  854. else if keyword='ENDEXESECTION' then
  855. ExeOutput.MemPos_EndExeSection
  856. else if keyword='HEADER' then
  857. ExeOutput.MemPos_Header;
  858. hp:=TCmdStrListItem(hp.next);
  859. end;
  860. end;
  861. procedure TInternalLinker.ParseScript_DataPos;
  862. var
  863. s,
  864. para,
  865. keyword : String;
  866. hp : TCmdStrListItem;
  867. begin
  868. exeoutput.DataPos_Start;
  869. hp:=TCmdStrListItem(linkscript.first);
  870. while assigned(hp) do
  871. begin
  872. s:=hp.str;
  873. if (s='') or (s[1]='#') then
  874. continue;
  875. keyword:=Upper(GetToken(s,' '));
  876. para:=GetToken(s,' ');
  877. if keyword='EXESECTION' then
  878. ExeOutput.DataPos_ExeSection(para)
  879. else if keyword='ENDEXESECTION' then
  880. ExeOutput.DataPos_EndExeSection
  881. else if keyword='HEADER' then
  882. ExeOutput.DataPos_Header
  883. else if keyword='SYMBOLS' then
  884. ExeOutput.DataPos_Symbols;
  885. hp:=TCmdStrListItem(hp.next);
  886. end;
  887. end;
  888. procedure TInternalLinker.PrintLinkerScript;
  889. var
  890. hp : TCmdStrListItem;
  891. begin
  892. if not assigned(exemap) then
  893. exit;
  894. exemap.Add('Used linker script');
  895. exemap.Add('');
  896. hp:=TCmdStrListItem(linkscript.first);
  897. while assigned(hp) do
  898. begin
  899. exemap.Add(hp.str);
  900. hp:=TCmdStrListItem(hp.next);
  901. end;
  902. end;
  903. function TInternalLinker.RunLinkScript(const outputname:TCmdStr):boolean;
  904. label
  905. myexit;
  906. var
  907. bsssize : aword;
  908. bsssec : TExeSection;
  909. dbgname : TCmdStr;
  910. begin
  911. result:=false;
  912. Message1(exec_i_linking,outputname);
  913. FlushOutput;
  914. { TODO: Load custom linker script}
  915. DefaultLinkScript;
  916. exeoutput:=CExeOutput.Create;
  917. if (cs_link_map in current_settings.globalswitches) then
  918. exemap:=texemap.create(current_module.mapfilename^);
  919. PrintLinkerScript;
  920. { Load .o files and resolve symbols }
  921. ParseScript_Load;
  922. exeoutput.ResolveSymbols(StaticLibraryList);
  923. { Generate symbols and code to do the importing }
  924. exeoutput.GenerateLibraryImports(ImportLibraryList);
  925. { Fill external symbols data }
  926. exeoutput.FixupSymbols;
  927. if ErrorCount>0 then
  928. goto myexit;
  929. { Create .exe sections and add .o sections }
  930. ParseScript_Order;
  931. exeoutput.RemoveUnreferencedSections;
  932. exeoutput.MergeStabs;
  933. exeoutput.RemoveEmptySections;
  934. if ErrorCount>0 then
  935. goto myexit;
  936. { Calc positions in mem }
  937. ParseScript_MemPos;
  938. exeoutput.FixupRelocations;
  939. exeoutput.PrintMemoryMap;
  940. if ErrorCount>0 then
  941. goto myexit;
  942. if cs_link_separate_dbg_file in current_settings.globalswitches then
  943. begin
  944. { create debuginfo, which is an executable without data on disk }
  945. dbgname:=ChangeFileExt(outputname,'.dbg');
  946. exeoutput.ExeWriteMode:=ewm_dbgonly;
  947. ParseScript_DataPos;
  948. exeoutput.WriteExeFile(dbgname);
  949. { create executable with link to just created debuginfo file }
  950. exeoutput.ExeWriteMode:=ewm_exeonly;
  951. exeoutput.RemoveDebugInfo;
  952. exeoutput.GenerateDebugLink(dbgname,GetFileCRC(dbgname));
  953. ParseScript_MemPos;
  954. ParseScript_DataPos;
  955. exeoutput.WriteExeFile(outputname);
  956. end
  957. else
  958. begin
  959. exeoutput.ExeWriteMode:=ewm_exefull;
  960. ParseScript_DataPos;
  961. exeoutput.WriteExeFile(outputname);
  962. end;
  963. { TODO: fixed section names}
  964. status.codesize:=exeoutput.findexesection('.text').size;
  965. status.datasize:=exeoutput.findexesection('.data').size;
  966. bsssec:=exeoutput.findexesection('.bss');
  967. if assigned(bsssec) then
  968. bsssize:=bsssec.size
  969. else
  970. bsssize:=0;
  971. { Executable info }
  972. Message1(execinfo_x_codesize,tostr(status.codesize));
  973. Message1(execinfo_x_initdatasize,tostr(status.datasize));
  974. Message1(execinfo_x_uninitdatasize,tostr(bsssize));
  975. Message1(execinfo_x_stackreserve,tostr(stacksize));
  976. myexit:
  977. { close map }
  978. if assigned(exemap) then
  979. begin
  980. exemap.free;
  981. exemap:=nil;
  982. end;
  983. { close exe }
  984. exeoutput.free;
  985. exeoutput:=nil;
  986. result:=true;
  987. end;
  988. function TInternalLinker.MakeExecutable:boolean;
  989. begin
  990. IsSharedLibrary:=false;
  991. result:=RunLinkScript(current_module.exefilename^);
  992. end;
  993. function TInternalLinker.MakeSharedLibrary:boolean;
  994. begin
  995. IsSharedLibrary:=true;
  996. result:=RunLinkScript(current_module.sharedlibfilename^);
  997. end;
  998. {*****************************************************************************
  999. Init/Done
  1000. *****************************************************************************}
  1001. procedure InitLinker;
  1002. var
  1003. lk : TlinkerClass;
  1004. begin
  1005. if (cs_link_extern in current_settings.globalswitches) and
  1006. assigned(target_info.linkextern) then
  1007. begin
  1008. lk:=TlinkerClass(target_info.linkextern);
  1009. linker:=lk.Create;
  1010. end
  1011. else
  1012. if assigned(target_info.link) then
  1013. begin
  1014. lk:=TLinkerClass(target_info.link);
  1015. linker:=lk.Create;
  1016. end
  1017. else
  1018. linker:=Tlinker.Create;
  1019. end;
  1020. procedure DoneLinker;
  1021. begin
  1022. if assigned(linker) then
  1023. Linker.Free;
  1024. end;
  1025. {*****************************************************************************
  1026. Initialize
  1027. *****************************************************************************}
  1028. const
  1029. ar_gnu_ar_info : tarinfo =
  1030. (
  1031. id : ar_gnu_ar;
  1032. arcmd : 'ar qS $LIB $FILES';
  1033. arfinishcmd : 'ar s $LIB'
  1034. );
  1035. ar_gnu_ar_scripted_info : tarinfo =
  1036. (
  1037. id : ar_gnu_ar_scripted;
  1038. arcmd : 'ar -M < $SCRIPT';
  1039. arfinishcmd : ''
  1040. );
  1041. ar_gnu_gar_info : tarinfo =
  1042. ( id : ar_gnu_gar;
  1043. arcmd : 'gar qS $LIB $FILES';
  1044. arfinishcmd : 'gar s $LIB'
  1045. );
  1046. initialization
  1047. RegisterAr(ar_gnu_ar_info);
  1048. RegisterAr(ar_gnu_ar_scripted_info);
  1049. RegisterAr(ar_gnu_gar_info);
  1050. end.