Linker.mod 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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" simply writes the code image as a DOS .COM.
  7. The globals are NOT in that file. They live at Compiler.DataBase = 8000h
  8. image (8100h after the load bias), and the runtime's initmem zeroes
  9. [hdrDS, hdrHeap) before the program runs. This is the flat-segment
  10. equivalent of the original's layout: TP3 puts the runtime and program in CS
  11. and the globals in a separate DS segment, so its .COM is code-only too.
  12. Keeping the data out of the file is what lets the code grow past 4 KiB
  13. without overlapping it, and it is why the file length is ImageBytes ().
  14. The original wrote its .COM through the overlay loader (TPSRC7 opendest)
  15. from a different buffer layout with a larger header. Our header is our
  16. own - see Runtime.EmitInitMem - but the file/data separation is the same. *)
  17. FROM SYSTEM IMPORT BYTE, ADR ;
  18. FROM Posix IMPORT open, write, close, unlink ;
  19. FROM Compiler IMPORT ImageBytes, ImageByteAt ;
  20. CONST
  21. O_WRONLY = 1 ;
  22. O_CREAT = 64 ;
  23. O_TRUNC = 512 ;
  24. PROCEDURE DropCh (v : CHAR) ;
  25. BEGIN
  26. END DropCh ;
  27. PROCEDURE DropB (v : BOOLEAN) ;
  28. BEGIN
  29. END DropB ;
  30. PROCEDURE DropC (v : CARDINAL) ;
  31. BEGIN
  32. END DropC ;
  33. PROCEDURE ZCopy (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR) ;
  34. (* Copy into a NUL-terminated buffer. The C bindings take a plain ADDRESS, so
  35. the string must be terminated in OUR buffer - passing the caller's
  36. unbounded array straight through would pass a pointer to whatever followed
  37. it.
  38. The VAR is load-bearing, and was found by running: without it the formal is
  39. a COPY, the writes land in the copy, and the caller writes to whatever
  40. garbage was in its buffer. The symptom was a .COM file named "END" -
  41. adjacent string data - created with the right size and the right contents
  42. in the wrong file. tests/OpenArrTest.mod measures the three variants. *)
  43. VAR i : CARDINAL ;
  44. BEGIN
  45. i := 0 ;
  46. WHILE (i <= HIGH (dst) - 1) AND (i <= HIGH (src)) AND (src [i] # 0C) DO
  47. dst [i] := src [i] ;
  48. INC (i)
  49. END ;
  50. dst [i] := 0C
  51. END ZCopy ;
  52. PROCEDURE LinkSize () : CARDINAL ;
  53. (* The .COM contains the runtime and the program's code ONLY. The globals
  54. live at Compiler.DataBase = 8000H in memory (8100H after the load bias),
  55. outside the file, and the runtime's initmem zeroes them at startup. This
  56. matches the original's separate DS segment: the file is code, the data
  57. area is zero-filled memory. *)
  58. BEGIN
  59. RETURN ImageBytes ()
  60. END LinkSize ;
  61. PROCEDURE WriteCom (path : ARRAY OF CHAR) : BOOLEAN ;
  62. (* Write the linked image to `path` as a DOS .COM. Returns TRUE on success;
  63. on failure the file is removed, so a half-written .COM is never left
  64. behind to be mistaken for a good one. *)
  65. VAR fd : INTEGER ;
  66. n : LONGINT ;
  67. i, total, chunk : CARDINAL ;
  68. buf : ARRAY [0..255] OF BYTE ;
  69. z : ARRAY [0..511] OF CHAR ;
  70. BEGIN
  71. ZCopy (z, path) ;
  72. total := LinkSize () ;
  73. fd := open (ADR (z), O_WRONLY + O_CREAT + O_TRUNC, 420) ;
  74. IF fd < 0 THEN
  75. RETURN FALSE
  76. END ;
  77. i := 0 ;
  78. WHILE i < total DO
  79. chunk := 0 ;
  80. WHILE (chunk < 256) AND (i + chunk < total) DO
  81. buf [chunk] := ImageByteAt (i + chunk) ;
  82. INC (chunk)
  83. END ;
  84. n := write (fd, ADR (buf), chunk) ;
  85. IF n # VAL (LONGINT, chunk) THEN
  86. DropC (VAL (CARDINAL, close (fd))) ;
  87. DropB (unlink (ADR (z)) = 0) ;
  88. RETURN FALSE
  89. END ;
  90. i := i + chunk
  91. END ;
  92. IF close (fd) # 0 THEN
  93. DropB (unlink (ADR (z)) = 0) ;
  94. RETURN FALSE
  95. END ;
  96. RETURN TRUE
  97. END WriteCom ;
  98. END Linker.