Linker.mod 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. IMPLEMENTATION MODULE Linker ;
  2. (* Linker and .COM writer for TP3-compiled programs.
  3. The compiler has already produced the whole image - Runtime is copied to
  4. offset 0 of the code buffer and the program follows it, so every address in
  5. the image is already absolute (see Compiler.Inittur). There is therefore
  6. no relocation to do: "linking" here means
  7. 1. pad the image so the data area exists, and
  8. 2. write it out as a DOS .COM file.
  9. Why padding is needed at all: a .COM file is loaded at CS:0100 with the
  10. whole 64 KiB segment available, but the program's globals live at
  11. Compiler.DataBase = rtSz + 1000H, which for a small program is far past
  12. the last code byte. Those globals are read and written by absolute
  13. address, so the bytes have to be present in the file. The gap between
  14. code and data is zero-filled, which is also what makes it safe: a .COM's
  15. stack lives at the TOP of the segment (DOS gives a .COM SS=SP=CS:FFFE), so
  16. code and data both have to stay well below it. A 4 KiB gap keeps them
  17. apart for programs up to 4 KiB of code, which is the documented limit.
  18. Faithfulness note: the original did this in the compiler's overlay loader
  19. (TPSRC7 opendest) and wrote a .COM from a different buffer layout with a
  20. larger header served by the overlay segment. Our header is our own - see
  21. Runtime.EmitInitMem - and the file layout is the same idea, smaller. *)
  22. FROM SYSTEM IMPORT BYTE, ADR ;
  23. FROM Posix IMPORT open, write, close, unlink ;
  24. FROM Compiler IMPORT ImageBytes, ImageByteAt, DataBase, DataBytes ;
  25. CONST
  26. O_WRONLY = 1 ;
  27. O_CREAT = 64 ;
  28. O_TRUNC = 512 ;
  29. VAR
  30. padByte : BYTE ; (* the filler for the code/data gap *)
  31. PROCEDURE DropCh (v : CHAR) ;
  32. BEGIN
  33. END DropCh ;
  34. PROCEDURE DropB (v : BOOLEAN) ;
  35. BEGIN
  36. END DropB ;
  37. PROCEDURE DropC (v : CARDINAL) ;
  38. BEGIN
  39. END DropC ;
  40. PROCEDURE ZCopy (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR) ;
  41. (* Copy into a NUL-terminated buffer. The C bindings take a plain ADDRESS, so
  42. the string must be terminated in OUR buffer - passing the caller's
  43. unbounded array straight through would pass a pointer to whatever followed
  44. it.
  45. The VAR is load-bearing, and was found by running: without it the formal is
  46. a COPY, the writes land in the copy, and the caller writes to whatever
  47. garbage was in its buffer. The symptom was a .COM file named "END" -
  48. adjacent string data - created with the right size and the right contents
  49. in the wrong file. tests/OpenArrTest.mod measures the three variants. *)
  50. VAR i : CARDINAL ;
  51. BEGIN
  52. i := 0 ;
  53. WHILE (i <= HIGH (dst) - 1) AND (i <= HIGH (src)) AND (src [i] # 0C) DO
  54. dst [i] := src [i] ;
  55. INC (i)
  56. END ;
  57. dst [i] := 0C
  58. END ZCopy ;
  59. PROCEDURE LinkSize () : CARDINAL ;
  60. (* Size of the linked file in bytes: the image, padded out to cover the whole
  61. data area. This is what a .COM must be, and it is larger than
  62. Compiler.ImageBytes whenever the program declares no globals, because the
  63. data base is a fixed 4 KiB above the code. *)
  64. BEGIN
  65. IF DataBase () + DataBytes () > ImageBytes () THEN
  66. RETURN DataBase () + DataBytes ()
  67. END ;
  68. RETURN ImageBytes ()
  69. END LinkSize ;
  70. PROCEDURE MakePad () ;
  71. BEGIN
  72. (* A local array is not required to be zeroed, and relying on it would be
  73. a silent-corruption bug: an uninitialised pad byte would appear in the
  74. middle of a program's data area. ISO will not let an array be assigned
  75. the ZType, so the filler is a single explicit BYTE. *)
  76. padByte := 0
  77. END MakePad ;
  78. PROCEDURE WriteCom (path : ARRAY OF CHAR) : BOOLEAN ;
  79. (* Write the linked image to `path` as a DOS .COM. Returns TRUE on success;
  80. on failure the file is removed, so a half-written .COM is never left
  81. behind to be mistaken for a good one. *)
  82. VAR fd : INTEGER ;
  83. n : LONGINT ;
  84. i, total, chunk : CARDINAL ;
  85. buf : ARRAY [0..255] OF BYTE ;
  86. z : ARRAY [0..511] OF CHAR ;
  87. BEGIN
  88. MakePad ;
  89. ZCopy (z, path) ;
  90. total := LinkSize () ;
  91. fd := open (ADR (z), O_WRONLY + O_CREAT + O_TRUNC, 420) ;
  92. IF fd < 0 THEN
  93. RETURN FALSE
  94. END ;
  95. i := 0 ;
  96. WHILE i < total DO
  97. chunk := 0 ;
  98. WHILE (chunk < 256) AND (i + chunk < total) DO
  99. IF i + chunk < ImageBytes () THEN
  100. buf [chunk] := ImageByteAt (i + chunk)
  101. ELSE
  102. buf [chunk] := padByte (* the gap before the data *)
  103. END ;
  104. INC (chunk)
  105. END ;
  106. n := write (fd, ADR (buf), chunk) ;
  107. IF n # VAL (LONGINT, chunk) THEN
  108. DropC (VAL (CARDINAL, close (fd))) ;
  109. DropB (unlink (ADR (z)) = 0) ;
  110. RETURN FALSE
  111. END ;
  112. i := i + chunk
  113. END ;
  114. IF close (fd) # 0 THEN
  115. DropB (unlink (ADR (z)) = 0) ;
  116. RETURN FALSE
  117. END ;
  118. RETURN TRUE
  119. END WriteCom ;
  120. END Linker.