t_msdos.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. {
  2. Copyright (c) 1998-2002 by Peter Vreman
  3. This unit implements support import,export,link routines
  4. for the (i8086) MS-DOS target
  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 t_msdos;
  19. {$i fpcdefs.inc}
  20. {$define USE_LINKER_WLINK}
  21. interface
  22. implementation
  23. uses
  24. SysUtils,
  25. cutils,cfileutl,cclasses,
  26. globtype,globals,systems,verbose,cscript,
  27. fmodule,i_msdos,
  28. link,aasmbase,cpuinfo,
  29. omfbase,ogbase,ogomf,owomflib;
  30. type
  31. { Borland TLINK support }
  32. TExternalLinkerMsDosTLink=class(texternallinker)
  33. private
  34. Function WriteResponseFile(isdll:boolean) : Boolean;
  35. public
  36. constructor Create;override;
  37. procedure SetDefaultInfo;override;
  38. function MakeExecutable:boolean;override;
  39. end;
  40. { the ALINK linker from http://alink.sourceforge.net/ }
  41. TExternalLinkerMsDosALink=class(texternallinker)
  42. private
  43. Function WriteResponseFile(isdll:boolean) : Boolean;
  44. public
  45. constructor Create;override;
  46. procedure SetDefaultInfo;override;
  47. function MakeExecutable:boolean;override;
  48. end;
  49. { the (Open) Watcom linker }
  50. TExternalLinkerMsDosWLink=class(texternallinker)
  51. private
  52. Function WriteResponseFile(isdll:boolean) : Boolean;
  53. Function PostProcessExecutable(const fn:string) : Boolean;
  54. public
  55. constructor Create;override;
  56. procedure SetDefaultInfo;override;
  57. function MakeExecutable:boolean;override;
  58. end;
  59. { TInternalLinkerMsDos }
  60. TInternalLinkerMsDos=class(tinternallinker)
  61. private
  62. function GetTotalSizeForSegmentClass(aExeOutput: TExeOutput; const SegClass: string): QWord;
  63. protected
  64. function GetCodeSize(aExeOutput: TExeOutput): QWord;override;
  65. function GetDataSize(aExeOutput: TExeOutput): QWord;override;
  66. function GetBssSize(aExeOutput: TExeOutput): QWord;override;
  67. procedure DefaultLinkScript;override;
  68. public
  69. constructor create;override;
  70. end;
  71. {****************************************************************************
  72. TExternalLinkerMsDosTLink
  73. ****************************************************************************}
  74. Constructor TExternalLinkerMsDosTLink.Create;
  75. begin
  76. Inherited Create;
  77. { allow duplicated libs (PM) }
  78. SharedLibFiles.doubles:=true;
  79. StaticLibFiles.doubles:=true;
  80. end;
  81. procedure TExternalLinkerMsDosTLink.SetDefaultInfo;
  82. begin
  83. with Info do
  84. begin
  85. ExeCmd[1]:='tlink $OPT $RES';
  86. end;
  87. end;
  88. Function TExternalLinkerMsDosTLink.WriteResponseFile(isdll:boolean) : Boolean;
  89. Var
  90. linkres : TLinkRes;
  91. s : string;
  92. begin
  93. WriteResponseFile:=False;
  94. { Open link.res file }
  95. LinkRes:=TLinkRes.Create(outputexedir+Info.ResName,true);
  96. { Add all options to link.res instead of passing them via command line:
  97. DOS command line is limited to 126 characters! }
  98. { add objectfiles, start with prt0 always }
  99. LinkRes.Add(GetShortName(FindObjectFile('prt0','',false)) + ' +');
  100. while not ObjectFiles.Empty do
  101. begin
  102. s:=ObjectFiles.GetFirst;
  103. if s<>'' then
  104. LinkRes.Add(GetShortName(s) + ' +');
  105. end;
  106. LinkRes.Add(', ' + maybequoted(current_module.exefilename));
  107. { Write and Close response }
  108. linkres.writetodisk;
  109. LinkRes.Free;
  110. WriteResponseFile:=True;
  111. end;
  112. function TExternalLinkerMsDosTLink.MakeExecutable:boolean;
  113. var
  114. binstr,
  115. cmdstr : TCmdStr;
  116. success : boolean;
  117. begin
  118. if not(cs_link_nolink in current_settings.globalswitches) then
  119. Message1(exec_i_linking,current_module.exefilename);
  120. { Write used files and libraries and our own tlink script }
  121. WriteResponsefile(false);
  122. { Call linker }
  123. SplitBinCmd(Info.ExeCmd[1],binstr,cmdstr);
  124. Replace(cmdstr,'$RES','@'+maybequoted(outputexedir+Info.ResName));
  125. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  126. success:=DoExec(FindUtil(utilsprefix+BinStr),cmdstr,true,false);
  127. { Remove ReponseFile }
  128. if (success) and not(cs_link_nolink in current_settings.globalswitches) then
  129. DeleteFile(outputexedir+Info.ResName);
  130. MakeExecutable:=success; { otherwise a recursive call to link method }
  131. end;
  132. {****************************************************************************
  133. TExternalLinkerMsDosALink
  134. ****************************************************************************}
  135. { TExternalLinkerMsDosALink }
  136. function TExternalLinkerMsDosALink.WriteResponseFile(isdll: boolean): Boolean;
  137. Var
  138. linkres : TLinkRes;
  139. s : string;
  140. begin
  141. WriteResponseFile:=False;
  142. { Open link.res file }
  143. LinkRes:=TLinkRes.Create(outputexedir+Info.ResName,true);
  144. { Add all options to link.res instead of passing them via command line:
  145. DOS command line is limited to 126 characters! }
  146. { add objectfiles, start with prt0 always }
  147. LinkRes.Add(maybequoted(FindObjectFile('prt0','',false)));
  148. while not ObjectFiles.Empty do
  149. begin
  150. s:=ObjectFiles.GetFirst;
  151. if s<>'' then
  152. LinkRes.Add(maybequoted(s));
  153. end;
  154. LinkRes.Add('-oEXE');
  155. LinkRes.Add('-o ' + maybequoted(current_module.exefilename));
  156. { Write and Close response }
  157. linkres.writetodisk;
  158. LinkRes.Free;
  159. WriteResponseFile:=True;
  160. end;
  161. constructor TExternalLinkerMsDosALink.Create;
  162. begin
  163. Inherited Create;
  164. { allow duplicated libs (PM) }
  165. SharedLibFiles.doubles:=true;
  166. StaticLibFiles.doubles:=true;
  167. end;
  168. procedure TExternalLinkerMsDosALink.SetDefaultInfo;
  169. begin
  170. with Info do
  171. begin
  172. ExeCmd[1]:='alink $OPT $RES';
  173. end;
  174. end;
  175. function TExternalLinkerMsDosALink.MakeExecutable: boolean;
  176. var
  177. binstr,
  178. cmdstr : TCmdStr;
  179. success : boolean;
  180. begin
  181. if not(cs_link_nolink in current_settings.globalswitches) then
  182. Message1(exec_i_linking,current_module.exefilename);
  183. { Write used files and libraries and our own tlink script }
  184. WriteResponsefile(false);
  185. { Call linker }
  186. SplitBinCmd(Info.ExeCmd[1],binstr,cmdstr);
  187. Replace(cmdstr,'$RES','@'+maybequoted(outputexedir+Info.ResName));
  188. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  189. success:=DoExec(FindUtil(utilsprefix+BinStr),cmdstr,true,false);
  190. { Remove ReponseFile }
  191. if (success) and not(cs_link_nolink in current_settings.globalswitches) then
  192. DeleteFile(outputexedir+Info.ResName);
  193. MakeExecutable:=success; { otherwise a recursive call to link method }
  194. end;
  195. {****************************************************************************
  196. TExternalLinkerMsDosWLink
  197. ****************************************************************************}
  198. { TExternalLinkerMsDosWLink }
  199. function TExternalLinkerMsDosWLink.WriteResponseFile(isdll: boolean): Boolean;
  200. Var
  201. linkres : TLinkRes;
  202. s : string;
  203. begin
  204. WriteResponseFile:=False;
  205. { Open link.res file }
  206. LinkRes:=TLinkRes.Create(outputexedir+Info.ResName,true);
  207. { Add all options to link.res instead of passing them via command line:
  208. DOS command line is limited to 126 characters! }
  209. LinkRes.Add('option quiet');
  210. if cs_debuginfo in current_settings.moduleswitches then
  211. begin
  212. if target_dbg.id in [dbg_dwarf2,dbg_dwarf3,dbg_dwarf4] then
  213. LinkRes.Add('debug dwarf')
  214. else if target_dbg.id=dbg_codeview then
  215. LinkRes.Add('debug codeview')
  216. else
  217. LinkRes.Add('debug watcom all');
  218. if cs_link_separate_dbg_file in current_settings.globalswitches then
  219. LinkRes.Add('option symfile');
  220. end;
  221. { add objectfiles, start with prt0 always }
  222. case current_settings.x86memorymodel of
  223. mm_tiny: LinkRes.Add('file ' + maybequoted(FindObjectFile('prt0t','',false)));
  224. mm_small: LinkRes.Add('file ' + maybequoted(FindObjectFile('prt0s','',false)));
  225. mm_medium: LinkRes.Add('file ' + maybequoted(FindObjectFile('prt0m','',false)));
  226. mm_compact: LinkRes.Add('file ' + maybequoted(FindObjectFile('prt0c','',false)));
  227. mm_large: LinkRes.Add('file ' + maybequoted(FindObjectFile('prt0l','',false)));
  228. mm_huge: LinkRes.Add('file ' + maybequoted(FindObjectFile('prt0h','',false)));
  229. end;
  230. while not ObjectFiles.Empty do
  231. begin
  232. s:=ObjectFiles.GetFirst;
  233. if s<>'' then
  234. LinkRes.Add('file ' + maybequoted(s));
  235. end;
  236. while not StaticLibFiles.Empty do
  237. begin
  238. s:=StaticLibFiles.GetFirst;
  239. if s<>'' then
  240. LinkRes.Add('library '+MaybeQuoted(s));
  241. end;
  242. if apptype=app_com then
  243. LinkRes.Add('format dos com')
  244. else
  245. LinkRes.Add('format dos');
  246. if current_settings.x86memorymodel=mm_tiny then
  247. LinkRes.Add('order clname CODE clname DATA clname BSS')
  248. else
  249. LinkRes.Add('order clname CODE clname FAR_DATA clname BEGDATA segment _NULL segment _AFTERNULL clname DATA clname BSS clname STACK clname HEAP');
  250. if (cs_link_map in current_settings.globalswitches) then
  251. LinkRes.Add('option map='+maybequoted(ChangeFileExt(current_module.exefilename,'.map')));
  252. LinkRes.Add('name ' + maybequoted(current_module.exefilename));
  253. { Write and Close response }
  254. linkres.writetodisk;
  255. LinkRes.Free;
  256. WriteResponseFile:=True;
  257. end;
  258. constructor TExternalLinkerMsDosWLink.Create;
  259. begin
  260. Inherited Create;
  261. { allow duplicated libs (PM) }
  262. SharedLibFiles.doubles:=true;
  263. StaticLibFiles.doubles:=true;
  264. end;
  265. procedure TExternalLinkerMsDosWLink.SetDefaultInfo;
  266. begin
  267. with Info do
  268. begin
  269. ExeCmd[1]:='wlink $OPT $RES';
  270. end;
  271. end;
  272. function TExternalLinkerMsDosWLink.MakeExecutable: boolean;
  273. var
  274. binstr,
  275. cmdstr : TCmdStr;
  276. success : boolean;
  277. begin
  278. if not(cs_link_nolink in current_settings.globalswitches) then
  279. Message1(exec_i_linking,current_module.exefilename);
  280. { Write used files and libraries and our own tlink script }
  281. WriteResponsefile(false);
  282. { Call linker }
  283. SplitBinCmd(Info.ExeCmd[1],binstr,cmdstr);
  284. Replace(cmdstr,'$RES','@'+maybequoted(outputexedir+Info.ResName));
  285. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  286. success:=DoExec(FindUtil(utilsprefix+BinStr),cmdstr,true,false);
  287. { Post process }
  288. if success then
  289. success:=PostProcessExecutable(current_module.exefilename);
  290. { Remove ReponseFile }
  291. if (success) and not(cs_link_nolink in current_settings.globalswitches) then
  292. DeleteFile(outputexedir+Info.ResName);
  293. MakeExecutable:=success; { otherwise a recursive call to link method }
  294. end;
  295. { In far data memory models, this function sets the MaxAlloc value in the DOS MZ
  296. header according to the difference between HeapMin and HeapMax. We have to do
  297. this manually, because WLink sets MaxAlloc to $FFFF and there seems to be no
  298. way to specify a different value with a linker option. }
  299. function TExternalLinkerMsDosWLink.PostProcessExecutable(const fn: string): Boolean;
  300. var
  301. f: file;
  302. minalloc,maxalloc: Word;
  303. heapmin_paragraphs, heapmax_paragraphs: Integer;
  304. begin
  305. { nothing to do in the near data memory models }
  306. if current_settings.x86memorymodel in x86_near_data_models then
  307. exit(true);
  308. { .COM files are not supported in the far data memory models }
  309. if apptype=app_com then
  310. internalerror(2014062501);
  311. { open file }
  312. assign(f,fn);
  313. {$push}{$I-}
  314. reset(f,1);
  315. if ioresult<>0 then
  316. Message1(execinfo_f_cant_open_executable,fn);
  317. { read minalloc }
  318. seek(f,$A);
  319. BlockRead(f,minalloc,2);
  320. if source_info.endian<>target_info.endian then
  321. minalloc:=SwapEndian(minalloc);
  322. { calculate the additional number of paragraphs needed }
  323. heapmin_paragraphs:=(heapsize + 15) div 16;
  324. heapmax_paragraphs:=(maxheapsize + 15) div 16;
  325. maxalloc:=min(minalloc-heapmin_paragraphs+heapmax_paragraphs,$FFFF);
  326. { write maxalloc }
  327. seek(f,$C);
  328. if source_info.endian<>target_info.endian then
  329. maxalloc:=SwapEndian(maxalloc);
  330. BlockWrite(f,maxalloc,2);
  331. close(f);
  332. {$pop}
  333. if ioresult<>0 then;
  334. Result:=true;
  335. end;
  336. {****************************************************************************
  337. TInternalLinkerMsDos
  338. ****************************************************************************}
  339. function TInternalLinkerMsDos.GetTotalSizeForSegmentClass(
  340. aExeOutput: TExeOutput; const SegClass: string): QWord;
  341. var
  342. objseclist: TFPObjectList;
  343. objsec: TOmfObjSection;
  344. i: Integer;
  345. begin
  346. Result:=0;
  347. objseclist:=TMZExeOutput(aExeOutput).MZFlatContentSection.ObjSectionList;
  348. for i:=0 to objseclist.Count-1 do
  349. begin
  350. objsec:=TOmfObjSection(objseclist[i]);
  351. if objsec.ClassName=SegClass then
  352. Inc(Result,objsec.Size);
  353. end;
  354. end;
  355. function TInternalLinkerMsDos.GetCodeSize(aExeOutput: TExeOutput): QWord;
  356. begin
  357. Result:=GetTotalSizeForSegmentClass(aExeOutput,'CODE');
  358. end;
  359. function TInternalLinkerMsDos.GetDataSize(aExeOutput: TExeOutput): QWord;
  360. begin
  361. Result:=GetTotalSizeForSegmentClass(aExeOutput,'DATA')+
  362. GetTotalSizeForSegmentClass(aExeOutput,'FAR_DATA');
  363. end;
  364. function TInternalLinkerMsDos.GetBssSize(aExeOutput: TExeOutput): QWord;
  365. begin
  366. Result:=GetTotalSizeForSegmentClass(aExeOutput,'BSS');
  367. end;
  368. procedure TInternalLinkerMsDos.DefaultLinkScript;
  369. var
  370. s: TCmdStr;
  371. begin
  372. { add objectfiles, start with prt0 always }
  373. case current_settings.x86memorymodel of
  374. mm_tiny: LinkScript.Concat('READOBJECT ' + maybequoted(FindObjectFile('prt0t','',false)));
  375. mm_small: LinkScript.Concat('READOBJECT ' + maybequoted(FindObjectFile('prt0s','',false)));
  376. mm_medium: LinkScript.Concat('READOBJECT ' + maybequoted(FindObjectFile('prt0m','',false)));
  377. mm_compact: LinkScript.Concat('READOBJECT ' + maybequoted(FindObjectFile('prt0c','',false)));
  378. mm_large: LinkScript.Concat('READOBJECT ' + maybequoted(FindObjectFile('prt0l','',false)));
  379. mm_huge: LinkScript.Concat('READOBJECT ' + maybequoted(FindObjectFile('prt0h','',false)));
  380. end;
  381. while not ObjectFiles.Empty do
  382. begin
  383. s:=ObjectFiles.GetFirst;
  384. if s<>'' then
  385. LinkScript.Concat('READOBJECT ' + maybequoted(s));
  386. end;
  387. LinkScript.Concat('GROUP');
  388. while not StaticLibFiles.Empty do
  389. begin
  390. s:=StaticLibFiles.GetFirst;
  391. if s<>'' then
  392. LinkScript.Concat('READSTATICLIBRARY '+MaybeQuoted(s));
  393. end;
  394. LinkScript.Concat('ENDGROUP');
  395. LinkScript.Concat('EXESECTION .MZ_flat_content');
  396. if current_settings.x86memorymodel=mm_tiny then
  397. begin
  398. LinkScript.Concat(' OBJSECTION _TEXT||CODE');
  399. LinkScript.Concat(' OBJSECTION *||CODE');
  400. LinkScript.Concat(' OBJSECTION *||DATA');
  401. LinkScript.Concat(' SYMBOL _edata');
  402. LinkScript.Concat(' OBJSECTION *||BSS');
  403. LinkScript.Concat(' SYMBOL _end');
  404. end
  405. else
  406. begin
  407. LinkScript.Concat(' OBJSECTION _TEXT||CODE');
  408. LinkScript.Concat(' OBJSECTION *||CODE');
  409. LinkScript.Concat(' OBJSECTION *||FAR_DATA');
  410. LinkScript.Concat(' OBJSECTION _NULL||BEGDATA');
  411. LinkScript.Concat(' OBJSECTION _AFTERNULL||BEGDATA');
  412. LinkScript.Concat(' OBJSECTION *||BEGDATA');
  413. LinkScript.Concat(' OBJSECTION *||DATA');
  414. LinkScript.Concat(' SYMBOL _edata');
  415. LinkScript.Concat(' OBJSECTION *||BSS');
  416. LinkScript.Concat(' SYMBOL _end');
  417. LinkScript.Concat(' OBJSECTION *||STACK');
  418. LinkScript.Concat(' OBJSECTION *||HEAP');
  419. end;
  420. LinkScript.Concat('ENDEXESECTION');
  421. if (cs_debuginfo in current_settings.moduleswitches) and
  422. (target_dbg.id in [dbg_dwarf2,dbg_dwarf3,dbg_dwarf4]) then
  423. begin
  424. LinkScript.Concat('EXESECTION .debug_info');
  425. LinkScript.Concat(' OBJSECTION .DEBUG_INFO||DWARF');
  426. LinkScript.Concat('ENDEXESECTION');
  427. LinkScript.Concat('EXESECTION .debug_abbrev');
  428. LinkScript.Concat(' OBJSECTION .DEBUG_ABBREV||DWARF');
  429. LinkScript.Concat('ENDEXESECTION');
  430. LinkScript.Concat('EXESECTION .debug_line');
  431. LinkScript.Concat(' OBJSECTION .DEBUG_LINE||DWARF');
  432. LinkScript.Concat('ENDEXESECTION');
  433. LinkScript.Concat('EXESECTION .debug_aranges');
  434. LinkScript.Concat(' OBJSECTION .DEBUG_ARANGES||DWARF');
  435. LinkScript.Concat('ENDEXESECTION');
  436. end;
  437. LinkScript.Concat('ENTRYNAME ..start');
  438. end;
  439. constructor TInternalLinkerMsDos.create;
  440. begin
  441. inherited create;
  442. CArObjectReader:=TOmfLibObjectReader;
  443. CExeOutput:=TMZExeOutput;
  444. CObjInput:=TOmfObjInput;
  445. end;
  446. {*****************************************************************************
  447. Initialize
  448. *****************************************************************************}
  449. initialization
  450. RegisterLinker(ld_int_msdos,TInternalLinkerMsDos);
  451. {$if defined(USE_LINKER_TLINK)}
  452. RegisterLinker(ld_msdos,TExternalLinkerMsDosTLink);
  453. {$elseif defined(USE_LINKER_ALINK)}
  454. RegisterLinker(ld_msdos,TExternalLinkerMsDosALink);
  455. {$elseif defined(USE_LINKER_WLINK)}
  456. RegisterLinker(ld_msdos,TExternalLinkerMsDosWLink);
  457. {$else}
  458. {$fatal no linker defined}
  459. {$endif}
  460. RegisterTarget(system_i8086_msdos_info);
  461. end.