-
Notifications
You must be signed in to change notification settings - Fork 0
/
disassemble.lisp
159 lines (144 loc) · 6.53 KB
/
disassemble.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
(in-package #:maclina.machine)
(defun dis-signed (x size)
(logior x (- (mask-field (byte 1 (1- size)) x))))
(defun bc-unsigned (bytecode ip nbytes)
;; Read NBYTES of little-endian integer.
(do* ((i 0 (1+ i))
(s 0 (+ 8 s))
(sum 0))
((= i nbytes) sum)
(incf sum (ash (aref bytecode (+ ip i)) s))))
(defun bc-signed (bytecode ip nbytes)
(dis-signed (bc-unsigned bytecode ip nbytes)
(* 8 nbytes)))
;;; Return the instruction description for OPCODE.
(defun decode-instr (opcode)
(let ((res (member opcode *full-codes* :key #'second)))
(if res (first res) (error "unknown bytecode opcode ~d" opcode))))
;;; Return a list of all IPs that are jumped to.
(defun gather-labels (bytecode ip end)
(let ((result ())
(longp ())
op)
(loop (setq op (decode-instr (aref bytecode ip)))
;; Go through the arguments, identifying any labels.
(let ((opip ip)) ; IP of the start of the instruction
(incf ip)
(dolist (argi (if longp (fourth op) (third op)))
(let ((nbytes (logandc2 argi +mask-arg+)))
(if (label-arg-p argi)
(cl:push (+ opip (bc-signed bytecode ip nbytes)) result))
(incf ip nbytes))))
;; If this is a LONG, set that for the next instruction.
;; (KLUDGE)
;; Otherwise reset longp to false.
(setq longp (cl:eq (first op) 'long))
(if (>= ip end) (cl:return (sort result #'<))))))
(defun disassemble-instruction (bytecode ip &key (labels () labelsp))
(let ((desc (decode-instr (aref bytecode ip)))
(longp cl:nil) (opip ip))
(when (cl:eq (first desc) 'long)
(setf longp t desc (decode-instr (aref bytecode (incf opip)))))
(setf ip (1+ opip))
(loop for argi in (if longp (fourth desc) (third desc))
for nbytes = (logandc2 argi +mask-arg+)
collect (cond ((constant-arg-p argi)
(list :constant
(bc-unsigned bytecode ip nbytes)))
((label-arg-p argi)
(let* ((lip (+ opip (bc-signed bytecode ip nbytes)))
(lpos (position lip labels)))
(cond (labelsp
(assert lpos)
(list :label lpos))
(t (list :label lip)))))
((keys-arg-p argi)
(list :keys
(bc-unsigned bytecode ip nbytes)))
(t
(list :operand
(bc-unsigned bytecode ip nbytes))))
into args
do (incf ip nbytes)
finally (cl:return (values (list* (first desc) longp args) ip)))))
(defun %display-instruction (name longp args textify-operand)
(if (string= name "PARSE-KEY-ARGS")
;; We special case this despite the keys-arg thing because it's
;; just pretty weird all around.
(let* ((more-start (second (first args)))
(kci (second (second args)))
(aokp (logbitp 0 kci))
(key-count (ash kci -1))
(keys (third args))
(framestart (second (fourth args))))
;; Print
(format t "~& ~:[~;long ~]~(~a~)~:[~;-aok~] ~d ~d ~a ~d"
longp name aokp more-start key-count
(funcall textify-operand keys key-count) framestart))
;; Normal case
(format t "~& ~:[~;long ~]~(~a~)~{ ~a~}"
longp name (mapcar textify-operand args))))
(defun operand-textifier (literals)
(flet ((textify-operand (thing &optional key-count)
(destructuring-bind (kind value) thing
(cond ((cl:eq kind :constant)
(format () "'~s" (aref literals value)))
((cl:eq kind :label) (format () "L~a" value))
((cl:eq kind :operand) (format () "~d" value))
((cl:eq kind :keys)
(let ((keys cl:nil) (keystart value))
(do ((i 0 (1+ i)))
((= i key-count) (setq keys (nreverse keys)))
(cl:push (aref literals (+ keystart i)) keys))
(format () "'~s" keys)))
(t (error "Illegal kind ~a" kind))))))
#'textify-operand))
;;; Used externally by tracers.
(defun display-instruction (bytecode literals ip)
(destructuring-bind (name longp . args)
(disassemble-instruction bytecode ip)
(%display-instruction name longp args (operand-textifier literals))))
(defun %disassemble-bytecode (bytecode start end)
(let* ((labels (gather-labels bytecode start end))
(ip start))
(loop ;; If this is a label position, mark that.
for labelpos = (position ip labels)
when labelpos
collect (write-to-string labelpos)
;; Decode.
collect (multiple-value-bind (inst new-ip)
(disassemble-instruction bytecode ip :labels labels)
(setf ip new-ip)
inst)
until (>= ip end))))
(defun disassemble-bytecode (bytecode literals
&key (start 0) (end (length bytecode)))
(let ((dis (%disassemble-bytecode bytecode start end))
(textify-operand (operand-textifier literals)))
(format t "~&---module---~%")
(dolist (item dis)
(cond
((consp item)
;; instruction
(destructuring-bind (name longp . args) item
(%display-instruction name longp args textify-operand)))
((or (stringp item) (symbolp item))
;; label
(format t "~&L~a:~%" item))
(t (error "Illegal item ~a" item)))))
(values))
(defgeneric disassemble (object))
(defmethod disassemble ((object bytecode-module))
(disassemble-bytecode (bytecode-module-bytecode object)
(bytecode-module-literals object)))
;; TODO: Record function boundaries, so that among other things we can
;; disassemble only the region for the function being disassembled.
(defmethod disassemble ((object bytecode-function))
(let ((module (bytecode-function-module object))
(entry-pc (bytecode-function-entry-pc object)))
(disassemble-bytecode (bytecode-module-bytecode module)
(bytecode-module-literals module)
:start entry-pc
:end (+ entry-pc (bytecode-function-size object)))))
(defmethod disassemble ((object bytecode-closure))
(disassemble (bytecode-closure-template object)))