debug-link.scm 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2018, 2023 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (guix build debug-link)
  19. #:use-module (guix elf)
  20. #:use-module ((guix build utils)
  21. #:select (find-files elf-file? make-file-writable))
  22. #:use-module (rnrs bytevectors)
  23. #:use-module (rnrs io ports)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (system foreign)
  26. #:use-module (ice-9 match)
  27. #:export (debuglink-crc32
  28. elf-debuglink
  29. set-debuglink-crc
  30. graft-debug-links))
  31. ;;; Commentary:
  32. ;;;
  33. ;;; This module provides tools to deal with '.gnu_debuglink' sections in ELF
  34. ;;; files. These sections are created by 'objcopy --add-gnu-debuglink' to
  35. ;;; create separate debug files (info "(gdb) Separate Debug Files").
  36. ;;;
  37. ;;; The main facility of this module is 'graft-debug-links', which allows us
  38. ;;; to update the cyclic redundancy check (CRC) that appears in
  39. ;;; '.gnu_debuglink' sections when grafting, such that separate debug files
  40. ;;; remain usable after grafting. Failing to do that, GDB would complain
  41. ;;; about CRC mismatch---see <https://issues.guix.gnu.org/19973>.
  42. ;;;
  43. ;;; Code:
  44. (define %crc32-table
  45. ;; CRC table taken from "(gdb) Separate Debug Files".
  46. ;; TODO: Wouldn't it be nice to generate it "from source" with a macro?
  47. #(#x00000000 #x77073096 #xee0e612c #x990951ba #x076dc419
  48. #x706af48f #xe963a535 #x9e6495a3 #x0edb8832 #x79dcb8a4
  49. #xe0d5e91e #x97d2d988 #x09b64c2b #x7eb17cbd #xe7b82d07
  50. #x90bf1d91 #x1db71064 #x6ab020f2 #xf3b97148 #x84be41de
  51. #x1adad47d #x6ddde4eb #xf4d4b551 #x83d385c7 #x136c9856
  52. #x646ba8c0 #xfd62f97a #x8a65c9ec #x14015c4f #x63066cd9
  53. #xfa0f3d63 #x8d080df5 #x3b6e20c8 #x4c69105e #xd56041e4
  54. #xa2677172 #x3c03e4d1 #x4b04d447 #xd20d85fd #xa50ab56b
  55. #x35b5a8fa #x42b2986c #xdbbbc9d6 #xacbcf940 #x32d86ce3
  56. #x45df5c75 #xdcd60dcf #xabd13d59 #x26d930ac #x51de003a
  57. #xc8d75180 #xbfd06116 #x21b4f4b5 #x56b3c423 #xcfba9599
  58. #xb8bda50f #x2802b89e #x5f058808 #xc60cd9b2 #xb10be924
  59. #x2f6f7c87 #x58684c11 #xc1611dab #xb6662d3d #x76dc4190
  60. #x01db7106 #x98d220bc #xefd5102a #x71b18589 #x06b6b51f
  61. #x9fbfe4a5 #xe8b8d433 #x7807c9a2 #x0f00f934 #x9609a88e
  62. #xe10e9818 #x7f6a0dbb #x086d3d2d #x91646c97 #xe6635c01
  63. #x6b6b51f4 #x1c6c6162 #x856530d8 #xf262004e #x6c0695ed
  64. #x1b01a57b #x8208f4c1 #xf50fc457 #x65b0d9c6 #x12b7e950
  65. #x8bbeb8ea #xfcb9887c #x62dd1ddf #x15da2d49 #x8cd37cf3
  66. #xfbd44c65 #x4db26158 #x3ab551ce #xa3bc0074 #xd4bb30e2
  67. #x4adfa541 #x3dd895d7 #xa4d1c46d #xd3d6f4fb #x4369e96a
  68. #x346ed9fc #xad678846 #xda60b8d0 #x44042d73 #x33031de5
  69. #xaa0a4c5f #xdd0d7cc9 #x5005713c #x270241aa #xbe0b1010
  70. #xc90c2086 #x5768b525 #x206f85b3 #xb966d409 #xce61e49f
  71. #x5edef90e #x29d9c998 #xb0d09822 #xc7d7a8b4 #x59b33d17
  72. #x2eb40d81 #xb7bd5c3b #xc0ba6cad #xedb88320 #x9abfb3b6
  73. #x03b6e20c #x74b1d29a #xead54739 #x9dd277af #x04db2615
  74. #x73dc1683 #xe3630b12 #x94643b84 #x0d6d6a3e #x7a6a5aa8
  75. #xe40ecf0b #x9309ff9d #x0a00ae27 #x7d079eb1 #xf00f9344
  76. #x8708a3d2 #x1e01f268 #x6906c2fe #xf762575d #x806567cb
  77. #x196c3671 #x6e6b06e7 #xfed41b76 #x89d32be0 #x10da7a5a
  78. #x67dd4acc #xf9b9df6f #x8ebeeff9 #x17b7be43 #x60b08ed5
  79. #xd6d6a3e8 #xa1d1937e #x38d8c2c4 #x4fdff252 #xd1bb67f1
  80. #xa6bc5767 #x3fb506dd #x48b2364b #xd80d2bda #xaf0a1b4c
  81. #x36034af6 #x41047a60 #xdf60efc3 #xa867df55 #x316e8eef
  82. #x4669be79 #xcb61b38c #xbc66831a #x256fd2a0 #x5268e236
  83. #xcc0c7795 #xbb0b4703 #x220216b9 #x5505262f #xc5ba3bbe
  84. #xb2bd0b28 #x2bb45a92 #x5cb36a04 #xc2d7ffa7 #xb5d0cf31
  85. #x2cd99e8b #x5bdeae1d #x9b64c2b0 #xec63f226 #x756aa39c
  86. #x026d930a #x9c0906a9 #xeb0e363f #x72076785 #x05005713
  87. #x95bf4a82 #xe2b87a14 #x7bb12bae #x0cb61b38 #x92d28e9b
  88. #xe5d5be0d #x7cdcefb7 #x0bdbdf21 #x86d3d2d4 #xf1d4e242
  89. #x68ddb3f8 #x1fda836e #x81be16cd #xf6b9265b #x6fb077e1
  90. #x18b74777 #x88085ae6 #xff0f6a70 #x66063bca #x11010b5c
  91. #x8f659eff #xf862ae69 #x616bffd3 #x166ccf45 #xa00ae278
  92. #xd70dd2ee #x4e048354 #x3903b3c2 #xa7672661 #xd06016f7
  93. #x4969474d #x3e6e77db #xaed16a4a #xd9d65adc #x40df0b66
  94. #x37d83bf0 #xa9bcae53 #xdebb9ec5 #x47b2cf7f #x30b5ffe9
  95. #xbdbdf21c #xcabac28a #x53b39330 #x24b4a3a6 #xbad03605
  96. #xcdd70693 #x54de5729 #x23d967bf #xb3667a2e #xc4614ab8
  97. #x5d681b02 #x2a6f2b94 #xb40bbe37 #xc30c8ea1 #x5a05df1b
  98. #x2d02ef8d))
  99. (define (debuglink-crc32 port)
  100. "Compute the 32-bit CRC used in in '.gnu_debuglink' over the data read from
  101. PORT and return it." ;(info "(gdb) Separate Debug Files")
  102. (let loop ((crc #xffffffff))
  103. (let ((byte (get-u8 port)))
  104. (if (eof-object? byte)
  105. (logand (lognot crc) #xffffffff)
  106. (let* ((index (logand (logxor crc byte) #xff))
  107. (lhs (vector-ref %crc32-table index)))
  108. (loop (logxor lhs (ash crc -8))))))))
  109. (define (section-contents elf section) ;XXX: copied from linux-modules.scm
  110. "Return the contents of SECTION in ELF as a bytevector."
  111. (let* ((contents (make-bytevector (elf-section-size section))))
  112. (bytevector-copy! (elf-bytes elf) (elf-section-offset section)
  113. contents 0
  114. (elf-section-size section))
  115. contents))
  116. (define null-terminated-bytevector->string
  117. (compose pointer->string bytevector->pointer))
  118. (define (elf-debuglink elf)
  119. "Return two values: the '.gnu_debuglink' file name of ELF and its CRC.
  120. Return #f for both if ELF lacks a '.gnu_debuglink' section."
  121. (let ((section (elf-section-by-name elf ".gnu_debuglink")))
  122. (if section
  123. (let ((size (elf-section-size section))
  124. (bv (section-contents elf section))
  125. (endianness (elf-byte-order elf)))
  126. (values (null-terminated-bytevector->string bv)
  127. (bytevector-u32-ref bv (- size 4) endianness)))
  128. (values #f #f))))
  129. (define (elf-debuglink-crc-offset elf)
  130. "Return the offset of the '.gnu_debuglink' 32-bit CRC, or #f if ELF lacks a
  131. '.gnu_debuglink' section."
  132. (let ((section (elf-section-by-name elf ".gnu_debuglink")))
  133. (and section
  134. (+ (elf-section-offset section)
  135. (elf-section-size section)
  136. -4))))
  137. (define (set-debuglink-crc file debug-file)
  138. "Compute the CRC of DEBUG-FILE and set it as the '.gnu_debuglink' CRC in
  139. FILE."
  140. (let* ((elf (parse-elf (call-with-input-file file get-bytevector-all)))
  141. (offset (elf-debuglink-crc-offset elf)))
  142. (and offset
  143. (let* ((crc (call-with-input-file debug-file debuglink-crc32))
  144. (bv (make-bytevector 4)))
  145. (bytevector-u32-set! bv 0 crc (elf-byte-order elf))
  146. (let ((port (open file O_RDWR)))
  147. (set-port-position! port offset)
  148. (put-bytevector port bv)
  149. (close-port port))))))
  150. ;;;
  151. ;;; Updating debuglink CRC.
  152. ;;;
  153. (define (find-elf-files outputs)
  154. "Return the list of ELF files found in OUTPUTS, a list of top-level store
  155. directories."
  156. (define directories
  157. (append-map (lambda (output)
  158. (list (string-append output "/bin")
  159. (string-append output "/sbin")
  160. (string-append output "/lib")
  161. (string-append output "/libexec")))
  162. outputs))
  163. (append-map (lambda (directory)
  164. (filter (lambda (file)
  165. (catch 'system-error
  166. (lambda ()
  167. (elf-file? file))
  168. (lambda args
  169. ;; FILE might be a dangling symlink.
  170. (if (= ENOENT (system-error-errno args))
  171. #f
  172. (apply throw args)))))
  173. (with-error-to-port (%make-void-port "w")
  174. (lambda ()
  175. (find-files directory)))))
  176. directories))
  177. (define* (graft-debug-links old-outputs new-outputs mapping
  178. #:key (log-port (current-error-port)))
  179. "Update the '.gnu_debuglink' CRCs found in ELF files of NEW-OUTPUTS,
  180. provided NEW-OUTPUTS contains a \"debug\" output, such that those CRCs match
  181. those of the corresponding '.debug' files found in the \"debug\" output.
  182. This procedure is meant to be used as a \"grafting hook\" by (guix build
  183. graft)."
  184. (match (assoc-ref new-outputs "debug")
  185. (#f #t) ;nothing to do
  186. (debug-directory
  187. (let ((files (find-elf-files (filter-map (match-lambda
  188. (("debug" . _)
  189. #f)
  190. ((name . directory)
  191. directory))
  192. new-outputs))))
  193. (for-each (lambda (file)
  194. (let ((debug (string-append debug-directory
  195. "/lib/debug" file ".debug")))
  196. (when (file-exists? debug)
  197. (format log-port
  198. "updating '.gnu_debuglink' CRC in '~a'~%"
  199. file)
  200. (make-file-writable file)
  201. (set-debuglink-crc file debug))))
  202. files)))))