1 ;;;; disassembler-related stuff not needed in cross-compilation host
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
12 (in-package "SB!DISASSEM")
14 ;;;; FIXME: A lot of stupid package prefixes would go away if DISASSEM
15 ;;;; would use the SB!DI package. And some more would go away if it would
16 ;;;; use SB!SYS (in order to get to the SAP-FOO operators).
18 ;;;; combining instructions where one specializes another
20 ;;; Return non-NIL if the instruction SPECIAL is a more specific
21 ;;; version of GENERAL (i.e., the same instruction, but with more
23 (defun inst-specializes-p (special general
)
24 (declare (type instruction special general
))
25 (let ((smask (inst-mask special
))
26 (gmask (inst-mask general
)))
27 (and (dchunk= (inst-id general
)
28 (dchunk-and (inst-id special
) gmask
))
29 (dchunk-strict-superset-p smask gmask
))))
31 ;;; a bit arbitrary, but should work ok...
33 ;;; Return an integer corresponding to the specificity of the
35 (defun specializer-rank (inst)
36 (declare (type instruction inst
))
37 (* (dchunk-count-bits (inst-mask inst
)) 4))
39 ;;; Order the list of instructions INSTS with more specific (more
40 ;;; constant bits, or same-as argument constains) ones first. Returns
42 (defun order-specializers (insts)
43 (declare (type list insts
))
44 (sort insts
#'> :key
#'specializer-rank
))
46 (defun specialization-error (insts)
48 "~@<Instructions either aren't related or conflict in some way: ~4I~_~S~:>"
51 ;;; Given a list of instructions INSTS, Sees if one of these instructions is a
52 ;;; more general form of all the others, in which case they are put into its
53 ;;; specializers list, and it is returned. Otherwise an error is signaled.
54 (defun try-specializing (insts)
55 (declare (type list insts
))
56 (let ((masters (copy-list insts
)))
57 (dolist (possible-master insts
)
58 (dolist (possible-specializer insts
)
59 (unless (or (eq possible-specializer possible-master
)
60 (inst-specializes-p possible-specializer possible-master
))
61 (setf masters
(delete possible-master masters
))
62 (return) ; exit the inner loop
65 (specialization-error insts
))
67 (error "multiple specializing masters: ~S" masters
))
69 (let ((master (car masters
)))
70 (setf (inst-specializers master
)
71 (order-specializers (remove master insts
)))
74 ;;;; choosing an instruction
76 #!-sb-fluid
(declaim (inline inst-matches-p choose-inst-specialization
))
78 ;;; Return non-NIL if all constant-bits in INST match CHUNK.
79 (defun inst-matches-p (inst chunk
)
80 (declare (type instruction inst
)
82 (dchunk= (dchunk-and (inst-mask inst
) chunk
) (inst-id inst
)))
84 ;;; Given an instruction object, INST, and a bit-pattern, CHUNK, pick
85 ;;; the most specific instruction on INST's specializer list whose
86 ;;; constraints are met by CHUNK. If none do, then return INST.
87 (defun choose-inst-specialization (inst chunk
)
88 (declare (type instruction inst
)
90 (or (dolist (spec (inst-specializers inst
) nil
)
91 (declare (type instruction spec
))
92 (when (inst-matches-p spec chunk
)
96 ;;;; searching for an instruction in instruction space
98 ;;; Return the instruction object within INST-SPACE corresponding to the
99 ;;; bit-pattern CHUNK, or NIL if there isn't one.
100 (defun find-inst (chunk inst-space
)
101 (declare (type dchunk chunk
)
102 (type (or null inst-space instruction
) inst-space
))
103 (etypecase inst-space
106 (if (inst-matches-p inst-space chunk
)
107 (choose-inst-specialization inst-space chunk
)
110 (let* ((mask (ispace-valid-mask inst-space
))
111 (id (dchunk-and mask chunk
)))
112 (declare (type dchunk id mask
))
113 (dolist (choice (ispace-choices inst-space
))
114 (declare (type inst-space-choice choice
))
115 (when (dchunk= id
(ischoice-common-id choice
))
116 (return (find-inst chunk
(ischoice-subspace choice
)))))))))
118 ;;;; building the instruction space
120 ;;; Returns an instruction-space object corresponding to the list of
121 ;;; instructions INSTS. If the optional parameter INITIAL-MASK is
122 ;;; supplied, only bits it has set are used.
123 (defun build-inst-space (insts &optional
(initial-mask dchunk-one
))
124 ;; This is done by finding any set of bits that's common to
125 ;; all instructions, building an instruction-space node that selects on those
126 ;; bits, and recursively handle sets of instructions with a common value for
127 ;; these bits (which, since there should be fewer instructions than in INSTS,
128 ;; should have some additional set of bits to select on, etc). If there
129 ;; are no common bits, or all instructions have the same value within those
130 ;; bits, TRY-SPECIALIZING is called, which handles the cases of many
131 ;; variations on a single instruction.
132 (declare (type list insts
)
133 (type dchunk initial-mask
))
139 (let ((vmask (dchunk-copy initial-mask
)))
141 (dchunk-andf vmask
(inst-mask inst
)))
142 (if (dchunk-zerop vmask
)
143 (try-specializing insts
)
146 (let* ((common-id (dchunk-and (inst-id inst
) vmask
))
147 (bucket (assoc common-id buckets
:test
#'dchunk
=)))
149 (push (list common-id inst
) buckets
))
151 (push inst
(cdr bucket
))))))
152 (let ((submask (dchunk-clear initial-mask vmask
)))
153 (if (= (length buckets
) 1)
154 (try-specializing insts
)
157 :choices
(mapcar (lambda (bucket)
158 (make-inst-space-choice
159 :subspace
(build-inst-space
162 :common-id
(car bucket
)))
165 ;;;; an inst-space printer for debugging purposes
167 (defun print-masked-binary (num mask word-size
&optional
(show word-size
))
168 (do ((bit (1- word-size
) (1- bit
)))
170 (write-char (cond ((logbitp bit mask
)
171 (if (logbitp bit num
) #\
1 #\
0))
175 (defun print-inst-bits (inst)
176 (print-masked-binary (inst-id inst
)
179 (bytes-to-bits (inst-length inst
))))
181 ;;; Print a nicely-formatted version of INST-SPACE.
182 (defun print-inst-space (inst-space &optional
(indent 0))
183 (etypecase inst-space
186 (format t
"~Vt[~A(~A)~40T" indent
187 (inst-name inst-space
)
188 (inst-format-name inst-space
))
189 (print-inst-bits inst-space
)
190 (dolist (inst (inst-specializers inst-space
))
191 (format t
"~%~Vt:~A~40T" indent
(inst-name inst
))
192 (print-inst-bits inst
))
196 (format t
"~Vt---- ~8,'0X ----~%"
198 (ispace-valid-mask inst-space
))
201 (format t
"~Vt~8,'0X ==>~%"
203 (ischoice-common-id choice
))
204 (print-inst-space (ischoice-subspace choice
)
206 (ispace-choices inst-space
)))))
208 ;;;; (The actual disassembly part follows.)
210 ;;; Code object layout:
212 ;;; code-size (starting from first inst, in bytes)
213 ;;; entry-points (points to first function header)
218 ;;; <padding to dual-word boundary>
219 ;;; start of instructions
221 ;;; fun-headers and lra's buried in here randomly
223 ;;; <padding to dual-word boundary>
225 ;;; Function header layout (dual word aligned):
228 ;;; next pointer (next function header)
233 ;;; LRA layout (dual word aligned):
236 #!-sb-fluid
(declaim (inline words-to-bytes bytes-to-words
))
238 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
239 ;;; Convert a word-offset NUM to a byte-offset.
240 (defun words-to-bytes (num)
241 (declare (type offset num
))
242 (ash num sb
!vm
:word-shift
))
245 ;;; Convert a byte-offset NUM to a word-offset.
246 (defun bytes-to-words (num)
247 (declare (type offset num
))
248 (ash num
(- sb
!vm
:word-shift
)))
250 (defconstant lra-size
(words-to-bytes 1))
252 (defstruct (offs-hook (:copier nil
))
253 (offset 0 :type offset
)
254 (fun (missing-arg) :type function
)
255 (before-address nil
:type
(member t nil
)))
257 (defmethod print-object ((seg segment
) stream
)
258 (print-unreadable-object (seg stream
:type t
)
259 (let ((addr (sb!sys
:sap-int
(funcall (seg-sap-maker seg
)))))
260 (format stream
"#X~X..~X[~W]~:[ (#X~X)~;~*~]~@[ in ~S~]"
261 addr
(+ addr
(seg-length seg
)) (seg-length seg
)
262 (= (seg-virtual-location seg
) addr
)
263 (seg-virtual-location seg
)
268 (defun fun-self (fun)
269 (declare (type compiled-function fun
))
270 (sb!kernel
:%simple-fun-self
(sb!kernel
:%fun-fun fun
)))
272 (defun fun-code (fun)
273 (declare (type compiled-function fun
))
274 (sb!kernel
:fun-code-header
(fun-self fun
)))
276 (defun fun-next (fun)
277 (declare (type compiled-function fun
))
278 (sb!kernel
:%simple-fun-next
(sb!kernel
:%fun-fun fun
)))
280 (defun fun-address (fun)
281 (declare (type compiled-function fun
))
282 (- (sb!kernel
:get-lisp-obj-address
(sb!kernel
:%fun-fun fun
)) sb
!vm
:fun-pointer-lowtag
))
284 ;;; the offset of FUNCTION from the start of its code-component's
286 (defun fun-insts-offset (function)
287 (declare (type compiled-function function
))
288 (- (fun-address function
)
289 (sb!sys
:sap-int
(sb!kernel
:code-instructions
(fun-code function
)))))
291 ;;; the offset of FUNCTION from the start of its code-component
292 (defun fun-offset (function)
293 (declare (type compiled-function function
))
294 (words-to-bytes (sb!kernel
:get-closure-length function
)))
296 ;;;; operations on code-components (which hold the instructions for
297 ;;;; one or more functions)
299 ;;; Return the length of the instruction area in CODE-COMPONENT.
300 (defun code-inst-area-length (code-component)
301 (declare (type sb
!kernel
:code-component code-component
))
302 (sb!kernel
:%code-code-size code-component
))
304 (defun segment-offs-to-code-offs (offset segment
)
305 (sb!sys
:without-gcing
306 (let* ((seg-base-addr (sb!sys
:sap-int
(funcall (seg-sap-maker segment
))))
308 (logandc1 sb
!vm
:lowtag-mask
309 (sb!kernel
:get-lisp-obj-address
(seg-code segment
))))
310 (addr (+ offset seg-base-addr
)))
311 (declare (type address seg-base-addr code-addr addr
))
312 (- addr code-addr
))))
314 (defun code-offs-to-segment-offs (offset segment
)
315 (sb!sys
:without-gcing
316 (let* ((seg-base-addr (sb!sys
:sap-int
(funcall (seg-sap-maker segment
))))
318 (logandc1 sb
!vm
:lowtag-mask
319 (sb!kernel
:get-lisp-obj-address
(seg-code segment
))))
320 (addr (+ offset code-addr
)))
321 (declare (type address seg-base-addr code-addr addr
))
322 (- addr seg-base-addr
))))
324 (defun code-insts-offs-to-segment-offs (offset segment
)
325 (sb!sys
:without-gcing
326 (let* ((seg-base-addr (sb!sys
:sap-int
(funcall (seg-sap-maker segment
))))
328 (sb!sys
:sap-int
(sb!kernel
:code-instructions
(seg-code segment
))))
329 (addr (+ offset code-insts-addr
)))
330 (declare (type address seg-base-addr code-insts-addr addr
))
331 (- addr seg-base-addr
))))
333 (defun lra-hook (chunk stream dstate
)
334 (declare (type dchunk chunk
)
336 (type (or null stream
) stream
)
337 (type disassem-state dstate
))
338 (when (and (aligned-p (+ (seg-virtual-location (dstate-segment dstate
))
339 (dstate-cur-offs dstate
))
340 (* 2 sb
!vm
:n-word-bytes
))
342 (= (sb!sys
:sap-ref-8
(dstate-segment-sap dstate
)
343 (if (eq (dstate-byte-order dstate
)
345 (dstate-cur-offs dstate
)
346 (+ (dstate-cur-offs dstate
)
348 sb
!vm
:return-pc-header-widetag
))
349 (unless (null stream
)
350 (note "possible LRA header" dstate
)))
353 ;;; Print the fun-header (entry-point) pseudo-instruction at the
354 ;;; current location in DSTATE to STREAM.
355 (defun fun-header-hook (stream dstate
)
356 (declare (type (or null stream
) stream
)
357 (type disassem-state dstate
))
358 (unless (null stream
)
359 (let* ((seg (dstate-segment dstate
))
360 (code (seg-code seg
))
363 (segment-offs-to-code-offs (dstate-cur-offs dstate
) seg
)))
365 (sb!kernel
:code-header-ref code
367 sb
!vm
:simple-fun-name-slot
)))
369 (sb!kernel
:code-header-ref code
371 sb
!vm
:simple-fun-arglist-slot
)))
373 (sb!kernel
:code-header-ref code
375 sb
!vm
:simple-fun-type-slot
))))
376 ;; if the function's name conveys its args, don't show ARGS too
377 (format stream
".~A ~S~:[~:A~;~]" 'entry name
378 (and (typep name
'(cons (eql lambda
) (cons list
)))
379 (equal args
(second name
)))
381 (note (lambda (stream)
382 (format stream
"~:S" type
)) ; use format to print NIL as ()
384 (incf (dstate-next-offs dstate
)
385 (words-to-bytes sb
!vm
:simple-fun-code-offset
)))
387 (defun alignment-hook (chunk stream dstate
)
388 (declare (type dchunk chunk
)
390 (type (or null stream
) stream
)
391 (type disassem-state dstate
))
393 (+ (seg-virtual-location (dstate-segment dstate
))
394 (dstate-cur-offs dstate
)))
395 (alignment (dstate-alignment dstate
)))
396 (unless (aligned-p location alignment
)
398 (format stream
"~A~Vt~W~%" '.align
399 (dstate-argument-column dstate
)
401 (incf (dstate-next-offs dstate
)
402 (- (align location alignment
) location
)))
405 (defun rewind-current-segment (dstate segment
)
406 (declare (type disassem-state dstate
)
407 (type segment segment
))
408 (setf (dstate-segment dstate
) segment
)
409 (setf (dstate-inst-properties dstate
) nil
)
410 (setf (dstate-cur-offs-hooks dstate
)
411 (stable-sort (nreverse (copy-list (seg-hooks segment
)))
413 (or (< (offs-hook-offset oh1
) (offs-hook-offset oh2
))
414 (and (= (offs-hook-offset oh1
)
415 (offs-hook-offset oh2
))
416 (offs-hook-before-address oh1
)
417 (not (offs-hook-before-address oh2
)))))))
418 (setf (dstate-cur-offs dstate
) 0)
419 (setf (dstate-cur-labels dstate
) (dstate-labels dstate
)))
421 (defun call-offs-hooks (before-address stream dstate
)
422 (declare (type (or null stream
) stream
)
423 (type disassem-state dstate
))
424 (let ((cur-offs (dstate-cur-offs dstate
)))
425 (setf (dstate-next-offs dstate
) cur-offs
)
427 (let ((next-hook (car (dstate-cur-offs-hooks dstate
))))
428 (when (null next-hook
)
430 (let ((hook-offs (offs-hook-offset next-hook
)))
431 (when (or (> hook-offs cur-offs
)
432 (and (= hook-offs cur-offs
)
434 (not (offs-hook-before-address next-hook
))))
436 (unless (< hook-offs cur-offs
)
437 (funcall (offs-hook-fun next-hook
) stream dstate
))
438 (pop (dstate-cur-offs-hooks dstate
))
439 (unless (= (dstate-next-offs dstate
) cur-offs
)
442 (defun call-fun-hooks (chunk stream dstate
)
443 (let ((hooks (dstate-fun-hooks dstate
))
444 (cur-offs (dstate-cur-offs dstate
)))
445 (setf (dstate-next-offs dstate
) cur-offs
)
446 (dolist (hook hooks nil
)
447 (let ((prefix-p (funcall hook chunk stream dstate
)))
448 (unless (= (dstate-next-offs dstate
) cur-offs
)
449 (return prefix-p
))))))
451 ;;; Print enough spaces to fill the column used for instruction bytes,
452 ;;; assuming that N-BYTES many instruction bytes have already been
453 ;;; printed in it, then print an additional space as separator to the
455 (defun pad-inst-column (stream n-bytes
)
456 (declare (type stream stream
)
457 (type text-width n-bytes
))
458 (when (> *disassem-inst-column-width
* 0)
459 (dotimes (i (- *disassem-inst-column-width
* (* 2 n-bytes
)))
460 (write-char #\space stream
))
461 (write-char #\space stream
)))
463 (defun handle-bogus-instruction (stream dstate prefix-len
)
464 (let ((alignment (dstate-alignment dstate
)))
465 (unless (null stream
)
466 (multiple-value-bind (words bytes
)
467 (truncate alignment sb
!vm
:n-word-bytes
)
469 (print-inst (* words sb
!vm
:n-word-bytes
) stream dstate
470 :trailing-space nil
))
472 (print-inst bytes stream dstate
:trailing-space nil
)))
473 (pad-inst-column stream
(+ prefix-len alignment
))
474 (decf (dstate-cur-offs dstate
) prefix-len
)
475 (print-bytes (+ prefix-len alignment
) stream dstate
))
476 (incf (dstate-next-offs dstate
) alignment
)))
478 ;;; Iterate through the instructions in SEGMENT, calling FUNCTION for
479 ;;; each instruction, with arguments of CHUNK, STREAM, and DSTATE.
480 ;;; Additionally, unless STREAM is NIL, several items are output to it:
481 ;;; things printed from several hooks, for example labels, and instruction
482 ;;; bytes before FUNCTION is called, notes and a newline afterwards.
483 ;;; Instructions having an INST-PRINTER of NIL are treated as prefix
484 ;;; instructions which makes them print on the same line as the following
485 ;;; instruction, outputting their INST-PRINT-NAME (unless that is NIL)
486 ;;; before FUNCTION is called for the following instruction.
487 (defun map-segment-instructions (function segment dstate
&optional stream
)
488 (declare (type function function
)
489 (type segment segment
)
490 (type disassem-state dstate
)
491 (type (or null stream
) stream
))
493 (let ((ispace (get-inst-space))
495 ;; If the segment starts with unboxed data,
496 ;; dump some number of words using the .WORD pseudo-ops.
497 (if (and (seg-unboxed-data-range segment
)
498 (= (segment-offs-to-code-offs 0 segment
)
499 (car (seg-unboxed-data-range segment
))))
500 (code-offs-to-segment-offs (cdr (seg-unboxed-data-range segment
))
503 (prefix-p nil
) ; just processed a prefix inst
504 (prefix-len 0) ; sum of lengths of any prefix instruction(s)
505 (prefix-print-names nil
)) ; reverse list of prefixes seen
507 (rewind-current-segment dstate segment
)
510 (when (>= (dstate-cur-offs dstate
) (seg-length (dstate-segment dstate
)))
512 (when (and stream
(> prefix-len
0))
513 (pad-inst-column stream prefix-len
)
514 (decf (dstate-cur-offs dstate
) prefix-len
)
515 (print-bytes prefix-len stream dstate
)
516 (incf (dstate-cur-offs dstate
) prefix-len
))
519 (setf (dstate-next-offs dstate
) (dstate-cur-offs dstate
))
521 (call-offs-hooks t stream dstate
)
522 (unless (or prefix-p
(null stream
))
523 (print-current-address stream dstate
))
524 (call-offs-hooks nil stream dstate
)
526 (when (< (dstate-cur-offs dstate
) data-end-offset
)
527 (sb!sys
:without-gcing
528 (format stream
"~A #x~v,'0x" '.word
529 (* 2 sb
!vm
:n-word-bytes
)
530 (sap-ref-int (funcall (seg-sap-maker segment
))
531 (dstate-cur-offs dstate
)
533 (dstate-byte-order dstate
))))
534 (setf (dstate-next-offs dstate
)
535 (+ (dstate-cur-offs dstate
) sb
!vm
:n-word-bytes
)))
537 (unless (> (dstate-next-offs dstate
) (dstate-cur-offs dstate
))
538 (sb!sys
:without-gcing
539 (setf (dstate-segment-sap dstate
) (funcall (seg-sap-maker segment
)))
542 (sap-ref-dchunk (dstate-segment-sap dstate
)
543 (dstate-cur-offs dstate
)
544 (dstate-byte-order dstate
)))
545 (fun-prefix-p (call-fun-hooks chunk stream dstate
)))
546 (if (> (dstate-next-offs dstate
) (dstate-cur-offs dstate
))
547 (setf prefix-p fun-prefix-p
)
548 (let ((inst (find-inst chunk ispace
)))
550 (handle-bogus-instruction stream dstate prefix-len
)
553 (setf (dstate-next-offs dstate
)
554 (+ (dstate-cur-offs dstate
)
556 (let ((orig-next (dstate-next-offs dstate
))
557 (control (inst-control inst
)))
558 (print-inst (inst-length inst
) stream dstate
561 (dolist (item (inst-prefilters inst
))
562 (declare (optimize (sb!c
::insert-array-bounds-checks
0)))
563 ;; item = #(INDEX FUNCTION SIGN-EXTEND-P BYTE-SPEC ...).
564 (flet ((extract-byte (spec-index)
565 (let* ((byte-spec (svref item spec-index
))
566 (integer (dchunk-extract chunk byte-spec
)))
567 (if (svref item
2) ; SIGN-EXTEND-P
568 (sign-extend integer
(byte-size byte-spec
))
570 (let ((item-length (length item
))
571 (fun (svref item
1)))
572 (setf (svref (dstate-filtered-values dstate
) (svref item
0))
574 (2 (funcall fun dstate
)) ; no subfields
575 (3 (bug "Bogus prefilter"))
576 (4 (funcall fun dstate
(extract-byte 3))) ; one subfield
577 (5 (funcall fun dstate
; two subfields
578 (extract-byte 3) (extract-byte 4)))
579 (t (apply fun dstate
; > 2 subfields
580 (loop for i from
3 below item-length
581 collect
(extract-byte i
)))))))))
583 (setf prefix-p
(null (inst-printer inst
)))
586 ;; Print any instruction bytes recognized by
587 ;; the prefilter which calls read-suffix and
588 ;; updates next-offs.
589 (let ((suffix-len (- (dstate-next-offs dstate
)
591 (when (plusp suffix-len
)
592 (print-inst suffix-len stream dstate
593 :offset
(inst-length inst
)
594 :trailing-space nil
))
595 ;; Keep track of the number of bytes
597 (incf prefix-len
(+ (inst-length inst
)
600 (let ((name (inst-print-name inst
)))
602 (push name prefix-print-names
)))
604 ;; PREFIX-LEN includes the length of the
605 ;; current (non-prefix) instruction here.
606 (pad-inst-column stream prefix-len
)
607 (dolist (name (reverse prefix-print-names
))
609 (write-char #\space stream
)))))
611 (funcall function chunk inst
)
614 (funcall control chunk inst stream dstate
))))))))))
616 (setf (dstate-cur-offs dstate
) (dstate-next-offs dstate
))
621 prefix-print-names nil
)
622 (print-notes-and-newline stream dstate
))
623 (setf (dstate-output-state dstate
) nil
))
625 (setf (dstate-inst-properties dstate
) nil
)))))
628 (defun collect-labelish-operands (args cache
)
629 (awhen (remove-if-not #'arg-use-label args
)
630 (let* ((list (mapcar (lambda (arg &aux
(fun (arg-use-label arg
))
631 (prefilter (arg-prefilter arg
))
632 (bytes (arg-fields arg
)))
633 ;; Require byte specs or a prefilter (or both).
634 ;; Prefilter alone is ok - it can use READ-SUFFIX.
635 ;; Additionally, you can't have :use-label T
636 ;; if multiple fields exist with no prefilter.
638 (if (eq fun t
) (singleton-p bytes
) bytes
)))
639 ;; If arg has a prefilter, just compute its index,
640 ;; otherwise keep the byte specs for extraction.
641 (coerce (cons (if (eq fun t
) #'identity fun
)
643 (list (posq arg args
))
644 (cons (arg-sign-extend-p arg
) bytes
)))
647 (repr (if (cdr list
) list
(car list
))) ; usually just 1 item
648 (table (assq :labeller cache
)))
649 (or (find repr
(cdr table
) :test
'equalp
)
650 (car (push repr
(cdr table
)))))))
652 ;;; Make an initial non-printing disassembly pass through DSTATE,
653 ;;; noting any addresses that are referenced by instructions in this
655 (defun add-segment-labels (segment dstate
)
656 ;; add labels at the beginning with a label-number of nil; we'll notice
657 ;; later and fill them in (and sort them)
658 (declare (type disassem-state dstate
))
659 (let ((labels (dstate-labels dstate
)))
660 (map-segment-instructions
662 (declare (type dchunk chunk
) (type instruction inst
))
663 (declare (optimize (sb!c
::insert-array-bounds-checks
0)))
664 (loop with list
= (inst-labeller inst
)
666 ;; item = #(FUNCTION PREFILTERED-VALUE-INDEX)
667 ;; | #(FUNCTION SIGN-EXTEND-P BYTE-SPEC ...)
668 for item
= (if (listp list
) (pop list
) (prog1 list
(setq list nil
)))
670 do
(let* ((item-length (length item
))
671 (index/signedp
(svref item
1))
675 (flet ((extract-byte (spec-index)
676 (let* ((byte-spec (svref item spec-index
))
677 (integer (dchunk-extract chunk byte-spec
)))
679 (sign-extend integer
(byte-size byte-spec
))
682 (2 (svref (dstate-filtered-values dstate
) index
/signedp
))
683 (3 (extract-byte 2)) ; extract exactly one byte
684 (t ; extract >1 byte.
685 ;; FIXME: this is strictly redundant.
686 ;; You should combine fields in the prefilter
687 ;; so that the labeller receives a single byte.
688 ;; AARCH64 and HPPA make use of this though.
689 (loop for i from
2 below item-length
690 collect
(extract-byte i
)))))
692 ;; If non-integer, the value is not a label.
693 (when (and (integerp adjusted-value
)
694 (not (assoc adjusted-value labels
)))
695 (push (cons adjusted-value nil
) labels
)))))
698 (setf (dstate-labels dstate
) labels
)
699 ;; erase any notes that got there by accident
700 (setf (dstate-notes dstate
) nil
)))
702 ;;; If any labels in DSTATE have been added since the last call to
703 ;;; this function, give them label-numbers, enter them in the
704 ;;; hash-table, and make sure the label list is in sorted order.
705 (defun number-labels (dstate)
706 (let ((labels (dstate-labels dstate
)))
707 (when (and labels
(null (cdar labels
)))
708 ;; at least one label left un-numbered
709 (setf labels
(sort labels
#'< :key
#'car
))
711 (label-hash (dstate-label-hash dstate
)))
712 (dolist (label labels
)
713 (when (not (null (cdr label
)))
714 (setf max
(max max
(cdr label
)))))
715 (dolist (label labels
)
716 (when (null (cdr label
))
718 (setf (cdr label
) max
)
719 (setf (gethash (car label
) label-hash
)
720 (format nil
"L~W" max
)))))
721 (setf (dstate-labels dstate
) labels
))))
723 (defun collect-inst-variants (base-name package variants cache
)
724 (loop for printer in variants
727 (destructuring-bind (format-name
728 (&rest arg-constraints
)
729 &optional
(printer :default
)
731 (without-package-locks (intern base-name package
)))
734 (declare (type (or symbol string
) print-name
))
735 (let* ((format (format-or-lose format-name
))
736 (args (copy-list (format-args format
)))
737 (format-length (bytes-to-bits (format-length format
))))
738 (dolist (constraint arg-constraints
)
739 (destructuring-bind (name . props
) constraint
740 (let ((cell (member name args
:key
#'arg-name
))
743 (setf (car cell
) (setf arg
(copy-structure (car cell
))))
744 (setf args
(nconc args
(list (setf arg
(%make-arg name
))))))
746 arg format-length
(and props
(cons :value props
))))))
747 (multiple-value-bind (mask id
) (compute-mask-id args
)
749 base-name format-name print-name
750 (format-length format
) mask id
751 (awhen (if (eq printer
:default
)
752 (format-default-printer format
)
754 (find-printer-fun it args cache
(list base-name index
)))
755 (collect-labelish-operands args cache
)
756 (collect-prefiltering-args args cache
)
759 (defun !compile-inst-printers
()
760 (let ((package sb
!assem
::*backend-instruction-set-package
*)
761 (cache (list (list :printer
) (list :prefilter
) (list :labeller
))))
762 (do-symbols (symbol package
)
763 (awhen (get symbol
'instruction-flavors
)
764 (setf (get symbol
'instruction-flavors
)
765 (collect-inst-variants
766 (string-upcase symbol
) package it cache
))))))
768 ;;; Get the instruction-space, creating it if necessary.
769 (defun get-inst-space (&key
(package sb
!assem
::*backend-instruction-set-package
*)
771 (let ((ispace *disassem-inst-space
*))
772 (when (or force
(null ispace
))
774 (do-symbols (symbol package
)
775 (setq insts
(nconc (copy-list (get symbol
'instruction-flavors
))
777 (setf ispace
(build-inst-space insts
)))
778 (setf *disassem-inst-space
* ispace
))
781 ;;;; Add global hooks.
783 (defun add-offs-hook (segment addr hook
)
784 (let ((entry (cons addr hook
)))
785 (if (null (seg-hooks segment
))
786 (setf (seg-hooks segment
) (list entry
))
787 (push entry
(cdr (last (seg-hooks segment
)))))))
789 (defun add-offs-note-hook (segment addr note
)
790 (add-offs-hook segment
792 (lambda (stream dstate
)
793 (declare (type (or null stream
) stream
)
794 (type disassem-state dstate
))
796 (note note dstate
)))))
798 (defun add-offs-comment-hook (segment addr comment
)
799 (add-offs-hook segment
801 (lambda (stream dstate
)
802 (declare (type (or null stream
) stream
)
805 (write-string ";;; " stream
)
808 (write-string comment stream
))
810 (funcall comment stream
)))
813 (defun add-fun-hook (dstate function
)
814 (push function
(dstate-fun-hooks dstate
)))
816 (defun set-location-printing-range (dstate from length
)
817 (setf (dstate-addr-print-len dstate
) ; in characters
818 ;; 4 bits per hex digit
819 (ceiling (integer-length (logxor from
(+ from length
))) 4)))
821 ;;; Print the current address in DSTATE to STREAM, plus any labels that
822 ;;; correspond to it, and leave the cursor in the instruction column.
823 (defun print-current-address (stream dstate
)
824 (declare (type stream stream
)
825 (type disassem-state dstate
))
827 (+ (seg-virtual-location (dstate-segment dstate
))
828 (dstate-cur-offs dstate
)))
829 (location-column-width *disassem-location-column-width
*)
830 (plen ; the number of rightmost hex chars of this address to print
831 (or (dstate-addr-print-len dstate
)
832 ;; Usually we've already set the width, but in case not...
833 (let ((seg (dstate-segment dstate
)))
834 (set-location-printing-range
835 dstate
(seg-virtual-location seg
) (seg-length seg
))))))
837 (if (eq (dstate-output-state dstate
) :beginning
) ; on the first line
838 (if location-column-width
839 ;; If there's a user-specified width, force that number of hex chars
840 ;; regardless of whether it's greater or smaller than PLEN.
841 (setq plen location-column-width
)
842 ;; No specified width. The PLEN of this line becomes the width.
843 ;; Adjust the DSTATE's argument column for it.
844 (incf (dstate-argument-column dstate
)
845 (setq location-column-width plen
)))
846 ;; not the first line
847 (if location-column-width
848 ;; A specified width smaller than that required clips significant
849 ;; digits, but larger should not cause leading zeros to appear.
850 (setq plen
(min plen location-column-width
))
851 ;; Otherwise use the previously computed addr-print-len
852 (setq location-column-width plen
)))
854 (incf location-column-width
2) ; account for leading "; "
858 ;; print the location
859 ;; [this is equivalent to (format stream "~V,'0x:" plen printed-value), but
860 ;; usually avoids any consing]
861 ;; FIXME: if this cruft is actually a speed win, the format-string compiler
862 ;; should be improved to obviate the obfuscation. If it is not a win,
863 ;; we should just replace it with the above format string already.
864 (tab0 (- location-column-width plen
) stream
)
865 (let* ((printed-bits (* 4 plen
))
866 (printed-value (ldb (byte printed-bits
0) location
))
868 (truncate (- printed-bits
(integer-length printed-value
)) 4)))
869 (dotimes (i leading-zeros
)
870 (write-char #\
0 stream
))
871 (unless (zerop printed-value
)
872 (write printed-value
:stream stream
:base
16 :radix nil
))
874 (write-char #\
: stream
)))
878 (let* ((next-label (car (dstate-cur-labels dstate
)))
879 (label-location (car next-label
)))
880 (when (or (null label-location
) (> label-location location
))
882 (unless (< label-location location
)
883 (format stream
" L~W:" (cdr next-label
)))
884 (pop (dstate-cur-labels dstate
))))
886 ;; move to the instruction column
887 (tab0 (+ location-column-width
1 label-column-width
) stream
)
890 (eval-when (:compile-toplevel
:execute
)
891 (sb!xc
:defmacro with-print-restrictions
(&rest body
)
892 `(let ((*print-pretty
* t
)
898 ;;; Print a newline to STREAM, inserting any pending notes in DSTATE
899 ;;; as end-of-line comments. If there is more than one note, a
900 ;;; separate line will be used for each one.
901 (defun print-notes-and-newline (stream dstate
)
902 (declare (type stream stream
)
903 (type disassem-state dstate
))
904 (with-print-restrictions
905 (dolist (note (dstate-notes dstate
))
906 (format stream
"~Vt " *disassem-note-column
*)
907 (pprint-logical-block (stream nil
:per-line-prefix
"; ")
910 (write-string note stream
))
912 (funcall note stream
))))
915 (setf (dstate-notes dstate
) nil
)))
917 ;;; Print NUM instruction bytes to STREAM as hex values.
918 (defun print-inst (num stream dstate
&key
(offset 0) (trailing-space t
))
919 (when (> *disassem-inst-column-width
* 0)
920 (let ((sap (dstate-segment-sap dstate
))
921 (start-offs (+ offset
(dstate-cur-offs dstate
))))
923 (format stream
"~2,'0x" (sb!sys
:sap-ref-8 sap
(+ offs start-offs
))))
925 (pad-inst-column stream num
)))))
927 ;;; Disassemble NUM bytes to STREAM as simple `BYTE' instructions.
928 (defun print-bytes (num stream dstate
)
929 (declare (type offset num
)
931 (type disassem-state dstate
))
932 (format stream
"~A~Vt" 'BYTE
(dstate-argument-column dstate
))
933 (let ((sap (dstate-segment-sap dstate
))
934 (start-offs (dstate-cur-offs dstate
)))
937 (write-string ", " stream
))
938 (format stream
"#X~2,'0x" (sb!sys
:sap-ref-8 sap
(+ offs start-offs
))))))
940 ;;; Disassemble NUM machine-words to STREAM as simple `WORD' instructions.
941 (defun print-words (num stream dstate
)
942 (declare (type offset num
)
944 (type disassem-state dstate
))
945 (format stream
"~A~Vt" 'WORD
(dstate-argument-column dstate
))
946 (let ((sap (dstate-segment-sap dstate
))
947 (start-offs (dstate-cur-offs dstate
))
948 (byte-order (dstate-byte-order dstate
)))
949 (dotimes (word-offs num
)
950 (unless (zerop word-offs
)
951 (write-string ", " stream
))
952 (let ((word 0) (bit-shift 0))
953 (dotimes (byte-offs sb
!vm
:n-word-bytes
)
958 (* word-offs sb
!vm
:n-word-bytes
)
961 (if (eq byte-order
:big-endian
)
962 (+ (ash word sb
!vm
:n-byte-bits
) byte
)
963 (+ word
(ash byte bit-shift
))))
964 (incf bit-shift sb
!vm
:n-byte-bits
)))
965 (format stream
"#X~V,'0X" (ash sb
!vm
:n-word-bits -
2) word
)))))
967 (defvar *default-dstate-hooks
* (list #'lra-hook
))
969 ;;; Make a disassembler-state object.
970 (defun make-dstate (&optional
(fun-hooks *default-dstate-hooks
*))
971 (let ((alignment *disassem-inst-alignment-bytes
*)
973 (+ 2 ; for the leading "; " on each line
974 (or *disassem-location-column-width
* 0)
977 *disassem-inst-column-width
*
978 (if (zerop *disassem-inst-column-width
*) 0 1)
979 *disassem-opcode-column-width
*)))
981 (when (> alignment
1)
982 (push #'alignment-hook fun-hooks
))
984 (%make-dstate
:fun-hooks fun-hooks
985 :argument-column arg-column
987 :byte-order sb
!c
:*backend-byte-order
*)))
989 (defun add-fun-header-hooks (segment)
990 (declare (type segment segment
))
991 (do ((fun (awhen (seg-code segment
) (sb!kernel
:%code-entry-points it
))
993 (length (seg-length segment
)))
995 (let ((offset (code-offs-to-segment-offs (fun-offset fun
) segment
)))
996 (when (<= 0 offset length
)
997 ;; Up to 2 words of zeros might be present to align the next
998 ;; simple-fun. Limit on OFFSET is to avoid incorrect triggering
999 ;; in case of unexpected weirdness. FIXME: verify all zero bytes
1000 (when (< 0 offset
(* sb
!vm
:n-word-bytes
2))
1001 (push (make-offs-hook
1002 :fun
(lambda (stream dstate
)
1004 (format stream
".SKIP ~D" offset
))
1005 (incf (dstate-next-offs dstate
) offset
))
1006 :offset
0) ; at 0 bytes into this seg, skip OFFSET bytes
1007 (seg-hooks segment
)))
1008 (push (make-offs-hook :offset offset
:fun
#'fun-header-hook
)
1009 (seg-hooks segment
))))))
1011 ;;; A SAP-MAKER is a no-argument function that returns a SAP.
1013 ;; FIXME: Are the objects we are taking saps for always pinned?
1014 #!-sb-fluid
(declaim (inline sap-maker
))
1015 (defun sap-maker (function input offset
)
1016 (declare (optimize (speed 3))
1017 (muffle-conditions compiler-note
)
1018 (type (function (t) sb
!sys
:system-area-pointer
) function
)
1019 (type offset offset
))
1020 (let ((old-sap (sb!sys
:sap
+ (funcall function input
) offset
)))
1021 (declare (type sb
!sys
:system-area-pointer old-sap
))
1024 (+ (sb!sys
:sap-int
(funcall function input
)) offset
)))
1025 ;; Saving the sap like this avoids consing except when the sap
1026 ;; changes (because the sap-int, arith, etc., get inlined).
1027 (declare (type address new-addr
))
1028 (if (= (sb!sys
:sap-int old-sap
) new-addr
)
1030 (setf old-sap
(sb!sys
:int-sap new-addr
)))))))
1032 (defun vector-sap-maker (vector offset
)
1033 (declare (optimize (speed 3))
1034 (type offset offset
))
1035 (sap-maker #'sb
!sys
:vector-sap vector offset
))
1037 (defun code-sap-maker (code offset
)
1038 (declare (optimize (speed 3))
1039 (type sb
!kernel
:code-component code
)
1040 (type offset offset
))
1041 (sap-maker #'sb
!kernel
:code-instructions code offset
))
1043 (defun memory-sap-maker (address)
1044 (declare (optimize (speed 3))
1045 (muffle-conditions compiler-note
)
1046 (type address address
))
1047 (let ((sap (sb!sys
:int-sap address
)))
1050 (defstruct (source-form-cache (:conc-name sfcache-
)
1052 (debug-source nil
:type
(or null sb
!di
:debug-source
))
1053 (toplevel-form-index -
1 :type fixnum
)
1054 (last-location-retrieved nil
:type
(or null sb
!di
:code-location
))
1055 (last-form-retrieved -
1 :type fixnum
))
1057 ;;; Return a memory segment located at the system-area-pointer returned by
1058 ;;; SAP-MAKER and LENGTH bytes long in the disassem-state object DSTATE.
1060 ;;; &KEY arguments include :VIRTUAL-LOCATION (by default the same as
1061 ;;; the address), :DEBUG-FUN, :SOURCE-FORM-CACHE (a
1062 ;;; SOURCE-FORM-CACHE object), and :HOOKS (a list of OFFS-HOOK
1064 (defun make-segment (sap-maker length
1066 code virtual-location
1067 debug-fun source-form-cache
1069 (declare (type (function () sb
!sys
:system-area-pointer
) sap-maker
)
1070 (type disassem-length length
)
1071 (type (or null address
) virtual-location
)
1072 (type (or null sb
!di
:debug-fun
) debug-fun
)
1073 (type (or null source-form-cache
) source-form-cache
))
1076 :sap-maker sap-maker
1078 :virtual-location
(or virtual-location
1079 (sb!sys
:sap-int
(funcall sap-maker
)))
1084 (let ((n-words (sb!kernel
:code-n-unboxed-data-words code
))
1085 (start (sb!kernel
:get-header-data code
)))
1086 (and (plusp n-words
)
1087 (cons (* sb
!vm
:n-word-bytes start
)
1088 (* sb
!vm
:n-word-bytes
(+ start n-words
)))))))))
1089 (add-debugging-hooks segment debug-fun source-form-cache
)
1090 (add-fun-header-hooks segment
)
1093 (defun make-vector-segment (vector offset
&rest args
)
1094 (declare (type vector vector
)
1095 (type offset offset
)
1096 (inline make-segment
))
1097 (apply #'make-segment
(vector-sap-maker vector offset
) args
))
1099 (defun make-code-segment (code offset length
&rest args
)
1100 (declare (type sb
!kernel
:code-component code
)
1101 (type offset offset
)
1102 (inline make-segment
))
1103 (apply #'make-segment
(code-sap-maker code offset
) length
:code code args
))
1105 (defun make-memory-segment (address &rest args
)
1106 (declare (type address address
)
1107 (inline make-segment
))
1108 (apply #'make-segment
(memory-sap-maker address
) args
))
1111 (defun print-fun-headers (function)
1112 (declare (type compiled-function function
))
1113 (let* ((self (fun-self function
))
1114 (code (sb!kernel
:fun-code-header self
)))
1115 (format t
"Code-header ~S: size: ~S~%"
1117 (sb!kernel
:%code-code-size code
))
1118 (do ((fun (sb!kernel
:code-header-ref code sb
!vm
:code-entry-points-slot
)
1121 (let ((fun-offset (sb!kernel
:get-closure-length fun
)))
1122 ;; There is function header fun-offset words from the
1124 (format t
"Fun-header ~S at offset ~W (words): ~S~A => ~S~%"
1127 (sb!kernel
:code-header-ref
1128 code
(+ fun-offset sb
!vm
:simple-fun-name-slot
))
1129 (sb!kernel
:code-header-ref
1130 code
(+ fun-offset sb
!vm
:simple-fun-arglist-slot
))
1131 (sb!kernel
:code-header-ref
1132 code
(+ fun-offset sb
!vm
:simple-fun-type-slot
)))))))
1134 ;;; getting at the source code...
1136 (defun get-different-source-form (loc context
&optional cache
)
1138 (eq (sb!di
:code-location-debug-source loc
)
1139 (sfcache-debug-source cache
))
1140 (eq (sb!di
:code-location-toplevel-form-offset loc
)
1141 (sfcache-toplevel-form-index cache
))
1142 (or (eql (sb!di
:code-location-form-number loc
)
1143 (sfcache-last-form-retrieved cache
))
1144 (awhen (sfcache-last-location-retrieved cache
)
1145 (sb!di
:code-location
= loc it
))))
1147 (let ((form (sb!debug
::code-location-source-form loc context nil
)))
1149 (setf (sfcache-debug-source cache
)
1150 (sb!di
:code-location-debug-source loc
))
1151 (setf (sfcache-toplevel-form-index cache
)
1152 (sb!di
:code-location-toplevel-form-offset loc
))
1153 (setf (sfcache-last-form-retrieved cache
)
1154 (sb!di
:code-location-form-number loc
))
1155 (setf (sfcache-last-location-retrieved cache
) loc
))
1158 ;;;; stuff to use debugging info to augment the disassembly
1160 (defun code-fun-map (code)
1161 (declare (type sb
!kernel
:code-component code
))
1162 (sb!c
::compiled-debug-info-fun-map
(sb!kernel
:%code-debug-info code
)))
1164 (defstruct (location-group (:copier nil
) (:predicate nil
))
1165 ;; This was (VECTOR (OR LIST FIXNUM)) but that doesn't have any
1166 ;; specialization other than T, and the cross-compiler has trouble
1167 ;; with (SB!XC:TYPEP #() '(VECTOR (OR LIST FIXNUM)))
1168 (locations #() :type simple-vector
))
1170 ;;; Return the vector of DEBUG-VARs currently associated with DSTATE.
1171 (defun dstate-debug-vars (dstate)
1172 (declare (type disassem-state dstate
))
1173 (storage-info-debug-vars (seg-storage-info (dstate-segment dstate
))))
1175 ;;; Given the OFFSET of a location within the location-group called
1176 ;;; LG-NAME, see whether there's a current mapping to a source
1177 ;;; variable in DSTATE, and if so, return the offset of that variable
1178 ;;; in the current debug-var vector.
1179 (defun find-valid-storage-location (offset lg-name dstate
)
1180 (declare (type offset offset
)
1181 (type symbol lg-name
)
1182 (type disassem-state dstate
))
1183 (let* ((storage-info
1184 (seg-storage-info (dstate-segment dstate
)))
1187 (cdr (assoc lg-name
(storage-info-groups storage-info
)))))
1189 (dstate-current-valid-locations dstate
)))
1191 (not (null currently-valid
))
1192 (let ((locations (location-group-locations location-group
)))
1193 (and (< offset
(length locations
))
1194 (let ((used-by (aref locations offset
)))
1196 (let ((debug-var-num
1200 (zerop (bit currently-valid used-by
)))
1206 (bit currently-valid num
)))
1211 ;; Found a valid storage reference!
1212 ;; can't use it again until it's revalidated...
1213 (setf (bit (dstate-current-valid-locations
1220 ;;; Return a new vector which has the same contents as the old one
1221 ;;; VEC, plus new cells (for a total size of NEW-LEN). The additional
1222 ;;; elements are initialized to INITIAL-ELEMENT.
1223 (defun grow-vector (vec new-len
&optional initial-element
)
1224 (declare (type vector vec
)
1225 (type fixnum new-len
))
1227 (make-sequence `(vector ,(array-element-type vec
) ,new-len
)
1229 :initial-element initial-element
)))
1230 (dotimes (i (length vec
))
1231 (setf (aref new i
) (aref vec i
)))
1234 ;;; Return a STORAGE-INFO struction describing the object-to-source
1235 ;;; variable mappings from DEBUG-FUN.
1236 (defun storage-info-for-debug-fun (debug-fun)
1237 (declare (type sb
!di
:debug-fun debug-fun
))
1238 (let ((sc-vec sb
!c
::*backend-sc-numbers
*)
1240 (debug-vars (sb!di
::debug-fun-debug-vars
1243 (dotimes (debug-var-offset
1245 (make-storage-info :groups groups
1246 :debug-vars debug-vars
))
1247 (let ((debug-var (aref debug-vars debug-var-offset
)))
1249 (format t
";;; At offset ~W: ~S~%" debug-var-offset debug-var
)
1251 (sb!di
::compiled-debug-var-sc-offset debug-var
))
1254 (sb!c
:sc-sb
(aref sc-vec
1255 (sb!c
:sc-offset-scn sc-offset
))))))
1257 (format t
";;; SET: ~S[~W]~%"
1258 sb-name
(sb!c
:sc-offset-offset sc-offset
))
1259 (unless (null sb-name
)
1260 (let ((group (cdr (assoc sb-name groups
))))
1262 (setf group
(make-location-group))
1263 (push `(,sb-name .
,group
) groups
))
1264 (let* ((locations (location-group-locations group
))
1265 (length (length locations
))
1266 (offset (sb!c
:sc-offset-offset sc-offset
)))
1267 (when (>= offset length
)
1269 (grow-vector locations
1273 (location-group-locations group
)
1275 (let ((already-there (aref locations offset
)))
1276 (cond ((null already-there
)
1277 (setf (aref locations offset
) debug-var-offset
))
1278 ((eql already-there debug-var-offset
))
1280 (if (listp already-there
)
1281 (pushnew debug-var-offset
1282 (aref locations offset
))
1283 (setf (aref locations offset
)
1284 (list debug-var-offset
1289 (defun source-available-p (debug-fun)
1291 (sb!di
:do-debug-fun-blocks
(block debug-fun
)
1292 (declare (ignore block
))
1294 (sb!di
:no-debug-blocks
() nil
)))
1296 (defun print-block-boundary (stream dstate
)
1297 (let ((os (dstate-output-state dstate
)))
1298 (when (not (eq os
:beginning
))
1299 (when (not (eq os
:block-boundary
))
1301 (setf (dstate-output-state dstate
)
1304 ;;; Add hooks to track the source code in SEGMENT during disassembly.
1305 ;;; SFCACHE can be either NIL or it can be a SOURCE-FORM-CACHE
1306 ;;; structure, in which case it is used to cache forms from files.
1307 (defun add-source-tracking-hooks (segment debug-fun
&optional sfcache
)
1308 (declare (type segment segment
)
1309 (type (or null sb
!di
:debug-fun
) debug-fun
)
1310 (type (or null source-form-cache
) sfcache
))
1311 (let ((last-block-pc -
1))
1312 (flet ((add-hook (pc fun
&optional before-address
)
1313 (push (make-offs-hook
1314 :offset
(code-insts-offs-to-segment-offs pc segment
)
1316 :before-address before-address
)
1317 (seg-hooks segment
))))
1319 (sb!di
:do-debug-fun-blocks
(block debug-fun
)
1320 (let ((first-location-in-block-p t
))
1321 (sb!di
:do-debug-block-locations
(loc block
)
1322 (let ((pc (sb!di
::compiled-code-location-pc loc
)))
1324 ;; Put blank lines in at block boundaries
1325 (when (and first-location-in-block-p
1326 (/= pc last-block-pc
))
1327 (setf first-location-in-block-p nil
)
1329 (lambda (stream dstate
)
1330 (print-block-boundary stream dstate
))
1332 (setf last-block-pc pc
))
1334 ;; Print out corresponding source; this information is not
1335 ;; all that accurate, but it's better than nothing
1336 (unless (zerop (sb!di
:code-location-form-number loc
))
1337 (multiple-value-bind (form new
)
1338 (get-different-source-form loc
0 sfcache
)
1340 (let ((at-block-begin (= pc last-block-pc
)))
1343 (lambda (stream dstate
)
1344 (declare (ignore dstate
))
1346 (unless at-block-begin
1348 (format stream
";;; [~W] "
1349 (sb!di
:code-location-form-number
1351 (prin1-short form stream
)
1356 ;; Keep track of variable live-ness as best we can.
1358 (copy-seq (sb!di
::compiled-code-location-live-set
1362 (lambda (stream dstate
)
1363 (declare (ignore stream
))
1364 (setf (dstate-current-valid-locations dstate
)
1367 (note (lambda (stream)
1368 (let ((*print-length
* nil
))
1369 (format stream
"live set: ~S"
1373 (sb!di
:no-debug-blocks
() nil
)))))
1375 (defvar *disassemble-annotate
* t
1377 "Annotate DISASSEMBLE output with source code.")
1379 (defun add-debugging-hooks (segment debug-fun
&optional sfcache
)
1381 (setf (seg-storage-info segment
)
1382 (storage-info-for-debug-fun debug-fun
))
1383 (when *disassemble-annotate
*
1384 (add-source-tracking-hooks segment debug-fun sfcache
))
1385 (let ((kind (sb!di
:debug-fun-kind debug-fun
)))
1386 (flet ((add-new-hook (n)
1387 (push (make-offs-hook
1389 :fun
(lambda (stream dstate
)
1390 (declare (ignore stream
))
1392 (seg-hooks segment
))))
1396 (add-new-hook "no-arg-parsing entry point"))
1398 (add-new-hook (lambda (stream)
1399 (format stream
"~S entry point" kind
)))))))))
1401 ;;; Return a list of the segments of memory containing machine code
1402 ;;; instructions for FUNCTION.
1403 (defun get-fun-segments (function)
1404 (declare (type compiled-function function
))
1405 (let* ((function (fun-self function
))
1406 (code (fun-code function
))
1407 (fun-map (code-fun-map code
))
1408 (fname (sb!kernel
:%simple-fun-name function
))
1409 (sfcache (make-source-form-cache)))
1410 (let ((first-block-seen-p nil
)
1411 (nil-block-seen-p nil
)
1413 (last-debug-fun nil
)
1415 (flet ((add-seg (offs len df
)
1417 (push (make-code-segment code offs len
1419 :source-form-cache sfcache
)
1421 (dotimes (fmap-index (length fun-map
))
1422 (let ((fmap-entry (aref fun-map fmap-index
)))
1423 (etypecase fmap-entry
1425 (when first-block-seen-p
1426 (add-seg last-offset
1427 (- fmap-entry last-offset
)
1429 (setf last-debug-fun nil
))
1430 (setf last-offset fmap-entry
))
1431 (sb!c
::compiled-debug-fun
1432 (let ((name (sb!c
::compiled-debug-fun-name fmap-entry
))
1433 (kind (sb!c
::compiled-debug-fun-kind fmap-entry
)))
1435 (format t
";;; SAW ~S ~S ~S,~S ~W,~W~%"
1436 name kind first-block-seen-p nil-block-seen-p
1438 (sb!c
::compiled-debug-fun-start-pc fmap-entry
))
1439 (cond (#+nil
(eq last-offset fun-offset
)
1440 (and (equal name fname
) (not first-block-seen-p
))
1441 (setf first-block-seen-p t
))
1442 ((eq kind
:external
)
1443 (when first-block-seen-p
1446 (when nil-block-seen-p
1448 (when first-block-seen-p
1449 (setf nil-block-seen-p t
))))
1450 (setf last-debug-fun
1451 (sb!di
::make-compiled-debug-fun fmap-entry code
)))))))
1452 (let ((max-offset (code-inst-area-length code
)))
1453 (when (and first-block-seen-p last-debug-fun
)
1454 (add-seg last-offset
1455 (- max-offset last-offset
)
1458 (let ((offs (fun-insts-offset function
)))
1460 (make-code-segment code offs
(- max-offset offs
))))
1461 (nreverse segments
)))))))
1463 ;;; Return a list of the segments of memory containing machine code
1464 ;;; instructions for the code-component CODE. If START-OFFSET and/or
1465 ;;; LENGTH is supplied, only that part of the code-segment is used
1466 ;;; (but these are constrained to lie within the code-segment).
1467 (defun get-code-segments (code
1470 (length (code-inst-area-length code
)))
1471 (declare (type sb
!kernel
:code-component code
)
1472 (type offset start-offset
)
1473 (type disassem-length length
))
1474 (let ((segments nil
))
1475 (when (sb!kernel
:%code-debug-info code
)
1476 (let ((fun-map (code-fun-map code
))
1477 (sfcache (make-source-form-cache)))
1478 (let ((last-offset 0)
1479 (last-debug-fun nil
))
1480 (flet ((add-seg (offs len df
)
1481 (let* ((restricted-offs
1482 (min (max start-offset offs
)
1483 (+ start-offset length
)))
1485 (- (min (max start-offset
(+ offs len
))
1486 (+ start-offset length
))
1488 (when (> restricted-len
0)
1489 (push (make-code-segment code
1490 restricted-offs restricted-len
1492 :source-form-cache sfcache
)
1494 (dotimes (fun-map-index (length fun-map
))
1495 (let ((fun-map-entry (aref fun-map fun-map-index
)))
1496 (etypecase fun-map-entry
1498 (add-seg last-offset
(- fun-map-entry last-offset
)
1500 (setf last-debug-fun nil
)
1501 (setf last-offset fun-map-entry
))
1502 (sb!c
::compiled-debug-fun
1503 (setf last-debug-fun
1504 (sb!di
::make-compiled-debug-fun fun-map-entry
1506 (when last-debug-fun
1507 (add-seg last-offset
1508 (- (code-inst-area-length code
) last-offset
)
1509 last-debug-fun
))))))
1511 (list (make-code-segment code start-offset length
))
1512 (nreverse segments
))))
1514 ;;; Compute labels for all the memory segments in SEGLIST and adds
1515 ;;; them to DSTATE. It's important to call this function with all the
1516 ;;; segments you're interested in, so that it can find references from
1518 (defun label-segments (seglist dstate
)
1519 (declare (type list seglist
)
1520 (type disassem-state dstate
))
1521 (dolist (seg seglist
)
1522 (add-segment-labels seg dstate
))
1523 ;; Now remove any labels that don't point anywhere in the segments
1525 (setf (dstate-labels dstate
)
1526 (remove-if (lambda (lab)
1529 (let ((start (seg-virtual-location seg
)))
1532 (+ start
(seg-length seg
)))))
1534 (dstate-labels dstate
))))
1536 ;;; Disassemble the machine code instructions in SEGMENT to STREAM.
1537 (defun disassemble-segment (segment stream dstate
)
1538 (declare (type segment segment
)
1539 (type stream stream
)
1540 (type disassem-state dstate
))
1541 (let ((*print-pretty
* nil
)) ; otherwise the pp conses hugely
1542 (number-labels dstate
)
1543 (map-segment-instructions
1544 (lambda (chunk inst
)
1545 (declare (type dchunk chunk
) (type instruction inst
))
1546 (awhen (inst-printer inst
)
1547 (funcall it chunk inst stream dstate
)))
1552 ;;; Disassemble the machine code instructions in each memory segment
1553 ;;; in SEGMENTS in turn to STREAM.
1554 (defun disassemble-segments (segments stream dstate
)
1555 (declare (type list segments
)
1556 (type stream stream
)
1557 (type disassem-state dstate
))
1558 (unless (null segments
)
1559 (let ((n-segments (length segments
))
1560 (first (car segments
))
1561 (last (car (last segments
))))
1562 ;; One origin per segment is printed. As with the per-line display,
1563 ;; the segment is thought of as immovable for rendering of addresses,
1564 ;; though in fact the disassembler transiently allows movement.
1565 (format stream
"~&; Size: ~a bytes. Origin: #x~x~@[ (segment 1 of ~D)~]"
1566 (reduce #'+ segments
:key
#'seg-length
)
1567 (seg-virtual-location first
)
1568 (if (> n-segments
1) n-segments
))
1569 (set-location-printing-range dstate
1570 (seg-virtual-location first
)
1571 (- (+ (seg-virtual-location last
)
1573 (seg-virtual-location first
)))
1574 (setf (dstate-output-state dstate
) :beginning
)
1576 (dolist (seg segments
)
1577 (when (> (incf i
) 1)
1578 (format stream
"~&; Origin #x~x (segment ~D of ~D)"
1579 (seg-virtual-location seg
) i n-segments
))
1580 (disassemble-segment seg stream dstate
))))))
1583 ;;;; top level functions
1585 ;;; Disassemble the machine code instructions for FUNCTION.
1586 (defun disassemble-fun (fun &key
1587 (stream *standard-output
*)
1589 (declare (type compiled-function fun
)
1590 (type stream stream
)
1591 (type (member t nil
) use-labels
))
1592 (let* ((dstate (make-dstate))
1593 (segments (get-fun-segments fun
)))
1595 (label-segments segments dstate
))
1596 (disassemble-segments segments stream dstate
)))
1598 (defun valid-extended-function-designators-for-disassemble-p (thing)
1600 ((satisfies legal-fun-name-p
)
1601 (compiled-funs-or-lose (fdefinition thing
) thing
))
1602 (sb!pcl
::%method-function
1603 ;; in a %METHOD-FUNCTION, the user code is in the fast function, so
1604 ;; we to disassemble both.
1605 ;; FIXME: interpreted methods need to be compiled as above.
1606 (list thing
(sb!pcl
::%method-function-fast-function thing
)))
1607 ((or (cons (eql lambda
))
1608 #!+sb-fasteval sb
!interpreter
:interpreted-function
1609 #!+sb-eval sb
!eval
:interpreted-function
)
1610 (compile nil thing
))
1614 (defun compiled-funs-or-lose (thing &optional
(name thing
))
1615 (let ((funs (valid-extended-function-designators-for-disassemble-p thing
)))
1618 (error 'simple-type-error
1620 :expected-type
'(satisfies valid-extended-function-designators-for-disassemble-p
)
1621 :format-control
"Can't make a compiled function from ~S"
1622 :format-arguments
(list name
)))))
1624 (defun disassemble (object &key
1625 (stream *standard-output
*)
1628 "Disassemble the compiled code associated with OBJECT, which can be a
1629 function, a lambda expression, or a symbol with a function definition. If
1630 it is not already compiled, the compiler is called to produce something to
1632 (declare (type (or function symbol cons
) object
)
1633 (type (or (member t
) stream
) stream
)
1634 (type (member t nil
) use-labels
))
1635 (flet ((disassemble1 (fun)
1636 (format stream
"~&; disassembly for ~S" (sb!kernel
:%fun-name fun
))
1637 (disassemble-fun fun
1639 :use-labels use-labels
)))
1640 (mapc #'disassemble1
(ensure-list (compiled-funs-or-lose object
))))
1643 ;;; Disassembles the given area of memory starting at ADDRESS and
1644 ;;; LENGTH long. Note that if CODE-COMPONENT is NIL and this memory
1645 ;;; could move during a GC, you'd better disable it around the call to
1647 (defun disassemble-memory (address
1650 (stream *standard-output
*)
1653 (declare (type (or address sb
!sys
:system-area-pointer
) address
)
1654 (type disassem-length length
)
1655 (type stream stream
)
1656 (type (or null sb
!kernel
:code-component
) code-component
)
1657 (type (member t nil
) use-labels
))
1659 (if (sb!sys
:system-area-pointer-p address
)
1660 (sb!sys
:sap-int address
)
1662 (dstate (make-dstate))
1668 (sb!kernel
:code-instructions code-component
)))))
1669 (when (or (< code-offs
0)
1670 (> code-offs
(code-inst-area-length code-component
)))
1671 (error "address ~X not in the code component ~S"
1672 address code-component
))
1673 (get-code-segments code-component code-offs length
))
1674 (list (make-memory-segment address length
)))))
1676 (label-segments segments dstate
))
1677 (disassemble-segments segments stream dstate
)))
1679 ;;; Disassemble the machine code instructions associated with
1680 ;;; CODE-COMPONENT (this may include multiple entry points).
1681 (defun disassemble-code-component (code-component &key
1682 (stream *standard-output
*)
1684 (declare (type (or sb
!kernel
:code-component compiled-function
)
1686 (type stream stream
)
1687 (type (member t nil
) use-labels
))
1688 (let* ((code-component
1689 (if (functionp code-component
)
1690 (fun-code code-component
)
1692 (dstate (make-dstate))
1693 (segments (get-code-segments code-component
)))
1695 (label-segments segments dstate
))
1696 (disassemble-segments segments stream dstate
)))
1698 ;;;; code to disassemble assembler segments
1700 (defun assem-segment-to-disassem-segment (assem-segment)
1701 (declare (type sb
!assem
:segment assem-segment
))
1702 (let ((contents (sb!assem
:segment-contents-as-vector assem-segment
)))
1703 (make-vector-segment contents
0 (length contents
) :virtual-location
0)))
1705 ;;; Disassemble the machine code instructions associated with
1706 ;;; ASSEM-SEGMENT (of type assem:segment).
1707 (defun disassemble-assem-segment (assem-segment stream
)
1708 (declare (type sb
!assem
:segment assem-segment
)
1709 (type stream stream
))
1710 (let ((dstate (make-dstate))
1712 (list (assem-segment-to-disassem-segment assem-segment
))))
1713 (label-segments disassem-segments dstate
)
1714 (disassemble-segments disassem-segments stream dstate
)))
1716 ;;; routines to find things in the Lisp environment
1718 ;;; an alist of (SYMBOL-SLOT-OFFSET . ACCESS-FUN-NAME) for slots
1719 ;;; in a symbol object that we know about
1720 (defparameter *grokked-symbol-slots
*
1721 (sort (copy-list `((,sb
!vm
:symbol-value-slot . symbol-value
)
1722 (,sb
!vm
:symbol-info-slot . symbol-info
)
1723 (,sb
!vm
:symbol-name-slot . symbol-name
)
1724 (,sb
!vm
:symbol-package-slot . symbol-package
)))
1728 ;;; Given ADDRESS, try and figure out if which slot of which symbol is
1729 ;;; being referred to. Of course we can just give up, so it's not a
1730 ;;; big deal... Return two values, the symbol and the name of the
1731 ;;; access function of the slot.
1732 (defun grok-symbol-slot-ref (address)
1733 (declare (type address address
))
1734 (if (not (aligned-p address sb
!vm
:n-word-bytes
))
1736 (do ((slots-tail *grokked-symbol-slots
* (cdr slots-tail
)))
1739 (let* ((field (car slots-tail
))
1740 (slot-offset (words-to-bytes (car field
)))
1741 (maybe-symbol-addr (- address slot-offset
))
1743 (sb!kernel
:make-lisp-obj
1744 (+ maybe-symbol-addr sb
!vm
:other-pointer-lowtag
))))
1745 (when (symbolp maybe-symbol
)
1746 (return (values maybe-symbol
(cdr field
))))))))
1748 (defvar *address-of-nil-object
* (sb!kernel
:get-lisp-obj-address nil
))
1750 ;;; Given a BYTE-OFFSET from NIL, try and figure out which slot of
1751 ;;; which symbol is being referred to. Of course we can just give up,
1752 ;;; so it's not a big deal... Return two values, the symbol and the
1753 ;;; access function.
1754 (defun grok-nil-indexed-symbol-slot-ref (byte-offset)
1755 (declare (type offset byte-offset
))
1756 (grok-symbol-slot-ref (+ *address-of-nil-object
* byte-offset
)))
1758 ;;; Return the Lisp object located BYTE-OFFSET from NIL.
1759 (defun get-nil-indexed-object (byte-offset)
1760 (declare (type offset byte-offset
))
1761 (sb!kernel
:make-lisp-obj
(+ *address-of-nil-object
* byte-offset
)))
1763 ;;; Return two values; the Lisp object located at BYTE-OFFSET in the
1764 ;;; constant area of the code-object in the current segment and T, or
1765 ;;; NIL and NIL if there is no code-object in the current segment.
1766 (defun get-code-constant (byte-offset dstate
)
1767 (declare (type offset byte-offset
)
1768 (type disassem-state dstate
))
1769 (let ((code (seg-code (dstate-segment dstate
))))
1772 (sb!kernel
:code-header-ref code
1774 sb
!vm
:other-pointer-lowtag
)
1775 (- sb
!vm
:word-shift
)))
1779 (defun get-code-constant-absolute (addr dstate
&optional width
)
1780 (declare (type address addr
))
1781 (declare (type disassem-state dstate
))
1782 (declare (ignore width
))
1783 (let ((code (seg-code (dstate-segment dstate
))))
1785 (return-from get-code-constant-absolute
(values nil nil
)))
1786 ;; This WITHOUT-GCING, while not technically broken, is extremely deceptive
1787 ;; because if it is really needed, then this function has a broken API.
1788 ;; Since ADDR comes in as absolute, CODE must not move between the caller's
1789 ;; computation and the comparison below. But we're already in WITHOUT-GCING
1790 ;; in MAP-SEGMENT-INSTRUCTIONS, so, who cares, I guess?
1791 (sb!sys
:without-gcing
1792 (let* ((n-header-bytes (* (sb!kernel
:get-header-data code
) sb
!vm
:n-word-bytes
))
1793 (header-addr (- (sb!kernel
:get-lisp-obj-address code
)
1794 sb
!vm
:other-pointer-lowtag
))
1795 (code-start (+ header-addr n-header-bytes
)))
1796 (cond ((< header-addr addr code-start
)
1797 (values (sb!sys
:sap-ref-lispobj
(sb!sys
:int-sap addr
) 0) t
))
1799 (values nil nil
)))))))
1801 (defvar *assembler-routines-by-addr
* nil
)
1803 (defvar *foreign-symbols-by-addr
* nil
)
1805 ;;; Build an address-name hash-table from the name-address hash
1806 (defun invert-address-hash (htable &optional
(addr-hash (make-hash-table)))
1807 (maphash (lambda (name address
)
1808 (setf (gethash address addr-hash
) name
))
1812 ;;; Return the name of the primitive Lisp assembler routine or foreign
1813 ;;; symbol located at ADDRESS, or NIL if there isn't one.
1814 (defun find-assembler-routine (address)
1815 (declare (type address address
))
1816 (when (null *assembler-routines-by-addr
*)
1817 (setf *assembler-routines-by-addr
*
1818 (invert-address-hash sb
!fasl
:*assembler-routines
*))
1820 (setf *assembler-routines-by-addr
*
1821 (invert-address-hash sb
!sys
:*static-foreign-symbols
*
1822 *assembler-routines-by-addr
*))
1823 (loop for static in sb
!vm
:*static-funs
*
1824 for address
= (+ sb
!vm
::nil-value
1825 (sb!vm
::static-fun-offset static
))
1827 (setf (gethash address
*assembler-routines-by-addr
*)
1829 ;; Not really a routine, but it uses the similar logic for annotations
1831 (setf (gethash sb
!vm
::gc-safepoint-page-addr
*assembler-routines-by-addr
*)
1833 (gethash address
*assembler-routines-by-addr
*))
1835 ;;;; some handy function for machine-dependent code to use...
1837 #!-sb-fluid
(declaim (maybe-inline sap-ref-int read-suffix
))
1839 (defun sap-ref-int (sap offset length byte-order
)
1840 (declare (type sb
!sys
:system-area-pointer sap
)
1841 (type (unsigned-byte 16) offset
)
1842 (type (member 1 2 4 8) length
)
1843 (type (member :little-endian
:big-endian
) byte-order
)
1844 (muffle-conditions compiler-note
) ; integer coercion, oh well
1845 (optimize (speed 3) (safety 0)))
1847 (1 (sb!sys
:sap-ref-8 sap offset
))
1848 (2 (if (eq byte-order
:big-endian
)
1849 (+ (ash (sb!sys
:sap-ref-8 sap offset
) 8)
1850 (sb!sys
:sap-ref-8 sap
(+ offset
1)))
1851 (+ (ash (sb!sys
:sap-ref-8 sap
(+ offset
1)) 8)
1852 (sb!sys
:sap-ref-8 sap offset
))))
1853 (4 (if (eq byte-order
:big-endian
)
1854 (+ (ash (sb!sys
:sap-ref-8 sap offset
) 24)
1855 (ash (sb!sys
:sap-ref-8 sap
(+ 1 offset
)) 16)
1856 (ash (sb!sys
:sap-ref-8 sap
(+ 2 offset
)) 8)
1857 (sb!sys
:sap-ref-8 sap
(+ 3 offset
)))
1858 (+ (sb!sys
:sap-ref-8 sap offset
)
1859 (ash (sb!sys
:sap-ref-8 sap
(+ 1 offset
)) 8)
1860 (ash (sb!sys
:sap-ref-8 sap
(+ 2 offset
)) 16)
1861 (ash (sb!sys
:sap-ref-8 sap
(+ 3 offset
)) 24))))
1862 (8 (if (eq byte-order
:big-endian
)
1863 (+ (ash (sb!sys
:sap-ref-8 sap offset
) 56)
1864 (ash (sb!sys
:sap-ref-8 sap
(+ 1 offset
)) 48)
1865 (ash (sb!sys
:sap-ref-8 sap
(+ 2 offset
)) 40)
1866 (ash (sb!sys
:sap-ref-8 sap
(+ 3 offset
)) 32)
1867 (ash (sb!sys
:sap-ref-8 sap
(+ 4 offset
)) 24)
1868 (ash (sb!sys
:sap-ref-8 sap
(+ 5 offset
)) 16)
1869 (ash (sb!sys
:sap-ref-8 sap
(+ 6 offset
)) 8)
1870 (sb!sys
:sap-ref-8 sap
(+ 7 offset
)))
1871 (+ (sb!sys
:sap-ref-8 sap offset
)
1872 (ash (sb!sys
:sap-ref-8 sap
(+ 1 offset
)) 8)
1873 (ash (sb!sys
:sap-ref-8 sap
(+ 2 offset
)) 16)
1874 (ash (sb!sys
:sap-ref-8 sap
(+ 3 offset
)) 24)
1875 (ash (sb!sys
:sap-ref-8 sap
(+ 4 offset
)) 32)
1876 (ash (sb!sys
:sap-ref-8 sap
(+ 5 offset
)) 40)
1877 (ash (sb!sys
:sap-ref-8 sap
(+ 6 offset
)) 48)
1878 (ash (sb!sys
:sap-ref-8 sap
(+ 7 offset
)) 56))))))
1880 (defun read-suffix (length dstate
)
1881 (declare (type (member 8 16 32 64) length
)
1882 (type disassem-state dstate
)
1883 (optimize (speed 3) (safety 0)))
1884 (let ((length (ecase length
(8 1) (16 2) (32 4) (64 8))))
1885 (declare (type (unsigned-byte 4) length
))
1887 (sap-ref-int (dstate-segment-sap dstate
)
1888 (dstate-next-offs dstate
)
1890 (dstate-byte-order dstate
))
1891 (incf (dstate-next-offs dstate
) length
))))
1893 ;;;; optional routines to make notes about code
1895 ;;; Store NOTE (which can be either a string or a function with a
1896 ;;; single stream argument) to be printed as an end-of-line comment
1897 ;;; after the current instruction is disassembled.
1898 (defun note (note dstate
)
1899 (declare (type (or string function
) note
)
1900 (type disassem-state dstate
))
1901 (push note
(dstate-notes dstate
)))
1903 (defun prin1-short (thing stream
)
1904 (with-print-restrictions
1905 (prin1 thing stream
)))
1907 (defun prin1-quoted-short (thing stream
)
1908 (if (self-evaluating-p thing
)
1909 (prin1-short thing stream
)
1910 (prin1-short `',thing stream
)))
1912 ;;; Store a note about the lisp constant located BYTE-OFFSET bytes
1913 ;;; from the current code-component, to be printed as an end-of-line
1914 ;;; comment after the current instruction is disassembled.
1915 (defun note-code-constant (byte-offset dstate
)
1916 (declare (type offset byte-offset
)
1917 (type disassem-state dstate
))
1918 (multiple-value-bind (const valid
)
1919 (get-code-constant byte-offset dstate
)
1921 (note (lambda (stream)
1922 (prin1-quoted-short const stream
))
1926 ;;; Store a note about the lisp constant located at ADDR in the
1927 ;;; current code-component, to be printed as an end-of-line comment
1928 ;;; after the current instruction is disassembled.
1929 (defun note-code-constant-absolute (addr dstate
&optional width
)
1930 (declare (type address addr
)
1931 (type disassem-state dstate
))
1932 (multiple-value-bind (const valid
)
1933 (get-code-constant-absolute addr dstate width
)
1935 (note (lambda (stream)
1936 (prin1-quoted-short const stream
))
1938 (values const valid
)))
1940 ;;; If the memory address located NIL-BYTE-OFFSET bytes from the
1941 ;;; constant NIL is a valid slot in a symbol, store a note describing
1942 ;;; which symbol and slot, to be printed as an end-of-line comment
1943 ;;; after the current instruction is disassembled. Returns non-NIL iff
1944 ;;; a note was recorded.
1945 (defun maybe-note-nil-indexed-symbol-slot-ref (nil-byte-offset dstate
)
1946 (declare (type offset nil-byte-offset
)
1947 (type disassem-state dstate
))
1948 (multiple-value-bind (symbol access-fun
)
1949 (grok-nil-indexed-symbol-slot-ref nil-byte-offset
)
1951 (note (lambda (stream)
1952 (prin1 (if (eq access-fun
'symbol-value
)
1954 `(,access-fun
',symbol
))
1959 ;;; If the memory address located NIL-BYTE-OFFSET bytes from the
1960 ;;; constant NIL is a valid lisp object, store a note describing which
1961 ;;; symbol and slot, to be printed as an end-of-line comment after the
1962 ;;; current instruction is disassembled. Returns non-NIL iff a note
1964 (defun maybe-note-nil-indexed-object (nil-byte-offset dstate
)
1965 (declare (type offset nil-byte-offset
)
1966 (type disassem-state dstate
))
1967 (let ((obj (get-nil-indexed-object nil-byte-offset
)))
1968 (note (lambda (stream)
1969 (prin1-quoted-short obj stream
))
1973 ;;; If ADDRESS is the address of a primitive assembler routine or
1974 ;;; foreign symbol, store a note describing which one, to be printed
1975 ;;; as an end-of-line comment after the current instruction is
1976 ;;; disassembled. Returns non-NIL iff a note was recorded. If
1977 ;;; NOTE-ADDRESS-P is non-NIL, a note of the address is also made.
1978 (defun maybe-note-assembler-routine (address note-address-p dstate
)
1979 (declare (type disassem-state dstate
))
1980 (unless (typep address
'address
)
1981 (return-from maybe-note-assembler-routine nil
))
1983 (find-assembler-routine address
)
1985 (sb!sys
:sap-foreign-symbol
(sb!sys
:int-sap address
)))))
1987 (note (lambda (stream)
1989 (format stream
"#x~8,'0x: ~a" address name
)
1990 (princ name stream
)))
1994 ;;; If there's a valid mapping from OFFSET in the storage class
1995 ;;; SC-NAME to a source variable, make a note of the source-variable
1996 ;;; name, to be printed as an end-of-line comment after the current
1997 ;;; instruction is disassembled. Returns non-NIL iff a note was
1999 (defun maybe-note-single-storage-ref (offset sc-name dstate
)
2000 (declare (type offset offset
)
2001 (type symbol sc-name
)
2002 (type disassem-state dstate
))
2003 (let ((storage-location
2004 (find-valid-storage-location offset sc-name dstate
)))
2005 (when storage-location
2006 (note (lambda (stream)
2007 (princ (sb!di
:debug-var-symbol
2008 (aref (storage-info-debug-vars
2009 (seg-storage-info (dstate-segment dstate
)))
2015 ;;; If there's a valid mapping from OFFSET in the storage-base called
2016 ;;; SB-NAME to a source variable, make a note equating ASSOC-WITH with
2017 ;;; the source-variable name, to be printed as an end-of-line comment
2018 ;;; after the current instruction is disassembled. Returns non-NIL iff
2019 ;;; a note was recorded.
2020 (defun maybe-note-associated-storage-ref (offset sb-name assoc-with dstate
)
2021 (declare (type offset offset
)
2022 (type symbol sb-name
)
2023 (type (or symbol string
) assoc-with
)
2024 (type disassem-state dstate
))
2025 (let ((storage-location
2026 (find-valid-storage-location offset sb-name dstate
)))
2027 (when storage-location
2028 (note (lambda (stream)
2029 (format stream
"~A = ~S"
2031 (sb!di
:debug-var-symbol
2032 (aref (dstate-debug-vars dstate
)
2033 storage-location
))))
2037 (defun maybe-note-static-symbol (offset dstate
)
2038 (dolist (symbol sb
!vm
:*static-symbols
*)
2039 (when (= (sb!kernel
:get-lisp-obj-address symbol
) offset
)
2040 (return (note (lambda (s) (prin1 symbol s
)) dstate
)))))
2042 (defun get-internal-error-name (errnum)
2043 (cdr (svref sb
!c
:+backend-internal-errors
+ errnum
)))
2045 (defun get-sc-name (sc-offs)
2046 (sb!c
:location-print-name
2047 ;; FIXME: This seems like an awful lot of computation just to get a name.
2048 ;; Couldn't we just use lookup in *BACKEND-SC-NAMES*, without having to cons
2050 (sb!c
:make-random-tn
:kind
:normal
2051 :sc
(svref sb
!c
:*backend-sc-numbers
*
2052 (sb!c
:sc-offset-scn sc-offs
))
2053 :offset
(sb!c
:sc-offset-offset sc-offs
))))
2055 ;;; When called from an error break instruction's :DISASSEM-CONTROL (or
2056 ;;; :DISASSEM-PRINTER) function, will correctly deal with printing the
2057 ;;; arguments to the break.
2059 ;;; ERROR-PARSE-FUN should be a function that accepts:
2060 ;;; 1) a SYSTEM-AREA-POINTER
2061 ;;; 2) a BYTE-OFFSET from the SAP to begin at
2062 ;;; 3) optionally, LENGTH-ONLY, which if non-NIL, means to only return
2063 ;;; the byte length of the arguments (to avoid unnecessary consing)
2064 ;;; It should read information from the SAP starting at BYTE-OFFSET, and
2065 ;;; return four values:
2066 ;;; 1) the error number
2067 ;;; 2) the total length, in bytes, of the information
2068 ;;; 3) a list of SC-OFFSETs of the locations of the error parameters
2069 ;;; 4) a list of the length (as read from the SAP), in bytes, of each
2070 ;;; of the return values.
2071 (defun handle-break-args (error-parse-fun stream dstate
)
2072 (declare (type function error-parse-fun
)
2073 (type (or null stream
) stream
)
2074 (type disassem-state dstate
))
2075 (multiple-value-bind (errnum adjust sc-offsets lengths
)
2076 (funcall error-parse-fun
2077 (dstate-segment-sap dstate
)
2078 (dstate-next-offs dstate
)
2081 (setf (dstate-cur-offs dstate
)
2082 (dstate-next-offs dstate
))
2083 (flet ((emit-err-arg ()
2084 (let ((num (pop lengths
)))
2085 (print-notes-and-newline stream dstate
)
2086 (print-current-address stream dstate
)
2087 (print-inst num stream dstate
)
2088 (print-bytes num stream dstate
)
2089 (incf (dstate-cur-offs dstate
) num
)))
2092 (note note dstate
))))
2094 ;; ARM64 encodes the error number in BRK instruction itself
2097 (emit-note (symbol-name (get-internal-error-name errnum
)))
2098 (dolist (sc-offs sc-offsets
)
2100 (if (= (sb!c
:sc-offset-scn sc-offs
)
2101 sb
!vm
:constant-sc-number
)
2102 (note-code-constant (* (1- (sb!c
:sc-offset-offset sc-offs
))
2105 (emit-note (get-sc-name sc-offs
))))))
2106 (incf (dstate-next-offs dstate
)
2109 ;; A prefilter set is a list of vectors specifying bytes to extract
2110 ;; and a function to call on the extracted value(s).
2111 ;; EQUALP lists of vectors can be coalesced, since they're immutable.
2112 (defun collect-prefiltering-args (args cache
)
2113 (awhen (remove-if-not #'arg-prefilter args
)
2115 (mapcar (lambda (arg &aux
(bytes (arg-fields arg
)))
2116 (coerce (list* (posq arg args
)
2118 (and bytes
(cons (arg-sign-extend-p arg
) bytes
)))
2121 (table (assq :prefilter cache
)))
2122 (or (find repr
(cdr table
) :test
'equalp
)
2123 (car (push repr
(cdr table
)))))))
2125 (defun unintern-init-only-stuff ()
2126 ;; Remove compile-time-only metadata. This preserves compatibility with the
2127 ;; older disassembler macros which wrapped GEN-ARG-TYPE-DEF-FORM and such
2128 ;; in (EVAL-WHEN (:COMPILE-TOPLEVEL :EXECUTE)), which in turn required that
2129 ;; all prefilters, labellers, and printers be defined at cross-compile-time.
2130 ;; A consequence of :LOAD-TOPLEVEL not being there was that was not possible
2131 ;; to add instruction definitions to an image without also recompiling
2132 ;; the backend's "insts" file. It also was not possible to incrementally
2133 ;; recompile and/or use slam.sh because of a bunch of mostly harmless bugs
2134 ;; in the function cache (a/k/a identical-code-folding) logic that was only
2135 ;; guaranteed to do the right thing from a clean compile. Additionally,
2136 ;; you had to use (GET-INST-SPACE :FORCE T) to pick up new definitions.
2137 ;; Given those considerations which made extending a running disassembler
2138 ;; nontrivial, the code-generating code is not so useful after the
2139 ;; initial instruction space is built, so it can all be removed.
2140 ;; But if you need all these macros to exist for some reason,
2141 ;; then define one of the two following features to keep them:
2142 #!+(or sb-fluid sb-retain-assembler-macros
)
2143 (return-from unintern-init-only-stuff
)
2145 (do-symbols (symbol sb
!assem
::*backend-instruction-set-package
*)
2146 (remf (symbol-plist symbol
) 'arg-type
)
2147 (remf (symbol-plist symbol
) 'inst-format
))
2149 ;; Get rid of functions that only make sense with metadata available.
2150 (dolist (s '(%def-arg-type %def-inst-format %gen-arg-forms
2151 all-arg-refs-relevant-p arg-or-lose arg-position arg-value-form
2152 collect-labelish-operands collect-prefiltering-args
2153 compare-fields-form compile-inst-printer compile-print
2154 compile-printer-body compile-printer-list compile-test
2155 correct-dchunk-bytespec-for-endianness
2156 define-arg-type define-instruction-format equal-mod-gensyms
2157 find-first-field-name find-printer-fun format-or-lose
2158 gen-arg-forms make-arg-temp-bindings make-funstate massage-arg
2159 maybe-listify modify-arg pd-error pick-printer-choice
2160 preprocess-chooses preprocess-conditionals preprocess-printer
2161 preprocess-test sharing-cons sharing-mapcar
2162 string-or-qsym-p strip-quote
))
2164 (unintern s
'sb-disassem
)))