types.scm 19 KB

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