Fix return count checking for (values &optional ...)
[sbcl.git] / tools-for-build / editcore.lisp
blob6ca7e52fcbc53321b25d364318a2b7fd49f49d04
1 ;;;; Utilities for separating an SBCL core file into two pieces:
2 ;;;; 1. An assembly language file containing the immobile code space
3 ;;;; 2. A '.o' file wrapping a core file containing everything else
4 ;;;; We operate as a "tool" that processes external files rather than
5 ;;;; operating on the in-process data, but it is also possible to dump
6 ;;;; the current image by creating a straight-through translation
7 ;;;; of internal/external code addresses.
9 ;;;; This software is part of the SBCL system. See the README file for
10 ;;;; more information.
11 ;;;;
12 ;;;; This software is derived from the CMU CL system, which was
13 ;;;; written at Carnegie Mellon University and released into the
14 ;;;; public domain. The software is in the public domain and is
15 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
16 ;;;; files for more information.
18 (load (merge-pathnames "corefile.lisp" *load-pathname*))
20 (defpackage "SB-EDITCORE"
21 (:use "CL" "SB-ALIEN" "SB-COREFILE" "SB-INT" "SB-EXT"
22 "SB-KERNEL" "SB-SYS" "SB-VM")
23 (:export #:move-dynamic-code-to-text-space #:redirect-text-space-calls
24 #:split-core #:copy-to-elf-obj #:reorganize-core)
25 (:import-from "SB-ALIEN-INTERNALS"
26 #:alien-type-bits #:parse-alien-type
27 #:alien-value-sap #:alien-value-type)
28 (:import-from "SB-C" #:+backend-page-bytes+)
29 (:import-from "SB-VM" #:map-objects-in-range #:reconstitute-object
30 #:%closure-callee #:code-object-size)
31 (:import-from "SB-DISASSEM" #:get-inst-space #:find-inst
32 #:make-dstate #:%make-segment #:make-code-segment
33 #:seg-virtual-location #:seg-length #:seg-sap-maker
34 #:map-segment-instructions #:inst-name
35 #:dstate-next-addr #:dstate-cur-offs
36 #:dstate-cur-addr #:sign-extend)
37 #+x86-64
38 (:import-from "SB-X86-64-ASM" #:near-jump-displacement
39 #:near-cond-jump-displacement #:mov #:call #:jmp
40 #:get-gpr #:reg-name
41 #:machine-ea #:machine-ea-base #:machine-ea-index #:machine-ea-disp)
42 (:import-from "SB-IMPL" #:symbol-table #:package-%name
43 #:symtbl-%cells
44 #:hash-table-pairs #:hash-table-%count))
46 (in-package "SB-EDITCORE")
48 (declaim (muffle-conditions compiler-note))
50 (eval-when (:execute)
51 (setq *evaluator-mode* :compile))
53 ;;; Some high address that won't conflict with any of the ordinary spaces
54 ;;; It's more-or-less arbitrary, but we must be able to discern whether a
55 ;;; pointer looks like it points to code in case coreparse has to walk the heap.
56 (defconstant +code-space-nominal-address+ #x550000000000)
58 (defstruct (core-space ; "space" is a CL symbol
59 (:conc-name space-)
60 (:constructor make-space (id addr data-page page-adjust nwords)))
61 (page-table nil :type (or null simple-vector))
62 id addr data-page page-adjust nwords)
63 (defmethod print-object ((self core-space) stream)
64 (print-unreadable-object (self stream :type t)
65 (format stream "~d" (space-id self))))
66 (defun space-size (space) (* (space-nwords space) n-word-bytes))
67 (defun space-end (space) (+ (space-addr space) (space-size space)))
68 (defun space-nbytes-aligned (space)
69 (align-up (space-size space) +backend-page-bytes+))
70 (defun space-physaddr (space spacemap)
71 (sap+ (car spacemap) (* (space-data-page space) +backend-page-bytes+)))
73 ;;; Given VADDR which is an address in the target core, return the address at which
74 ;;; VADDR is currently mapped while performing the split.
75 ;;; SPACEMAP is a cons of a SAP and an alist whose elements are (ADDR . CORE-SPACE)
76 (defun translate-ptr (vaddr spacemap)
77 (let ((space (find vaddr (cdr spacemap) :key #'space-addr :test #'>=)))
78 ;; FIXME: duplicates SPACE-PHYSADDR to avoid consing a SAP.
79 ;; macroize or something.
80 (+ (sap-int (car spacemap)) (* (space-data-page space) +backend-page-bytes+)
81 (- vaddr (space-addr space)))))
83 ;;;
84 (defun get-space (id spacemap)
85 (find id (cdr spacemap) :key #'space-id))
86 (defun compute-nil-addr (spacemap)
87 (let ((space (get-space static-core-space-id spacemap)))
88 ;; TODO: The core should store its address of NIL in the initial function entry
89 ;; so this kludge can be removed.
90 (logior (space-addr space) #x117))) ; SUPER KLUDGE
91 (defun compute-nil-object (spacemap) ; terrible, don't use!
92 (%make-lisp-obj (compute-nil-addr spacemap)))
94 ;;; Given OBJ which is tagged pointer into the target core, translate it into
95 ;;; the range at which the core is now mapped during execution of this tool,
96 ;;; so that host accessors can dereference its slots.
97 ;;; Use extreme care: while it works to use host accessors on the target core,
98 ;;; we must avoid type checks on instances because LAYOUTs need translation.
99 ;;; Printing boxed objects from the target core will almost always crash.
100 (defun translate (obj spacemap)
101 (%make-lisp-obj (translate-ptr (get-lisp-obj-address obj) spacemap)))
103 (defstruct (core-sym (:copier nil) (:predicate nil)
104 (:constructor make-core-sym (package name external)))
105 (package nil)
106 (name nil :read-only t)
107 (external nil :read-only t))
109 (defstruct (bounds (:constructor make-bounds (low high)))
110 (low 0 :type word) (high 0 :type word))
112 (defstruct (core (:predicate nil)
113 (:copier nil)
114 (:constructor %make-core))
115 (spacemap)
116 (nil-object)
117 ;; mapping from small integer ID to package
118 (pkg-id->package)
119 ;; mapping from string naming a package to list of symbol names (strings)
120 ;; that are external in the package.
121 (packages (make-hash-table :test 'equal))
122 ;; hashset of symbol names (as strings) that should be package-qualified.
123 ;; (Prefer not to package-qualify unambiguous names)
124 (nonunique-symbol-names)
125 (code-bounds nil :type bounds :read-only t)
126 (fixedobj-bounds nil :type bounds :read-only t)
127 (linkage-bounds nil :type bounds :read-only t)
128 (linkage-symbols nil)
129 (linkage-symbol-usedp nil)
130 (linkage-entry-size nil)
131 (new-fixups (make-hash-table))
132 (new-fixup-words-used 0)
133 ;; For assembler labels that we want to invent at random
134 (label-counter 0)
135 (enable-pie nil)
136 (dstate (make-dstate nil) :read-only t)
137 (seg (%make-segment :sap-maker (lambda () (error "Bad sap maker"))
138 :virtual-location 0) :read-only t)
139 (fixup-addrs nil)
140 (call-inst nil :read-only t)
141 (jmp-inst nil :read-only t)
142 (pop-inst nil :read-only t))
144 (defglobal *editcore-ppd*
145 ;; copy no entries for macros/special-operators (flet, etc)
146 (let ((ppd (sb-pretty::make-pprint-dispatch-table #() nil nil)))
147 (set-pprint-dispatch 'string
148 ;; Write strings without string quotes
149 (lambda (stream string) (write-string string stream))
151 ppd)
152 ppd))
154 (defmethod print-object ((sym core-sym) stream)
155 (format stream "~(~:[~*~;~:*~A~:[:~;~]:~]~A~)"
156 (core-sym-package sym)
157 (core-sym-external sym)
158 (core-sym-name sym)))
160 (defun space-bounds (id spacemap)
161 (let ((space (get-space id spacemap)))
162 (if space
163 (make-bounds (space-addr space) (space-end space))
164 (make-bounds 0 0))))
165 (defun in-bounds-p (addr bounds)
166 (and (>= addr (bounds-low bounds)) (< addr (bounds-high bounds))))
168 (defun make-string-hashset (contents count)
169 (let ((hs (sb-int:make-hashset count #'string= #'sxhash)))
170 (dolist (string contents hs)
171 (sb-int:hashset-insert hs string))))
173 (defun scan-symbol-table (function table core)
174 (let* ((spacemap (core-spacemap core))
175 (nil-object (core-nil-object core))
176 (cells (translate (symtbl-%cells (truly-the symbol-table
177 (translate table spacemap)))
178 spacemap)))
179 (dovector (x (translate (cdr cells) spacemap))
180 (unless (fixnump x)
181 (funcall function
182 (if (eq x nil-object) ; any random package can export NIL. wow.
183 "NIL"
184 (translate (symbol-name (translate x spacemap)) spacemap))
185 x)))))
187 (defun core-package-from-id (id core)
188 (if (/= id 0)
189 (let ((package (aref (core-pkg-id->package core) id)))
190 (translate (package-%name (truly-the package package))
191 (core-spacemap core)))))
193 (defun remove-name-junk (name)
194 (setq name
195 (named-let recurse ((x name))
196 (cond ((typep x '(cons (eql lambda)))
197 (let ((args (second x)))
198 `(lambda ,(if args #\# "()")
199 ,@(recurse (cddr x)))))
200 ((eq x :in) "in")
201 ((and (typep x '(or string symbol))
202 (let ((mismatch (mismatch (string x) "CLEANUP-FUN-")))
203 (or (eql mismatch nil) (= mismatch (length "CLEANUP-FUN-")))))
204 '#:cleanup-fun)
205 ;; Try to chop off all the directory names in strings resembling
206 ;; (lambda () in "/some/very/long/pathname/to/a/thing.lisp")
207 ((stringp x)
208 (let ((p (position #\/ x :from-end t)))
209 (if p (subseq x (1+ p)) x)))
210 ((consp x) (recons x (recurse (car x)) (recurse (cdr x))))
211 (t x))))
212 ;; Shorten obnoxiously long printed representations of methods.
213 (flet ((unpackageize (thing)
214 (when (typep thing 'core-sym)
215 (setf (core-sym-package thing) nil))
216 thing))
217 (when (typep name '(cons (member sb-pcl::slow-method sb-pcl::fast-method
218 sb-pcl::slot-accessor)))
219 (setq name `(,(case (car name)
220 (sb-pcl::fast-method "method")
221 (sb-pcl::slow-method "Method") ; something visually distinct
222 (sb-pcl::slot-accessor "accessor"))
223 ,@(cdr name)))
224 (setf (second name) (unpackageize (second name)))
225 (let ((last (car (last name))))
226 (when (listp last)
227 (dolist (qual last)
228 (unpackageize qual))))))
229 name)
231 (defstruct (descriptor (:constructor make-descriptor (bits)))
232 (bits 0 :type word))
233 (defmethod print-object ((self descriptor) stream)
234 (format stream "#<ptr ~x>" (descriptor-bits self)))
235 (defun descriptorize (obj)
236 (if (is-lisp-pointer (get-lisp-obj-address obj))
237 (make-descriptor (get-lisp-obj-address obj))
238 obj))
239 (defun undescriptorize (target-descriptor)
240 (%make-lisp-obj (descriptor-bits target-descriptor)))
242 (defun target-hash-table-alist (table spacemap)
243 (let ((table (truly-the hash-table (translate table spacemap))))
244 (let ((cells (the simple-vector (translate (hash-table-pairs table) spacemap))))
245 (collect ((pairs))
246 (do ((count (hash-table-%count table) (1- count))
247 (i 2 (+ i 2)))
248 ((zerop count)
249 (pairs))
250 (pairs (cons (descriptorize (svref cells i))
251 (descriptorize (svref cells (1+ i))))))))))
253 (defmacro package-id (name) (sb-impl::package-id (find-package name)))
255 ;;; Return either the physical or logical address of the specified symbol.
256 (defun %find-target-symbol (package-id symbol-name spacemap
257 &optional (address-mode :physical))
258 (dolist (id `(,immobile-fixedobj-core-space-id
259 ,static-core-space-id
260 ,dynamic-core-space-id))
261 (binding* ((space (get-space id spacemap) :exit-if-null)
262 (start (translate-ptr (space-addr space) spacemap))
263 (end (+ start (space-size space)))
264 (physaddr start))
265 (loop
266 (when (>= physaddr end) (return))
267 (let* ((word (sap-ref-word (int-sap physaddr) 0))
268 (size
269 (if (= (logand word widetag-mask) filler-widetag)
270 (ash (ash word -32) word-shift)
271 (let ((obj (reconstitute-object (ash physaddr (- n-fixnum-tag-bits)))))
272 (when (and (symbolp obj)
273 (string= symbol-name (translate (symbol-name obj) spacemap))
274 (= (symbol-package-id obj) package-id))
275 (return-from %find-target-symbol
276 (%make-lisp-obj
277 (logior (ecase address-mode
278 (:physical physaddr)
279 (:logical (+ (space-addr space) (- physaddr start))))
280 other-pointer-lowtag))))
281 (primitive-object-size obj)))))
282 (incf physaddr size))))))
283 (defun find-target-symbol (package-id symbol-name spacemap &optional (address-mode :physical))
284 (or (%find-target-symbol package-id symbol-name spacemap address-mode)
285 (bug "Can't find symbol ~A::~A" package-id symbol-name)))
287 (defparameter label-prefix (if (member :darwin *features*) "_" ""))
288 (defun labelize (x) (concatenate 'string label-prefix x))
290 (defun compute-linkage-symbols (spacemap)
291 (let* ((linkage-info (symbol-global-value
292 (find-target-symbol (package-id "SB-SYS") "*LINKAGE-INFO*"
293 spacemap :physical)))
294 (hashtable (car (translate linkage-info spacemap)))
295 (pairs (target-hash-table-alist hashtable spacemap))
296 (min (reduce #'min pairs :key #'cdr))
297 (max (reduce #'max pairs :key #'cdr))
298 (n (1+ (- max min)))
299 (vector (make-array n)))
300 (dolist (entry pairs vector)
301 (let* ((key (undescriptorize (car entry)))
302 (entry-index (- (cdr entry) min))
303 (string (labelize (translate (if (consp key) (car (translate key spacemap)) key)
304 spacemap))))
305 (setf (aref vector entry-index)
306 (if (consp key) (list string) string))))))
308 (defun make-core (spacemap code-bounds fixedobj-bounds &optional enable-pie)
309 (let* ((linkage-bounds
310 (let ((text-space (get-space immobile-text-core-space-id spacemap)))
311 (if text-space
312 (let ((text-addr (space-addr text-space)))
313 (make-bounds (- text-addr alien-linkage-table-space-size) text-addr))
314 (make-bounds 0 0))))
315 (linkage-entry-size
316 (symbol-global-value
317 (find-target-symbol (package-id "SB-VM") "ALIEN-LINKAGE-TABLE-ENTRY-SIZE"
318 spacemap :physical)))
319 (linkage-symbols (compute-linkage-symbols spacemap))
320 (nil-object (compute-nil-object spacemap))
321 (ambiguous-symbols (make-hash-table :test 'equal))
322 (core
323 (%make-core
324 :spacemap spacemap
325 :nil-object nil-object
326 :nonunique-symbol-names ambiguous-symbols
327 :code-bounds code-bounds
328 :fixedobj-bounds fixedobj-bounds
329 :linkage-bounds linkage-bounds
330 :linkage-entry-size linkage-entry-size
331 :linkage-symbols linkage-symbols
332 :linkage-symbol-usedp (make-array (length linkage-symbols) :element-type 'bit
333 :initial-element 0)
334 :enable-pie enable-pie)))
335 (let ((package-table
336 (symbol-global-value
337 (find-target-symbol (package-id "SB-IMPL") "*ALL-PACKAGES*" spacemap :physical)))
338 (package-alist)
339 (symbols (make-hash-table :test 'equal)))
340 (labels ((scan-symtbl (table)
341 (scan-symbol-table
342 (lambda (str sym)
343 (pushnew (get-lisp-obj-address sym) (gethash str symbols)))
344 table core))
345 (scan-package (x)
346 (let ((package (truly-the package (translate x spacemap))))
347 ;; a package can appear in *ALL-PACKAGES* under each of its nicknames
348 (unless (assoc (sb-impl::package-id package) package-alist)
349 (push (cons (sb-impl::package-id package) package) package-alist)
350 (scan-symtbl (package-external-symbols package))
351 (scan-symtbl (package-internal-symbols package))))))
352 (dovector (x (translate package-table spacemap))
353 (cond ((%instancep x) (scan-package x))
354 ((listp x) (loop (if (eq x nil-object) (return))
355 (setq x (translate x spacemap))
356 (scan-package (car x))
357 (setq x (cdr x)))))))
358 (let ((package-by-id (make-array (1+ (reduce #'max package-alist :key #'car))
359 :initial-element nil)))
360 (loop for (id . package) in package-alist
361 do (setf (aref package-by-id id) package))
362 (setf (core-pkg-id->package core) package-by-id))
363 (dohash ((string symbols) symbols)
364 (when (cdr symbols)
365 (setf (gethash string ambiguous-symbols) t))))
366 core))
368 (defun code-fixup-locs (code spacemap)
369 (let ((locs (sb-vm::%code-fixups code)))
370 ;; Return only the absolute fixups
371 ;; Ensure that a bignum LOCS is translated before using it.
372 (values (sb-c::unpack-code-fixup-locs
373 (if (fixnump locs) locs (translate locs spacemap))))))
375 (declaim (ftype function extract-object-from-core))
376 (defun extract-fun-map (code core)
377 ;; Pointers to target objects should be SAPified before passing them,
378 ;; so that this is safe under precise GC. Consider what happens if you pass an object
379 ;; via its tagged pointer that looks like it's into the host's heap, but it's physically
380 ;; mapped elsewhere. GC sees the bits of the alleged object and thinks you mean to refer
381 ;; to the host's heap. That's completely wrong, but it mostly does no harm on
382 ;; conservative GC. However, it _does_ do harm even on conservative GC if we actually
383 ;; store such pointer somewhere that pointer tracing sees it. So we're technically
384 ;; in the clear only as long as the pointer is _always_ ambiguous (i.e. on the stack)
385 ;; or else made into a proper SAP. And all deref operations should read via the SAP
386 ;; and return a SAP. I didn't feel up to the task of emulating every single primitive object
387 ;; reader and structure slot reader needed in this file. Though maybe I'll get around
388 ;; to it some day, as all the emulations could be autogenerated somehow.
389 (let* ((di-sap (int-sap (get-lisp-obj-address (%code-debug-info code))))
390 (proxy-di (extract-object-from-core di-sap core)))
391 (sb-di::uncompact-fun-map proxy-di)))
393 ;;; Examine CODE, returning a list of lists describing how to emit
394 ;;; the contents into the assembly file.
395 ;;; ({:data | :padding} . N) | (start-pc . end-pc)
396 ;;; CODE is supplied as a _physical_ object, i.e. whever it is currently
397 ;;; mapped into memory which on AMD64 Linux is typically around #x7F.........F
398 (defun get-text-ranges (code core)
399 (let* ((fun-map (extract-fun-map code core))
400 (next-simple-fun-pc-offs (%code-fun-offset code 0))
401 (start-pc (code-n-unboxed-data-bytes code))
402 (simple-fun-index -1)
403 (simple-fun)
404 (blobs)
405 (i 1)
406 (len (length fun-map)))
407 (when (plusp start-pc)
408 (aver (zerop (rem start-pc n-word-bytes)))
409 (push `(:data . ,(ash start-pc (- word-shift))) blobs))
410 (loop
411 (let* ((end-pc (if (= i (length fun-map))
412 (%code-text-size code)
413 (aref fun-map i))))
414 (cond
415 ((= start-pc end-pc)) ; crazy shiat. do not add to blobs
416 ((<= start-pc next-simple-fun-pc-offs (1- end-pc))
417 (incf simple-fun-index)
418 (setq simple-fun (%code-entry-point code simple-fun-index))
419 (let ((padding (- next-simple-fun-pc-offs start-pc)))
420 (when (plusp padding)
421 ;; Assert that SIMPLE-FUN always begins at an entry
422 ;; in the fun-map, and not somewhere in the middle:
423 ;; |<-- fun -->|<-- fun -->|
424 ;; ^- start (GOOD) ^- alleged start (BAD)
425 (cond ((eq simple-fun (%code-entry-point code 0))
426 (bug "Misaligned fun start"))
427 (t ; sanity-check the length of the filler
428 (aver (< padding (* 2 n-word-bytes)))))
429 (push `(:pad . ,padding) blobs)
430 (incf start-pc padding)))
431 (push `(,start-pc . ,end-pc) blobs)
432 (setq next-simple-fun-pc-offs
433 (if (< (1+ simple-fun-index) (code-n-entries code))
434 (%code-fun-offset code (1+ simple-fun-index))
435 -1)))
437 (let ((current-blob (car blobs)))
438 (setf (cdr current-blob) end-pc)))) ; extend this blob
439 (setq start-pc end-pc))
440 (when (= i len)
441 (return (nreverse blobs)))
442 (incf i 2))))
444 (defun %widetag-of (word) (logand word widetag-mask))
446 (defun make-code-obj (addr spacemap)
447 (let ((translation (translate-ptr addr spacemap)))
448 (aver (= (%widetag-of (sap-ref-word (int-sap translation) 0))
449 code-header-widetag))
450 (%make-lisp-obj (logior translation other-pointer-lowtag))))
452 (defun copy-bytes (in-stream out-stream nbytes
453 &optional (buffer
454 (make-array 1024 :element-type '(unsigned-byte 8))))
455 (loop (let ((chunksize (min (length buffer) nbytes)))
456 (aver (eql (read-sequence buffer in-stream :end chunksize) chunksize))
457 (write-sequence buffer out-stream :end chunksize)
458 (when (zerop (decf nbytes chunksize)) (return)))))
460 ;;;;
462 (defun read-core-header (input core-header verbose &aux (core-offset 0))
463 (read-sequence core-header input)
464 (cond ((= (%vector-raw-bits core-header 0) core-magic))
465 (t ; possible embedded core
466 (file-position input (- (file-length input)
467 (* 2 n-word-bytes)))
468 (aver (eql (read-sequence core-header input) (* 2 n-word-bytes)))
469 (aver (= (%vector-raw-bits core-header 1) core-magic))
470 (setq core-offset (%vector-raw-bits core-header 0))
471 (when verbose
472 (format t "~&embedded core starts at #x~x into input~%" core-offset))
473 (file-position input core-offset)
474 (read-sequence core-header input)
475 (aver (= (%vector-raw-bits core-header 0) core-magic))))
476 core-offset)
478 (defmacro do-core-header-entry (((id-var len-var ptr-var) buffer) &body body)
479 `(let ((,ptr-var 1))
480 (loop
481 (let ((,id-var (%vector-raw-bits ,buffer ,ptr-var))
482 (,len-var (%vector-raw-bits ,buffer (1+ ,ptr-var))))
483 ;; (format t "~&entry type ~D @ ~d len ~d words~%" id ptr len)
484 (incf ,ptr-var 2)
485 (decf ,len-var 2)
486 (when (= ,id-var end-core-entry-type-code)
487 (aver (not (find 0 ,buffer :start (ash ,ptr-var word-shift) :test #'/=)))
488 (return ,ptr-var))
489 ,@body
490 (incf ,ptr-var ,len-var)))))
492 (defmacro do-directory-entry (((index-var start-index input-nbytes) buffer) &body body)
493 `(let ((words-per-dirent 5))
494 (multiple-value-bind (n-entries remainder)
495 (floor ,input-nbytes words-per-dirent)
496 (aver (zerop remainder))
497 (symbol-macrolet ((id (%vector-raw-bits ,buffer index))
498 (nwords (%vector-raw-bits ,buffer (+ index 1)))
499 (data-page (%vector-raw-bits ,buffer (+ index 2)))
500 (addr (%vector-raw-bits ,buffer (+ index 3)))
501 (npages (%vector-raw-bits ,buffer (+ index 4))))
502 (do ((,index-var ,start-index (+ ,index-var words-per-dirent)))
503 ((= ,index-var (+ ,start-index (* n-entries words-per-dirent))))
504 ,@body)))))
506 (defmacro with-mapped-core ((sap-var start npages stream) &body body)
507 `(let (,sap-var)
508 (unwind-protect
509 (progn
510 (setq ,sap-var
511 (alien-funcall
512 (extern-alien "load_core_bytes"
513 (function system-area-pointer
514 int int unsigned unsigned int))
515 (sb-sys:fd-stream-fd ,stream)
516 (+ ,start +backend-page-bytes+) ; Skip the core header
517 0 ; place it anywhere
518 (* ,npages +backend-page-bytes+) ; len
520 ,@body)
521 (when ,sap-var
522 (alien-funcall
523 (extern-alien "os_deallocate"
524 (function void system-area-pointer unsigned))
525 ,sap-var (* ,npages +backend-page-bytes+))))))
527 (defun core-header-nwords (core-header &aux (sum 2))
528 ;; SUM starts as 2, as the core's magic number occupies 1 word
529 ;; and the ending tag of END-CORE-ENTRY-TYPE-CODE counts as 1.
530 (do-core-header-entry ((id len ptr) core-header)
531 ;; LEN as bound by the macro does not count 1 for the
532 ;; the entry identifier or LEN itself so add them in.
533 (incf sum (+ len 2)))
534 sum)
536 (defun change-dynamic-space-size (core-header new-size) ; expressed in MiB
537 (unless new-size
538 (return-from change-dynamic-space-size core-header))
539 (let ((new (copy-seq core-header)))
540 ;; memsize options if present must immediately follow the core magic number
541 ;; so it might require a byte-blt to move other entries over.
542 (unless (= (%vector-raw-bits new 1) runtime-options-magic)
543 ;; slide the header to right by 5 words
544 (replace new core-header :start1 (* 6 n-word-bytes) :start2 (* 1 n-word-bytes))
545 ;; see write_memsize_options for the format of this entry
546 ;; All words have to be stored since we're creating it from nothing.
547 (setf (%vector-raw-bits new 1) runtime-options-magic
548 (%vector-raw-bits new 2) 5 ; number of words in this entry
549 (%vector-raw-bits new 4) (extern-alien "thread_control_stack_size" unsigned)
550 (%vector-raw-bits new 5) (extern-alien "dynamic_values_bytes" (unsigned 32))))
551 (setf (%vector-raw-bits new 3) (* new-size 1024 1024))
552 new))
554 ;; These will get set to 0 if the target is not using mark-region-gc
555 (defglobal *bitmap-bits-per-page* (/ gencgc-page-bytes (* cons-size n-word-bytes)))
556 (defglobal *bitmap-bytes-per-page* (/ *bitmap-bits-per-page* n-byte-bits))
558 (defstruct page
559 words-used
560 (single-obj-p 0 :type bit)
561 type
562 scan-start
563 bitmap)
565 (defun read-page-table (stream n-ptes nbytes data-page &optional (print nil))
566 (declare (ignore nbytes))
567 (let ((table (make-array n-ptes)))
568 (file-position stream (* (1+ data-page) sb-c:+backend-page-bytes+))
569 (dotimes (i n-ptes)
570 (let* ((bitmap (make-array *bitmap-bits-per-page* :element-type 'bit))
571 (temp (make-array *bitmap-bytes-per-page* :element-type '(unsigned-byte 8))))
572 (when (plusp *bitmap-bits-per-page*)
573 (read-sequence temp stream))
574 (dotimes (i (/ (length bitmap) n-word-bits))
575 (setf (%vector-raw-bits bitmap i) (%vector-raw-bits temp i)))
576 (setf (aref table i) (make-page :bitmap bitmap))))
577 ;; a PTE is a lispword and a uint16_t
578 (let ((buf (make-array 10 :element-type '(unsigned-byte 8))))
579 (with-pinned-objectS (buf)
580 (dotimes (i n-ptes)
581 (read-sequence buf stream)
582 (let ((sso (sap-ref-word (vector-sap buf) 0))
583 (words-used (sap-ref-16 (vector-sap buf) 8))
584 (p (aref table i)))
585 (setf (page-words-used p) (logandc2 words-used 1)
586 (page-single-obj-p p) (logand words-used 1)
587 (page-scan-start p) (logandc2 sso 7)
588 (page-type p) (logand sso 7))
589 (when (and print (plusp (page-words-used p)))
590 (format t "~4d: ~4x ~2x~:[~; -~x~]~%"
591 i (ash (page-words-used p) word-shift)
592 (page-type p)
593 (if (= (page-single-obj-p p) 0) nil 1)
594 (page-scan-start p)))))))
595 table))
597 (defglobal page-type-symbols
598 #(:free :unboxed :boxed :mixed :small-mixed :cons nil :code))
599 (defun encode-page-type (keyword)
600 (the (not null) (position (the keyword keyword) page-type-symbols)))
601 (defun decode-page-type (type) (svref page-type-symbols type))
603 (defun calc-page-index (vaddr space)
604 (let ((vaddr (if (system-area-pointer-p vaddr) (sap-int vaddr) vaddr)))
605 (floor (- vaddr (space-addr space)) gencgc-page-bytes)))
606 (defun calc-page-base (vaddr)
607 (logandc2 vaddr (1- gencgc-page-bytes)))
608 (defun calc-object-index (vaddr)
609 (ash (- vaddr (calc-page-base vaddr)) (- n-lowtag-bits)))
611 (defun page-bytes-used (index ptes)
612 (ash (page-words-used (svref ptes index)) word-shift))
614 (defun find-ending-page (index ptes)
615 ;; A page ends a contiguous block if it is not wholly used,
616 ;; or if there is no next page,
617 ;; or the next page starts its own contiguous block
618 (if (or (< (page-bytes-used index ptes) gencgc-page-bytes)
619 (= (1+ index) (length ptes))
620 (zerop (page-scan-start (svref ptes (1+ index)))))
621 index
622 (find-ending-page (1+ index) ptes)))
624 (defun page-addr (index space) (+ (space-addr space) (* index gencgc-page-bytes)))
626 (defun walk-dynamic-space (page-type spacemap function)
627 (do* ((space (get-space dynamic-core-space-id spacemap))
628 (ptes (space-page-table space))
629 (nptes (length ptes))
630 (page-ranges)
631 (first-page 0))
632 ((>= first-page nptes) (nreverse page-ranges))
633 #+gencgc
634 (let* ((last-page (find-ending-page first-page ptes))
635 (pte (aref (space-page-table space) first-page))
636 (start-vaddr (page-addr first-page space))
637 (end-vaddr (+ (page-addr last-page space) (page-bytes-used last-page ptes))))
638 (when (and (plusp (page-type pte))
639 (or (null page-type) (eq page-type (decode-page-type (page-type pte)))))
640 ;; Because gencgc has page-spanning objects, it's easiest to zero-fill later
641 ;; if we track the range boundaries now.
642 (push (list nil first-page last-page) page-ranges) ; NIL = no funcallable-instance
643 (do ((vaddr (int-sap start-vaddr))
644 (paddr (int-sap (translate-ptr start-vaddr spacemap))))
645 ((>= (sap-int vaddr) end-vaddr))
646 (let* ((word (sap-ref-word paddr 0))
647 (widetag (logand word widetag-mask))
648 (size (if (eq widetag filler-widetag)
649 (ash (ash word -32) word-shift) ; -> words -> bytes
650 (let* ((obj (reconstitute-object (%make-lisp-obj (sap-int paddr))))
651 (size (primitive-object-size obj)))
652 ;; page types codes are never defined for Lisp
653 (when (eq page-type 7) ; KLUDGE: PAGE_TYPE_CODE
654 (aver (or (= widetag code-header-widetag)
655 (= widetag funcallable-instance-widetag))))
656 (when (= widetag funcallable-instance-widetag)
657 (setf (caar page-ranges) t)) ; T = has funcallable-instance
658 (funcall function obj vaddr size :ignore)
659 size))))
660 (setq vaddr (sap+ vaddr size)
661 paddr (sap+ paddr size)))))
662 (setq first-page (1+ last-page)))
663 #+mark-region-gc
664 (let* ((vaddr (int-sap (+ (space-addr space) (* first-page gencgc-page-bytes))))
665 (paddr (int-sap (translate-ptr (sap-int vaddr) spacemap)))
666 (pte (aref (space-page-table space) first-page))
667 (bitmap (page-bitmap pte)))
668 (cond ((= (page-single-obj-p pte) 1)
669 ;; last page is located by doing some arithmetic
670 (let* ((obj (reconstitute-object (%make-lisp-obj (sap-int paddr))))
671 (size (primitive-object-size obj))
672 (last-page (calc-page-index (sap+ vaddr (1- size)) space)))
673 #+nil (format t "~&Page ~4d..~4d ~A LARGE~%" first-page last-page (decode-page-type (page-type pte)))
674 (funcall function obj vaddr size t)
675 (setq first-page last-page)))
676 ((plusp (page-type pte))
677 #+nil (format t "~&Page ~4D : ~A~%" first-page (decode-page-type (page-type pte)))
678 (when (or (null page-type) (eq page-type (decode-page-type (page-type pte))))
679 (do ((object-offset-in-dualwords 0))
680 ((>= object-offset-in-dualwords *bitmap-bits-per-page*))
681 (let ((size
682 (cond ((zerop (sbit bitmap object-offset-in-dualwords))
683 (unless (and (zerop (sap-ref-word paddr 0))
684 (zerop (sap-ref-word paddr 8)))
685 (error "Unallocated object @ ~X: ~X ~X"
686 vaddr (sap-ref-word paddr 0) (sap-ref-word paddr 8)))
687 (* 2 n-word-bytes))
689 (let* ((obj (reconstitute-object (%make-lisp-obj (sap-int paddr))))
690 (size (primitive-object-size obj)))
691 (funcall function obj vaddr size nil)
692 size)))))
693 (setq vaddr (sap+ vaddr size)
694 paddr (sap+ paddr size))
695 (incf object-offset-in-dualwords (ash size (- (1+ word-shift)))))))))
696 (incf first-page))))
698 ;;; Unfortunately the idea of using target features to decide whether to
699 ;;; read a bitmap from PAGE_TABLE_CORE_ENTRY_TYPE_CODE falls flat,
700 ;;; because we can't scan for symbols until the core is read, but we can't
701 ;;; read the core until we decide whether there is a bitmap, which needs the
702 ;;; feature symbols. Some possible solutions (and there are others too):
703 ;;; 1) make a separate core entry for the bitmap
704 ;;; 2) add a word to that core entry indicating that it has a bitmap
705 ;;; 3) make a different entry type code for PTES_WITH_BITMAP
706 (defun detect-target-features (spacemap &aux result)
707 (flet ((scan (symbol)
708 (let ((list (symbol-global-value symbol))
709 (target-nil (compute-nil-object spacemap)))
710 (loop
711 (when (eq list target-nil) (return))
712 (setq list (translate list spacemap))
713 (let ((feature (translate (car list) spacemap)))
714 (aver (symbolp feature))
715 ;; convert keywords and only keywords into host keywords
716 (when (eq (symbol-package-id feature) (symbol-package-id :sbcl))
717 (let ((string (translate (symbol-name feature) spacemap)))
718 (push (intern string "KEYWORD") result))))
719 (setq list (cdr list))))))
720 (walk-dynamic-space
722 spacemap
723 (lambda (obj vaddr size large)
724 (declare (ignore vaddr size large))
725 (when (symbolp obj)
726 (when (or (and (eq (symbol-package-id obj) #.(symbol-package-id 'sb-impl:+internal-features+))
727 (string= (translate (symbol-name obj) spacemap) "+INTERNAL-FEATURES+"))
728 (and (eq (symbol-package-id obj) #.(symbol-package-id '*features*))
729 (string= (translate (symbol-name obj) spacemap) "*FEATURES*")))
730 (scan obj))))))
731 ;;(format t "~&Target-features=~S~%" result)
732 result)
734 (defun transport-code (from-vaddr from-paddr to-vaddr to-paddr size)
735 (%byte-blt from-paddr 0 to-paddr 0 size)
736 (let* ((new-physobj (%make-lisp-obj (logior (sap-int to-paddr) other-pointer-lowtag)))
737 (header-bytes (ash (code-header-words new-physobj) word-shift))
738 (new-insts (code-instructions new-physobj)))
739 ;; fix the jump table words which, if present, start at NEW-INSTS
740 (let ((wordcount (code-jump-table-words new-physobj))
741 (disp (sap- to-vaddr from-vaddr)))
742 (loop for i from 1 below wordcount
743 do (let ((w (sap-ref-word new-insts (ash i word-shift))))
744 (unless (zerop w)
745 (setf (sap-ref-word new-insts (ash i word-shift)) (+ w disp))))))
746 ;; fix the simple-fun pointers
747 (dotimes (i (code-n-entries new-physobj))
748 (let ((fun-offs (%code-fun-offset new-physobj i)))
749 ;; Assign the address that each simple-fun will have assuming
750 ;; the object will reside at its new logical address.
751 (setf (sap-ref-sap new-insts (+ fun-offs n-word-bytes))
752 (sap+ to-vaddr (+ header-bytes fun-offs (* 2 n-word-bytes))))))))
754 (defun transport-dynamic-space-code (codeblobs spacemap new-space free-ptr)
755 (do ((list codeblobs (cdr list))
756 (offsets-vector-data (sap+ new-space (* 2 n-word-bytes)))
757 (object-index 0 (1+ object-index)))
758 ((null list))
759 ;; FROM-VADDR is the original logical (virtual) address, and FROM-PADDR
760 ;; is where the respective object is currently resident in memory now.
761 ;; Similarly-named "TO-" values correspond to the location in new space.
762 (destructuring-bind (from-vaddr . size) (car list)
763 (let ((from-paddr (int-sap (translate-ptr (sap-int from-vaddr) spacemap)))
764 (to-vaddr (+ +code-space-nominal-address+ free-ptr))
765 (to-paddr (sap+ new-space free-ptr)))
766 (setf (sap-ref-32 offsets-vector-data (ash object-index 2)) free-ptr)
767 (transport-code from-vaddr from-paddr (int-sap to-vaddr) to-paddr size)
768 (incf free-ptr size)))))
770 (defun remap-to-quasi-static-code (val spacemap fwdmap)
771 (when (is-lisp-pointer (get-lisp-obj-address val))
772 (binding* ((translated (translate val spacemap))
773 (vaddr (get-lisp-obj-address val))
774 (code-base-addr
775 (cond ((simple-fun-p translated)
776 ;; the code component has to be computed "by hand" because FUN-CODE-HEADER
777 ;; would return the physically mapped object, but we need
778 ;; to get the logical address of the code.
779 (- (- vaddr fun-pointer-lowtag)
780 (ash (ldb (byte 24 8)
781 (sap-ref-word (int-sap (get-lisp-obj-address translated))
782 (- fun-pointer-lowtag)))
783 word-shift)))
784 ((code-component-p translated)
785 (- vaddr other-pointer-lowtag)))
786 :exit-if-null)
787 (new-code-offset (gethash code-base-addr fwdmap) :exit-if-null))
788 (%make-lisp-obj (+ (if (functionp translated)
789 (- vaddr code-base-addr) ; function tag is in the difference
790 other-pointer-lowtag)
791 +code-space-nominal-address+
792 new-code-offset)))))
794 ;;; It's not worth trying to use the host's DO-REFERENCED-OBJECT because it requires
795 ;;; completely different behavior for INSTANCE and FUNCALLABLE-INSTANCE to avoid using
796 ;;; the layout pointers as-is. And closures don't really work either. So unfortunately
797 ;;; this is essentially a reimplementation. Thankfully we only have to deal with pointers
798 ;;; that could possibly point to code.
799 (defun update-quasi-static-code-ptrs
800 (obj spacemap fwdmap displacement &optional print
801 &aux (sap (int-sap (logandc2 (get-lisp-obj-address obj) lowtag-mask))))
802 (when print
803 (format t "paddr ~X vaddr ~X~%" (get-lisp-obj-address obj)
804 (+ (get-lisp-obj-address obj) displacement)))
805 (macrolet ((visit (place)
806 `(let* ((oldval ,place) (newval (remap oldval)))
807 (when newval
808 (setf ,place newval)))))
809 (flet ((fun-entrypoint (fun)
810 (+ (get-lisp-obj-address fun) (- fun-pointer-lowtag) (ash 2 word-shift)))
811 (remap (x)
812 (remap-to-quasi-static-code x spacemap fwdmap)))
813 (cond
814 ((listp obj) (visit (car obj)) (visit (cdr obj)))
815 ((simple-vector-p obj)
816 (dotimes (i (length obj)) (visit (svref obj i))))
817 ((%instancep obj)
818 (let ((type (truly-the layout (translate (%instance-layout obj) spacemap))))
819 (do-layout-bitmap (i taggedp type (%instance-length obj))
820 (when taggedp (visit (%instance-ref obj i))))))
821 ((functionp obj)
822 (let ((start
823 (cond ((funcallable-instance-p obj)
824 ;; The trampoline points to the function itself (so is ignorable)
825 ;; and following that word are 2 words of machine code.
828 (aver (closurep obj))
829 (let ((fun (remap (%closure-fun obj))))
830 ;; there is no setter for closure-fun
831 (setf (sap-ref-word sap n-word-bytes) (fun-entrypoint fun)))
832 2))))
833 (loop for i from start to (logior (get-closure-length obj) 1)
834 do (visit (sap-ref-lispobj sap (ash i word-shift))))))
835 ((code-component-p obj)
836 (loop for i from 2 below (code-header-words obj)
837 do (visit (code-header-ref obj i))))
838 ((symbolp obj)
839 (visit (sap-ref-lispobj sap (ash symbol-value-slot word-shift))))
840 ((weak-pointer-p obj)
841 (visit (sap-ref-lispobj sap (ash weak-pointer-value-slot word-shift))))
842 ((fdefn-p obj)
843 (let ((raw (sap-ref-word sap (ash fdefn-raw-addr-slot word-shift))))
844 (unless (in-bounds-p raw (space-bounds static-core-space-id spacemap))
845 (awhen (remap (%make-lisp-obj (+ raw (ash -2 word-shift) fun-pointer-lowtag)))
846 (setf (sap-ref-word sap (ash fdefn-raw-addr-slot word-shift))
847 (fun-entrypoint it)))))
848 (visit (sap-ref-lispobj sap (ash fdefn-fun-slot word-shift))))
849 ((= (%other-pointer-widetag obj) value-cell-widetag)
850 (visit (sap-ref-lispobj sap (ash value-cell-value-slot word-shift))))))))
852 ;;; Clear all the old objects. Funcallable instances can be co-mingled with
853 ;;; code, so a code page might not be empty but most will be. Free those pages.
854 (defun zerofill-old-code (spacemap codeblobs page-ranges)
855 (declare (ignorable page-ranges))
856 (with-alien ((memset (function void unsigned int unsigned) :extern))
857 (flet ((reset-pte (pte)
858 (setf (page-words-used pte) 0
859 (page-single-obj-p pte) 0
860 (page-type pte) 0
861 (page-scan-start pte) 0)))
862 (let ((space (get-space dynamic-core-space-id spacemap)))
863 #+gencgc
864 (dolist (range page-ranges (aver (null codeblobs)))
865 (destructuring-bind (in-use first last) range
866 ;;(format t "~&Working on range ~D..~D~%" first last)
867 (loop while codeblobs
868 do (destructuring-bind (vaddr . size) (car codeblobs)
869 (let ((page (calc-page-index vaddr space)))
870 (cond ((> page last) (return))
871 ((< page first) (bug "Incorrect sort"))
873 (let ((paddr (translate-ptr (sap-int vaddr) spacemap)))
874 (alien-funcall memset paddr 0 size)
875 (when in-use ; store a filler widetag
876 (let* ((nwords (ash size (- word-shift)))
877 (header (logior (ash nwords 32) filler-widetag)))
878 (setf (sap-ref-word (int-sap paddr) 0) header))))
879 (pop codeblobs))))))
880 (unless in-use
881 (loop for page-index from first to last
882 do (reset-pte (svref (space-page-table space) page-index))))))
883 #+mark-region-gc
884 (dolist (code codeblobs)
885 (destructuring-bind (vaddr . size) code
886 (alien-funcall memset (translate-ptr (sap-int vaddr) spacemap) 0 size)
887 (let* ((page-index (calc-page-index vaddr space))
888 (pte (aref (space-page-table space) page-index))
889 (object-index (calc-object-index (sap-int vaddr))))
890 (setf (sbit (page-bitmap pte) object-index) 0)
891 (cond ((= (page-single-obj-p pte) 1)
892 ;(format t "~&Cleared large-object pages @ ~x~%" (sap-int vaddr))
893 (loop for p from page-index to (calc-page-index (sap+ vaddr (1- size)) space)
894 do (let ((pte (svref (space-page-table space) p)))
895 (aver (not (find 1 (page-bitmap pte))))
896 (reset-pte pte))))
897 ((not (find 1 (page-bitmap pte)))
898 ;; is the #+gencgc logic above actually more efficient?
899 ;;(format t "~&Code page ~D is now empty~%" page-index)
900 (reset-pte pte))))))))))
902 (defun parse-core-header (input core-header)
903 (let ((space-list)
904 (total-npages 0) ; excluding core header page
905 (card-mask-nbits)
906 (core-dir-start)
907 (initfun))
908 (do-core-header-entry ((id len ptr) core-header)
909 (ecase id
910 (#.directory-core-entry-type-code
911 (setq core-dir-start (- ptr 2))
912 (do-directory-entry ((index ptr len) core-header)
913 (incf total-npages npages)
914 (push (make-space id addr data-page 0 nwords) space-list)))
915 (#.page-table-core-entry-type-code
916 (aver (= len 4))
917 (symbol-macrolet ((n-ptes (%vector-raw-bits core-header (+ ptr 1)))
918 (nbytes (%vector-raw-bits core-header (+ ptr 2)))
919 (data-page (%vector-raw-bits core-header (+ ptr 3))))
920 (aver (= data-page total-npages))
921 (setf card-mask-nbits (%vector-raw-bits core-header ptr))
922 (format nil "~&card-nbits = ~D~%" card-mask-nbits)
923 (let ((space (get-space dynamic-core-space-id (cons nil space-list))))
924 (setf (space-page-table space) (read-page-table input n-ptes nbytes data-page)))))
925 (#.build-id-core-entry-type-code
926 (let ((string (make-string (%vector-raw-bits core-header ptr)
927 :element-type 'base-char)))
928 (%byte-blt core-header (* (1+ ptr) n-word-bytes) string 0 (length string))
929 (format nil "Build ID [~a] len=~D ptr=~D actual-len=~D~%" string len ptr (length string))))
930 (#.runtime-options-magic) ; ignore
931 (#.initial-fun-core-entry-type-code
932 (setq initfun (%vector-raw-bits core-header ptr)))))
933 (values total-npages (reverse space-list) card-mask-nbits core-dir-start initfun)))
935 (defconstant +lispwords-per-corefile-page+ (/ sb-c:+backend-page-bytes+ n-word-bytes))
937 (defun rewrite-core (directory spacemap card-mask-nbits initfun core-header offset output
938 &aux (dynamic-space (get-space dynamic-core-space-id spacemap)))
939 (aver (= (%vector-raw-bits core-header offset) directory-core-entry-type-code))
940 (let ((nwords (+ (* (length directory) 5) 2)))
941 (setf (%vector-raw-bits core-header (incf offset)) nwords))
942 (let ((page-count 0)
943 (n-ptes (length (space-page-table dynamic-space))))
944 (dolist (dir-entry directory)
945 (setf (car dir-entry) page-count)
946 (destructuring-bind (id paddr vaddr nwords) (cdr dir-entry)
947 (declare (ignore paddr))
948 (let ((npages (ceiling nwords +lispwords-per-corefile-page+)))
949 (when (= id dynamic-core-space-id)
950 (aver (= npages n-ptes)))
951 (dolist (word (list id nwords page-count vaddr npages))
952 (setf (%vector-raw-bits core-header (incf offset)) word))
953 (incf page-count npages))))
954 (let* ((sizeof-corefile-pte (+ n-word-bytes 2))
955 (pte-bytes (align-up (* sizeof-corefile-pte n-ptes) n-word-bytes)))
956 (dolist (word (list page-table-core-entry-type-code
957 6 ; = number of words in this core header entry
958 card-mask-nbits
959 n-ptes (+ (* n-ptes *bitmap-bytes-per-page*) pte-bytes)
960 page-count))
961 (setf (%vector-raw-bits core-header (incf offset)) word)))
962 (dolist (word (list initial-fun-core-entry-type-code 3 initfun
963 end-core-entry-type-code 2))
964 (setf (%vector-raw-bits core-header (incf offset)) word))
965 (write-sequence core-header output)
966 ;; write out the data from each space
967 (dolist (dir-entry directory)
968 (destructuring-bind (page id paddr vaddr nwords) dir-entry
969 (declare (ignore id vaddr))
970 (aver (= (file-position output) (* sb-c:+backend-page-bytes+ (1+ page))))
971 (let* ((npages (ceiling nwords +lispwords-per-corefile-page+))
972 (nbytes (* npages sb-c:+backend-page-bytes+))
973 (wrote
974 (sb-unix:unix-write (sb-impl::fd-stream-fd output) paddr 0 nbytes)))
975 (aver (= wrote nbytes)))))
976 (aver (= (file-position output) (* sb-c:+backend-page-bytes+ (1+ page-count))))
977 #+mark-region-gc ; write the bitmap
978 (dovector (pte (space-page-table dynamic-space))
979 (let ((bitmap (page-bitmap pte)))
980 (sb-sys:with-pinned-objects (bitmap)
981 ;; WRITE-SEQUENCE on a bit vector would write one octet per bit
982 (sb-unix:unix-write (sb-impl::fd-stream-fd output) bitmap 0 (/ (length bitmap) 8)))))
983 ;; write the PTEs
984 (let ((buffer (make-array 10 :element-type '(unsigned-byte 8))))
985 (sb-sys:with-pinned-objects (buffer)
986 (let ((sap (vector-sap buffer)))
987 (dovector (pte (space-page-table dynamic-space))
988 (setf (sap-ref-64 sap 0) (logior (page-scan-start pte) (page-type pte))
989 (sap-ref-16 sap 8) (logior (page-words-used pte) (page-single-obj-p pte)))
990 (write-sequence buffer output)))
991 (let* ((bytes-written (* 10 (length (space-page-table dynamic-space))))
992 (diff (- (align-up bytes-written n-word-bytes)
993 bytes-written)))
994 (fill buffer 0)
995 (write-sequence buffer output :end diff))))
996 ;; write the trailer
997 (let ((buffer (make-array 16 :element-type '(unsigned-byte 8)
998 :initial-element 0)))
999 (sb-sys:with-pinned-objects (buffer)
1000 (setf (%vector-raw-bits buffer 0) 0
1001 (%vector-raw-bits buffer 1) core-magic)
1002 (write-sequence buffer output)))
1003 (force-output output)))
1005 (defun walk-target-space (function space-id spacemap)
1006 (let* ((space (get-space space-id spacemap))
1007 (paddr (space-physaddr space spacemap)))
1008 (map-objects-in-range function
1009 (%make-lisp-obj
1010 (if (= space-id static-core-space-id)
1011 ;; must not visit NIL, bad things happen
1012 (translate-ptr (+ static-space-start sb-vm::static-space-objects-offset)
1013 spacemap)
1014 (sap-int paddr)))
1015 (%make-lisp-obj (sap-int (sap+ paddr (space-size space)))))))
1017 (defun find-target-asm-code (spacemap)
1018 (walk-target-space (lambda (obj widetag size)
1019 (declare (ignore size))
1020 (when (= widetag code-header-widetag)
1021 (return-from find-target-asm-code
1022 (let* ((space (get-space static-core-space-id spacemap))
1023 (vaddr (space-addr space))
1024 (paddr (space-physaddr space spacemap)))
1025 (%make-lisp-obj
1026 (+ vaddr (- (get-lisp-obj-address obj)
1027 (sap-int paddr))))))))
1028 static-core-space-id spacemap))
1030 (defconstant simple-array-uword-widetag
1031 #+64-bit simple-array-unsigned-byte-64-widetag
1032 #-64-bit simple-array-unsigned-byte-32-widetag)
1034 (defun move-dynamic-code-to-text-space (input-pathname output-pathname)
1035 ;; Remove old files
1036 (ignore-errors (delete-file output-pathname))
1037 ;; Ensure that all files can be opened
1038 (with-open-file (input input-pathname :element-type '(unsigned-byte 8))
1039 (with-open-file (output output-pathname :direction :output
1040 :element-type '(unsigned-byte 8) :if-exists :supersede)
1041 ;; KLUDGE: see comment above DETECT-TARGET-FEATURES
1042 #+gencgc (setq *bitmap-bits-per-page* 0 *bitmap-bytes-per-page* 0)
1043 (binding* ((core-header (make-array +backend-page-bytes+ :element-type '(unsigned-byte 8)))
1044 (core-offset (read-core-header input core-header t))
1045 ((npages space-list card-mask-nbits core-dir-start initfun)
1046 (parse-core-header input core-header)))
1047 ;; Map the core file to memory
1048 (with-mapped-core (sap core-offset npages input)
1049 (let* ((spacemap (cons sap (sort (copy-list space-list) #'> :key #'space-addr)))
1050 (target-features (detect-target-features spacemap))
1051 (codeblobs nil)
1052 (fwdmap (make-hash-table))
1053 (n-objects)
1054 (offsets-vector-size)
1055 ;; We only need enough space to write C linkage call redirections from the
1056 ;; assembler routine codeblob, because those are the calls which assume that
1057 ;; asm code can directly call into the linkage space using "CALL rel32" form.
1058 ;; Dynamic-space calls do not assume that - they use "CALL [ea]" form.
1059 (c-linkage-reserved-words 12) ; arbitrary overestimate
1060 (reserved-amount)
1061 ;; text space will contain a copy of the asm code so it can use call rel32 form
1062 (asm-code (find-target-asm-code spacemap))
1063 (asm-code-size (primitive-object-size (translate asm-code spacemap)))
1064 (freeptr asm-code-size)
1065 (page-ranges
1066 (walk-dynamic-space
1067 :code spacemap
1068 (lambda (obj vaddr size large)
1069 (declare (ignore large))
1070 (when (code-component-p obj)
1071 (push (cons vaddr size) codeblobs)
1072 ;; new object will be at FREEPTR bytes from new space start
1073 (setf (gethash (sap-int vaddr) fwdmap) freeptr)
1074 (incf freeptr size))))))
1075 ;; FIXME: this _still_ doesn't work, because if the buid has :IMMOBILE-SPACE
1076 ;; then the symbols CL:*FEATURES* and SB-IMPL:+INTERNAL-FEATURES+
1077 ;; are not in dynamic space.
1078 (when (member :immobile-space target-features)
1079 (error "Can't relocate code to text space since text space already exists"))
1080 (setq codeblobs
1081 (acons (int-sap (logandc2 (get-lisp-obj-address asm-code) lowtag-mask))
1082 asm-code-size
1083 (nreverse codeblobs))
1084 n-objects (length codeblobs))
1085 ;; Preceding the code objects are two vectors:
1086 ;; (1) a vector of uint32_t indicating the starting offset (from the space start)
1087 ;; of each code object.
1088 ;; (2) a vector of uint64_t which embeds a JMP instruction to a C linkage table entry.
1089 ;; These instructions are near enough to be called via 'rel32' form. (The ordinary
1090 ;; alien linkage space is NOT near enough, after code is moved to text space)
1091 ;; The size of the new text space has to account for the sizes of the vectors.
1092 (let* ((n-vector1-data-words (ceiling n-objects 2)) ; two uint32s fit in a lispword
1093 (vector1-size (ash (+ (align-up n-vector1-data-words 2) ; round to even
1094 vector-data-offset)
1095 word-shift))
1096 (n-vector2-data-words c-linkage-reserved-words)
1097 (vector2-size (ash (+ n-vector2-data-words vector-data-offset)
1098 word-shift)))
1099 (setf offsets-vector-size vector1-size
1100 reserved-amount (+ vector1-size vector2-size))
1101 ;; Adjust all code offsets upward to avoid doing more math later
1102 (maphash (lambda (k v)
1103 (setf (gethash k fwdmap) (+ v reserved-amount)))
1104 fwdmap)
1105 (incf freeptr reserved-amount)
1106 (format nil "~&Code: ~D objects, ~D bytes~%" (length codeblobs) freeptr))
1107 (let* ((new-space-nbytes (align-up freeptr sb-c:+backend-page-bytes+))
1108 (new-space (sb-sys:allocate-system-memory new-space-nbytes)))
1109 ;; Write header of "vector 1"
1110 (setf (sap-ref-word new-space 0) simple-array-unsigned-byte-32-widetag
1111 (sap-ref-word new-space n-word-bytes) (fixnumize n-objects))
1112 ;; write header of "vector 2"
1113 (setf (sap-ref-word new-space offsets-vector-size) simple-array-uword-widetag
1114 (sap-ref-word new-space (+ offsets-vector-size n-word-bytes))
1115 (fixnumize c-linkage-reserved-words))
1116 ;; Transport code contiguously into new space
1117 (transport-dynamic-space-code codeblobs spacemap new-space reserved-amount)
1118 ;; Walk spaces except for newspace, changing any pointers that
1119 ;; should point to new space.
1120 (dolist (space-id `(,dynamic-core-space-id ,static-core-space-id
1121 ,permgen-core-space-id))
1122 (binding* ((space (get-space space-id spacemap) :exit-if-null)
1123 (vaddr (space-addr space))
1124 (paddr (space-physaddr space spacemap))
1125 (diff (+ (- (sap-int paddr)) vaddr)))
1126 (format nil "~&Fixing ~A~%" space)
1127 (walk-target-space
1128 (lambda (object widetag size)
1129 (declare (ignore widetag size))
1130 (unless (and (code-component-p object) (= space-id dynamic-core-space-id))
1131 (update-quasi-static-code-ptrs object spacemap fwdmap diff)))
1132 space-id spacemap)))
1133 ;; Walk new space and fix pointers into itself
1134 (format nil "~&Fixing newspace~%")
1135 (map-objects-in-range
1136 (lambda (object widetag size)
1137 (declare (ignore widetag size))
1138 (update-quasi-static-code-ptrs object spacemap fwdmap 0))
1139 (%make-lisp-obj (sap-int new-space))
1140 (%make-lisp-obj (sap-int (sap+ new-space freeptr))))
1141 ;; don't zerofill asm code in static space
1142 (zerofill-old-code spacemap (cdr codeblobs) page-ranges)
1143 ;; Update the core header to contain newspace
1144 (let ((spaces (nconc
1145 (mapcar (lambda (space)
1146 (list 0 (space-id space)
1147 (int-sap (translate-ptr (space-addr space) spacemap))
1148 (space-addr space)
1149 (space-nwords space)))
1150 space-list)
1151 `((0 ,immobile-text-core-space-id ,new-space
1152 ,+code-space-nominal-address+
1153 ,(ash freeptr (- word-shift)))))))
1154 (rewrite-core spaces spacemap card-mask-nbits initfun
1155 core-header core-dir-start output)
1156 ))))))))
1158 ;;; Processing a core without immobile-space
1160 ;;; This file provides a recipe which gets a little bit closer to being able to
1161 ;;; emulate #+immobile-space in so far as producing an ELF core is concerned.
1162 ;;; The recipe is a bit more complicated than I'd like, but it works.
1163 ;;; Let's say you want a core with contiguous text space containing the code
1164 ;;; of a quicklisp system.
1166 ;;; $ run-sbcl.sh
1167 ;;; * (ql:quickload :one-more-re-nightmare-tests)
1168 ;;; * (save-lisp-and-die "step1.core")
1169 ;;; $ run-sbcl.sh
1170 ;;; * (load "tools-for-build/editcore")
1171 ;;; * (sb-editcore:move-dynamic-code-to-text-space "step1.core" "step2.core")
1172 ;;; * (sb-editcore:redirect:text-space-calls "step2.core")
1173 ;;; Now "step2.core" has a text space, and all lisp-to-lisp calls bypass their FDEFN.
1174 ;;; At this point split-core on "step2.core" can run in the manner of elfcore.test.sh
1176 (defun get-code-segments (code vaddr core)
1177 (let ((di (%code-debug-info code))
1178 (spacemap (core-spacemap core))
1179 (inst-base (+ vaddr (ash (code-header-words code) word-shift)))
1180 (result))
1181 (aver (%instancep di))
1182 (if (zerop (code-n-entries code)) ; assembler routines
1183 (dolist (entry (target-hash-table-alist di spacemap))
1184 (let* ((val (translate (undescriptorize (cdr entry)) spacemap))
1185 ;; VAL is (start end . index)
1186 (start (the fixnum (car val)))
1187 (end (the fixnum (car (translate (cdr val) spacemap)))))
1188 (push (make-code-segment code start (- (1+ end) start)
1189 :virtual-location (+ inst-base start))
1190 result)))
1191 (dolist (range (get-text-ranges code core))
1192 (let ((car (car range)))
1193 (when (integerp car)
1194 (push (make-code-segment code car (- (cdr range) car)
1195 :virtual-location (+ inst-base car))
1196 result)))))
1197 (sort result #'< :key #'sb-disassem:seg-virtual-location)))
1199 (defstruct (range (:constructor make-range (labeled vaddr bytecount)))
1200 labeled vaddr bytecount)
1202 (defun inst-vaddr (inst) (range-vaddr (car inst)))
1203 (defun inst-length (inst) (range-bytecount (car inst)))
1204 (defun inst-end (inst &aux (range (car inst)))
1205 (+ (range-vaddr range) (range-bytecount range)))
1207 (defmethod print-object ((self range) stream)
1208 (format stream "~A~x,~x"
1209 (if (range-labeled self) "L:" " ")
1210 (range-vaddr self)
1211 (range-bytecount self)))
1212 (defun get-code-instruction-model (code vaddr core)
1213 (let* ((segments (get-code-segments code vaddr core))
1214 (insts-vaddr (+ vaddr (ash (code-header-words code) word-shift)))
1215 (dstate (sb-disassem:make-dstate))
1216 (fun-header-locs
1217 (loop for i from 0 below (code-n-entries code)
1218 collect (+ insts-vaddr (%code-fun-offset code i))))
1219 (labels))
1220 (sb-disassem:label-segments segments dstate)
1221 ;; are labels not already sorted?
1222 (setq labels (sort (sb-disassem::dstate-labels dstate) #'< :key #'car))
1223 (sb-int:collect ((result))
1224 (dolist (seg segments (coerce (result) 'vector))
1225 (setf (sb-disassem:dstate-segment dstate) seg
1226 (sb-disassem:dstate-segment-sap dstate)
1227 (funcall (sb-disassem:seg-sap-maker seg)))
1228 (setf (sb-disassem:dstate-cur-offs dstate) 0)
1229 (loop
1230 (when (eql (sb-disassem:dstate-cur-addr dstate) (car fun-header-locs))
1231 (incf (sb-disassem:dstate-cur-offs dstate) (* simple-fun-insts-offset n-word-bytes))
1232 (pop fun-header-locs))
1233 (let* ((pc (sb-disassem:dstate-cur-addr dstate))
1234 (labeled (when (and labels (= pc (caar labels)))
1235 (pop labels)
1237 (inst (sb-disassem:disassemble-instruction dstate))
1238 (nbytes (- (sb-disassem:dstate-cur-addr dstate) pc)))
1239 (result (cons (make-range labeled pc nbytes) inst)))
1240 (when (>= (sb-disassem:dstate-cur-offs dstate) (sb-disassem:seg-length seg))
1241 (return)))))))
1243 (defun get-text-space-asm-code-replica (space spacemap)
1244 (let* ((physaddr (sap-int (space-physaddr space spacemap)))
1245 (offsets-vector (%make-lisp-obj (logior physaddr other-pointer-lowtag)))
1246 (offset (aref offsets-vector 0)))
1247 (values (+ (space-addr space) offset)
1248 (%make-lisp-obj (+ physaddr offset other-pointer-lowtag)))))
1250 (defun get-static-space-asm-code (space spacemap)
1251 (let ((found
1252 (block nil
1253 (sb-editcore::walk-target-space
1254 (lambda (x widetag size)
1255 (declare (ignore widetag size))
1256 (when (code-component-p x)
1257 (return x)))
1258 static-core-space-id spacemap))))
1259 (values (+ (- (get-lisp-obj-address found)
1260 (sap-int (space-physaddr space spacemap))
1261 other-pointer-lowtag)
1262 (space-addr space))
1263 found)))
1265 (defun persist-to-file (spacemap core-offset stream)
1266 (aver (zerop core-offset))
1267 (dolist (space-id `(,static-core-space-id
1268 ,immobile-text-core-space-id
1269 ,dynamic-core-space-id))
1270 (let ((space (get-space space-id spacemap)))
1271 (file-position stream (* (1+ (space-data-page space)) +backend-page-bytes+))
1272 (sb-unix:unix-write (sb-impl::fd-stream-fd stream)
1273 (space-physaddr space spacemap)
1275 (align-up (* (space-nwords space) n-word-bytes)
1276 +backend-page-bytes+)))))
1278 ;;;; Offline mark-region compactor
1279 (declaim (inline load-bits-wordindexed))
1280 (defun load-bits-wordindexed (sap index)
1281 (declare (type (signed-byte 32) index))
1282 (sap-ref-word sap (ash index word-shift)))
1283 (defun load-wordindexed (sap index)
1284 (let ((word (load-bits-wordindexed sap index)))
1285 (if (not (is-lisp-pointer word))
1286 (%make-lisp-obj word) ; fixnum, character, single-float, unbound-marker
1287 (make-descriptor word))))
1289 (defun physical-sap (taggedptr spacemap)
1290 (let ((bits (if (descriptor-p taggedptr) (descriptor-bits taggedptr) taggedptr)))
1291 (int-sap (translate-ptr (logandc2 bits lowtag-mask) spacemap))))
1293 (defun size-of (sap)
1294 (with-alien ((primitive-object-size (function unsigned system-area-pointer) :extern))
1295 (alien-funcall primitive-object-size sap)))
1297 (defmacro get-layout (sap widetag)
1298 (declare (ignorable widetag))
1299 ;; FIXME: should depend on target feature, not host feature
1300 #.(if (member :compact-instance-header sb-impl:+internal-features+)
1301 '`(sap-ref-32 ,sap 4)
1302 '`(sap-ref-word ,sap (ash (ecase ,widetag
1303 (,funcallable-instance-widetag 5) ; KLUDGE
1304 (,instance-widetag 1))
1305 word-shift))))
1306 (defun set-layout (instance-sap widetag layout-bits)
1307 (declare (ignorable widetag))
1308 (setf (get-layout instance-sap widetag) layout-bits))
1310 ;;; Return T if WIDETAG is for a pointerless object.
1311 (defun leafp (widetag)
1312 (declare ((integer 0 255) widetag))
1313 (macrolet ((compute-leaves (&aux (result 0))
1314 (loop for w in ; these are the nonleaves
1315 `(,closure-widetag ,code-header-widetag ,symbol-widetag ,value-cell-widetag
1316 ,instance-widetag ,funcallable-instance-widetag ,weak-pointer-widetag
1317 ,fdefn-widetag ,ratio-widetag ,complex-rational-widetag
1318 ,simple-vector-widetag ,simple-array-widetag ,complex-array-widetag
1319 ,complex-base-string-widetag #+sb-unicode ,complex-character-string-widetag
1320 ,complex-vector-widetag ,complex-bit-vector-widetag)
1321 do (setf result (logior result (ash 1 (ash w -2)))))
1322 (lognot result)))
1323 (logbitp (ash widetag -2) (compute-leaves))))
1325 (defun instance-slot-count (sap widetag)
1326 (let ((header (sap-ref-word sap 0)))
1327 (ecase widetag
1328 (#.funcallable-instance-widetag
1329 (ldb (byte 8 n-widetag-bits) header))
1330 (#.instance-widetag
1331 ;; See instance_length() in src/runtime/instance.inc
1332 (+ (logand (ash header (- instance-length-shift)) instance-length-mask)
1333 (logand (ash header -10) (ash header -9) 1))))))
1335 (defun target-listp (taggedptr)
1336 (= (logand taggedptr lowtag-mask) list-pointer-lowtag))
1338 (defun target-widetag-of (descriptor spacemap)
1339 (if (target-listp (descriptor-bits descriptor))
1340 list-pointer-lowtag
1341 (logand (sap-ref-word (physical-sap descriptor spacemap) 0) widetag-mask)))
1343 ;;; Target objects will be represented by a DESCRIPTOR, making this safe even for
1344 ;;; precise GC. i.e. we never load into a register the bits of a target pointer that
1345 ;;; could be mistaken for something in the host's dynamic-space.
1346 ;;; DESCRIPTOR-BITS has a lowtag so that we can easily discriminate the 4 pointer types.
1347 (macrolet ((scan-slot (index &optional value)
1348 (if value
1349 `(funcall function sap ,index ,value widetag)
1350 `(let ((.i. ,index))
1351 (funcall function sap .i. (load-wordindexed sap .i.) widetag))))
1352 (asm-call-p (x name)
1353 `(eq ,x (load-time-value (sb-fasl:get-asm-routine ,name) t)))
1354 (fun-entry->descriptor (addr)
1355 `(make-descriptor (+ ,addr (* -2 n-word-bytes) fun-pointer-lowtag))))
1357 (defun trace-symbol (function sap &aux (widetag symbol-widetag))
1358 (scan-slot symbol-value-slot)
1359 (scan-slot symbol-fdefn-slot)
1360 (scan-slot symbol-info-slot)
1361 (scan-slot symbol-name-slot ; decode the packed NAME word
1362 (make-descriptor (ldb (byte 48 0) (load-bits-wordindexed sap symbol-name-slot)))))
1364 ;;; This is a less general variant of do-referenced-object, but more efficient.
1365 ;;; I think it's the most concisely an object slot visitor can be expressed.
1366 (defun trace-obj (function descriptor spacemap
1367 &optional (layout-translator
1368 (lambda (ptr) (translate-ptr ptr spacemap)))
1369 &aux (widetag (target-widetag-of descriptor spacemap))
1370 (sap (physical-sap descriptor spacemap)))
1371 (declare (function function layout-translator))
1372 (multiple-value-bind (first last)
1373 (cond ((= widetag list-pointer-lowtag) (values 0 cons-size))
1374 ((member widetag `(,instance-widetag ,funcallable-instance-widetag))
1375 ;; These two primitive types have a bitmap
1376 (let* ((ld (get-layout sap widetag)) ; layout descriptor
1377 (layout
1378 (truly-the layout
1379 (%make-lisp-obj (funcall layout-translator ld)))))
1380 (scan-slot 0 (make-descriptor ld))
1381 (do-layout-bitmap (i taggedp layout (instance-slot-count sap widetag))
1382 (when taggedp (scan-slot (1+ i))))
1383 (return-from trace-obj)))
1384 ((leafp widetag) (return-from trace-obj))
1385 ((= widetag symbol-widetag)
1386 (return-from trace-obj (trace-symbol function sap)))
1387 ((= widetag code-header-widetag)
1388 (values 2 ; code_header_words() can't be called from Lisp, so emulate it
1389 (ash (ldb (byte 32 0) (load-bits-wordindexed sap code-boxed-size-slot))
1390 (- word-shift))))
1392 (let ((first 1) (last (ash (size-of sap) (- word-shift))))
1393 (case widetag
1394 (#.fdefn-widetag ; wordindex 3 is an untagged simple-fun entry address
1395 (let ((bits (load-bits-wordindexed sap fdefn-raw-addr-slot)))
1396 (unless (or (eq bits 0)
1397 (asm-call-p bits 'sb-vm::undefined-tramp)
1398 (asm-call-p bits 'sb-vm::closure-tramp))
1399 (scan-slot fdefn-raw-addr-slot (fun-entry->descriptor bits))))
1400 (setq last 3))
1401 (#.closure-widetag ; wordindex 1 is an untagged simple-fun entry address
1402 (let ((bits (load-bits-wordindexed sap closure-fun-slot)))
1403 (scan-slot closure-fun-slot (fun-entry->descriptor bits)))
1404 (setq first 2)))
1405 (values first last))))
1406 (loop for i from first below last do (scan-slot i))))
1407 ) ; end MACROLET
1409 ;;; Convert the object at SAP (which represents a tagged lispobj)
1410 ;;; into a host proxy for that object, with a few caveats:
1411 ;;; - Structure types *must* match the host's type for the classoid,
1412 ;;; or bad things happen.
1413 ;;; - Symbols can optionally be returned as instances of CORE-SYM
1415 ;;; Structures use the host's LAYOUT instances. The addresses don't
1416 ;;; have to match, but the slots do have to.
1418 ;;; Shared substructure / circularity are OK (I think)
1420 (defparameter *allowed-instance-types*
1421 '(sb-c::compiled-debug-info sb-c::debug-source))
1422 (defparameter *ignored-instance-types*
1423 '("CORE-DEBUG-SOURCE"))
1425 (dolist (type *allowed-instance-types*)
1426 (let ((dd (find-defstruct-description type)))
1427 (assert (= (sb-kernel::dd-bitmap dd) +layout-all-tagged+))))
1429 (defun extract-object-from-core (sap core &optional proxy-symbols
1430 &aux (spacemap (core-spacemap core))
1431 (targ-nil (compute-nil-addr spacemap))
1432 ;; address (an integer) -> host object
1433 (seen (make-hash-table)))
1434 (declare (ignorable proxy-symbols)) ; not done
1435 (macrolet ((word (i)
1436 `(sap-ref-word sap (ash ,i word-shift)))
1437 (memoize (result)
1438 `(setf (gethash addr seen) ,result)))
1439 (labels ((recurse (addr)
1440 (unless (is-lisp-pointer addr)
1441 (return-from recurse (%make-lisp-obj addr)))
1442 (awhen (gethash addr seen) ; NIL is not recorded
1443 (return-from recurse it))
1444 (when (eql addr targ-nil)
1445 (return-from recurse nil))
1446 (let ((sap (int-sap (translate-ptr (logandc2 addr lowtag-mask)
1447 spacemap))))
1448 (flet ((translated-obj ()
1449 (%make-lisp-obj (translate-ptr addr spacemap))))
1450 (case (logand addr lowtag-mask)
1451 (#.list-pointer-lowtag
1452 (let ((new (memoize (cons 0 0))))
1453 (rplaca new (recurse (word 0)))
1454 (rplacd new (recurse (word 1)))
1455 new))
1456 (#.instance-pointer-lowtag
1457 (let* ((layout
1458 (truly-the layout
1459 (translate (%instance-layout (translated-obj)) spacemap)))
1460 (classoid
1461 (truly-the classoid
1462 (translate (layout-classoid layout) spacemap)))
1463 (classoid-name
1464 (truly-the symbol
1465 (translate (classoid-name classoid) spacemap)))
1466 (classoid-name-string
1467 (translate (symbol-name classoid-name) spacemap))
1468 (allowed
1469 (find classoid-name-string *allowed-instance-types*
1470 :test 'string=)))
1471 ;; In general, I want to correctly intern the symbol into the host
1472 ;; and then perform FIND-LAYOUT on that symbol.
1473 ;; These few cases are enough to get by.
1474 (cond
1475 (allowed
1476 (let* ((nslots (%instance-length (translated-obj)))
1477 (new (memoize (%make-instance nslots)))
1478 (exclude-slot-mask
1479 (logior
1480 ;; skip the layout slot if #-compact-instance-header
1481 (if (= sb-vm:instance-data-start 1) 1 0)
1482 0)))
1483 (setf (%instance-layout new) (find-layout allowed))
1484 (dotimes (i nslots new)
1485 (unless (logbitp i exclude-slot-mask)
1486 (setf (%instance-ref new i)
1487 (recurse (word (+ instance-slots-offset i))))))))
1488 ((string= classoid-name-string "PACKAGE")
1489 ;; oh dear, this is completely wrong
1490 (let ((package-name
1491 (translate
1492 (sb-impl::package-%name (truly-the package (translated-obj)))
1493 spacemap)))
1494 (memoize (or (find-package package-name)
1495 (make-package package-name)))))
1496 ((member classoid-name-string *ignored-instance-types* :test 'string=)
1497 (sb-kernel:make-unbound-marker))
1499 (error "Not done: type ~s" classoid-name-string)))))
1500 (#.fun-pointer-lowtag
1501 ;; CORE-DEBUG-SOURCE has a :FUNCTION but don't care the value
1502 #'error)
1503 (#.other-pointer-lowtag
1504 (let ((widetag (logand (word 0) widetag-mask)))
1505 (cond ((= widetag simple-vector-widetag)
1506 (let* ((len (ash (word 1) (- n-fixnum-tag-bits)))
1507 (new (memoize (make-array len))))
1508 (dotimes (i len new)
1509 (setf (aref new i)
1510 (recurse (word (+ vector-data-offset i)))))))
1511 ((and (>= widetag #x80) (typep (translated-obj) 'simple-array))
1512 (memoize (translated-obj))) ; unboxed array is OK in place
1513 ((= widetag symbol-widetag)
1514 (let* ((sym (translated-obj))
1515 (name (translate (symbol-name sym) spacemap))
1516 (pkg-name (core-package-from-id (symbol-package-id sym)
1517 core)))
1518 (memoize (if (null pkg-name)
1519 (make-symbol name)
1520 (without-package-locks
1521 (intern name
1522 (or (find-package pkg-name)
1523 (make-package pkg-name))))))))
1524 ((< widetag symbol-widetag) ; a number of some kind
1525 (copy-number-to-heap (translated-obj)))
1527 (error "can't translate other fancy stuff yet"))))))))))
1528 (recurse (sap-int sap)))))
1530 (defun compute-nil-symbol-sap (spacemap)
1531 (let ((space (get-space static-core-space-id spacemap)))
1532 ;; TODO: The core should store its address of NIL in the initial function entry
1533 ;; so this kludge can be removed.
1534 (int-sap (translate-ptr (logior (space-addr space) #x108) spacemap))))
1536 (defun is-code (taggedptr spacemap)
1537 (and (= (logand taggedptr lowtag-mask) other-pointer-lowtag)
1538 (= (logand (sap-ref-word (physical-sap taggedptr spacemap) 0) widetag-mask)
1539 code-header-widetag)))
1541 (defun is-simple-fun (descriptor spacemap)
1542 (and (= (logand (descriptor-bits descriptor) lowtag-mask) fun-pointer-lowtag)
1543 (= (logand (sap-ref-word (physical-sap descriptor spacemap) 0) widetag-mask)
1544 simple-fun-widetag)))
1546 (defun fun-ptr-to-code-ptr (descriptor spacemap)
1547 (let ((backptr (ldb (byte 24 n-widetag-bits)
1548 (sap-ref-word (physical-sap descriptor spacemap) 0))))
1549 (+ (- (descriptor-bits descriptor) (ash backptr word-shift))
1550 (- other-pointer-lowtag fun-pointer-lowtag))))
1552 (defun maybe-fun-ptr-to-code-ptr (descriptor spacemap)
1553 (if (is-simple-fun descriptor spacemap)
1554 (make-descriptor (fun-ptr-to-code-ptr descriptor spacemap))
1555 descriptor))
1557 (defun widetag-name (i)
1558 (if (> i 1) (deref (extern-alien "widetag_names" (array c-string 64)) i) "cons"))
1560 (defun summarize-object-counts (spacemap seen)
1561 (let ((widetags (make-array 64 :initial-element 0)))
1562 (maphash (lambda (taggedptr v)
1563 (declare (ignore v))
1564 (assert (integerp taggedptr))
1565 ;; FIXME: should use GET-SPACE to find the vaddr
1566 (when (>= taggedptr dynamic-space-start)
1567 (let* ((descriptor (make-descriptor taggedptr))
1568 (widetag
1569 (if (target-listp taggedptr)
1570 list-pointer-lowtag
1571 (logand (sap-ref-word (physical-sap descriptor spacemap) 0)
1572 widetag-mask))))
1573 (incf (aref widetags (ash widetag -2))))))
1574 seen)
1575 (let ((tot 0))
1576 (dotimes (i 64)
1577 (let ((ct (aref widetags i)))
1578 (when (plusp ct)
1579 (incf tot ct)
1580 (format t "~8d ~a~%" ct (widetag-name i)))))
1581 (format t "~8d TOTAL~%" tot))))
1583 (defun make-visited-table () (make-hash-table))
1584 (defun visited (hashset obj) (setf (gethash (descriptor-bits obj) hashset) t))
1585 (defun unvisited (hashset obj) (remhash (descriptor-bits obj) hashset))
1586 (defun was-visitedp (hashset obj) (gethash (descriptor-bits obj) hashset))
1588 (defun call-with-each-static-object (function spacemap)
1589 (declare (function function))
1590 (dolist (id `(,static-core-space-id ,permgen-core-space-id))
1591 (binding* ((space (get-space id spacemap) :exit-if-null)
1592 (physaddr (space-physaddr space spacemap))
1593 (limit (sap+ physaddr (ash (space-nwords space) word-shift))))
1594 (do ((object (if (= id static-core-space-id)
1595 (sap+ (compute-nil-symbol-sap spacemap) (ash 7 word-shift)) ; KLUDGE
1596 (sap+ physaddr (ash (+ 256 2) word-shift))) ; KLUDGE
1597 (sap+ object (size-of object))))
1598 ((sap>= object limit))
1599 ;; There are no static cons cells
1600 (let ((lowtag (logand (deref (extern-alien "widetag_lowtag" (array char 256))
1601 (sap-ref-8 object 0))
1602 lowtag-mask)))
1603 (funcall function (make-descriptor (+ (space-addr space) (sap- object physaddr)
1604 lowtag))))))))
1606 ;;; Gather all the objects in the order we want to reallocate them in.
1607 ;;; This relies on MAPHASH in SBCL iterating in insertion order.
1608 (defun visit-everything (spacemap initfun
1609 &optional print
1610 &aux (seen (make-visited-table))
1611 (defer-debug-info
1612 (make-array 10000 :fill-pointer 0 :adjustable t))
1613 stack)
1614 (visited seen (make-descriptor nil-value))
1615 (labels ((root (descriptor)
1616 (visited seen descriptor)
1617 (trace-obj #'visit descriptor spacemap))
1618 (visit (sap slot value widetag)
1619 (declare (ignorable sap slot widetag))
1620 (when print (format t "~& slot ~d = ~a" slot value))
1621 (when (and (= widetag code-header-widetag) (< slot 4) defer-debug-info)
1622 (unless (and (plusp (fill-pointer defer-debug-info))
1623 (sap= (aref defer-debug-info (1- (fill-pointer defer-debug-info)))
1624 sap))
1625 (vector-push-extend sap defer-debug-info))
1626 (return-from visit))
1627 (when (descriptor-p value)
1628 (let ((value (maybe-fun-ptr-to-code-ptr value spacemap)))
1629 (unless (was-visitedp seen value)
1630 (when print (format t " (pushed)"))
1631 (visited seen value)
1632 (push value stack)))))
1633 (transitive-closure ()
1634 (loop while stack
1635 do (let ((descriptor (pop stack)))
1636 (when print (format t "~&Popped ~x~%" descriptor))
1637 (trace-obj #'visit descriptor spacemap)))))
1638 (root (make-descriptor initfun))
1639 (trace-symbol #'visit (compute-nil-symbol-sap spacemap))
1640 (call-with-each-static-object #'root spacemap)
1641 (transitive-closure)
1642 (dovector (sap (prog1 defer-debug-info (setq defer-debug-info nil)))
1643 (visit sap 2 (load-wordindexed sap 2) 0)
1644 (visit sap 3 (load-wordindexed sap 3) 0))
1645 (transitive-closure))
1646 seen)
1648 (defstruct (newspace (:include core-space))
1649 ;; alist of page type to list of pages with any space
1650 (available-ranges (mapcar 'list '(:cons :boxed :unboxed :mixed :code))))
1652 (defun unboxed-like-simple-vector (sap)
1653 (declare (ignore sap))
1654 ;; TODO: return T if the vector has the 'shareable' header bit (is effectively
1655 ;; a constant) and contains no pointers.
1656 nil)
1658 (defun vector-alloc-mixed-p (sap)
1659 (logtest (logior (ash (logior vector-weak-flag vector-hashing-flag) array-flags-position)
1660 sb-vm::+vector-alloc-mixed-region-bit+)
1661 (sap-ref-word sap 0)))
1663 (defun instance-strictly-boxed-p (sap spacemap)
1664 (let ((layout (translate-ptr (get-layout sap instance-widetag) spacemap)))
1665 (logtest (layout-flags (truly-the layout (%make-lisp-obj layout)))
1666 +strictly-boxed-flag+)))
1668 (defun pick-page-type (descriptor sap largep spacemap)
1669 (when (target-listp (descriptor-bits descriptor))
1670 (return-from pick-page-type :cons))
1671 (let ((widetag (target-widetag-of descriptor spacemap)))
1672 (if largep
1673 ;; Choose from among {boxed, mixed, code}
1674 (cond ((= widetag code-header-widetag) :code)
1675 ((or (/= widetag simple-vector-widetag) (vector-alloc-mixed-p sap))
1676 :mixed)
1678 :boxed))
1679 ;; Choose from among {raw, boxed, mixed, code}
1680 (cond ((member widetag `(,code-header-widetag ,funcallable-instance-widetag))
1681 :code)
1682 ((or (leafp widetag)
1683 (and (member widetag `(,ratio-widetag ,complex-rational-widetag))
1684 (fixnump (load-wordindexed sap 1))
1685 (fixnump (load-wordindexed sap 2)))
1686 (unboxed-like-simple-vector sap))
1687 :unboxed)
1688 ((or (member widetag `(,symbol-widetag ,weak-pointer-widetag ,fdefn-widetag))
1689 (and (= widetag instance-widetag)
1690 (not (instance-strictly-boxed-p sap spacemap)))
1691 (and (= widetag simple-vector-widetag)
1692 (vector-alloc-mixed-p sap)))
1693 :mixed)
1695 :boxed)))))
1697 (defun find-sufficient-gap (gaps size)
1698 (dolist (gap gaps)
1699 (when (>= (cdr gap) size)
1700 (return gap))))
1702 (defun space-next-free-page (space)
1703 (let ((words-per-page (/ gencgc-page-bytes n-word-bytes)))
1704 (ceiling (space-nwords space) words-per-page)))
1706 (defconstant-eqx instance-len-byte
1707 (byte (integer-length instance-length-mask) instance-length-shift)
1708 #'equal)
1709 (defun reallocate (descriptor old-spacemap new-spacemap)
1710 (let* ((sap (physical-sap descriptor old-spacemap))
1711 (old-size (size-of sap))
1712 (size old-size)
1713 ;;; Prevent page-spanning small objects for gencgc. Reorganizing is
1714 ;;; primarily for mark-region GC which disallows page-spanning other than
1715 ;;; for large objects. With gencgc we'd have to compute the scan-start
1716 ;;; on subsequent pages, and put the end-of-page free space in a list.
1717 ;;; It's not worth the hassle.
1718 (largep #+gencgc (>= size gencgc-page-bytes)
1719 #-gencgc (>= size large-object-size))
1720 (page-type (pick-page-type descriptor sap largep old-spacemap))
1721 (newspace (get-space dynamic-core-space-id new-spacemap))
1722 (new-nslots) ; only set if it's a resized instance
1723 (new-vaddr))
1724 (when (and (= (logand (descriptor-bits descriptor) lowtag-mask)
1725 instance-pointer-lowtag)
1726 (= (ldb (byte 2 8) (sap-ref-word sap 0)) 1)) ; hashed not moved
1727 (setf new-nslots (1+ (ldb instance-len-byte (sap-ref-word sap 0)))
1728 ;; size change can't affect largep
1729 size (ash (1+ (logior new-nslots 1)) word-shift)))
1730 (flet ((claim-page (scan-start words-used)
1731 (let ((index (space-next-free-page newspace)))
1732 ;(format t "next-free-page=~s~%" index)
1733 ;; should be free
1734 (aver (not (aref (space-page-table newspace) index)))
1735 ;; previous should be used or nonexistent
1736 (aver (or (= index 0) (aref (space-page-table newspace) (1- index))))
1737 (incf (space-nwords newspace) (/ gencgc-page-bytes n-word-bytes))
1738 (setf (aref (space-page-table newspace) index)
1739 (make-page :words-used words-used
1740 :single-obj-p (if largep 1 0)
1741 :type page-type
1742 :scan-start scan-start
1743 :bitmap (make-array *bitmap-bits-per-page*
1744 :element-type 'bit))))))
1745 (if largep
1746 (let* ((npages (ceiling size gencgc-page-bytes))
1747 (page (space-next-free-page newspace))
1748 (bytes-to-go size)
1749 (scan-start 0))
1750 (setq new-vaddr (+ dynamic-space-start (* page gencgc-page-bytes)))
1751 (dotimes (i npages)
1752 (let ((bytes-this-page (min gencgc-page-bytes bytes-to-go)))
1753 (claim-page scan-start (ash bytes-this-page (- word-shift)))
1754 (incf scan-start gencgc-page-bytes)
1755 (decf bytes-to-go bytes-this-page)))
1756 (setf (bit (page-bitmap (aref (newspace-page-table newspace) page)) 0) 1))
1757 (let* ((list (assoc page-type (newspace-available-ranges newspace)))
1758 (nwords (ash size (- word-shift)))
1759 (gap (find-sufficient-gap (cdr list) size)))
1760 (if gap
1761 (let* ((page (floor (- (setq new-vaddr (car gap)) dynamic-space-start)
1762 gencgc-page-bytes))
1763 (pte (aref (newspace-page-table newspace) page))
1764 (page-base (+ dynamic-space-start (* page gencgc-page-bytes)))
1765 (object-index (ash (- new-vaddr page-base) (- n-lowtag-bits))))
1766 (incf (page-words-used pte) nwords)
1767 (setf (sbit (page-bitmap pte) object-index) 1)
1768 (aver (>= (cdr gap) size))
1769 (if (plusp (decf (cdr gap) size))
1770 (incf (car gap) size) ; the gap is moved upward by SIZE
1771 (setf (cdr list) (delq1 gap (cdr list)))))
1772 ;; claim a page, use initial portion of it, append rest onto gaps
1773 (let ((page (space-next-free-page newspace)))
1774 (setq new-vaddr (+ dynamic-space-start (* page gencgc-page-bytes)))
1775 (setf (bit (page-bitmap (claim-page 0 nwords)) 0) 1)
1776 (let ((gap (cons (+ new-vaddr size) (- gencgc-page-bytes size))))
1777 (aver (> (cdr gap) 0))
1778 (nconc list (list gap))))))))
1779 (let ((new-sap (physical-sap new-vaddr new-spacemap))
1780 (old-sap (physical-sap descriptor old-spacemap))
1781 (widetag (target-widetag-of descriptor old-spacemap)))
1782 (%byte-blt old-sap 0 new-sap 0 old-size)
1783 (case widetag
1784 (#.instance-widetag
1785 (when new-nslots
1786 (setf (sap-ref-word new-sap 0)
1787 (logior (sap-ref-word new-sap 0) (ash 1 hash-slot-present-flag)))
1788 (let* ((prehash (sb-impl::murmur3-fmix-word (descriptor-bits descriptor)))
1789 (hash (ash (logand (ash prehash (1+ n-fixnum-tag-bits)) most-positive-word)
1790 -1)))
1791 (setf (sap-ref-word new-sap (ash new-nslots word-shift)) hash))))
1792 (#.code-header-widetag ; redundantly performs a memmove first, but that's fine
1793 (transport-code (int-sap (logandc2 (descriptor-bits descriptor) lowtag-mask)) old-sap
1794 (int-sap new-vaddr) new-sap size))
1795 (#.funcallable-instance-widetag
1796 (setf (sap-ref-sap new-sap (ash 1 word-shift)) ; set the entry point
1797 (sap+ (int-sap new-vaddr) (ash 2 word-shift))))))
1798 new-vaddr))
1800 (defun fixup-compacted (old-spacemap new-spacemap seen &optional print)
1801 (flet ((visit (sap slot value widetag)
1802 (unless (descriptor-p value) (return-from visit))
1803 (let ((newspace-ptr
1804 (cond ((is-simple-fun value old-spacemap)
1805 (let* ((old-code (fun-ptr-to-code-ptr value old-spacemap))
1806 (new-code (gethash old-code seen)))
1807 (+ new-code (- (descriptor-bits value) old-code))))
1808 ((gethash (descriptor-bits value) seen)))))
1809 (unless newspace-ptr (return-from visit))
1810 ;; handle special cases of index + widetag
1811 (case (logand most-positive-word (logior (ash slot 8) widetag))
1812 ((#.instance-widetag #.funcallable-instance-widetag)
1813 (set-layout sap widetag newspace-ptr))
1814 ((#.(logior (ash fdefn-raw-addr-slot 8) fdefn-widetag)
1815 #.(logior (ash closure-fun-slot 8) closure-widetag))
1816 (setf (sap-ref-word sap (ash slot word-shift))
1817 (+ newspace-ptr (- (ash simple-fun-insts-offset word-shift)
1818 fun-pointer-lowtag))))
1819 ((#.(logior (ash symbol-name-slot 8) symbol-widetag))
1820 ;; symbol names are in readonly space now
1821 (bug "symbol name change"))
1823 (setf (sap-ref-word sap (ash slot word-shift)) newspace-ptr)))
1824 #+nil
1825 (format t "~& - ~x[~x] + ~d ~x -> ~X" (sap-int sap) widetag index value
1826 (sap-ref-word sap (ash slot word-shift)))
1828 ;; Use _oldspace_ layouts when scanning bitmaps.
1829 (layout-vaddr->paddr (ptr)
1830 (translate-ptr ptr old-spacemap)))
1831 (when print (format t "~&Fixing static space~%"))
1832 (trace-symbol #'visit (compute-nil-symbol-sap old-spacemap))
1833 (call-with-each-static-object
1834 (lambda (descriptor) (trace-obj #'visit descriptor old-spacemap))
1835 old-spacemap)
1836 (when print (format t "~&Fixing dynamic space~%"))
1837 (dohash ((old-taggedptr new-taggedptr) seen)
1838 (declare (ignorable old-taggedptr))
1839 #+nil
1840 (format t "~&Fixing @ old-vaddr=~x new-vaddr=~x new-paddr=~x~%"
1841 old-taggedptr new-taggedptr (sap-int (physical-sap new-taggedptr new-spacemap)))
1842 (trace-obj #'visit (make-descriptor new-taggedptr) new-spacemap
1843 #'layout-vaddr->paddr)
1844 ;; mark every address-sensitive hash-table as needing rehash
1845 (let* ((sap (physical-sap new-taggedptr new-spacemap))
1846 (header (sap-ref-word sap 0)))
1847 (when (and (= (logand header widetag-mask) simple-vector-widetag)
1848 (logtest header (ash vector-addr-hashing-flag array-flags-position)))
1849 (setf (sap-ref-word sap (ash 3 word-shift)) (fixnumize 1)))))))
1851 (defun reorganize-core (input-pathname output-pathname &optional print)
1852 (with-open-file (input input-pathname :element-type '(unsigned-byte 8))
1853 (binding* ((core-header (make-array +backend-page-bytes+ :element-type '(unsigned-byte 8)))
1854 (core-offset (read-core-header input core-header t))
1855 ((npages space-list card-mask-nbits core-dir-start initfun)
1856 (parse-core-header input core-header)))
1857 (declare (ignorable card-mask-nbits core-dir-start))
1858 (with-mapped-core (sap core-offset npages input)
1859 ;; FIXME: WITH-MAPPED-CORE should bind spacemap
1860 (let* ((spacemap (cons sap (sort (copy-list space-list) #'> :key #'space-addr)))
1861 (seen (visit-everything spacemap initfun print))
1862 (oldspace (get-space dynamic-core-space-id spacemap)))
1863 ;;(summarize-object-counts spacemap seen)
1864 (let* ((oldspace-size (ash (space-nwords oldspace) word-shift))
1865 (newspace-mem (sb-sys:allocate-system-memory oldspace-size))
1866 (newspace (make-newspace
1867 :id dynamic-core-space-id
1868 :addr dynamic-space-start
1869 :page-table (make-array (length (space-page-table oldspace))
1870 :initial-element nil)
1871 :data-page 0
1872 :nwords 0))
1873 (new-spacemap (list newspace-mem newspace)))
1874 (alien-funcall (extern-alien "memset" (function void system-area-pointer int unsigned))
1875 newspace-mem 0 oldspace-size)
1876 ;; pass 1: assign new address per object, also copy the object over.
1877 ;; But actually do two sub-passes, one to copy everything except code,
1878 ;; one for code, so that all code is at the end of the space.
1879 ;; FIXME: linearize lists (of conses and lockfree) just like gencgc does.
1880 (dotimes (sub-pass 2)
1881 (dohash ((taggedptr dummy) seen)
1882 (declare (ignore dummy))
1883 (when (<= (space-addr oldspace) taggedptr (space-end oldspace))
1884 (when (eq (is-code taggedptr spacemap) (eql sub-pass 1))
1885 (let* ((new-addr (reallocate (make-descriptor taggedptr)
1886 spacemap new-spacemap))
1887 (new-taggedptr (logior new-addr (logand taggedptr lowtag-mask))))
1888 #+nil (format t "~x -> ~x~%" taggedptr new-taggedptr)
1889 (setf (gethash taggedptr seen) new-taggedptr)))))
1890 ;; Prevent sharing of funinstance pages with code from next pass
1891 (let ((avail (assoc :code (newspace-available-ranges (second new-spacemap)))))
1892 (setf (cdr avail) nil)))
1893 ;; pass 2: visit every object again, fixing pointers
1894 ;; Start by removing objects from SEEN that were not forwarded
1895 (maphash (lambda (key value) (if (eq value t) (remhash key seen))) seen)
1896 (fixup-compacted spacemap new-spacemap seen)
1897 (setf initfun (gethash initfun seen))
1898 (flet ((n (spaces)
1899 (space-next-free-page (get-space dynamic-core-space-id spaces))))
1900 (when print
1901 (format t "Compactor: n-pages was ~D, is ~D~%" (n spacemap) (n new-spacemap))))
1902 ;; Shrink the page table and encode the page types as required
1903 (setf (space-page-table newspace)
1904 (subseq (space-page-table newspace) 0 (space-next-free-page newspace)))
1905 (dovector (pte (space-page-table newspace))
1906 (setf (page-type pte) (encode-page-type (page-type pte))))
1907 ;; Switch dynamic space in spacemap to that of compacted space
1908 (let* ((cell (member dynamic-core-space-id (cdr spacemap) :key #'space-id))
1909 (oldspace (car cell))
1910 (oldspace-mem (space-physaddr oldspace spacemap))
1911 (nbytes (ash (space-nwords newspace) word-shift)))
1912 (%byte-blt newspace-mem 0 oldspace-mem 0 nbytes)
1913 ;; don't clobber the physical address translation of oldspace
1914 (setf (space-data-page newspace) (space-data-page oldspace))
1915 (rplaca cell newspace)))
1916 (with-open-file (output output-pathname :direction :output
1917 :element-type '(unsigned-byte 8) :if-exists :supersede)
1918 (rewrite-core
1919 (mapcar (lambda (space)
1920 (list 0 (space-id space) (space-physaddr space spacemap)
1921 (space-addr space) (space-nwords space)))
1922 (cdr spacemap))
1923 spacemap card-mask-nbits initfun
1924 core-header core-dir-start output)))))))
1926 (defun test-some-objects (core-pathname addrlist-pathname)
1927 (with-open-file (input core-pathname :element-type '(unsigned-byte 8))
1928 (binding* ((core-header (make-array +backend-page-bytes+ :element-type '(unsigned-byte 8)))
1929 (core-offset (read-core-header input core-header t))
1930 ((npages space-list card-mask-nbits core-dir-start initfun)
1931 (parse-core-header input core-header)))
1932 (declare (ignorable card-mask-nbits core-dir-start initfun))
1933 (with-mapped-core (sap core-offset npages input)
1934 ;; FIXME: WITH-MAPPED-CORE should bind spacemap
1935 (let* ((spacemap (cons sap (sort (copy-list space-list) #'> :key #'space-addr)))
1936 (core (make-core spacemap (make-bounds 0 0) (make-bounds 0 0)))
1937 (list (with-open-file (f addrlist-pathname) (read f))))
1938 (dolist (x list)
1939 (let* ((code-physaddr (translate-ptr x spacemap))
1940 (di (sb-sys:int-sap
1941 (get-lisp-obj-address
1942 (%code-debug-info
1943 (truly-the code-component
1944 (%make-lisp-obj code-physaddr)))))))
1945 (when (= (logand (sb-sys:sap-int di) lowtag-mask) instance-pointer-lowtag)
1946 (let ((copy (extract-object-from-core di core)))
1947 (print copy))))))))))