comprrlc.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  1. /* comprrlc.c: Routines for compressed relocation tables
  2. Copyright (C) 2003-2007 Kevin Kofler
  3. Portions copyright (C) 2002-2003 Sebastian Reichelt
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software Foundation,
  14. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  15. #include "comprrlc.h"
  16. #include "../integers.h"
  17. #include "../special.h"
  18. #include "../manip.h"
  19. #include "model/list.h"
  20. #include <stdlib.h>
  21. #include <string.h>
  22. // Append a new I1 to the section. Return TRUE on success, FALSE on failure.
  23. static BOOLEAN AppendI1ToSection (SECTION *Section, I1 NewI1)
  24. {
  25. TI1 *NewSpace = (TI1 *) AllocateSpaceInSection (Section, 1);
  26. if (!NewSpace)
  27. return FALSE;
  28. WriteTI1 (*NewSpace, NewI1);
  29. return TRUE;
  30. }
  31. // Append a new I2 to the section. Return TRUE on success, FALSE on failure.
  32. static BOOLEAN AppendI2ToSection (SECTION *Section, I2 NewI2)
  33. {
  34. TI2 *NewSpace = (TI2 *) AllocateSpaceInSection (Section, 2);
  35. if (!NewSpace)
  36. return FALSE;
  37. WriteTI2 (*NewSpace, NewI2);
  38. return TRUE;
  39. }
  40. // Emit a compressed reloc nibble buffer. Return TRUE on success, FALSE on
  41. // failure.
  42. static BOOLEAN EmitCompressedRelocNibbles (SECTION *Section, I4 NibbleCount, I1 *NibbleBuffer)
  43. {
  44. I4 CurrentNibble;
  45. I4 ByteCount = (NibbleCount + 1) >> 1; // number of bytes emitted as nibbles
  46. #define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) return FALSE;})
  47. #define AppendNibbles(n1, n2) AppendI1 (((n1) << 4) + (n2))
  48. if (NibbleCount < 3)
  49. {
  50. // Too few entries for nibbles, emit bytes.
  51. for (CurrentNibble = 0; CurrentNibble < NibbleCount; CurrentNibble++)
  52. AppendI1 (NibbleBuffer[CurrentNibble] + 1);
  53. return TRUE;
  54. }
  55. // Emit the first nibble pair.
  56. AppendNibbles (ByteCount + 6, *NibbleBuffer);
  57. // Emit the remaining nibble pairs.
  58. for (CurrentNibble = 1; CurrentNibble < NibbleCount - 1; CurrentNibble += 2)
  59. AppendNibbles (NibbleBuffer[CurrentNibble], NibbleBuffer[CurrentNibble + 1]);
  60. // If the number of nibbles is even, emit the last one as a byte.
  61. if (!(NibbleCount & 1))
  62. AppendI1 (NibbleBuffer[NibbleCount - 1] + 1);
  63. #undef AppendI1
  64. #undef AppendNibbles
  65. return TRUE;
  66. }
  67. // Emit a compressed reloc. If Offset is -1, end the relocation table. Return
  68. // TRUE on success, FALSE on failure.
  69. static BOOLEAN EmitCompressedReloc (SECTION *Section, OFFSET Offset)
  70. {
  71. // Maximum number of nibbles in a nibble group.
  72. #define MAX_NIBBLES 9
  73. static I4 NibbleCount = 0;
  74. static I1 NibbleBuffer[MAX_NIBBLES] = {};
  75. #define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) return FALSE;})
  76. #define AppendI2(i2) ({if (!AppendI2ToSection (Section, (i2))) return FALSE;})
  77. #define EmitNibbles() ({if (!EmitCompressedRelocNibbles (Section, NibbleCount, NibbleBuffer)) return FALSE; NibbleCount = 0;})
  78. if (Offset == -1)
  79. {
  80. // End the relocation table.
  81. EmitNibbles();
  82. AppendI1(0);
  83. }
  84. else
  85. {
  86. Continue:
  87. if (Offset < 16)
  88. {
  89. // We have a nibble. Check if we have room left in the buffer.
  90. if (NibbleCount == MAX_NIBBLES)
  91. {
  92. // Room full. Emit the nibbles in the buffer.
  93. EmitNibbles();
  94. }
  95. // Put the nibble into the buffer.
  96. NibbleBuffer[NibbleCount++] = Offset;
  97. }
  98. else
  99. {
  100. // We have a byte or a word. Emit the nibbles in the buffer.
  101. EmitNibbles();
  102. if (Offset < 127)
  103. {
  104. // We have a byte.
  105. AppendI1 (Offset + 1);
  106. }
  107. else if (Offset < 16510)
  108. {
  109. // We have a word.
  110. AppendI2 (Offset + 49025);
  111. }
  112. else
  113. {
  114. // We have an 0xFFFF word and an additional offset.
  115. AppendI2 (65535);
  116. Offset -= 16510;
  117. goto Continue;
  118. }
  119. }
  120. }
  121. #undef AppendI1
  122. #undef AppendI2
  123. #undef EmitNibbles
  124. #undef MAX_NIBBLES
  125. return TRUE;
  126. }
  127. // Emit a compressed reloc given the true offset between the 2 relocs. Compute
  128. // the offset as used in the compressed reloc table or give an error message if
  129. // it isn't representable. Call EmitCompressedReloc with the computed offset.
  130. // Return TRUE on success, FALSE on failure.
  131. static BOOLEAN EmitCompressedRelocFromActualOffset (SECTION *Section, OFFSET Offset)
  132. {
  133. if ((Offset > 0) && (Offset & 1))
  134. {
  135. Error (Section->FileName, "Odd offset `%ld' between 2 absolute relocs. Even offset needed.", (long) Offset);
  136. return FALSE;
  137. }
  138. if (Offset < 4)
  139. {
  140. Error (Section->FileName, (Offset >= 0) ? "Cannot emit overlapping absolute relocs (offset `0x%lx', need offset >=4)."
  141. : "Invalid internal reloc sorting order (offset `-0x%lx', need positive offset).",
  142. (Offset >= 0) ? (long)Offset : -(long)Offset);
  143. return FALSE;
  144. }
  145. return EmitCompressedReloc (Section, (Offset - 4) >> 1);
  146. }
  147. // Emit a reloc table in compressed format for the items enumerated in the
  148. // list model specified by Model.
  149. static BOOLEAN EmitCompressedFormatRelocs (LIST_MODEL *Model, SECTION *SourceSection, const LOCATION *SourceBase, void *UserData, SECTION *Section)
  150. {
  151. if (!SourceSection)
  152. SourceSection = Section;
  153. SourceSection->Frozen = TRUE;
  154. {
  155. void *NextItem = NULL;
  156. OFFSET Offset;
  157. // Output the first item in the list...
  158. // Output the target offset, and get the reloc's location.
  159. Offset = Model (SourceSection, &NextItem, UserData, FALSE, TRUE);
  160. // A return value < 0 indicates that the list was empty.
  161. if (Offset >= 0)
  162. {
  163. OFFSET BaseAddress = GetLocationOffset (SourceSection, SourceBase);
  164. if (BaseAddress & 1)
  165. {
  166. Error (Section->FileName, "Invalid base address `0x%lx'. Even base address needed.", (long) BaseAddress);
  167. return FALSE;
  168. }
  169. if (Offset & 1)
  170. {
  171. Error (Section->FileName, "Odd offset between base address `0x%lx' and first absolute reloc `0x%lx'. Even offset needed.", (long) BaseAddress, (long) Offset);
  172. return FALSE;
  173. }
  174. if (Offset < BaseAddress)
  175. {
  176. Error (Section->FileName, "Cannot emit absolute reloc located at `0x%lx' before base address.", (long) Offset);
  177. return FALSE;
  178. }
  179. // Emit the reloc.
  180. if (!(EmitCompressedReloc (Section, (Offset - BaseAddress) >> 1)))
  181. return FALSE;
  182. // Output the remaining items in the list...
  183. while (NextItem)
  184. {
  185. // Save the previous offset.
  186. OFFSET LastOffset = Offset;
  187. // Output the target offset, and get the reloc's location.
  188. Offset = Model (SourceSection, &NextItem, UserData, FALSE, TRUE);
  189. // A negative offset means the list ends here.
  190. if (Offset >= 0)
  191. {
  192. // Emit the reloc.
  193. if (!(EmitCompressedRelocFromActualOffset (Section, Offset - LastOffset)))
  194. return FALSE;
  195. }
  196. }
  197. }
  198. return EmitCompressedReloc (Section, -1);
  199. }
  200. }
  201. // Append relocation entries in the format required by the TIGCCLIB relocation
  202. // code. If TargetSection is NULL, append all relocation entries that point to
  203. // unhandled sections. Otherwise, append all relocation entries pointing to this
  204. // section.
  205. // Warning: Inserting relocs is special: Since the relocs are changed
  206. // during the process, they can be inserted only once.
  207. BOOLEAN InsertCompressedRelocs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
  208. {
  209. // Initialize user data for list model.
  210. RELOC_USER_DATA UserData = {TargetSection};
  211. // Do code optimizations now, since this might reduce the number
  212. // of relocs. This is useful even if a target section was specified
  213. // because we can't fix the code anymore after the section is frozen.
  214. // This could even cause us to emit invalid code (bra +0).
  215. FixCode (Section->Parent);
  216. // If a target section is specified, it is essential now, and it may
  217. // not be modified any more.
  218. if (TargetSection)
  219. TargetSection->Frozen = TargetSection->Essential = TRUE;
  220. // Apply the format documented in _compressed_format_relocs.s.
  221. if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) RelocListModel, MergedSection, Reference, &UserData, Section)))
  222. return FALSE;
  223. // If a target section was specified, and its Handled flag was not
  224. // set yet, now setting it is probably correct.
  225. if (TargetSection)
  226. TargetSection->Handled = TRUE;
  227. return TRUE;
  228. }
  229. // Append relocation entries in the format required by the TIGCCLIB relocation
  230. // code, using InsertCompressedRelocs. If TargetSection is NULL, output an empty
  231. // relocation table. Otherwise, append all relocation entries pointing to this
  232. // section.
  233. // Warning: Inserting relocs is special: Since the relocs are changed
  234. // during the process, they can be inserted only once.
  235. BOOLEAN InsertCompressedSectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
  236. {
  237. if (TargetSection)
  238. return InsertCompressedRelocs (Section, TargetSection, MergedSection, Reference);
  239. else
  240. return (AllocateSpaceInSection (Section, 1) != NULL);
  241. }
  242. // Append ROM calls in the format required by the TIGCCLIB relocation code.
  243. BOOLEAN InsertCompressedROMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
  244. {
  245. PROGRAM *Program = Section->Parent;
  246. // Initialize user data for list model.
  247. ROM_CALL_USER_DATA UserData = {NULL, 0, -1};
  248. // Allocate space for dynamic user data.
  249. if (!(UserData.ROMFunctions = calloc (Program->HighestROMCall + 1, sizeof (ROM_CALL_FUNCTION_DATA))))
  250. {
  251. Error (NULL, "Out of memory while inserting ROM calls with compressed relocs.");
  252. return FALSE;
  253. }
  254. {
  255. BOOLEAN Result = TRUE;
  256. // Go through all ROM calls and increment the appropriate counters.
  257. COUNT ROMRelocCount = GetSectionItemCount ((LIST_MODEL *) ROMCallListModel, MergedSection ? : Section, &UserData);
  258. // If no ROM calls are used, do not output anything but the final null-terminator.
  259. if (ROMRelocCount > 0)
  260. {
  261. // Apply the format documented in _compressed_format_rom_calls.s.
  262. OFFSET LastFunction = -1;
  263. for (UserData.CurFunction = 0; UserData.CurFunction <= Program->HighestROMCall; UserData.CurFunction++)
  264. {
  265. if (UserData.ROMFunctions[UserData.CurFunction].RelocCount)
  266. {
  267. // Emit the function number as a compressed offset.
  268. // Yes, the offset is intentionally integrated into the table that follows!
  269. OFFSET FunctionOffset = UserData.CurFunction - (LastFunction + 1);
  270. if (!(EmitCompressedReloc (Section, FunctionOffset)))
  271. {
  272. Result = FALSE;
  273. break;
  274. }
  275. LastFunction = UserData.CurFunction;
  276. // Emit all ROM calls using this function.
  277. if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) ROMCallListModel, MergedSection, Reference, &UserData, Section)))
  278. {
  279. Result = FALSE;
  280. break;
  281. }
  282. }
  283. }
  284. }
  285. // Output the final null-terminator.
  286. if (!(AppendI1ToSection (Section, 0)))
  287. Result = FALSE;
  288. // Free the extra information.
  289. free (UserData.ROMFunctions);
  290. return Result;
  291. }
  292. }
  293. // Emit a compressed index in PreOs format. Return TRUE on success, FALSE on
  294. // failure. This is NOT the same format as the compressed relocs!
  295. static BOOLEAN EmitPreOsCompressedIndex (SECTION *Section, COUNT LastIndex, COUNT Index)
  296. {
  297. #define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) return FALSE;})
  298. #define AppendI2(i2) ({if (!AppendI2ToSection (Section, (i2))) return FALSE;})
  299. OFFSET Offset = Index - (LastIndex + 1);
  300. if (Offset < 0)
  301. {
  302. // The computed offset must be nonnegative (positive or zero).
  303. Error (NULL, "Invalid index sequence during PreOs compressed relocs.");
  304. return FALSE;
  305. }
  306. else
  307. {
  308. if (Offset <= 253)
  309. {
  310. // Offsets in the range 0 .. 253 are encoded as a single byte.
  311. AppendI1 (Offset);
  312. }
  313. else if (Offset <= (254 + 255))
  314. {
  315. // Offsets in the range 254+0 .. 254+255 are encoded as 2 bytes.
  316. AppendI1 (254);
  317. AppendI1 (Offset - 254);
  318. }
  319. else
  320. {
  321. // For offsets larger than 254+255, we don't encode the offset, but
  322. // the actual index, in a 3-byte encoding.
  323. AppendI1 (255);
  324. AppendI2 (Index);
  325. }
  326. }
  327. #undef AppendI1
  328. #undef AppendI2
  329. return TRUE;
  330. }
  331. // Append the section size and relocs to the specified section in the
  332. // format required by PreOS.
  333. // Warning: Inserting relocs is special: Since the relocs are changed
  334. // during the process, they can be inserted only once.
  335. BOOLEAN InsertPreOSSectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
  336. {
  337. if (TargetSection)
  338. {
  339. // Initialize user data for list model.
  340. RELOC_USER_DATA UserData = {TargetSection};
  341. // Target size. Round up to the next multiple of 4, and output the size
  342. // divided by 4.
  343. if (!(AppendI2ToSection (Section, (TargetSection->Size + 3) >> 2)))
  344. return FALSE;
  345. // Apply the format documented in _preos_program_header.s.
  346. return EmitCompressedFormatRelocs ((LIST_MODEL *) RelocListModel, MergedSection, Reference, &UserData, Section);
  347. }
  348. else
  349. {
  350. // Target size:
  351. return (AllocateSpaceInSection (Section, 2) != NULL);
  352. // The empty relocation table can be omitted.
  353. }
  354. }
  355. // Append ROM calls in the format required by PreOs.
  356. BOOLEAN InsertPreOsROMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
  357. {
  358. PROGRAM *Program = Section->Parent;
  359. // Initialize user data for list model.
  360. ROM_CALL_USER_DATA UserData = {NULL, 0, -1};
  361. // Allocate space for dynamic user data.
  362. if (!(UserData.ROMFunctions = calloc (Program->HighestROMCall + 1, sizeof (ROM_CALL_FUNCTION_DATA))))
  363. {
  364. Error (NULL, "Out of memory while inserting ROM calls with compressed relocs.");
  365. return FALSE;
  366. }
  367. {
  368. BOOLEAN Result = TRUE;
  369. #define EmitIndex(oldidx,idx) ({if (!EmitPreOsCompressedIndex (Section, (oldidx), (idx))) { Result = FALSE; goto ROMCallsOutOfMem; } })
  370. // Go through all ROM calls and increment the appropriate counters.
  371. COUNT ROMRelocCount = GetItemCount ((LIST_MODEL *) ROMCallListModel, Program, NULL, &UserData);
  372. // If no ROM calls are used, output an empty table.
  373. if (!ROMRelocCount)
  374. {
  375. EmitIndex (-1, 0);
  376. }
  377. else
  378. {
  379. // Apply the format documented in _preos_program_header.s.
  380. OFFSET LastFunction = -1;
  381. // Output the number of functions used.
  382. EmitIndex (-1, UserData.ROMFunctionCount);
  383. for (UserData.CurFunction = 0; UserData.CurFunction <= Program->HighestROMCall; UserData.CurFunction++)
  384. {
  385. if (UserData.ROMFunctions[UserData.CurFunction].RelocCount)
  386. {
  387. // Output the current function number.
  388. EmitIndex (LastFunction, UserData.CurFunction);
  389. LastFunction = UserData.CurFunction;
  390. // Emit all ROM calls using this function.
  391. if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) ROMCallListModel, MergedSection, Reference, &UserData, Section)))
  392. {
  393. Result = FALSE;
  394. goto ROMCallsOutOfMem;
  395. }
  396. }
  397. }
  398. }
  399. #undef EmitIndex
  400. ROMCallsOutOfMem:
  401. // Free the extra information.
  402. free (UserData.ROMFunctions);
  403. return FALSE;
  404. }
  405. }
  406. // Append RAM calls in the format required by PreOs.
  407. BOOLEAN InsertPreOsRAMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
  408. {
  409. PROGRAM *Program = Section->Parent;
  410. BOOLEAN Result = TRUE;
  411. // Initialize user data for list model.
  412. RAM_CALL_USER_DATA UserData = {{{NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}}, 0, RT_ALL_TYPES, -1};
  413. // Allocate space for dynamic user data.
  414. RAM_CALL_TYPE_DATA *CurType = UserData.RAMTypes;
  415. RAM_CALL_TYPE LoopType;
  416. for (LoopType = 0; LoopType < RAM_CALL_TYPE_COUNT; LoopType++, CurType++)
  417. {
  418. if (!(CurType->Functions = calloc (Program->HighestRAMCall + 1, sizeof (RAM_CALL_FUNCTION_DATA))))
  419. {
  420. Error (NULL, "Out of memory while inserting RAM calls with compressed relocs.");
  421. Result = FALSE;
  422. goto RAMCallsOutOfMem;
  423. }
  424. }
  425. {
  426. #define EmitIndex(oldidx,idx) ({if (!EmitPreOsCompressedIndex (Section, (oldidx), (idx))) { Result = FALSE; goto RAMCallsOutOfMem; } })
  427. // Go through all RAM calls and increment the appropriate counters.
  428. COUNT RAMRelocCount = GetItemCount ((LIST_MODEL *) RAMCallListModel, Program, NULL, &UserData);
  429. // If no RAM calls are used, output an empty table.
  430. if (!RAMRelocCount)
  431. {
  432. EmitIndex (-1, 0);
  433. }
  434. else
  435. {
  436. // Apply the format documented in _preos_program_header.s.
  437. OFFSET LastFunction = -1;
  438. // Output the number of functions used.
  439. EmitIndex (-1, UserData.RAMFunctionCount);
  440. // For each possible RAM call type...
  441. for (UserData.CurType = 0; UserData.CurType < RAM_CALL_TYPE_COUNT; UserData.CurType++)
  442. {
  443. // Check if the type has been used.
  444. if (UserData.RAMTypes[UserData.CurType].FunctionCount)
  445. {
  446. // Gather information about the type.
  447. SIZE TypeSize = ((UserData.CurType == RT_RAM_CALL_2) || (UserData.CurType == RT_EXTRA_RAM_2) ? 2 : 4);
  448. BOOLEAN TypeExtraRAM = (UserData.CurType == RT_EXTRA_RAM_4) || (UserData.CurType == RT_EXTRA_RAM_2);
  449. I2 TypeFlags = 0;
  450. if (TypeSize < 4)
  451. TypeFlags |= (1 << 15);
  452. if (TypeExtraRAM)
  453. TypeFlags |= (1 << 14);
  454. // For each possible RAM function number...
  455. for (UserData.CurFunction = 0; UserData.CurFunction <= Program->HighestRAMCall; UserData.CurFunction++)
  456. {
  457. // Check if a RAM function of this type and number has been used.
  458. if (UserData.RAMTypes[UserData.CurType].Functions[UserData.CurFunction].RelocCount)
  459. {
  460. // Output the type and number.
  461. COUNT RAMCallNumber = UserData.CurFunction | TypeFlags;
  462. EmitIndex (LastFunction, RAMCallNumber);
  463. LastFunction = RAMCallNumber;
  464. // Emit all RAM calls using this function.
  465. if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) RAMCallListModel, MergedSection, Reference, &UserData, Section)))
  466. {
  467. Result = FALSE;
  468. goto RAMCallsOutOfMem;
  469. }
  470. }
  471. }
  472. }
  473. }
  474. }
  475. #undef EmitIndex
  476. RAMCallsOutOfMem:
  477. // Free the extra information.
  478. for (UserData.CurType = 0; UserData.CurType < RAM_CALL_TYPE_COUNT; UserData.CurType++)
  479. {
  480. RAM_CALL_FUNCTION_DATA *Functions = UserData.RAMTypes[UserData.CurType].Functions;
  481. if (Functions)
  482. free (Functions);
  483. }
  484. }
  485. return Result;
  486. }
  487. // Append libraries and library calls in the format required by PreOs.
  488. BOOLEAN InsertPreOsLibraries (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
  489. {
  490. PROGRAM *Program = Section->Parent;
  491. // Output an empty table if no libraries are referenced.
  492. if (IsEmpty (Program->Libraries))
  493. return (AppendI1ToSection (Section, 0)); // library count
  494. else
  495. {
  496. BOOLEAN Result = TRUE;
  497. // Initialize user data for list model.
  498. LIB_CALL_USER_DATA UserData = {0, NULL, 0, 0, NULL, -1, -1};
  499. if (InitializeLibCallUserData (&UserData, Program))
  500. {
  501. #define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) { Result = FALSE; goto PreOsLibsOutOfMem; } })
  502. #define EmitIndex(oldidx,idx) ({if (!EmitPreOsCompressedIndex (Section, (oldidx), (idx))) { Result = FALSE; goto PreOsLibsOutOfMem; } })
  503. // Go through all library calls and increment the appropriate counters.
  504. COUNT LibRelocCount = GetItemCount ((LIST_MODEL *) LibCallListModel, Program, MergedSection, &UserData);
  505. // Output an empty table if no libraries are referenced.
  506. if (!LibRelocCount)
  507. {
  508. AppendI1 (0); // library count
  509. }
  510. else
  511. {
  512. // Pointer to the inserted data.
  513. I1 *NewData;
  514. // The number of requested libraries.
  515. COUNT LibCount = UserData.UsedLibCount;
  516. AppendI1 (LibCount);
  517. // Apply the format documented in _preos_program_header.s.
  518. // Allocate space for the library names.
  519. NewData = AllocateSpaceInSection (Section, 10 * UserData.UsedLibCount);
  520. // Now handle the library names.
  521. if (NewData)
  522. {
  523. // For each referenced library...
  524. LIB_DATA *CurLib = UserData.Libs;
  525. for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
  526. {
  527. if (CurLib->FunctionCount)
  528. {
  529. // Output its name.
  530. if (strlen (CurLib->Lib->Name) > 8)
  531. Warning (Section->FileName, "Library name `%s' too long; cutting off at 8th character.", CurLib->Lib->Name);
  532. strncpy ((char *)NewData, CurLib->Lib->Name, 8);
  533. WriteTI1 (*((TI1 *) (NewData + 9)), CurLib->Lib->Version);
  534. NewData += 10;
  535. }
  536. }
  537. }
  538. else
  539. {
  540. Error (NULL, "Out of memory while inserting libraries with compressed relocs.");
  541. Result = FALSE;
  542. goto PreOsLibsOutOfMem;
  543. }
  544. {
  545. // For each referenced library...
  546. LIB_DATA *CurLib = UserData.Libs;
  547. for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
  548. {
  549. if (CurLib->FunctionCount)
  550. {
  551. OFFSET LastFunction = -1;
  552. // Output the number of exports used.
  553. EmitIndex (-1, CurLib->FunctionCount - 1);
  554. // For each referenced export...
  555. for (UserData.CurFunction = 0; UserData.CurFunction <= CurLib->Lib->Highest; UserData.CurFunction++)
  556. {
  557. if (CurLib->Functions[UserData.CurFunction].RelocCount)
  558. {
  559. // Output the export number.
  560. EmitIndex (LastFunction, UserData.CurFunction);
  561. LastFunction = UserData.CurFunction;
  562. // Emit all library calls using this export.
  563. if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) LibCallListModel, MergedSection, Reference, &UserData, Section)))
  564. {
  565. Result = FALSE;
  566. goto PreOsLibsOutOfMem;
  567. }
  568. }
  569. }
  570. }
  571. }
  572. }
  573. }
  574. }
  575. else
  576. Result = FALSE;
  577. PreOsLibsOutOfMem:
  578. // Free the extra information.
  579. FinalizeLibCallUserData (&UserData);
  580. #undef AppendI1
  581. #undef EmitIndex
  582. return Result;
  583. }
  584. }
  585. // The reference address used for the compressed relocation tables in PreOs.
  586. #define PREOS_BASE_ADDR 36
  587. // Append import tables and relocation entries in the format required by PreOs.
  588. // In order: library imports, ROM calls, RAM calls, relocs, BSS references. All
  589. // in one stream, pointed to by a single pointer.
  590. // Warning: Inserting relocs is special: Since the relocs are changed
  591. // during the process, they can be inserted only once.
  592. BOOLEAN InsertPreOsCompressedTables (SECTION *Section)
  593. {
  594. PROGRAM *Program = Section->Parent;
  595. SECTION *MergedSection = Program->MainSection;
  596. LOCATION Reference = {Program->EntryPoint.Symbol, Program->EntryPoint.SymbolName, Program->EntryPoint.Offset + PREOS_BASE_ADDR, FALSE};
  597. // FIXME: Remove as soon as specification is not preliminary any more.
  598. Warning (NULL, "Encoding relocation tables according to a PRELIMINARY specification!");
  599. return (InsertPreOsLibraries (Section, MergedSection, &Reference)
  600. && InsertPreOsROMCalls (Section, MergedSection, &Reference)
  601. && InsertPreOsRAMCalls (Section, MergedSection, &Reference)
  602. && InsertCompressedRelocs (Section, NULL, MergedSection, &Reference)
  603. && InsertPreOSSectionRefs (Section, Program->BSSSection, MergedSection, &Reference));
  604. }
  605. #ifdef FARGO_SUPPORT
  606. // Append BSS relocation entries in the format required by Fargo 0.2.1. Append
  607. // all relocation entries pointing to the BSS section.
  608. // Warning: Inserting relocs is special: Since the relocs are changed
  609. // during the process, they can be inserted only once.
  610. BOOLEAN InsertFargo021SectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
  611. {
  612. if (TargetSection)
  613. {
  614. // Initialize user data for list model.
  615. RELOC_USER_DATA UserData = {TargetSection};
  616. if (!(AppendI2ToSection (Section, TargetSection->Size)))
  617. return FALSE;
  618. // Apply the format documented in _fargo021_program_header.s.
  619. return EmitCompressedFormatRelocs ((LIST_MODEL *) RelocListModel, MergedSection, Reference, &UserData, Section);
  620. }
  621. else
  622. return (AllocateSpaceInSection (Section, 2) != NULL);
  623. }
  624. // Append library calls in the format required by Fargo 0.2.1.
  625. BOOLEAN InsertFargo021Libraries (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
  626. {
  627. PROGRAM *Program = Section->Parent;
  628. // Output an empty table if no libraries are referenced.
  629. if (IsEmpty (Program->Libraries))
  630. return (AllocateSpaceInSection (Section, 2) != NULL);
  631. else
  632. {
  633. BOOLEAN Result = TRUE;
  634. // Initialize user data for list model.
  635. LIB_CALL_USER_DATA UserData = {0, NULL, 0, 0, NULL, -1, -1};
  636. if (InitializeLibCallUserData (&UserData, Program))
  637. {
  638. // Go through all library calls and increment the appropriate counters.
  639. COUNT LibRelocCount = GetItemCount ((LIST_MODEL *) LibCallListModel, Program, NULL, &UserData);
  640. // Output an empty table if no libraries are referenced.
  641. if (!LibRelocCount)
  642. {
  643. if (!(AllocateSpaceInSection (Section, 2)))
  644. Result = FALSE;
  645. }
  646. else
  647. {
  648. #define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) { Result = FALSE; goto Fargo021LibsOutOfMem; } })
  649. #define AppendI2(i2) ({if (!AppendI2ToSection (Section, (i2))) { Result = FALSE; goto Fargo021LibsOutOfMem; } })
  650. // Pointer to the inserted data.
  651. I1 *NewData;
  652. SIZE SizeToAllocate;
  653. I1 *LibName;
  654. // The number of requested libraries.
  655. COUNT LibCount = UserData.UsedLibCount;
  656. // The placeholder for the library names.
  657. OFFSET LibNamesLocation;
  658. AppendI2 (LibCount);
  659. LibNamesLocation = Section->Size;
  660. AppendI2 (0);
  661. // Apply the format documented in _fargo021_program_header.s.
  662. {
  663. // For each referenced library...
  664. LIB_DATA *CurLib = UserData.Libs;
  665. for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
  666. {
  667. if (CurLib->FunctionCount)
  668. {
  669. OFFSET LastFunction = -1;
  670. // For each referenced export...
  671. for (UserData.CurFunction = 0; UserData.CurFunction <= CurLib->Lib->Highest; UserData.CurFunction++)
  672. {
  673. if (CurLib->Functions[UserData.CurFunction].RelocCount)
  674. {
  675. // Output the export number.
  676. OFFSET FunctionOffset = UserData.CurFunction - LastFunction;
  677. // Emit the compressed offset. This is NOT the same
  678. // format as the compressed relocs!
  679. if (FunctionOffset < 128)
  680. AppendI1 (FunctionOffset);
  681. else
  682. AppendI2 (0x8000 + FunctionOffset);
  683. LastFunction = UserData.CurFunction;
  684. // Emit all library calls using this export.
  685. if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) LibCallListModel, MergedSection, Reference, &UserData, Section)))
  686. {
  687. Result = FALSE;
  688. goto Fargo021LibsOutOfMem;
  689. }
  690. }
  691. }
  692. AppendI1 (0);
  693. }
  694. }
  695. // Add the redundant null-terminator.
  696. AppendI1 (0);
  697. // Allocate space for the library names.
  698. SizeToAllocate = 0;
  699. // For each referenced library...
  700. CurLib = UserData.Libs;
  701. for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
  702. {
  703. if (CurLib->FunctionCount)
  704. SizeToAllocate += strlen (CurLib->Lib->Name) + 1;
  705. }
  706. NewData = AllocateSpaceInSection (Section, SizeToAllocate);
  707. LibName = NewData;
  708. // Now handle the library names.
  709. if (NewData)
  710. {
  711. // Create a new reloc at the given location, pointing to
  712. // the location of the library names.
  713. {
  714. I1 *LibNamePlaceholder = Section->Data + LibNamesLocation;
  715. RELOC *Reloc = CreateProgramRelativeReloc (Section, &LibNamePlaceholder, Section, LibName - Section->Data, 2);
  716. if (!Reloc)
  717. {
  718. Error (NULL, "Out of memory while inserting libraries with compressed relocs.");
  719. Result = FALSE;
  720. goto Fargo021LibsOutOfMem;
  721. }
  722. }
  723. // For each referenced library...
  724. CurLib = UserData.Libs;
  725. for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
  726. {
  727. if (CurLib->FunctionCount)
  728. {
  729. // Output its name.
  730. strcpy ((char *)LibName, CurLib->Lib->Name);
  731. LibName += strlen (CurLib->Lib->Name) + 1;
  732. }
  733. }
  734. }
  735. else
  736. {
  737. Error (NULL, "Out of memory while inserting libraries with compressed relocs.");
  738. Result = FALSE;
  739. }
  740. }
  741. }
  742. }
  743. else
  744. Result = FALSE;
  745. Fargo021LibsOutOfMem:
  746. // Free the extra information.
  747. FinalizeLibCallUserData (&UserData);
  748. return Result;
  749. }
  750. #undef AppendI1
  751. #undef AppendI2
  752. }
  753. #endif /* FARGO_SUPPORT */
  754. // Emit an mlink-format compressed reloc. If Offset is -1, end the relocation
  755. // table. Return TRUE on success, FALSE on failure.
  756. static BOOLEAN EmitMlinkReloc (SECTION *Section, OFFSET Offset)
  757. {
  758. #define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) return FALSE;})
  759. #define AppendI2(i2) ({if (!AppendI2ToSection (Section, (i2))) return FALSE;})
  760. if (Offset == -1)
  761. {
  762. // End the relocation table.
  763. AppendI1(0);
  764. }
  765. else
  766. {
  767. if (Offset < 128)
  768. {
  769. // We have 1 byte.
  770. AppendI1 (Offset + 0x80);
  771. }
  772. else if (Offset < 16384)
  773. {
  774. // We have 2 bytes.
  775. AppendI1 (Offset >> 7);
  776. AppendI1 ((Offset & 0x7F) + 0x80);
  777. }
  778. else if (Offset < 2097152)
  779. {
  780. // We have 3 bytes.
  781. AppendI1 (Offset >> 14);
  782. AppendI1 ((Offset >> 7) & 0x7F);
  783. AppendI1 ((Offset & 0x7F) + 0x80);
  784. }
  785. else
  786. Error (Section->FileName, "Offset `%ld' for mlink-format reloc doesn't fit into 3 bytes.", (long) Offset);
  787. }
  788. #undef AppendI1
  789. #undef AppendI2
  790. return TRUE;
  791. }
  792. // Emit an mlink-format compressed reloc given the true offset between the 2
  793. // relocs. Compute the offset as used in the compressed reloc table or give
  794. // an error message if it isn't representable. Call EmitMlinkReloc with the
  795. // computed offset. Return TRUE on success, FALSE on failure.
  796. static BOOLEAN EmitMlinkRelocFromActualOffset (SECTION *Section, OFFSET Offset)
  797. {
  798. if ((Offset > 0) && (Offset & 1))
  799. {
  800. Error (Section->FileName, "Odd offset `%ld' between 2 absolute relocs. Even offset needed.", (long) Offset);
  801. return FALSE;
  802. }
  803. if (Offset < 0)
  804. {
  805. Error (Section->FileName, "Invalid internal reloc sorting order (offset `-0x%lx', need positive offset).",
  806. -(long)Offset);
  807. return FALSE;
  808. }
  809. return EmitMlinkReloc (Section, Offset >> 1);
  810. }
  811. // Emit a reloc table in mlink compressed format for the items enumerated in the
  812. // list model specified by Model.
  813. static BOOLEAN EmitMlinkFormatRelocs (LIST_MODEL *Model, SECTION *SourceSection, const LOCATION *SourceBase, void *UserData, SECTION *Section)
  814. {
  815. if (!SourceSection)
  816. SourceSection = Section;
  817. SourceSection->Frozen = TRUE;
  818. {
  819. void *NextItem = NULL;
  820. OFFSET Offset;
  821. // Output the first item in the list...
  822. // Output the target offset, and get the reloc's location.
  823. Offset = Model (SourceSection, &NextItem, UserData, FALSE, TRUE);
  824. // A return value < 0 indicates that the list was empty.
  825. if (Offset >= 0)
  826. {
  827. OFFSET BaseAddress = GetLocationOffset (SourceSection, SourceBase);
  828. if (BaseAddress & 1)
  829. {
  830. Error (Section->FileName, "Invalid base address `0x%lx'. Even base address needed.", (long) BaseAddress);
  831. return FALSE;
  832. }
  833. if (Offset & 1)
  834. {
  835. Error (Section->FileName, "Odd offset between base address `0x%lx' and first absolute reloc `0x%lx'. Even offset needed.", (long) BaseAddress, (long) Offset);
  836. return FALSE;
  837. }
  838. if (Offset < BaseAddress)
  839. {
  840. Error (Section->FileName, "Cannot emit absolute reloc located at `0x%lx' before base address.", (long) Offset);
  841. return FALSE;
  842. }
  843. // Emit the reloc.
  844. if (!(EmitMlinkReloc (Section, (Offset - BaseAddress) >> 1)))
  845. return FALSE;
  846. // Output the remaining items in the list...
  847. while (NextItem)
  848. {
  849. // Save the previous offset.
  850. OFFSET LastOffset = Offset;
  851. // Output the target offset, and get the reloc's location.
  852. Offset = Model (SourceSection, &NextItem, UserData, FALSE, TRUE);
  853. // A negative offset means the list ends here.
  854. if (Offset >= 0)
  855. {
  856. // Emit the reloc.
  857. if (!(EmitMlinkRelocFromActualOffset (Section, Offset - LastOffset)))
  858. return FALSE;
  859. }
  860. }
  861. }
  862. return EmitMlinkReloc (Section, -1);
  863. }
  864. }
  865. // Append mlink-style relocation entries in the format required by the TIGCCLIB
  866. // relocation code. If TargetSection is NULL, append all relocation entries that
  867. // point to unhandled sections. Otherwise, append all relocation entries
  868. // pointing to this section.
  869. // Warning: Inserting relocs is special: Since the relocs are changed
  870. // during the process, they can be inserted only once.
  871. BOOLEAN InsertMlinkRelocs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
  872. {
  873. // Initialize user data for list model.
  874. RELOC_USER_DATA UserData = {TargetSection};
  875. // Do code optimizations now, since this might reduce the number
  876. // of relocs. This is useful even if a target section was specified
  877. // because we can't fix the code anymore after the section is frozen.
  878. // This could even cause us to emit invalid code (bra +0).
  879. FixCode (Section->Parent);
  880. // If a target section is specified, it is essential now, and it may
  881. // not be modified any more.
  882. if (TargetSection)
  883. TargetSection->Frozen = TargetSection->Essential = TRUE;
  884. // Apply the format documented in _mlink_format_relocs.s.
  885. if (!(EmitMlinkFormatRelocs ((LIST_MODEL *) RelocListModel, MergedSection, Reference, &UserData, Section)))
  886. return FALSE;
  887. // If a target section was specified, and its Handled flag was not
  888. // set yet, now setting it is probably correct.
  889. if (TargetSection)
  890. TargetSection->Handled = TRUE;
  891. return TRUE;
  892. }
  893. // Append mlink-style relocation entries in the format required by the TIGCCLIB
  894. // relocation code, using InsertMlinkRelocs. If TargetSection is NULL, output
  895. // an empty relocation table. Otherwise, append all relocation entries pointing
  896. // to this section.
  897. // Warning: Inserting relocs is special: Since the relocs are changed
  898. // during the process, they can be inserted only once.
  899. BOOLEAN InsertMlinkSectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
  900. {
  901. if (TargetSection)
  902. return InsertMlinkRelocs (Section, TargetSection, MergedSection, Reference);
  903. else
  904. return (AllocateSpaceInSection (Section, 1) != NULL);
  905. }
  906. // Append ROM calls in the mlink-style format required by the TIGCCLIB
  907. // relocation code.
  908. BOOLEAN InsertMlinkROMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
  909. {
  910. PROGRAM *Program = Section->Parent;
  911. // Initialize user data for list model.
  912. ROM_CALL_USER_DATA UserData = {NULL, 0, -1};
  913. // Allocate space for dynamic user data.
  914. if (!(UserData.ROMFunctions = calloc (Program->HighestROMCall + 1, sizeof (ROM_CALL_FUNCTION_DATA))))
  915. {
  916. Error (NULL, "Out of memory while inserting ROM calls with mlink-format relocs.");
  917. return FALSE;
  918. }
  919. {
  920. BOOLEAN Result = TRUE;
  921. // Go through all ROM calls and increment the appropriate counters.
  922. COUNT ROMRelocCount = GetSectionItemCount ((LIST_MODEL *) ROMCallListModel, MergedSection ? : Section, &UserData);
  923. // If no ROM calls are used, do not output anything but the final null-terminator.
  924. if (ROMRelocCount > 0)
  925. {
  926. // Apply the format documented in _mlink_format_rom_calls.s.
  927. OFFSET LastFunction = 0;
  928. for (UserData.CurFunction = 0; UserData.CurFunction <= Program->HighestROMCall; UserData.CurFunction++)
  929. {
  930. if (UserData.ROMFunctions[UserData.CurFunction].RelocCount)
  931. {
  932. // Emit the function number as a mlink-format compressed offset.
  933. OFFSET FunctionOffset = UserData.CurFunction - LastFunction;
  934. if (!(EmitMlinkReloc (Section, FunctionOffset)))
  935. {
  936. Result = FALSE;
  937. break;
  938. }
  939. LastFunction = UserData.CurFunction;
  940. // Emit all ROM calls using this function.
  941. if (!(EmitMlinkFormatRelocs ((LIST_MODEL *) ROMCallListModel, MergedSection, Reference, &UserData, Section)))
  942. {
  943. Result = FALSE;
  944. break;
  945. }
  946. }
  947. }
  948. }
  949. // Output the final null-terminator.
  950. if (!(AppendI1ToSection (Section, 0)))
  951. Result = FALSE;
  952. // Free the extra information.
  953. free (UserData.ROMFunctions);
  954. return Result;
  955. }
  956. }