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.
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
)
38 (:import-from
"SB-X86-64-ASM" #:near-jump-displacement
39 #:near-cond-jump-displacement
#:mov
#:call
#:jmp
41 #:machine-ea
#:machine-ea-base
#:machine-ea-index
#:machine-ea-disp
)
42 (:import-from
"SB-IMPL" #:symbol-table
#:package-%name
44 #:hash-table-pairs
#:hash-table-%count
))
46 (in-package "SB-EDITCORE")
48 (declaim (muffle-conditions compiler-note
))
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
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
)))))
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
)))
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
)
114 (:constructor %make-core
))
117 ;; mapping from small integer ID to 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
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
)
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
))
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
)))
163 (make-bounds (space-addr space
) (space-end space
))
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
)))
179 (dovector (x (translate (cdr cells
) spacemap
))
182 (if (eq x nil-object
) ; any random package can export NIL. wow.
184 (translate (symbol-name (translate x spacemap
)) spacemap
))
187 (defun core-package-from-id (id core
)
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)
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
)))))
201 ((and (typep x
'(or string symbol
))
202 (let ((mismatch (mismatch (string x
) "CLEANUP-FUN-")))
203 (or (eql mismatch nil
) (= mismatch
(length "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")
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
))))
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
))
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"))
224 (setf (second name
) (unpackageize (second name
)))
225 (let ((last (car (last name
))))
228 (unpackageize qual
))))))
231 (defstruct (descriptor (:constructor make-descriptor
(bits)))
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
))
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
))))
246 (do ((count (hash-table-%count table
) (1- count
))
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
)))
266 (when (>= physaddr end
) (return))
267 (let* ((word (sap-ref-word (int-sap physaddr
) 0))
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
277 (logior (ecase address-mode
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
))
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
)
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
)))
312 (let ((text-addr (space-addr text-space
)))
313 (make-bounds (- text-addr alien-linkage-table-space-size
) text-addr
))
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
))
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
334 :enable-pie enable-pie
)))
337 (find-target-symbol (package-id "SB-IMPL") "*ALL-PACKAGES*" spacemap
:physical
)))
339 (symbols (make-hash-table :test
'equal
)))
340 (labels ((scan-symtbl (table)
343 (pushnew (get-lisp-obj-address sym
) (gethash str symbols
)))
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
)
365 (setf (gethash string ambiguous-symbols
) t
))))
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)
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
))
411 (let* ((end-pc (if (= i
(length fun-map
))
412 (%code-text-size code
)
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
))
437 (let ((current-blob (car blobs
)))
438 (setf (cdr current-blob
) end-pc
)))) ; extend this blob
439 (setq start-pc end-pc
))
441 (return (nreverse blobs
)))
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
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)))))
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
)
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))
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
))))
478 (defmacro do-core-header-entry
(((id-var len-var ptr-var
) buffer
) &body body
)
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)
486 (when (= ,id-var end-core-entry-type-code
)
487 (aver (not (find 0 ,buffer
:start
(ash ,ptr-var word-shift
) :test
#'/=)))
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
))))
506 (defmacro with-mapped-core
((sap-var start npages stream
) &body body
)
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
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)))
536 (defun change-dynamic-space-size (core-header new-size
) ; expressed in MiB
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))
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
))
560 (single-obj-p 0 :type bit
)
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
+))
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)
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))
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
)
593 (if (= (page-single-obj-p p
) 0) nil
1)
594 (page-scan-start p
)))))))
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
)))))
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
))
632 ((>= first-page nptes
) (nreverse page-ranges
))
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
)
660 (setq vaddr
(sap+ vaddr size
)
661 paddr
(sap+ paddr size
)))))
662 (setq first-page
(1+ last-page
)))
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
*))
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)))
689 (let* ((obj (reconstitute-object (%make-lisp-obj
(sap-int paddr
))))
690 (size (primitive-object-size obj
)))
691 (funcall function obj vaddr size nil
)
693 (setq vaddr
(sap+ vaddr size
)
694 paddr
(sap+ paddr size
))
695 (incf object-offset-in-dualwords
(ash size
(- (1+ word-shift
)))))))))
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
)))
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
))))))
723 (lambda (obj vaddr size large
)
724 (declare (ignore vaddr size large
))
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*")))
731 ;;(format t "~&Target-features=~S~%" 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
))))
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
)))
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
))
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
)))
784 ((code-component-p translated
)
785 (- vaddr other-pointer-lowtag
)))
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
+
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
))))
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
)))
808 (setf ,place newval
)))))
809 (flet ((fun-entrypoint (fun)
810 (+ (get-lisp-obj-address fun
) (- fun-pointer-lowtag
) (ash 2 word-shift
)))
812 (remap-to-quasi-static-code x spacemap fwdmap
)))
814 ((listp obj
) (visit (car obj
)) (visit (cdr obj
)))
815 ((simple-vector-p obj
)
816 (dotimes (i (length obj
)) (visit (svref obj i
))))
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
))))))
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
)))
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
))))
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
))))
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
861 (page-scan-start pte
) 0)))
862 (let ((space (get-space dynamic-core-space-id spacemap
)))
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
))))
881 (loop for page-index from first to last
882 do
(reset-pte (svref (space-page-table space
) page-index
))))))
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
))))
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
)
904 (total-npages 0) ; excluding core header page
908 (do-core-header-entry ((id len ptr
) core-header
)
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
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
))
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
959 n-ptes
(+ (* n-ptes
*bitmap-bytes-per-page
*) pte-bytes
)
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
+))
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)))))
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
)
995 (write-sequence buffer output
:end diff
))))
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
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
)
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
)))
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
)
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
))
1052 (fwdmap (make-hash-table))
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
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
)
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"))
1081 (acons (int-sap (logandc2 (get-lisp-obj-address asm-code
) lowtag-mask
))
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
1096 (n-vector2-data-words c-linkage-reserved-words
)
1097 (vector2-size (ash (+ n-vector2-data-words vector-data-offset
)
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
)))
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
)
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
))
1149 (space-nwords space
)))
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
)
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.
1167 ;;; * (ql:quickload :one-more-re-nightmare-tests)
1168 ;;; * (save-lisp-and-die "step1.core")
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
)))
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
))
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
))
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:" " ")
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
))
1217 (loop for i from
0 below
(code-n-entries code
)
1218 collect
(+ insts-vaddr
(%code-fun-offset code i
))))
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)
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
)))
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
))
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
)
1253 (sb-editcore::walk-target-space
1254 (lambda (x widetag size
)
1255 (declare (ignore widetag size
))
1256 (when (code-component-p 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
)
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))
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)))))
1323 (logbitp (ash widetag -
2) (compute-leaves))))
1325 (defun instance-slot-count (sap widetag
)
1326 (let ((header (sap-ref-word sap
0)))
1328 (#.funcallable-instance-widetag
1329 (ldb (byte 8 n-widetag-bits
) header
))
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
))
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
)
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
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
))
1392 (let ((first 1) (last (ash (size-of sap
) (- word-shift
))))
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
))))
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
)))
1405 (values first last
))))
1406 (loop for i from first below last do
(scan-slot i
))))
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
)))
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
)
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)))
1456 (#.instance-pointer-lowtag
1459 (translate (%instance-layout
(translated-obj)) spacemap
)))
1462 (translate (layout-classoid layout
) spacemap
)))
1465 (translate (classoid-name classoid
) spacemap
)))
1466 (classoid-name-string
1467 (translate (symbol-name classoid-name
) spacemap
))
1469 (find classoid-name-string
*allowed-instance-types
*
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.
1476 (let* ((nslots (%instance-length
(translated-obj)))
1477 (new (memoize (%make-instance nslots
)))
1480 ;; skip the layout slot if #-compact-instance-header
1481 (if (= sb-vm
:instance-data-start
1) 1 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
1492 (sb-impl::package-%name
(truly-the package
(translated-obj)))
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
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
)
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
)
1518 (memoize (if (null pkg-name
)
1520 (without-package-locks
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
))
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
))
1569 (if (target-listp taggedptr
)
1571 (logand (sap-ref-word (physical-sap descriptor spacemap
) 0)
1573 (incf (aref widetags
(ash widetag -
2))))))
1577 (let ((ct (aref widetags i
)))
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))
1603 (funcall function
(make-descriptor (+ (space-addr space
) (sap- object physaddr
)
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
1610 &aux
(seen (make-visited-table))
1612 (make-array 10000 :fill-pointer
0 :adjustable t
))
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
)))
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 ()
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))
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.
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
)))
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
))
1679 ;; Choose from among {raw, boxed, mixed, code}
1680 (cond ((member widetag
`(,code-header-widetag
,funcallable-instance-widetag
))
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
))
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
)))
1697 (defun find-sufficient-gap (gaps size
)
1699 (when (>= (cdr gap
) size
)
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
)
1709 (defun reallocate (descriptor old-spacemap new-spacemap
)
1710 (let* ((sap (physical-sap descriptor old-spacemap
))
1711 (old-size (size-of sap
))
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
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)
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)
1742 :scan-start scan-start
1743 :bitmap
(make-array *bitmap-bits-per-page
*
1744 :element-type
'bit
))))))
1746 (let* ((npages (ceiling size gencgc-page-bytes
))
1747 (page (space-next-free-page newspace
))
1750 (setq new-vaddr
(+ dynamic-space-start
(* page gencgc-page-bytes
)))
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
)))
1761 (let* ((page (floor (- (setq new-vaddr
(car gap
)) dynamic-space-start
)
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
)
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
)
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
))))))
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
))
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
)))
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
))
1836 (when print
(format t
"~&Fixing dynamic space~%"))
1837 (dohash ((old-taggedptr new-taggedptr
) seen
)
1838 (declare (ignorable old-taggedptr
))
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
)
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
))
1899 (space-next-free-page (get-space dynamic-core-space-id spaces
))))
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
)
1919 (mapcar (lambda (space)
1920 (list 0 (space-id space
) (space-physaddr space spacemap
)
1921 (space-addr space
) (space-nwords space
)))
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
))))
1939 (let* ((code-physaddr (translate-ptr x spacemap
))
1941 (get-lisp-obj-address
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
))))))))))