t_sinclairql.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. {
  2. Copyright (c) 2020 by Free Pascal Development Team
  3. This unit implements support import, export, link routines
  4. for the m68k Sinclair QL 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_sinclairql;
  19. {$i fpcdefs.inc}
  20. interface
  21. uses
  22. rescmn, comprsrc, link;
  23. type
  24. PLinkerSinclairQL = ^TLinkerSinclairQL;
  25. TLinkerSinclairQL = class(texternallinker)
  26. private
  27. Origin: DWord;
  28. UseVLink: boolean;
  29. function WriteResponseFile(isdll: boolean): boolean;
  30. procedure SetSinclairQLInfo;
  31. function MakeSinclairQLExe: boolean;
  32. public
  33. constructor Create; override;
  34. procedure SetDefaultInfo; override;
  35. procedure InitSysInitUnitName; override;
  36. function MakeExecutable: boolean; override;
  37. end;
  38. implementation
  39. uses
  40. sysutils,cutils,cfileutl,cclasses,aasmbase,
  41. globtype,globals,systems,verbose,cscript,fmodule,i_sinclairql;
  42. type
  43. TQLHeader = packed record
  44. hdr_id: array[0..17] of char;
  45. hdr_reserved: byte;
  46. hdr_length: byte;
  47. hdr_access: byte;
  48. hdr_type: byte;
  49. hdr_data: dword;
  50. hdr_extra: dword;
  51. end;
  52. TXTccData = packed record
  53. xtcc_id: array[0..3] of char;
  54. xtcc_data: dword;
  55. end;
  56. const
  57. DefaultQLHeader: TQLHeader = (
  58. hdr_id: ']!QDOS File Header';
  59. hdr_reserved: 0;
  60. hdr_length: $f;
  61. hdr_access: 0;
  62. hdr_type: 1;
  63. hdr_data: 0;
  64. hdr_extra: 0;
  65. );
  66. DefaultXTccData: TXTCCData = (
  67. xtcc_id: 'XTcc';
  68. xtcc_data: 0;
  69. );
  70. const
  71. DefaultOrigin = $0;
  72. ProgramHeaderName = 'main';
  73. constructor TLinkerSinclairQL.Create;
  74. begin
  75. UseVLink:=(cs_link_vlink in current_settings.globalswitches);
  76. Inherited Create;
  77. { allow duplicated libs (PM) }
  78. SharedLibFiles.doubles:=true;
  79. StaticLibFiles.doubles:=true;
  80. end;
  81. procedure TLinkerSinclairQL.SetSinclairQLInfo;
  82. begin
  83. if ImageBaseSetExplicity then
  84. Origin:=ImageBase
  85. else
  86. Origin:=DefaultOrigin;
  87. with Info do
  88. begin
  89. if not UseVLink then
  90. begin
  91. ExeCmd[1]:='ld $DYNLINK $OPT -d -n -o $EXE $RES';
  92. end
  93. else
  94. begin
  95. ExeCmd[1]:='vlink $QLFLAGS $FLAGS $GCSECTIONS $OPT $STRIP $MAP -o $EXE -T $RES';
  96. end;
  97. end;
  98. end;
  99. procedure TLinkerSinclairQL.SetDefaultInfo;
  100. begin
  101. if target_info.system = system_m68k_sinclairql then
  102. SetSinclairQLInfo;
  103. end;
  104. procedure TLinkerSinclairQL.InitSysInitUnitName;
  105. begin
  106. sysinitunit:='si_prc';
  107. end;
  108. function TLinkerSinclairQL.WriteResponseFile(isdll: boolean): boolean;
  109. var
  110. linkres : TLinkRes;
  111. HPath : TCmdStrListItem;
  112. s : string;
  113. begin
  114. WriteResponseFile:=False;
  115. { Open link.res file }
  116. LinkRes:=TLinkRes.Create(outputexedir+Info.ResName,true);
  117. if UseVLink and (source_info.dirsep <> '/') then
  118. LinkRes.fForceUseForwardSlash:=true;
  119. { Write path to search libraries }
  120. HPath:=TCmdStrListItem(current_module.locallibrarysearchpath.First);
  121. while assigned(HPath) do
  122. begin
  123. s:=HPath.Str;
  124. if (cs_link_on_target in current_settings.globalswitches) then
  125. s:=ScriptFixFileName(s);
  126. LinkRes.Add('-L'+s);
  127. HPath:=TCmdStrListItem(HPath.Next);
  128. end;
  129. HPath:=TCmdStrListItem(LibrarySearchPath.First);
  130. while assigned(HPath) do
  131. begin
  132. s:=HPath.Str;
  133. if s<>'' then
  134. LinkRes.Add('SEARCH_DIR("'+s+'")');
  135. HPath:=TCmdStrListItem(HPath.Next);
  136. end;
  137. LinkRes.Add('INPUT (');
  138. { add objectfiles, start with prt0 always }
  139. if not (target_info.system in systems_internal_sysinit) then
  140. begin
  141. s:=FindObjectFile('prt0','',false);
  142. LinkRes.AddFileName(maybequoted(s));
  143. end;
  144. while not ObjectFiles.Empty do
  145. begin
  146. s:=ObjectFiles.GetFirst;
  147. if s<>'' then
  148. begin
  149. { vlink doesn't use SEARCH_DIR for object files }
  150. if UseVLink then
  151. s:=FindObjectFile(s,'',false);
  152. LinkRes.AddFileName(maybequoted(s));
  153. end;
  154. end;
  155. { Write staticlibraries }
  156. if not StaticLibFiles.Empty then
  157. begin
  158. { vlink doesn't need, and doesn't support GROUP }
  159. if not UseVLink then
  160. begin
  161. LinkRes.Add(')');
  162. LinkRes.Add('GROUP(');
  163. end;
  164. while not StaticLibFiles.Empty do
  165. begin
  166. S:=StaticLibFiles.GetFirst;
  167. LinkRes.AddFileName(maybequoted(s));
  168. end;
  169. end;
  170. LinkRes.Add(')');
  171. with LinkRes do
  172. begin
  173. Add('');
  174. Add('PHDRS {');
  175. Add(' '+ProgramHeaderName+' PT_LOAD;');
  176. Add('}');
  177. Add('SECTIONS');
  178. Add('{');
  179. Add(' . = 0x'+hexstr(Origin,8)+';');
  180. Add(' .text : {');
  181. Add(' _stext = .;');
  182. Add(' *(.text .text.* )');
  183. Add(' *(.data .data.* .rodata .rodata.* .fpc.* )');
  184. Add(' *(.stack .stack.*)');
  185. { force the end of section to be word aligned }
  186. Add(' . = ALIGN(2); SHORT(0x514C);');
  187. Add(' _etext = .;');
  188. Add(' } :'+ProgramHeaderName);
  189. Add(' .bss (NOLOAD): {');
  190. Add(' _sbss = .;');
  191. Add(' *(.bss .bss.*)');
  192. Add(' . = ALIGN(2); SHORT(0x0000);');
  193. Add(' _ebss = .;');
  194. Add(' } :'+ProgramHeaderName);
  195. Add('}');
  196. end;
  197. { Write and Close response }
  198. linkres.writetodisk;
  199. linkres.free;
  200. WriteResponseFile:=True;
  201. end;
  202. function TLinkerSinclairQL.MakeSinclairQLExe: boolean;
  203. var
  204. BinStr,
  205. CmdStr : TCmdStr;
  206. StripStr: string[40];
  207. DynLinkStr : ansistring;
  208. GCSectionsStr : string;
  209. FlagsStr : string;
  210. QLFlagsStr: string;
  211. MapStr : string;
  212. ExeName: string;
  213. fd,fs: file;
  214. fhdr: text;
  215. buf: pointer;
  216. bufread,bufsize: longint;
  217. HdrName: string;
  218. HeaderLine: string;
  219. HeaderSize: longint;
  220. code: word;
  221. QLHeader: TQLHeader;
  222. XTccData: TXTccData;
  223. BinSize: longint;
  224. RelocSize: longint;
  225. DataSpace: DWord;
  226. begin
  227. StripStr:='';
  228. GCSectionsStr:='';
  229. DynLinkStr:='';
  230. FlagsStr:='';
  231. QLFlagsStr:='';
  232. MapStr:='';
  233. if (cs_link_map in current_settings.globalswitches) then
  234. MapStr:='-M'+maybequoted(ScriptFixFilename(current_module.mapfilename));
  235. if (cs_link_strip in current_settings.globalswitches) then
  236. StripStr:='-s';
  237. if rlinkpath<>'' then
  238. DynLinkStr:='--rpath-link '+rlinkpath;
  239. if UseVLink then
  240. begin
  241. if create_smartlink_sections then
  242. GCSectionsStr:='-gc-all';
  243. if sinclairql_vlink_experimental then
  244. QLFlagsStr:='-b sinclairql -q -'+lower(sinclairql_metadata_format)+' -stack='+tostr(StackSize)
  245. else
  246. QLFlagsStr:='-b rawseg -q';
  247. end;
  248. ExeName:=current_module.exefilename;
  249. HdrName:=ExeName+'.hdr';
  250. { Call linker }
  251. SplitBinCmd(Info.ExeCmd[1],BinStr,CmdStr);
  252. binstr:=FindUtil(utilsprefix+BinStr);
  253. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  254. Replace(cmdstr,'$EXE',maybequoted(ScriptFixFileName(ExeName)));
  255. Replace(cmdstr,'$RES',maybequoted(ScriptFixFileName(outputexedir+Info.ResName)));
  256. Replace(cmdstr,'$MAP',MapStr);
  257. Replace(cmdstr,'$FLAGS',FlagsStr);
  258. Replace(cmdstr,'$STRIP',StripStr);
  259. Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
  260. Replace(cmdstr,'$DYNLINK',DynLinkStr);
  261. Replace(cmdstr,'$QLFLAGS',QLFlagsStr);
  262. MakeSinclairQLExe:=DoExec(BinStr,CmdStr,true,false);
  263. { Kludge:
  264. With the above linker script, vlink will produce two files. The main binary
  265. and the relocation info. Here we copy the two together. (KB) }
  266. if MakeSinclairQLExe and not sinclairql_vlink_experimental then
  267. begin
  268. QLHeader:=DefaultQLHeader;
  269. XTccData:=DefaultXTccData;
  270. BinSize:=0;
  271. RelocSize:=0;
  272. bufsize:=16384;
  273. {$push}
  274. {$i-}
  275. { Rename vlink's output file into the header file it is, then parse the
  276. expected length from it. Later we use either this size or the final binary
  277. size in the BASIC loader, depending on which one is bigger. (KB) }
  278. RenameFile(ExeName,HdrName);
  279. assign(fhdr,HdrName);
  280. reset(fhdr);
  281. readln(fhdr,HeaderLine);
  282. Val(Copy(HeaderLine,RPos('0x',HeaderLine),Length(HeaderLine)),HeaderSize,code);
  283. close(fhdr);
  284. buf:=GetMem(bufsize);
  285. assign(fd,ExeName);
  286. rewrite(fd,1);
  287. assign(fs,ExeName+'.'+ProgramHeaderName+'.rel'+ProgramHeaderName);
  288. reset(fs,1);
  289. RelocSize := FileSize(fs);
  290. close(fs);
  291. assign(fs,ExeName+'.'+ProgramHeaderName);
  292. reset(fs,1);
  293. BinSize := FileSize(fs);
  294. { We assume .bss size is total size indicated by linker minus emmited binary.
  295. DataSpace size is .bss + stack space }
  296. DataSpace := NToBE(DWord(max((HeaderSize - BinSize) - RelocSize + StackSize,0)));
  297. { Option: prepend QEmuLator and QPC2 v5 compatible header to EXE }
  298. if sinclairql_metadata_format='QHDR' then
  299. begin
  300. QLHeader.hdr_data:=DataSpace;
  301. blockwrite(fd, QLHeader, sizeof(QLHeader));
  302. end;
  303. repeat
  304. blockread(fs,buf^,bufsize,bufread);
  305. blockwrite(fd,buf^,bufread);
  306. until eof(fs);
  307. close(fs);
  308. // erase(fs);
  309. assign(fs,ExeName+'.'+ProgramHeaderName+'.rel'+ProgramHeaderName);
  310. reset(fs,1);
  311. repeat
  312. blockread(fs,buf^,bufsize,bufread);
  313. blockwrite(fd,buf^,bufread);
  314. until eof(fs);
  315. close(fs);
  316. // erase(fs);
  317. { Option: append cross compilation data space marker, this can be picked up by
  318. a special version of InfoZIP (compiled with -DQLZIP and option -Q) or by any
  319. of the XTcc unpack utilities }
  320. if sinclairql_metadata_format='XTCC' then
  321. begin
  322. XTccData.xtcc_data:=DataSpace;
  323. blockwrite(fd, XTccData, sizeof(XTccData));
  324. end;
  325. close(fd);
  326. {$pop}
  327. FreeMem(buf);
  328. MakeSinclairQLExe:=(code = 0) and not (BinSize = 0) and (IOResult = 0);
  329. end;
  330. end;
  331. function TLinkerSinclairQL.MakeExecutable:boolean;
  332. var
  333. success : boolean;
  334. bootfile : TScript;
  335. ExeName: String;
  336. begin
  337. if not(cs_link_nolink in current_settings.globalswitches) then
  338. Message1(exec_i_linking,current_module.exefilename);
  339. { Write used files and libraries }
  340. WriteResponseFile(false);
  341. success:=MakeSinclairQLExe;
  342. { Remove ReponseFile }
  343. if (success) and not(cs_link_nolink in current_settings.globalswitches) then
  344. DeleteFile(outputexedir+Info.ResName);
  345. MakeExecutable:=success; { otherwise a recursive call to link method }
  346. end;
  347. {*****************************************************************************
  348. Initialize
  349. *****************************************************************************}
  350. initialization
  351. RegisterLinker(ld_sinclairql,TLinkerSinclairQL);
  352. RegisterTarget(system_m68k_sinclairql_info);
  353. end.