1 ;;; Code to support (simple) linking of object files into GEMDOS PRG
2 ;;; files. These are basically A.OUT format.
4 (in-package :st-linker
)
6 (defparameter *aout-obj-header-length
* 32) ; bytes
8 ;; Object magic is STO^Z
9 (defun check-magic (stream)
10 (unless (= (read-big-endian-data stream
32) #x53544F26
)
11 (error "Bad magic.")))
24 (text-relocations-size)
25 (data-relocations-size)
37 (defstruct linker-symbol
41 ;; global-p, debug-info
45 (defun read-relocations (n-relocations stream
)
46 (let ((table (make-array (list n-relocations
)
47 :element-type
'linker-symbol
)))
48 (dotimes (i n-relocations
)
49 (let ((address (read-big-endian-data stream
32))
50 (rest (read-big-endian-data stream
32)))
52 (make-relocation :address address
53 :symbol
(ldb (byte 24 8) rest
)
54 :pc-relative-p
(= 1 (ldb (byte 1 7) rest
))
55 :length
(ldb (byte 2 5) rest
)
56 :extern-p
(= 1 (ldb (byte 1 4) rest
))))))
59 (defun read-symbol-table (n-symbols stream module
)
60 (let ((table (make-array (list n-symbols
) :element-type
'relocation
)))
61 (dotimes (i n-symbols
)
62 (let ((name-idx (read-big-endian-data stream
32))
63 (type (read-big-endian-data stream
32))
64 (value (read-big-endian-data stream
32)))
66 (make-linker-symbol :name name-idx
67 :type
(ldb (byte 7 25) type
)
72 (defun read-string-table (stream symbols
)
73 (dotimes (i (length symbols
))
74 (setf (linker-symbol-name (aref symbols i
))
75 (read-nul-terminated-string stream
))))
78 (defun link-prg (objects &key
(out-name "aout.prg"))
79 (let ((text-segment-size 0)
83 (dolist (object objects
)
84 ;; load each object file, figure out sizes of segments
85 (with-open-file (stream object
:element-type
'unsigned-byte
)
87 (let ((text (read-big-endian-data stream
32))
88 (data (read-big-endian-data stream
32))
89 (bss (read-big-endian-data stream
32))
90 (symbol-count (read-big-endian-data stream
32))
91 (entry (read-big-endian-data stream
32))
92 (text-reloc (read-big-endian-data stream
32))
93 (data-reloc (read-big-endian-data stream
32)))
94 (file-position stream
(+ *aout-obj-header-length
*
96 (let ((relocs (read-relocations text-reloc stream
))
97 (symbols (read-symbol-table symbol-count stream object
)))
98 (read-string-table stream symbols
)
99 (push (make-module :name object
:text-size text
:data-size data
100 :bss-size bss
:symbol-table-size symbol-count
102 :text-relocations-size text-reloc
103 :data-relocations-size data-reloc
104 :relocs relocs
:symbols symbols
)
106 (incf text-segment-size text
)
107 (incf data-segment-size data
)
108 (incf bss-size bss
))))
110 (allocate-module-bases modules text-segment-size data-segment-size
)
112 (with-open-file (prg-stream out-name
:direction
:output
113 :element-type
'unsigned-byte
114 :if-exists
:new-version
115 :if-does-not-exist
:create
)
116 ;; start writing header
117 (write-big-endian-data prg-stream
#x601a
16)
118 (write-big-endian-data prg-stream text-segment-size
32)
119 (write-big-endian-data prg-stream data-segment-size
32)
120 (write-big-endian-data prg-stream bss-size
32)
121 (write-big-endian-data prg-stream
0 32) ; no symbol table
122 (write-big-endian-data prg-stream
0 (* 2 32)) ; reserved, flags
123 (write-big-endian-data prg-stream
0 16) ; absflag
125 (dolist (module modules
)
126 ;; over each object, read text segments
127 (with-open-file (obj-stream (module-name module
)
128 :element-type
'unsigned-byte
)
129 (file-position obj-stream
*aout-obj-header-length
*) ;start of text segment
130 (copy-from-stream obj-stream prg-stream
131 (module-text-size module
))))
132 (dolist (module modules
)
133 ;; over each object, read data segments
134 (with-open-file (obj-stream (module-name module
)
135 :element-type
'unsigned-byte
)
136 (file-position obj-stream
(+ *aout-obj-header-length
*
137 (module-text-size module
)))
138 (copy-from-stream obj-stream prg-stream
139 (module-data-size module
))))
141 (let ((relocs (process-link-time-relocations prg-stream modules
)))
142 (output-load-time-relocations prg-stream relocs
)))))
145 (defun allocate-module-bases (modules text-segment-size data-segment-size
)
146 (let ((cur-text-base 0)
147 (cur-data-base text-segment-size
)
148 (cur-bss-base (+ text-segment-size data-segment-size
)))
149 (dolist (module modules
)
150 (setf (module-text-base module
) cur-text-base
151 (module-data-base module
) cur-data-base
152 (module-bss-base module
) cur-bss-base
)
153 (format t
"~&relocating ~A at ~A, ~A, ~A" (module-name module
)
154 (module-text-base module
) (module-data-base module
)
155 (module-bss-base module
))
156 (incf cur-text-base
(module-text-size module
))
157 (incf cur-data-base
(module-data-size module
))
158 (incf cur-bss-base
(module-bss-size module
)))))
160 (defun process-link-time-relocations (stream modules
)
162 (dolist (module modules
)
163 ;; for each relocation, try and process.
164 ;; if it needs to be relocated at load time, adjust for long
165 ;; relocs, and push it onto the reloc stack.
168 (defun output-load-time-relocations (stream relocs
)
169 (write-big-endian-data stream
0 32) ;fixup offset
170 ;; output relocations in PC order... make sure relocation is on word
171 ;; boundry, and fix smaller relocs so that they're long (eg, word
172 ;; fixups need to start two bytes earlier). if delta from current
173 ;; position to next fixup > 254, output byte 1 until < 254. then
174 ;; output offset of fixup.
175 (write-byte 0 stream
)) ; no more relocs.
180 (defun copy-from-stream (source destination length
181 &key
(element-type 'unsigned-byte
))
182 "Copy LENGTH bytes of data from open stream SOURCE to open stream
184 (let ((buffer (make-array '(4096) :element-type element-type
)))
185 (do ((bytes #1=(read-sequence buffer source
) #1#)
186 (length length
(- length bytes
)))
187 ((or (= bytes
0) (<= length
0)))
188 (write-sequence buffer destination
:end
(if (> bytes length
)
192 (defun read-big-endian-data (stream length
)
193 "Read LENGTH bits of data encoded big-endian from STREAM, returning
194 an integer. LENGTH must be a multiple of 8."
195 (assert (zerop (logand length
7)))
196 (do ((pos (- length
8) (- pos
8))
197 (value (read-byte stream
) (logior (read-byte stream
)
201 (defun write-big-endian-data (stream data length
)
202 "Write LENGTH bits of the integer DATA to STREAM, in big-endian
203 order. LENGTH must be a multiple of 8."
204 (assert (zerop (logand length
7)))
205 (do ((pos (- length
8) (- pos
8)))
207 (write-byte (ldb (byte 8 pos
) data
) stream
)))
210 (defun read-nul-terminated-string (stream)
211 (do ((char (read-byte stream
) (read-byte stream
))
212 (string (make-array '(0) :element-type
'character
:adjustable t
214 ((eql char
0) string
)
215 (vector-push-extend (code-char char
) string
)))