summaryrefslogtreecommitdiff
path: root/tools-for-build
diff options
context:
space:
mode:
authorDouglas Katzman <dougk@google.com>2024-07-03 11:15:01 -0400
committerDouglas Katzman <dougk@google.com>2024-07-03 11:22:51 -0400
commite69a2fdd2d2647f67a3f1bc80d9b2b591ddea76f (patch)
tree18aef902d97ad6024706e3a85b4a3aaf45e5e57f /tools-for-build
parenta87b8826c0904b719d9ca555adfc6dba786093c2 (diff)
x86-64: Implement lisp linkage-space as per doc/internals-notes
This rectifies many awkward aspects of the immobile space fdefns which it replaces. All other architectures should be unaffected by the patch. Tested with with +/- {immobile-space, sb-thread, mark-region} on linux, and the default config on windows and macOS.
Diffstat (limited to 'tools-for-build')
-rw-r--r--tools-for-build/editcore.lisp71
-rw-r--r--tools-for-build/elftool.lisp586
2 files changed, 291 insertions, 366 deletions
diff --git a/tools-for-build/editcore.lisp b/tools-for-build/editcore.lisp
index 6be0cafbe..9b729fe45 100644
--- a/tools-for-build/editcore.lisp
+++ b/tools-for-build/editcore.lisp
@@ -48,6 +48,8 @@
(declaim (muffle-conditions compiler-note))
(eval-when (:compile-toplevel :execute)
+ (when (member :linkage-space sb-impl:+internal-features+)
+ (pushnew :linkage-space *features*))
(when (member :immobile-space sb-impl:+internal-features+)
(pushnew :immobile-space *features*)))
(eval-when (:execute)
@@ -858,10 +860,12 @@
(loop for i from 2 below (code-header-words obj)
do (visit (code-header-ref obj i))))
((symbolp obj)
+ #+linkage-space (visit (sap-ref-lispobj sap (ash symbol-fdefn-slot word-shift)))
(visit (sap-ref-lispobj sap (ash symbol-value-slot word-shift))))
((weak-pointer-p obj)
(visit (sap-ref-lispobj sap (ash weak-pointer-value-slot word-shift))))
((fdefn-p obj)
+ #-linkage-space
(let ((raw (sap-ref-word sap (ash fdefn-raw-addr-slot word-shift))))
(unless (in-bounds-p raw (space-bounds static-core-space-id spacemap))
(awhen (remap (%make-lisp-obj (+ raw (ash -2 word-shift) fun-pointer-lowtag)))
@@ -1007,7 +1011,7 @@
(aver (= (%vector-raw-bits core-header offset) directory-core-entry-type-code))
(let ((nwords (+ (* (length directory) 5) 2)))
(setf (%vector-raw-bits core-header (incf offset)) nwords))
- (let ((page-count 0)
+ (let ((page-count (linkage-space-npages (core-header-linkage-space-info parsed-header)))
(n-ptes (length (space-page-table dynamic-space))))
(dolist (dir-entry directory)
(setf (car dir-entry) page-count)
@@ -1031,6 +1035,21 @@
end-core-entry-type-code 2))
(setf (%vector-raw-bits core-header (incf offset)) word))
(write-sequence core-header output)
+ #+linkage-space
+ (binding* ((linkage-info (core-header-linkage-space-info parsed-header))
+ (count (linkage-space-count linkage-info))
+ (nbytes (ash count word-shift))
+ ((npages remainder) (ceiling nbytes +backend-page-bytes+))
+ (words (linkage-space-cells linkage-info))
+ (pad-bytes (- remainder))
+ (padding (make-array pad-bytes
+ :element-type '(unsigned-byte 8)
+ :initial-element 0)))
+ (assert (= npages (linkage-space-npages linkage-info)))
+ (assert (zerop (rem (+ nbytes pad-bytes) +backend-page-bytes+)))
+ (with-pinned-objects (words padding)
+ (assert (= (sb-unix:unix-write fd (vector-sap words) 0 nbytes)))
+ (assert (= (sb-unix:unix-write fd (vector-sap padding) 0 (length padding))))))
;; write out the data from each space
(dolist (dir-entry directory)
(destructuring-bind (page id paddr vaddr nwords) dir-entry
@@ -1098,6 +1117,22 @@
#+64-bit simple-array-unsigned-byte-64-widetag
#-64-bit simple-array-unsigned-byte-32-widetag)
+(defun adjust-linkage-space (spacemap parsed-header fwdmap)
+ (let ((dspace-bounds (space-bounds dynamic-core-space-id spacemap))
+ (cells (linkage-space-cells
+ (core-header-linkage-space-info parsed-header))))
+ (dotimes (i (length cells))
+ (let ((entrypoint (aref cells i)))
+ (when (and (in-bounds-p entrypoint dspace-bounds)
+ (= (sap-ref-8 (int-sap (translate-ptr entrypoint spacemap))
+ (ash -2 word-shift))
+ simple-fun-widetag))
+ (let* ((lispobj (%make-lisp-obj (+ entrypoint (ash -2 word-shift) fun-pointer-lowtag)))
+ (new (remap-to-quasi-static-code lispobj spacemap fwdmap)))
+ (setf (aref cells i) (+ (get-lisp-obj-address new)
+ (- fun-pointer-lowtag)
+ (ash 2 word-shift)))))))))
+
(defun move-dynamic-code-to-text-space (input-pathname output-pathname)
;; Remove old files
(ignore-errors (delete-file output-pathname))
@@ -1182,6 +1217,7 @@
(fixnumize c-linkage-reserved-words))
;; Transport code contiguously into new space
(transport-dynamic-space-code codeblobs spacemap new-space reserved-amount)
+ #+linkage-space (adjust-linkage-space spacemap parsed-header fwdmap)
;; Walk spaces except for newspace, changing any pointers that
;; should point to new space.
(dolist (space-id `(,dynamic-core-space-id ,static-core-space-id
@@ -1234,9 +1270,7 @@
;;; $ run-sbcl.sh
;;; * (load "tools-for-build/editcore")
;;; * (sb-editcore:move-dynamic-code-to-text-space "step1.core" "step2.core")
-;;; * (sb-editcore:redirect:text-space-calls "step2.core")
-;;; Now "step2.core" has a text space, and all lisp-to-lisp calls bypass their FDEFN.
-;;; At this point split-core on "step2.core" can run in the manner of elfcore.test.sh
+;;; Now "step2.core" has a text space and you can run split-core on it
(defun get-code-segments (code vaddr core)
(let ((di (%code-debug-info code))
@@ -1344,12 +1378,11 @@
found)))
(defun persist-to-file (spacemap core-offset stream)
- (aver (zerop core-offset))
(dolist (space-id `(,static-core-space-id
,immobile-text-core-space-id
,dynamic-core-space-id))
(let ((space (get-space space-id spacemap)))
- (file-position stream (* (1+ (space-data-page space)) +backend-page-bytes+))
+ (file-position stream (+ (* (1+ (space-data-page space)) +backend-page-bytes+) core-offset))
(sb-unix:unix-write (sb-impl::fd-stream-fd stream)
(space-physaddr space spacemap)
0
@@ -1473,6 +1506,7 @@
(t
(let ((first 1) (last (ash (size-of sap) (- word-shift))))
(case widetag
+ #-linkage-space
(#.fdefn-widetag ; wordindex 3 is an untagged simple-fun entry address
(let ((bits (load-bits-wordindexed sap fdefn-raw-addr-slot)))
(unless (or (eq bits 0)
@@ -1694,7 +1728,7 @@
;;; Gather all the objects in the order we want to reallocate them in.
;;; This relies on MAPHASH in SBCL iterating in insertion order.
-(defun visit-everything (spacemap initfun
+(defun visit-everything (spacemap initfun linkage-info
&optional print
&aux (seen (make-visited-table))
(defer-debug-info
@@ -1725,6 +1759,13 @@
(when print (format t "~&Popped ~x~%" descriptor))
(trace-obj #'visit descriptor spacemap)))))
(root (make-descriptor initfun))
+ (dotimes (i (linkage-space-count linkage-info))
+ (let ((word (aref (linkage-space-cells linkage-info) i)))
+ (unless (= word 0)
+ (let ((header (sap-ref-word (int-sap (translate-ptr word spacemap))
+ (ash -2 word-shift))))
+ (when (= (logand header widetag-mask) funcallable-instance-widetag)
+ (root (fun-entry->descriptor word)))))))
(trace-symbol #'visit (compute-nil-symbol-sap spacemap))
(call-with-each-static-object #'root spacemap)
(transitive-closure)
@@ -1886,7 +1927,7 @@
(sap+ (int-sap new-vaddr) (ash 2 word-shift))))))
new-vaddr))
-(defun fixup-compacted (old-spacemap new-spacemap seen &optional print)
+(defun fixup-compacted (linkage-cells old-spacemap new-spacemap seen &optional print)
(labels
((visit (sap slot value widetag)
(unless (descriptor-p value) (return-from visit))
@@ -1896,7 +1937,7 @@
(case (logand most-positive-word (logior (ash slot 8) widetag))
((#.instance-widetag #.funcallable-instance-widetag)
(set-layout sap widetag newspace-ptr))
- ((#.(logior (ash fdefn-raw-addr-slot 8) fdefn-widetag)
+ ((#-linkage-space #.(logior (ash fdefn-raw-addr-slot 8) fdefn-widetag)
#.(logior (ash closure-fun-slot 8) closure-widetag))
(setf (sap-ref-word sap (ash slot word-shift))
(+ newspace-ptr (- (ash simple-fun-insts-offset word-shift)
@@ -1924,6 +1965,13 @@
(call-with-each-static-object
(lambda (descriptor) (trace-obj #'visit descriptor old-spacemap))
old-spacemap)
+ (dotimes (i (length linkage-cells))
+ (let ((val (aref linkage-cells i)))
+ (unless (= val 0)
+ (let* ((function (fun-entry->descriptor val))
+ (new-function (forward function))
+ (diff (- new-function (descriptor-bits function))))
+ (incf (aref linkage-cells i) diff)))))
(when print (format t "~&Fixing dynamic space~%"))
(dohash ((old-taggedptr new-taggedptr) seen)
(declare (ignorable old-taggedptr))
@@ -1950,6 +1998,7 @@
(let* ((spacemap (cons sap (sort (copy-list space-list) #'> :key #'space-addr)))
(seen (visit-everything spacemap
(core-header-initfun parsed-header)
+ (core-header-linkage-space-info parsed-header)
print))
(oldspace (get-space dynamic-core-space-id spacemap)))
;;(summarize-object-counts spacemap seen)
@@ -1984,7 +2033,9 @@
;; pass 2: visit every object again, fixing pointers
;; Start by removing objects from SEEN that were not forwarded
(maphash (lambda (key value) (if (eq value t) (remhash key seen))) seen)
- (fixup-compacted spacemap new-spacemap seen)
+ (fixup-compacted (linkage-space-cells
+ (core-header-linkage-space-info parsed-header))
+ spacemap new-spacemap seen)
(setf (core-header-initfun parsed-header)
(gethash (core-header-initfun parsed-header) seen))
(flet ((n (spaces)
diff --git a/tools-for-build/elftool.lisp b/tools-for-build/elftool.lisp
index 87a3360e4..b15bf1b62 100644
--- a/tools-for-build/elftool.lisp
+++ b/tools-for-build/elftool.lisp
@@ -138,112 +138,69 @@
;;; Disassemble the function pointed to by SAP for LENGTH bytes, returning
;;; all instructions that should be emitted using assembly language
-;;; instead of .quad and/or .byte directives.
-;;; This includes (at least) two categories of instructions:
-;;; - function prologue instructions that setup the call frame
-;;; - jmp/call instructions that transfer control to the fixedoj space
-;;; delimited by bounds in STATE.
+;;; instead of .quad and/or .byte directives including:
+;;; - instructions that manipulate the call frame
+;;; - lisp-linkage table JMP, CALL, or LEA
+;;; - alien-linkage table CALL or LEA
;;; At execution time the function will have virtual address LOAD-ADDR.
#+x86-64
(defun list-textual-instructions (sap length core load-addr emit-cfi)
- (let ((dstate (core-dstate core))
- (seg (core-seg core))
- (next-fixup-addr
- (or (car (core-fixup-addrs core)) most-positive-word))
- (list)
- (inst-call (load-time-value (find-inst #b11101000 (get-inst-space))))
- (inst-jmp (load-time-value (find-inst #b11101001 (get-inst-space))))
- (inst-jmpz (load-time-value (find-inst #x840f (get-inst-space))))
- (inst-pop (load-time-value (find-inst #x5d (get-inst-space))))
- (inst-mov (load-time-value (find-inst #x8B (get-inst-space))))
- (inst-lea (load-time-value (find-inst #x8D (get-inst-space)))))
- (setf (seg-virtual-location seg) load-addr
- (seg-length seg) length
- (seg-sap-maker seg) (lambda () sap))
- ;; KLUDGE: "8f 45 08" is the standard prologue
- (when (and emit-cfi (= (logand (sap-ref-32 sap 0) #xFFFFFF) #x08458f))
- (push (list* 0 3 "pop" "8(%rbp)") list))
- (map-segment-instructions
- (lambda (dchunk inst)
- (cond
- ((< next-fixup-addr (dstate-next-addr dstate))
- (let ((operand (sap-ref-32 sap (- next-fixup-addr load-addr)))
- (offs (dstate-cur-offs dstate)))
- (when (in-bounds-p operand (core-code-bounds core))
- (cond
- ((and (eq (inst-name inst) 'mov) ; match "mov eax, imm32"
- (eql (sap-ref-8 sap offs) #xB8))
- (let ((text (format nil "mov $(CS+0x~x),%eax"
- (- operand (bounds-low (core-code-bounds core))))))
- (push (list* (dstate-cur-offs dstate) 5 "mov" text) list)))
- ((and (eq (inst-name inst) 'mov) ; match "mov qword ptr [R+disp8], imm32"
- (member (sap-ref-8 sap (1- offs)) '(#x48 #x49)) ; REX.w and maybe REX.b
- (eql (sap-ref-8 sap offs) #xC7)
- ;; modRegRm = #b01 #b000 #b___
- (eql (logand (sap-ref-8 sap (1+ offs)) #o370) #o100))
- (let* ((reg (ldb (byte 3 0) (sap-ref-8 sap (1+ offs))))
- (text (format nil "movq $(CS+0x~x),~d(%~a)"
- (- operand (bounds-low (core-code-bounds core)))
- (signed-sap-ref-8 sap (+ offs 2))
- (reg-name (get-gpr :qword reg)))))
- (push (list* (1- (dstate-cur-offs dstate)) 8 "mov" text) list)))
- ((let ((bytes (ldb (byte 24 0) (sap-ref-32 sap offs))))
- (or (and (eq (inst-name inst) 'call) ; match "{call,jmp} qword ptr [addr]"
- (eql bytes #x2514FF)) ; ModRM+SIB encodes disp32, no base, no index
- (and (eq (inst-name inst) 'jmp)
- (eql bytes #x2524FF))))
- (let ((new-opcode (ecase (sap-ref-8 sap (1+ offs))
- (#x14 "call *")
- (#x24 "jmp *"))))
- ;; This instruction form is employed for asm routines when
- ;; compile-to-memory-space is :AUTO. If the code were to be loaded
- ;; into dynamic space, the offset to the called routine isn't
- ;; a (signed-byte 32), so we need the indirection.
- (push (list* (dstate-cur-offs dstate) 7 new-opcode operand) list)))
- (t
- (bug "Can't reverse-engineer fixup: ~s ~x"
- (inst-name inst) (sap-ref-64 sap offs))))))
- (pop (core-fixup-addrs core))
- (setq next-fixup-addr (or (car (core-fixup-addrs core)) most-positive-word)))
- ((or (eq inst inst-jmp) (eq inst inst-call))
- (let ((target-addr (+ (near-jump-displacement dchunk dstate)
- (dstate-next-addr dstate))))
- (when (or (in-bounds-p target-addr (core-fixedobj-bounds core))
- (in-bounds-p target-addr (core-linkage-bounds core)))
- (push (list* (dstate-cur-offs dstate)
- 5 ; length
- (if (eq inst inst-call) "call" "jmp")
- target-addr)
- list))))
- ((eq inst inst-jmpz)
- (let ((target-addr (+ (near-cond-jump-displacement dchunk dstate)
- (dstate-next-addr dstate))))
- (when (in-bounds-p target-addr (core-linkage-bounds core))
- (push (list* (dstate-cur-offs dstate) 6 "je" target-addr)
- list))))
- ((and (or (and (eq inst inst-mov)
- (eql (sap-ref-8 sap (dstate-cur-offs dstate)) #x8B))
- (eq inst inst-lea))
- (let ((modrm (sap-ref-8 sap (1+ (dstate-cur-offs dstate)))))
- (= (logand modrm #b11000111) #b00000101)) ; RIP-relative mode
- (in-bounds-p (+ (signed-sap-ref-32 sap (+ (dstate-cur-offs dstate) 2))
- (dstate-next-addr dstate))
- (core-linkage-bounds core)))
- (let* ((abs-addr (+ (signed-sap-ref-32 sap (+ (dstate-cur-offs dstate) 2))
- (dstate-next-addr dstate)))
- (reg (logior (ldb (byte 3 3) (sap-ref-8 sap (1+ (dstate-cur-offs dstate))))
- (if (logtest (sb-disassem::dstate-inst-properties dstate)
- #b0100) ; REX.r
- 8 0)))
- (op (if (eq inst inst-lea) "lea" "mov-gotpcrel"))
- (args (list abs-addr (reg-name (get-gpr :qword reg)))))
- (push (list* (1- (dstate-cur-offs dstate)) 7 op args) list)))
- ((and (eq inst inst-pop) (eq (logand dchunk #xFF) #x5D))
- (push (list* (dstate-cur-offs dstate) 1 "pop" "%rbp") list))))
- seg
- dstate
- nil)
- (nreverse list)))
+ (setf (core-fixup-addrs core) nil) ; don't need these
+ (let ((insts (simple-collect-inst-model sap length load-addr))
+ (alien-linkage-end
+ (+ (bounds-low (core-linkage-bounds core)) alien-linkage-space-size))
+ (result))
+ (flet ((pc-relative-ea-p (x)
+ (when (consp x) (setq x (car x)))
+ (and (typep x 'machine-ea) (eq (machine-ea-base x) :rip)))
+ (ea-disp-of (x)
+ (when (consp x) (setq x (car x)))
+ (machine-ea-disp x)))
+ (when (and emit-cfi
+ (equalp (car insts) '(0 pop (#s(machine-ea :disp 8 :base 5) . :qword))))
+ (push (list* 0 3 "pop" "8(%rbp)") result))
+ (do ((insts insts (cdr insts)))
+ ((endp insts))
+ (let* ((inst (car insts))
+ (inst-len (if (cdr insts) (- (caadr insts) (car inst))))
+ (ea (car (last inst))))
+ (when (or (member (cadr inst) '(sb-x86-64-asm::jmp sb-x86-64-asm::call))
+ (pc-relative-ea-p ea))
+ (cond ((and (integerp ea) (in-bounds-p ea (core-linkage-bounds core)))
+ (aver (eq (cadr inst) 'sb-x86-64-asm::call))
+ (aver (< ea alien-linkage-end)) ; CALL via alien linkage
+ (aver (= inst-len 5))
+ ;; Note: This defers to the ASM writer inference of which alien symbol
+ ;; is referenced which in retrospect is a bad approach.
+ (push (list* (car inst) 5 "call" ea) result))
+ ((pc-relative-ea-p ea)
+ (let* ((next-pc (+ load-addr (caadr insts)))
+ (ea (+ next-pc (ea-disp-of ea)))
+ (table-offset (- ea alien-linkage-end)))
+ (when (in-bounds-p ea (core-linkage-bounds core))
+ (cond ((< ea alien-linkage-end) ; alien linkage
+ (aver (= inst-len 7))
+ (let ((op (ecase (cadr inst)
+ (sb-x86-64-asm::mov "mov-gotpcrel")
+ (sb-x86-64-asm::lea "lea")))
+ (args (list ea (string-downcase (princ-to-string (third inst))))))
+ ;; same Note as above pertains here
+ (push (list* (car inst) 7 op args) result)))
+ ((eq (cadr inst) 'sb-x86-64-asm::lea)
+ ;; Get ADDRESS of lisp linkage cell in stepping-enabled code
+ (aver (eq (third inst) (get-gpr :qword 0))) ; %rax
+ (aver (= inst-len 7))
+ (push (list* (car inst) 7 "lea" (format nil "(fntbl+~d)(%rip),%rax"
+ table-offset))
+ result))
+ (t ; lisp CALL or JMP
+ (aver (= inst-len 6))
+ (let ((new-inst
+ (format nil "~a *(fntbl+~d)(%rip)"
+ (string-downcase (cadr inst))
+ table-offset)))
+ (push (list* (car inst) 6 "lispcall" new-inst) result))))))))))))
+ (nreverse result)))
;;; Using assembler directives and/or real mnemonics, dump COUNT bytes
;;; of memory at PADDR (physical addr) to STREAM.
@@ -374,13 +331,16 @@
c-symbol))
(t (error "unreachable")))))
#+x86-64
- ((string= opcode "lea") ; lea becomes "mov" with gotpcrel as src, which becomes lea
- (let* ((entry-index
- (/ (- (car operand) (bounds-low (core-linkage-bounds core)))
- (core-alien-linkage-entry-size core)))
- (c-symbol (aref (core-alien-linkage-symbols core) entry-index)))
- (setf (bit (core-alien-linkage-symbol-usedp core) entry-index) 1)
- (format stream " mov ~A@GOTPCREL(%rip), %~(~A~)~%" c-symbol (cadr operand))))
+ ((string= opcode "lea")
+ (if (stringp operand)
+ (format stream " lea ~A~%" operand) ; the "operand" is the whole instructon
+ (let* ((entry-index
+ (/ (- (car operand) (bounds-low (core-linkage-bounds core)))
+ (core-alien-linkage-entry-size core)))
+ (c-symbol (aref (core-alien-linkage-symbols core) entry-index)))
+ (setf (bit (core-alien-linkage-symbol-usedp core) entry-index) 1)
+ ;; lea becomes "mov" with gotpcrel as src, which will become lea
+ (format stream " mov ~A@GOTPCREL(%rip), %~(~A~)~%" c-symbol (cadr operand)))))
#+x86-64
((string= opcode "pop")
(format stream " ~A ~A~%" opcode operand)
@@ -391,7 +351,7 @@
nil)
(t)))
#+x86-64
- ((string= opcode "mov")
+ ((or (string= opcode "mov") (string= opcode "lispcall"))
;; the so-called "operand" is the entire instruction
(write-string operand stream)
(terpri stream))
@@ -471,13 +431,11 @@
c-name start (- end start))))))
(defun emit-funs (code vaddr core dumpwords output base-symbol emit-cfi)
- (let* ((spacemap (core-spacemap core))
- (ranges (get-text-ranges code core))
+ (let* ((ranges (get-text-ranges code core))
(text-sap (code-instructions code))
(text (sap-int text-sap))
;; Like CODE-INSTRUCTIONS, but where the text virtually was
(text-vaddr (+ vaddr (* (code-header-words code) n-word-bytes)))
- (additional-relative-fixups)
(max-end 0))
;; There is *always* at least 1 word of unboxed data now
(aver (eq (caar ranges) :data))
@@ -515,8 +473,7 @@
(let ((new-relative-fixups
(emit-lisp-function (+ text start) (+ text-vaddr start) (- end start)
output emit-cfi core)))
- (setq additional-relative-fixups
- (nconc new-relative-fixups additional-relative-fixups)))
+ (aver (null new-relative-fixups)))
(cond ((not ranges) (return))
((eq (caar ranges) :pad)
(format output " .byte ~{0x~x~^,~}~%"
@@ -533,25 +490,7 @@
below (- (code-object-size code)
(* (code-header-words code) n-word-bytes))
collect (sap-ref-8 text-sap i)))
- (when additional-relative-fixups
- (binding* ((existing-fixups (sb-vm::%code-fixups code))
- ((absolute relative immediate)
- (sb-c::unpack-code-fixup-locs
- (if (fixnump existing-fixups)
- existing-fixups
- (translate existing-fixups spacemap))))
- (new-sorted
- (sort (mapcar (lambda (x)
- ;; compute offset of the fixup from CODE-INSTRUCTIONS.
- ;; X is the location of the CALL instruction,
- ;; 1+ is the location of the fixup.
- (- (1+ x)
- (+ vaddr (ash (code-header-words code)
- word-shift))))
- additional-relative-fixups)
- #'<)))
- (sb-c:pack-code-fixup-locs
- absolute (merge 'list relative new-sorted #'<) immediate)))))
+ nil))
(defconstant +gf-name-slot+ 5)
@@ -567,7 +506,12 @@
(defconstant core-align #+x86-64 4096 #+arm64 65536)
-(defun write-preamble (output)
+(defun write-preamble (output n-lisp-linkage-words)
+ #+linkage-space (format output " .local fntbl
+ .comm fntbl,~D,8
+ .globl lisp_function_linkage_table
+ .equiv lisp_function_linkage_table, fntbl
+" (* n-lisp-linkage-words n-word-bytes))
(format output " .text~% .file \"sbcl.core\"
~:[~; .macro .size sym size # ignore
.endm
@@ -590,8 +534,8 @@
core-align
label-prefix))
-(defun output-lisp-asm-routines (core spacemap code-addr output &aux (skip 0))
- (write-preamble output)
+(defun output-lisp-asm-routines (core spacemap code-addr n-lisp-linkage-words output &aux (skip 0))
+ (write-preamble output n-lisp-linkage-words)
(dotimes (i 2)
(let* ((paddr (int-sap (translate-ptr code-addr spacemap)))
(word (sap-ref-word paddr 0)))
@@ -737,7 +681,9 @@
(- word (bounds-low code-bounds))))))))
(emit-asm-directives :qword sap count stream exceptions)))
- (let ((skip (output-lisp-asm-routines core spacemap code-addr output)))
+ (let ((skip (output-lisp-asm-routines core spacemap code-addr
+ (linkage-space-count linkage-space-info)
+ output)))
(incf code-addr skip)
(incf total-code-size skip))
(loop
@@ -776,24 +722,8 @@
(let* ((base (emit-symbols (code-symbols code core) core pp-state output))
(altered-fixups
(emit-funs code code-addr core #'dumpwords temp-output base emit-cfi))
- (header-exceptions (vector nil nil nil nil))
- (fixups-ptr))
- (when altered-fixups
- (setf (aref header-exceptions sb-vm:code-fixups-slot)
- (cond ((fixnump altered-fixups)
- (format nil "0x~x" (ash altered-fixups n-fixnum-tag-bits)))
- (t
- (let ((ht (core-new-fixups core)))
- (setq fixups-ptr (gethash altered-fixups ht))
- (unless fixups-ptr
- (setq fixups-ptr (ash (core-new-fixup-words-used core)
- word-shift))
- (setf (gethash altered-fixups ht) fixups-ptr)
- (incf (core-new-fixup-words-used core)
- (align-up (1+ (sb-bignum:%bignum-length altered-fixups)) 2))))
- ;; tag the pointer properly for a bignum
- (format nil "lisp_fixups+0x~x"
- (logior fixups-ptr other-pointer-lowtag))))))
+ (header-exceptions (vector nil nil nil nil)))
+ (aver (null altered-fixups))
(dumpwords (int-sap code-physaddr)
(code-header-words code) output header-exceptions code-addr)
(write-string (get-output-stream-string temp-output) output))))
@@ -999,7 +929,7 @@
; symbol table -- ^ ^ -- for which section
(:note ".note.GNU-stack" ,+sht-progbits+ 0 0 0 1 0)))
(extern-c-symbols
- '("lisp_code_start"))
+ '("lisp_code_start" #+linkage-space "lisp_function_linkage_table"))
(string-table
(string-table (append extern-c-symbols (map 'list #'second sections))))
(strings (cdr string-table))
@@ -1120,16 +1050,6 @@
;; #(kind, where symbol-index addend)
`#(,R_ABS64 ,(+ core-header-size core-offs) 1 ,(- referent code-start))
fixups)))
- (abs32-fixup (core-offs referent)
- (aver (not pie))
- (incf n-abs)
- (when print
- (format t "~x = 0x~(~x~): (a)~%" core-offs (core-to-logical core-offs) #+nil referent))
- (touch-core-page core-offs)
- (setf (sap-ref-32 (car spacemap) core-offs) 0)
- (vector-push-extend `(,(+ core-header-size core-offs)
- ,(- referent code-start) . ,R_ABS32)
- fixups))
(touch-core-page (core-offs)
;; use the OS page size, not +backend-page-bytes+
(setf (gethash (floor core-offs core-align) affected-pages) t))
@@ -1222,11 +1142,6 @@
;; mixed boxed/unboxed objects
(#.code-header-widetag
(aver (not pie))
- (dolist (loc (code-fixup-locs obj spacemap))
- (let ((val (sap-ref-32 (code-instructions obj) loc)))
- (when (in-bounds-p val code-bounds)
- (abs32-fixup (sap- (sap+ (code-instructions obj) loc) (car spacemap))
- val))))
(dotimes (i (code-n-entries obj))
;; I'm being lazy and not computing vaddr, which is wrong,
;; but does not matter if non-pie; and if PIE, we can't get here.
@@ -1272,7 +1187,7 @@
;;; The ".core" file is a native core file used for starting a binary that
;;; contains the asm code using the "--core" argument. The "-core.o" file
;;; is for linking in to a binary that needs no "--core" argument.
-(defun split-core
+(defun really-split-core
(input-pathname asm-pathname
&key enable-pie (verbose nil) dynamic-space-size
&aux (elf-core-pathname
@@ -1412,6 +1327,11 @@
;; for code space. If PIE-enabled, we'll figure it out in the C code
;; because space relocation is going to happen no matter what.
(setf (aref relocs 0) `#(,R_ABS64 ,(ash code-start-fixup-ofs word-shift) 1 0)))
+ #+linkage-space
+ (let ((where (ash (linkage-space-header-ptr linkage-space-info) word-shift)))
+ ;; LINKAGE_SPACE core entry gets a linker fixup to the second ELF symbol
+ (vector-push-extend `#(,R_ABS64 ,where 2 0) relocs)
+ (read-linkage-cells input linkage-space-info))
;; Map the original core file to memory
(with-mapped-core (sap core-offset original-total-npages input)
(let* ((data-spaces
@@ -1424,6 +1344,7 @@
:direction :output :if-exists :supersede
:element-type '(unsigned-byte 8))
(prepare-elf (+ (apply #'+ (mapcar #'space-nbytes-aligned data-spaces))
+ (* (linkage-space-npages linkage-space-info) +backend-page-bytes+)
+backend-page-bytes+ ; core header
pte-nbytes)
relocs output enable-pie)
@@ -1452,6 +1373,21 @@
(awhen (%find-target-symbol (package-id "SB-C") symbol spacemap)
(%set-symbol-global-value
it (find-target-symbol (package-id "KEYWORD") value spacemap :logical)))))
+ #+linkage-space
+ (let ((start (sb-sys:sap+ (car spacemap)
+ (* (linkage-space-data-page linkage-space-info)
+ +backend-page-bytes+)))
+ (size (* (linkage-space-npages linkage-space-info) +backend-page-bytes+))
+ (code-bounds (space-bounds immobile-text-core-space-id spacemap)))
+ ;; Words pointing to text space get the space base address subtracted.
+ ;; And we toggle the low bit to signify that it needs correction at startup.
+ (dotimes (i (linkage-space-count linkage-space-info))
+ (let ((val (sap-ref-word start (ash i word-shift))))
+ (when (in-bounds-p val code-bounds)
+ (setf (sap-ref-word start (ash i word-shift))
+ (logior (- val (bounds-low code-bounds)) 1)))))
+ (aver (eql (sb-unix:unix-write (sb-sys:fd-stream-fd output) start 0 size)
+ size)))
(let ((fd (sb-sys:fd-stream-fd output)))
(dolist (space data-spaces) ; Copy pages from memory
(let ((start (space-physaddr space spacemap))
@@ -1627,203 +1563,124 @@
(setf (signed-sap-ref-32 (code-instructions new-code) (- next-ip-rel 4))
(- branch-target new-next-ip))))))))))
-(defun get-mov-src-constant (code code-vaddr inst ea spacemap)
- (let* ((next-ip (inst-end inst))
- ;; this is a virtual adrress
- (abs-addr (+ next-ip (machine-ea-disp ea))))
- (when (and (not (logtest abs-addr #b111)) ; lispword-aligned
- (>= abs-addr code-vaddr)
- (< abs-addr (+ code-vaddr (ash (code-header-words code) word-shift))))
- (let ((paddr (translate-ptr abs-addr spacemap)))
- (translate (sap-ref-lispobj (int-sap paddr) 0) spacemap)))))
-
-#+x86-64
-(defun locate-const-move-to-rax (code vaddr insts start spacemap fdefns)
- ;; Look for a MOV to RAX from a code header constant
- ;; Technically this should fail if it finds _any_ instruction
- ;; that affects RAX before it finds the one we're looking for.
- (loop for i downfrom start to 1
- do (let ((inst (svref insts i)))
- (cond ((range-labeled (first inst)) (return)) ; labeled statement - fail
- ((and (eq (second inst) 'mov)
- (eq (third inst) (load-time-value (get-gpr :qword 0)))
- (typep (fourth inst) '(cons machine-ea (eql :qword))))
- (let ((ea (car (fourth inst))))
- (when (and (eq (machine-ea-base ea) :rip)
- (minusp (machine-ea-disp ea)))
- (return
- (let ((fdefn (get-mov-src-constant code vaddr inst ea spacemap)))
- (when (and (fdefn-p fdefn) (memq fdefn fdefns))
- (sb-vm::set-fdefn-has-static-callers fdefn 1)
- (values i (fdefn-fun fdefn))))))))))))
-
-#+x86-64
-(defun replacement-opcode (inst)
- (ecase (second inst) ; opcode
- (jmp #xE9)
- (call #xE8)))
-
-#+x86-64
-(defun patch-fdefn-call (code vaddr insts inst i spacemap fdefns &optional print)
- ;; START is the index into INSTS of the instructon that loads RAX
- (multiple-value-bind (start callee)
- (locate-const-move-to-rax code vaddr insts (1- i) spacemap fdefns)
- (when (and start
- (let ((text-space (get-space immobile-text-core-space-id spacemap)))
- (< (space-addr text-space)
- ;; CALLEE is an untranslated address
- (get-lisp-obj-address callee)
- (space-end text-space))))
- (when print
- (let ((addr (inst-vaddr (svref insts start))) ; starting address
- (end (inst-end inst)))
- (sb-c:dis (translate-ptr addr spacemap) (- end addr))))
- ;; Several instructions have to be replaced to make room for the new CALL
- ;; which is a longer than the old, but it's ok since a MOV is eliminated.
- (let* ((sum-lengths
- (loop for j from start to i sum (inst-length (svref insts j))))
- (new-bytes (make-array sum-lengths :element-type '(unsigned-byte 8)))
- (new-index 0))
- (loop for j from (1+ start) below i
- do (let* ((old-inst (svref insts j))
- (ip (inst-vaddr old-inst))
- (physaddr (int-sap (translate-ptr ip spacemap)))
- (nbytes (inst-length old-inst)))
- (dotimes (k nbytes)
- (setf (aref new-bytes new-index) (sap-ref-8 physaddr k))
- (incf new-index))))
- ;; insert padding given that the new call takes 5 bytes to encode
- (let* ((nop-len (- sum-lengths (+ new-index 5)))
- (nop-pattern (ecase nop-len
- (5 '(#x0f #x1f #x44 #x00 #x00)))))
- (dolist (byte nop-pattern)
- (setf (aref new-bytes new-index) byte)
- (incf new-index)))
- ;; change the call
- (let* ((branch-target
- (simple-fun-entry-sap (translate callee spacemap)))
- (next-pc (int-sap (inst-end inst)))
- (rel32 (sap- branch-target next-pc)))
- (setf (aref new-bytes new-index) (replacement-opcode inst))
- (with-pinned-objects (new-bytes)
- (setf (signed-sap-ref-32 (vector-sap new-bytes) (1+ new-index)) rel32)
- (when print
- (format t "~&Replaced by:~%")
- (let ((s (sb-disassem::make-vector-segment new-bytes 0 sum-lengths
- :virtual-location vaddr)))
- (sb-disassem::disassemble-segment
- s *standard-output* (sb-disassem:make-dstate))))
- (let* ((vaddr (inst-vaddr (svref insts start)))
- (paddr (translate-ptr vaddr spacemap)))
- (%byte-blt new-bytes 0 (int-sap paddr) 0 sum-lengths))))))))
-
-(defun find-static-call-target-in-text-space (inst addr spacemap static-asm-code text-asm-code)
- (declare (ignorable inst))
- ;; this will (for better or for worse) find static fdefns as well as asm routines,
- ;; so we have to figure out which it is.
- (let ((asm-codeblob-size
- (primitive-object-size
- (%make-lisp-obj (logior (translate-ptr static-asm-code spacemap)
- other-pointer-lowtag)))))
- (cond ((<= static-asm-code addr (+ static-asm-code (1- asm-codeblob-size)))
- (let* ((offset-from-base (- addr static-asm-code))
- (new-vaddr (+ text-asm-code offset-from-base)))
- (sap-ref-word (int-sap (translate-ptr new-vaddr spacemap)) 0)))
- (t
- (let* ((fdefn-vaddr (- addr (ash fdefn-raw-addr-slot word-shift)))
- (fdefn-paddr (int-sap (translate-ptr fdefn-vaddr spacemap))))
- ;; Confirm it looks like a static fdefn
- (aver (= (logand (sap-ref-word fdefn-paddr 0) widetag-mask) fdefn-widetag))
- (let ((entrypoint (sap-ref-word fdefn-paddr (ash fdefn-raw-addr-slot word-shift))))
- ;; Confirm there is a simple-fun header where expected
- (let ((header
- (sap-ref-word (int-sap (translate-ptr entrypoint spacemap))
- (- (ash simple-fun-insts-offset word-shift)))))
- (aver (= (logand header widetag-mask) simple-fun-widetag))
- ;; Return the entrypoint which already point to text space
- entrypoint)))))))
-
-;; Patch either a ca through a static-space fdefn or an asm routine indirect jump.
-(defun patch-static-space-call (inst spacemap static-asm-code text-asm-code)
- (let* ((new-bytes (make-array 7 :element-type '(unsigned-byte 8)))
- (addr (machine-ea-disp (third inst)))
- (branch-target
- (find-static-call-target-in-text-space
- inst addr spacemap static-asm-code text-asm-code)))
- (when branch-target
- (setf (aref new-bytes 0) #x66 (aref new-bytes 1) #x90) ; 2-byte NOP
- (setf (aref new-bytes 2) (replacement-opcode inst))
- (let ((next-ip (inst-end inst)))
- (with-pinned-objects (new-bytes)
- (setf (signed-sap-ref-32 (vector-sap new-bytes) 3) (- branch-target next-ip)))
- (%byte-blt new-bytes 0 (int-sap (translate-ptr (inst-vaddr inst) spacemap)) 0 7)))))
-
-;;; Avoid splicing out any fdefn not uniquely identified by its function binding.
-(defun get-patchable-fdefns (code spacemap &aux alist result)
- (multiple-value-bind (start count) (code-header-fdefn-range code)
- (loop for i from start repeat count
- do (let* ((fdefn (translate (code-header-ref code i) spacemap))
- (fun (translate (fdefn-fun fdefn) spacemap)))
- (when (simple-fun-p fun)
- ;; It is dangerous to create heap cons cells holding pointers to
- ;; objects at their logical address in the target core.
- ;; TBH, all target objects should be wrapped in a DESCRIPTOR
- ;; structure defined at the top of this file.
- (push (cons fun fdefn) alist)))))
- (dolist (cell alist result)
- (destructuring-bind (fun . fdefn) cell
- (unless (find-if (lambda (other)
- (and (eq (car other) fun) (neq (cdr other) fdefn)))
- alist)
- (push fdefn result)))))
-
-;;; Since dynamic-space code is pretty much relocatable,
-;;; disassembling it at a random physical address is fine.
-#+x86-64
+(defconstant smallvec-elts
+ (- (ash gencgc-page-bytes (- word-shift)) vector-data-offset))
+(defvar *name-map* nil)
+(defun linkage-index-to-name (index spacemap)
+ (unless *name-map*
+ (let ((sym (find-target-symbol (package-id "SB-VM") "*LINKAGE-NAME-MAP*"
+ spacemap :physical)))
+ (setf *name-map* (translate (symbol-global-value sym) spacemap))))
+ (multiple-value-bind (hi lo) (floor index smallvec-elts)
+ (let ((inner (translate (svref *name-map* hi) spacemap)))
+ (translate (weak-vector-ref inner lo) spacemap))))
+(defun use-indirection-p (index spacemap)
+ (let ((name (linkage-index-to-name index spacemap)))
+ (if (symbolp name)
+ (let ((str (translate (symbol-name name) spacemap)))
+ (case (symbol-package-id name)
+ (#.(sb-impl::package-id (find-package "CL"))
+ ;; Users like to encapsulate this apparently
+ (string= str "FIND-PACKAGE"))
+ (#.(sb-impl::package-id (find-package "SB-VM"))
+ ;; These alway get redefined on startup
+ (member str '(sb-vm::simd-reverse32 sb-vm::simd-reverse8
+ sb-vm::simd-nreverse32 sb-vm::simd-nreverse8)
+ :test 'string=)))))))
(defun bypass-indirection-cells
- (code vaddr core static-asm-code text-asm-code
+ (code vaddr core
+ &optional print
&aux (insts (get-code-instruction-model code vaddr core))
(spacemap (core-spacemap core))
- (fdefns (get-patchable-fdefns code spacemap)))
+ (text-bounds (space-bounds immobile-text-core-space-id spacemap))
+ (linkage-bounds (core-linkage-bounds core))
+ (alien-linkage-end (+ (bounds-low linkage-bounds) alien-linkage-space-size))
+ (indices))
(declare (simple-vector insts))
- (do ((i 0 (1+ i)))
- ((>= i (length insts)))
- (let* ((inst (svref insts i))
- (this-op (second inst)))
- (when (member this-op '(call jmp))
- ;; is it potentially a call via an fdefn or an asm code indirection?
- (let ((ea (third inst)))
- (when (and (typep ea 'machine-ea)
- (or (and (eql (machine-ea-base ea) 0) ; [RAX-9]
- (eql (machine-ea-disp ea) 9)
- (not (machine-ea-index ea)))
- (and (not (machine-ea-base ea))
- (not (machine-ea-index ea))
- (<= static-space-start (machine-ea-disp ea)
- (sap-int *static-space-free-pointer*)))))
- (if (eql (machine-ea-base ea) 0) ; based on RAX
- (patch-fdefn-call code vaddr insts inst i spacemap fdefns)
- (patch-static-space-call inst spacemap
- static-asm-code text-asm-code))))))))
+ (labels ((linkage-table-load-p (prev-inst)
+ (and (eq (second prev-inst) 'mov)
+ (eq (third prev-inst) (get-gpr :qword 0))
+ (let ((src (fourth prev-inst)))
+ (and (typep src '(cons machine-ea (eql :qword)))
+ (let ((ea (car src)))
+ (and (eql (machine-ea-base ea) 13)
+ (eql (machine-ea-disp ea)
+ (ash sb-vm::thread-linkage-table-slot word-shift))
+ (null (machine-ea-index ea))))))))
+ (linkage-index (ea inst i)
+ (cond ((eq (machine-ea-base ea) :rip) ; RIP+n format
+ (let* ((next-pc (+ (range-vaddr (car inst)) (range-bytecount (car inst))))
+ (addr (+ next-pc (machine-ea-disp ea))))
+ (when (and (>= addr alien-linkage-end)
+ (< addr (bounds-high linkage-bounds)))
+ (ash (- addr alien-linkage-end) (- word-shift)))))
+ ((and (eql (machine-ea-base ea) 0) ; RAX+n format
+ (null (machine-ea-index ea))
+ ;; 0 is not a usable linkage cell
+ (typep (machine-ea-disp ea) '(integer 8))
+ (linkage-table-load-p (svref insts (1- i))))
+ (ash (machine-ea-disp ea) (- word-shift))))))
+ (do ((i 0 (1+ i)))
+ ((>= i (length insts)))
+ (let* ((inst (svref insts i))
+ (op (second inst))
+ (ea (third inst)))
+ (when (and (member op '(call jmp)) (typep ea 'machine-ea))
+ (binding* ((linkage-index (linkage-index ea inst i) :exit-if-null)
+ (cell (assoc linkage-index indices)))
+ (unless cell
+ (setq cell (list linkage-index))
+ (push cell indices))
+ ;; Collect list of insts replace for the particular linkage-index.
+ (push inst (cdr cell)))))))
+ ;; Change each linkage table call to instead go directly to the target
+ ;; but only if the target uniquely identifies its linkage index within
+ ;; this code component for purposes of undoing the optimization.
+ (let* ((linkage-cells (linkage-space-cells (core-linkage-space-info core)))
+ (indices (coerce indices 'vector))
+ (values (map 'vector (lambda (x) (aref linkage-cells (car x)))
+ indices)))
+ (dotimes (i (length indices))
+ (let ((linkage-index (car (aref indices i))) (value (aref values i)))
+ (cond ((not (in-bounds-p value text-bounds))
+ (if print (format t "Can't patch ~X: outside of text space~%" value)))
+ ((> (count value values) 1)
+ (if print (format t "Can't patch ~X: not unique~%" value)))
+ ((use-indirection-p linkage-index spacemap)
+ (if print (format t "Won't direct call ~D~%" linkage-index)))
+ (t
+ (dolist (inst (cdr (aref indices i)))
+ (let* ((range (car inst))
+ (sap (sap+ (sap+ (int-sap (get-lisp-obj-address code))
+ (- other-pointer-lowtag))
+ (- (range-vaddr range) vaddr)))
+ (next-pc (+ (range-vaddr range) (range-bytecount range)))
+ (disp (the (signed-byte 32) (- value next-pc))))
+ (ecase (cadr inst)
+ (call
+ (setf (sap-ref-8 sap 0) #x40 ; add a do-nothing prefix
+ (sap-ref-8 sap 1) #xe8 ; CALL rel32
+ (signed-sap-ref-32 sap 2) disp))
+ (jmp
+ (setf (sap-ref-8 sap 0) #xe9 ; JMP rel32
+ (signed-sap-ref-32 sap 1) (1+ disp)
+ (sap-ref-8 sap 5) #x90))))))))))) ; followed by NOP
(defun redirect-text-space-calls (pathname)
(with-open-file (stream pathname :element-type '(unsigned-byte 8)
:direction :io :if-exists :overwrite)
(let* ((core-header (make-array +backend-page-bytes+ :element-type '(unsigned-byte 8)))
- (core-offset (read-core-header stream core-header t))
+ (core-offset (read-core-header stream core-header))
(parsed-header (parse-core-header stream core-header))
(space-list (core-header-space-list parsed-header)))
(with-mapped-core (sap core-offset (core-header-total-npages parsed-header) stream)
(let* ((spacemap (cons sap (sort (copy-list space-list) #'> :key #'space-addr)))
(core (make-core spacemap (make-bounds 0 0) (make-bounds 0 0)
:linkage-space-info (core-header-linkage-space-info parsed-header))))
+ #-immobile-space
(let* ((text-space (get-space immobile-text-core-space-id spacemap))
(offsets-vector (%make-lisp-obj (logior (sap-int (space-physaddr text-space spacemap))
- lowtag-mask)))
- (static-space-asm-code
- (get-static-space-asm-code (get-space static-core-space-id spacemap) spacemap))
- (text-space-asm-code
- (get-text-space-asm-code-replica text-space spacemap)))
+ lowtag-mask))))
(assert text-space)
(patch-asm-codeblob core)
;; offset 0 is the offset of the ASM routine codeblob which was already processed.
@@ -1833,10 +1690,27 @@
(logior (sap-int (sap+ (space-physaddr text-space spacemap)
(aref offsets-vector j)))
other-pointer-lowtag))))
- (bypass-indirection-cells physobj vaddr core
- static-space-asm-code text-space-asm-code))))
+ (bypass-indirection-cells physobj vaddr core))))
+ #+immobile-space
+ (let* ((text-space (get-space immobile-text-core-space-id spacemap))
+ (delta (- (translate-ptr (space-addr text-space) spacemap)
+ (space-addr text-space))))
+ (walk-target-space (lambda (obj widetag size
+ &aux (vaddr (- (get-lisp-obj-address obj)
+ other-pointer-lowtag delta)))
+ (declare (ignore widetag size))
+ (bypass-indirection-cells obj vaddr core))
+ immobile-text-core-space-id spacemap))
(persist-to-file spacemap core-offset stream))))))
+(defun split-core (input-pathname asm-pathname &rest args)
+ (let ((tmp (format nil "/tmp/sbcl~D.tmpcore" (sb-unix:unix-getpid))))
+ ;; input core could be readonly
+ (unwind-protect (progn (run-program "/bin/cp" `("--no-preserve=mode" ,input-pathname ,tmp))
+ (redirect-text-space-calls tmp)
+ (apply #'really-split-core tmp asm-pathname args))
+ (delete-file tmp))))
+
(defun cl-user::elfinate (&optional (args (cdr *posix-argv*)))
(cond ((string= (car args) "split")
(pop args)