types.scm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. ;;; 'SCM' type tag decoding.
  2. ;;; Copyright (C) 2014, 2015, 2017, 2018, 2022 Free Software Foundation, Inc.
  3. ;;;
  4. ;;; This library is free software; you can redistribute it and/or modify it
  5. ;;; under the terms of the GNU Lesser General Public License as published by
  6. ;;; the Free Software Foundation; either version 3 of the License, or (at
  7. ;;; your option) any later version.
  8. ;;;
  9. ;;; This library is distributed in the hope that it will be useful, but
  10. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
  12. ;;; General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU Lesser General Public License
  15. ;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. (define-module (system base types)
  17. #:use-module (rnrs bytevectors)
  18. #:use-module (rnrs io ports)
  19. #:use-module (srfi srfi-1)
  20. #:use-module (srfi srfi-9)
  21. #:use-module (srfi srfi-9 gnu)
  22. #:use-module (srfi srfi-11)
  23. #:use-module (srfi srfi-26)
  24. #:use-module (srfi srfi-60)
  25. #:use-module (ice-9 match)
  26. #:use-module ((ice-9 iconv) #:prefix iconv:)
  27. #:use-module (ice-9 format)
  28. #:use-module (ice-9 vlist)
  29. #:use-module (system foreign)
  30. #:use-module (system base types internal)
  31. #:export (%word-size
  32. memory-backend
  33. memory-backend?
  34. %ffi-memory-backend
  35. dereference-word
  36. memory-port
  37. type-number->name
  38. inferior-object?
  39. inferior-object-kind
  40. inferior-object-sub-kind
  41. inferior-object-address
  42. inferior-struct?
  43. inferior-struct-name
  44. inferior-struct-fields
  45. scm->object))
  46. ;; This module can be loaded from GDB-linked-against-2.0, so use 2.2
  47. ;; features conditionally.
  48. (cond-expand
  49. (guile-2.2 (use-modules (system syntax internal))) ;for 'make-syntax'
  50. (else #t))
  51. ;;; Commentary:
  52. ;;;
  53. ;;; 'SCM' type tag decoding, primarily to support Guile debugging in GDB.
  54. ;;;
  55. ;;; Code:
  56. ;;;
  57. ;;; Memory back-ends.
  58. ;;;
  59. (define %word-size
  60. ;; The pointer size.
  61. (sizeof '*))
  62. (define-record-type <memory-backend>
  63. (memory-backend peek open type-name)
  64. memory-backend?
  65. (peek memory-backend-peek)
  66. (open memory-backend-open)
  67. (type-name memory-backend-type-name)) ;for SMOBs
  68. (define %ffi-memory-backend
  69. ;; The FFI back-end to access the current process's memory. The main
  70. ;; purpose of this back-end is to allow testing.
  71. (let ()
  72. (define (dereference-word address)
  73. (let* ((ptr (make-pointer address))
  74. (bv (pointer->bytevector ptr %word-size)))
  75. (bytevector-uint-ref bv 0 (native-endianness) %word-size)))
  76. (define (open address size)
  77. (define current-address address)
  78. (define (read-memory! bv index count)
  79. (let* ((ptr (make-pointer current-address))
  80. (mem (pointer->bytevector ptr count)))
  81. (bytevector-copy! mem 0 bv index count)
  82. (set! current-address (+ current-address count))
  83. count))
  84. (if size
  85. (let* ((ptr (make-pointer address))
  86. (bv (pointer->bytevector ptr size)))
  87. (open-bytevector-input-port bv))
  88. (let ((port (make-custom-binary-input-port "ffi-memory"
  89. read-memory!
  90. #f #f #f)))
  91. (setvbuf port 'none)
  92. port)))
  93. (memory-backend dereference-word open #f)))
  94. (define-inlinable (dereference-word backend address)
  95. "Return the word at ADDRESS, using BACKEND."
  96. (let ((peek (memory-backend-peek backend)))
  97. (peek address)))
  98. (define-syntax memory-port
  99. (syntax-rules ()
  100. "Return an input port to the SIZE bytes at ADDRESS, using BACKEND. When
  101. SIZE is omitted, return an unbounded port to the memory at ADDRESS."
  102. ((_ backend address)
  103. (let ((open (memory-backend-open backend)))
  104. (open address #f)))
  105. ((_ backend address size)
  106. (if (zero? size)
  107. ;; GDB's 'open-memory' raises an error when size
  108. ;; is zero, so we must handle that case specially.
  109. (open-bytevector-input-port '#vu8())
  110. (let ((open (memory-backend-open backend)))
  111. (open address size))))))
  112. (define (get-word port)
  113. "Read a word from PORT and return it as an integer."
  114. (let ((bv (get-bytevector-n port %word-size)))
  115. (bytevector-uint-ref bv 0 (native-endianness) %word-size)))
  116. (define (read-c-string backend address)
  117. "Read a NUL-terminated string from ADDRESS, decode it as UTF-8, and
  118. return the corresponding string."
  119. (define port
  120. (memory-port backend address))
  121. (let loop ((bytes '()))
  122. (let ((byte (get-u8 port)))
  123. (if (zero? byte)
  124. (utf8->string (u8-list->bytevector (reverse bytes)))
  125. (loop (cons byte bytes))))))
  126. (define-inlinable (type-number->name backend kind number)
  127. "Return the name of the type NUMBER of KIND, where KIND is one of
  128. 'smob or 'port, or #f if the information is unavailable."
  129. (let ((proc (memory-backend-type-name backend)))
  130. (and proc (proc kind number))))
  131. ;;;
  132. ;;; Matching bit patterns and cells.
  133. ;;;
  134. (define-syntax match-cell-words
  135. (syntax-rules (bytevector)
  136. ((_ port ((bytevector name len) rest ...) body)
  137. (let ((name (get-bytevector-n port len))
  138. (remainder (modulo len %word-size)))
  139. (unless (zero? remainder)
  140. (get-bytevector-n port (- %word-size remainder)))
  141. (match-cell-words port (rest ...) body)))
  142. ((_ port (name rest ...) body)
  143. (let ((name (get-word port)))
  144. (match-cell-words port (rest ...) body)))
  145. ((_ port () body)
  146. body)))
  147. (define-syntax match-bit-pattern
  148. (syntax-rules (& || = _)
  149. ((match-bit-pattern bits ((a || b) & n = c) consequent alternate)
  150. (let ((tag (logand bits n)))
  151. (if (= tag c)
  152. (let ((b tag)
  153. (a (logand bits (bitwise-not n))))
  154. consequent)
  155. alternate)))
  156. ((match-bit-pattern bits (= c) consequent alternate)
  157. (if (= bits c)
  158. consequent
  159. alternate))
  160. ((match-bit-pattern bits (x & n = c) consequent alternate)
  161. (let ((tag (logand bits n)))
  162. (if (= tag c)
  163. (let ((x bits))
  164. consequent)
  165. alternate)))
  166. ((match-bit-pattern bits (_ & n = c) consequent alternate)
  167. (let ((tag (logand bits n)))
  168. (if (= tag c)
  169. consequent
  170. alternate)))
  171. ((match-bit-pattern bits ((a << n) || c) consequent alternate)
  172. (let ((tag (bitwise-and bits (- (expt 2 n) 1))))
  173. (if (= tag c)
  174. (let ((a (arithmetic-shift bits (- n))))
  175. consequent)
  176. alternate)))))
  177. (define-syntax match-cell-clauses
  178. (syntax-rules ()
  179. ((_ port tag (((tag-pattern thing ...) body) rest ...))
  180. (match-bit-pattern tag tag-pattern
  181. (match-cell-words port (thing ...) body)
  182. (match-cell-clauses port tag (rest ...))))
  183. ((_ port tag ())
  184. (inferior-object 'unmatched-tag tag))))
  185. (define-syntax match-cell
  186. (syntax-rules ()
  187. "Match a cell---i.e., a non-immediate value other than a pair. The
  188. cell's contents are read from PORT."
  189. ((_ port (pattern body ...) ...)
  190. (let ((port* port)
  191. (tag (get-word port)))
  192. (match-cell-clauses port* tag
  193. ((pattern (begin body ...))
  194. ...))))))
  195. (define-syntax match-scm-clauses
  196. (syntax-rules ()
  197. ((_ bits
  198. (bit-pattern body ...)
  199. rest ...)
  200. (match-bit-pattern bits bit-pattern
  201. (begin body ...)
  202. (match-scm-clauses bits rest ...)))
  203. ((_ bits)
  204. 'unmatched-scm)))
  205. (define-syntax match-scm
  206. (syntax-rules ()
  207. "Match BITS, an integer representation of an 'SCM' value, against
  208. CLAUSES. Each clause must have the form:
  209. (PATTERN BODY ...)
  210. PATTERN is a bit pattern that may specify bitwise operations on BITS to
  211. determine if it matches. TEMPLATE specify the name of the variable to bind
  212. the matching bits, possibly with bitwise operations to extract it from BITS."
  213. ((_ bits clauses ...)
  214. (let ((bits* bits))
  215. (match-scm-clauses bits* clauses ...)))))
  216. ;; "Stringbufs".
  217. (define-record-type <stringbuf>
  218. (stringbuf string)
  219. stringbuf?
  220. (string stringbuf-contents))
  221. (set-record-type-printer! <stringbuf>
  222. (lambda (stringbuf port)
  223. (display "#<stringbuf " port)
  224. (write (stringbuf-contents stringbuf) port)
  225. (display "#>" port)))
  226. ;; Structs.
  227. (define-record-type <inferior-struct>
  228. (inferior-struct name fields)
  229. inferior-struct?
  230. (name inferior-struct-name)
  231. (fields inferior-struct-fields set-inferior-struct-fields!))
  232. (define print-inferior-struct
  233. (let ((%printed-struct (make-parameter vlist-null)))
  234. (lambda (struct port)
  235. (if (vhash-assq struct (%printed-struct))
  236. (format port "#-1#")
  237. (begin
  238. (format port "#<struct ~a"
  239. (inferior-struct-name struct))
  240. (parameterize ((%printed-struct
  241. (vhash-consq struct #t (%printed-struct))))
  242. (for-each (lambda (field)
  243. (if (eq? field struct)
  244. (display " #0#" port)
  245. (format port " ~s" field)))
  246. (inferior-struct-fields struct)))
  247. (format port " ~x>" (object-address struct)))))))
  248. (set-record-type-printer! <inferior-struct> print-inferior-struct)
  249. ;; Object type to represent complex objects from the inferior process that
  250. ;; cannot be really converted to usable Scheme objects in the current
  251. ;; process.
  252. (define-record-type <inferior-object>
  253. (%inferior-object kind sub-kind address)
  254. inferior-object?
  255. (kind inferior-object-kind)
  256. (sub-kind inferior-object-sub-kind)
  257. (address inferior-object-address))
  258. (define inferior-object
  259. (case-lambda
  260. "Return an object representing an inferior object at ADDRESS, of type
  261. KIND/SUB-KIND."
  262. ((kind address)
  263. (%inferior-object kind #f address))
  264. ((kind sub-kind address)
  265. (%inferior-object kind sub-kind address))))
  266. (set-record-type-printer! <inferior-object>
  267. (lambda (io port)
  268. (match io
  269. (($ <inferior-object> kind sub-kind address)
  270. (format port "#<~a ~:[~*~;~a ~]~x>"
  271. kind sub-kind sub-kind
  272. address)))))
  273. (define (inferior-smob backend type-number address)
  274. "Return an object representing the SMOB at ADDRESS whose type is
  275. TYPE-NUMBER."
  276. (inferior-object 'smob
  277. (or (type-number->name backend 'smob type-number)
  278. type-number)
  279. address))
  280. (define (inferior-port-type backend address)
  281. "Return an object representing the 'scm_t_port_type' structure at
  282. ADDRESS."
  283. (inferior-object 'port-type
  284. ;; The 'name' field lives at offset 0.
  285. (let ((name (dereference-word backend address)))
  286. (if (zero? name)
  287. "(nameless)"
  288. (read-c-string backend name)))
  289. address))
  290. (define (inferior-port backend type-number address)
  291. "Return an object representing the port at ADDRESS whose type is
  292. TYPE-NUMBER."
  293. (inferior-object 'port
  294. (let ((address (+ address (* 3 %word-size))))
  295. (inferior-port-type backend
  296. (dereference-word backend address)))
  297. address))
  298. (define %visited-cells
  299. ;; Vhash of mapping addresses of already visited cells to the
  300. ;; corresponding inferior object. This is used to detect and represent
  301. ;; cycles.
  302. (make-parameter vlist-null))
  303. (define-syntax visited
  304. (syntax-rules (->)
  305. ((_ (address -> object) body ...)
  306. (parameterize ((%visited-cells (vhash-consv address object
  307. (%visited-cells))))
  308. body ...))))
  309. (define (address->inferior-struct address vtable-address backend)
  310. "Read the struct at ADDRESS using BACKEND. Return an 'inferior-struct'
  311. object representing it."
  312. (define %vtable-layout-index vtable-index-layout)
  313. (define %vtable-name-index 4)
  314. (let* ((vtable-data-address (+ vtable-address %word-size))
  315. (layout-address (+ vtable-data-address
  316. (* %vtable-layout-index %word-size)))
  317. (layout-bits (dereference-word backend layout-address))
  318. (layout (scm->object layout-bits backend))
  319. (name-address (+ vtable-data-address
  320. (* %vtable-name-index %word-size)))
  321. (name-bits (dereference-word backend name-address))
  322. (name (scm->object name-bits backend)))
  323. (if (symbol? layout)
  324. (let* ((layout (symbol->string layout))
  325. (len (/ (string-length layout) 2))
  326. (slots (+ address %word-size))
  327. (port (memory-port backend slots (* len %word-size)))
  328. (fields (get-bytevector-n port (* len %word-size)))
  329. (result (inferior-struct name #f)))
  330. ;; Keep track of RESULT so callees can refer to it if we are
  331. ;; decoding a circular struct.
  332. (visited (address -> result)
  333. (let ((values (map (cut scm->object <> backend)
  334. (bytevector->uint-list fields
  335. (native-endianness)
  336. %word-size))))
  337. (set-inferior-struct-fields! result values)
  338. result)))
  339. (inferior-object 'invalid-struct address))))
  340. (define* (cell->object address #:optional (backend %ffi-memory-backend))
  341. "Return an object representing the object at ADDRESS, reading from memory
  342. using BACKEND."
  343. (or (and=> (vhash-assv address (%visited-cells)) cdr) ; circular object
  344. (let ((port (memory-port backend address)))
  345. (match-cell port
  346. (((vtable-address & 7 = %tc3-struct))
  347. (address->inferior-struct address
  348. (- vtable-address %tc3-struct)
  349. backend))
  350. (((_ & #x7f = %tc7-symbol) buf hash)
  351. (match (cell->object buf backend)
  352. (($ <stringbuf> string)
  353. (string->symbol string))))
  354. (((_ & #x7f = %tc7-variable) obj)
  355. (inferior-object 'variable address))
  356. (((_ & #x7f = %tc7-string) buf start len)
  357. (match (cell->object buf backend)
  358. (($ <stringbuf> string)
  359. (substring string start (+ start len)))))
  360. (((_ & #x047f = %tc7-stringbuf) len (bytevector buf len))
  361. (stringbuf (iconv:bytevector->string buf "ISO-8859-1")))
  362. (((_ & #x047f = (bitwise-ior #x400 %tc7-stringbuf))
  363. len (bytevector buf (* 4 len)))
  364. (stringbuf (iconv:bytevector->string buf
  365. (match (native-endianness)
  366. ('little "UTF-32LE")
  367. ('big "UTF-32BE")))))
  368. (((_ & #x7f = %tc7-bytevector) len address)
  369. (let ((bv-port (memory-port backend address len)))
  370. (get-bytevector-n bv-port len)))
  371. ((((len << 8) || %tc7-vector))
  372. (let ((words (get-bytevector-n port (* len %word-size)))
  373. (vector (make-vector len)))
  374. (visited (address -> vector)
  375. (fold (lambda (element index)
  376. (vector-set! vector index element)
  377. (+ 1 index))
  378. 0
  379. (map (cut scm->object <> backend)
  380. (bytevector->uint-list words (native-endianness)
  381. %word-size)))
  382. vector)))
  383. (((_ & #x7f = %tc7-weak-vector))
  384. (inferior-object 'weak-vector address)) ; TODO: show elements
  385. (((_ & #x7f = %tc7-fluid) init-value)
  386. (inferior-object 'fluid address))
  387. (((_ & #x7f = %tc7-dynamic-state))
  388. (inferior-object 'dynamic-state address))
  389. ((((flags << 8) || %tc7-port))
  390. (inferior-port backend (logand flags #xff) address))
  391. (((_ & #x7f = %tc7-program))
  392. (inferior-object 'program address))
  393. (((_ & #xffff = %tc16-bignum))
  394. (inferior-object 'bignum address))
  395. (((_ & #xffff = %tc16-flonum) pad)
  396. (let* ((address (+ address (match %word-size (4 8) (8 8))))
  397. (port (memory-port backend address (sizeof double)))
  398. (words (get-bytevector-n port (sizeof double))))
  399. (bytevector-ieee-double-ref words 0 (native-endianness))))
  400. (((_ & #x7f = %tc7-heap-number) mpi)
  401. (inferior-object 'number address))
  402. (((_ & #x7f = %tc7-hash-table) buckets meta-data unused)
  403. (inferior-object 'hash-table address))
  404. (((_ & #x7f = %tc7-pointer) address)
  405. (make-pointer address))
  406. (((_ & #x7f = %tc7-keyword) symbol)
  407. (symbol->keyword (cell->object symbol backend)))
  408. (((_ & #x7f = %tc7-syntax) expression wrap module)
  409. (cond-expand
  410. (guile-2.2
  411. (make-syntax (cell->object expression backend)
  412. (cell->object wrap backend)
  413. (cell->object module backend)))
  414. (else
  415. (inferior-object 'syntax address))))
  416. (((_ & #x7f = %tc7-vm-continuation))
  417. (inferior-object 'vm-continuation address))
  418. (((_ & #x7f = %tc7-weak-set))
  419. (inferior-object 'weak-set address))
  420. (((_ & #x7f = %tc7-weak-table))
  421. (inferior-object 'weak-table address))
  422. (((_ & #x7f = %tc7-array))
  423. (inferior-object 'array address))
  424. (((_ & #x7f = %tc7-bitvector))
  425. (inferior-object 'bitvector address))
  426. ((((smob-type << 8) || %tc7-smob) word1)
  427. (inferior-smob backend smob-type address))))))
  428. (define* (scm->object bits #:optional (backend %ffi-memory-backend))
  429. "Return the Scheme object corresponding to BITS, the bits of an 'SCM'
  430. object."
  431. (match-scm bits
  432. (((integer << 2) || %tc2-fixnum)
  433. integer)
  434. ((address & 7 = %tc3-heap-object)
  435. (let* ((type (dereference-word backend address))
  436. (pair? (= (logand type #b1) %tc1-pair)))
  437. (if pair?
  438. (or (and=> (vhash-assv address (%visited-cells)) cdr)
  439. (let ((car type)
  440. (cdrloc (+ address %word-size))
  441. (pair (cons *unspecified* *unspecified*)))
  442. (visited (address -> pair)
  443. (set-car! pair (scm->object car backend))
  444. (set-cdr! pair
  445. (scm->object (dereference-word backend cdrloc)
  446. backend))
  447. pair)))
  448. (cell->object address backend))))
  449. (((char << 8) || %tc8-char)
  450. (integer->char char))
  451. ((= %tc16-false) #f)
  452. ((= %tc16-nil) #nil)
  453. ((= %tc16-null) '())
  454. ((= %tc16-true) #t)
  455. ((= %tc16-unspecified) (if #f #f))
  456. ((= %tc16-undefined) (inferior-object 'undefined bits))
  457. ((= %tc16-eof) (eof-object))))
  458. ;;; Local Variables:
  459. ;;; eval: (put 'match-scm 'scheme-indent-function 1)
  460. ;;; eval: (put 'match-cell 'scheme-indent-function 1)
  461. ;;; eval: (put 'visited 'scheme-indent-function 1)
  462. ;;; End:
  463. ;;; types.scm ends here