~ruther/guix-local

0f39db9c1942969bcbc603b306d8e47f8feb8566 — Maxim Cournoyer 6 months ago 9d60fdf
Revert "Use mmap for the elf parser, reducing memory usage."

This reverts commit 2c1fe0df11ae0f66392b8abb6f62430d79305538.
M gnu/packages/gnuzilla.scm => gnu/packages/gnuzilla.scm +7 -1
@@ 996,10 996,16 @@ preferences/advanced-scripts.dtd"
                 (search-input-file inputs "lib/libavcodec.so")))))
          (add-after 'fix-ffmpeg-runtime-linker 'build-sandbox-whitelist
            (lambda* (#:key inputs #:allow-other-keys)
              (define (runpath-of lib)
                (call-with-input-file lib
                  (compose elf-dynamic-info-runpath
                           elf-dynamic-info
                           parse-elf
                           get-bytevector-all)))
              (define (runpaths-of-input label)
                (let* ((dir (string-append (assoc-ref inputs label) "/lib"))
                       (libs (find-files dir "\\.so$")))
                  (append-map file-runpath libs)))
                  (append-map runpath-of libs)))
              ;; Populate the sandbox read-path whitelist as needed by ffmpeg.
              (let* ((whitelist
                      (map (cut string-append <> "/")

M gnu/packages/librewolf.scm => gnu/packages/librewolf.scm +5 -1
@@ 530,11 530,15 @@
              ;; The following two functions are from Guix's icecat package in
              ;; (gnu packages gnuzilla).  See commit
              ;; b7a0935420ee630a29b7e5ac73a32ba1eb24f00b.
              (define (runpath-of lib)
                (call-with-input-file lib
                  (compose elf-dynamic-info-runpath elf-dynamic-info
                           parse-elf get-bytevector-all)))
              (define (runpaths-of-input label)
                (let* ((dir (string-append (assoc-ref inputs label)
                                           "/lib"))
                       (libs (find-files dir "\\.so$")))
                  (append-map file-runpath libs)))
                  (append-map runpath-of libs)))
              (let* ((out (assoc-ref outputs "out"))
                     (lib (string-append out "/lib"))
                     (libs (map

M guix/build/debug-link.scm => guix/build/debug-link.scm +9 -8
@@ 1,6 1,5 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2018, 2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2025 Maxim Cournoyer <maxim@guixotic.coop>
;;;
;;; This file is part of GNU Guix.
;;;


@@ 19,7 18,6 @@

(define-module (guix build debug-link)
  #:use-module (guix elf)
  #:use-module (guix build io)
  #:use-module ((guix build utils)
                #:select (find-files elf-file? make-file-writable))
  #:use-module (rnrs bytevectors)


@@ 149,13 147,16 @@ Return #f for both if ELF lacks a '.gnu_debuglink' section."
(define (set-debuglink-crc file debug-file)
  "Compute the CRC of DEBUG-FILE and set it as the '.gnu_debuglink' CRC in
FILE."
  (let* ((bv (file->bytevector file #:protection (logior PROT_READ PROT_WRITE)))
         (elf (parse-elf bv))
  (let* ((elf    (parse-elf (call-with-input-file file get-bytevector-all)))
         (offset (elf-debuglink-crc-offset elf)))
    (when offset
      (let ((crc (call-with-input-file debug-file debuglink-crc32)))
        (bytevector-u32-set! bv offset crc (elf-byte-order elf))
        (munmap bv)))))
    (and offset
         (let* ((crc (call-with-input-file debug-file debuglink-crc32))
                (bv  (make-bytevector 4)))
           (bytevector-u32-set! bv 0 crc (elf-byte-order elf))
           (let ((port (open file O_RDWR)))
             (set-port-position! port offset)
             (put-bytevector port bv)
             (close-port port))))))


;;;

M guix/build/gremlin.scm => guix/build/gremlin.scm +34 -28
@@ 1,6 1,5 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2015, 2018, 2020 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2025 Maxim Cournoyer <maxim@guixotic.coop>
;;;
;;; This file is part of GNU Guix.
;;;


@@ 19,7 18,6 @@

(define-module (guix build gremlin)
  #:use-module (guix elf)
  #:use-module (guix build io)
  #:use-module ((guix build utils) #:select (store-file-name?))
  #:use-module (ice-9 match)
  #:use-module (srfi srfi-1)


@@ 250,7 248,9 @@ string table if the type is a string."
(define (file-dynamic-info file)
  "Return the <elf-dynamic-info> record of FILE, or #f if FILE lacks dynamic
info."
  (elf-dynamic-info (parse-elf (file->bytevector file))))
  (call-with-input-file file
    (lambda (port)
      (elf-dynamic-info (parse-elf (get-bytevector-all port))))))

(define (file-runpath file)
  "Return the DT_RUNPATH dynamic entry of FILE as a list of strings, or #f if


@@ 362,7 362,8 @@ exceeds total size~%"
                       (elf-segment-type segment))
               #f)))

    (let* ((elf     (parse-elf (file->bytevector file)))
    (let* ((elf     (call-with-input-file file
                      (compose parse-elf get-bytevector-all)))
           (expand  (cute expand-origin <> (dirname file)))
           (dyninfo (elf-dynamic-info elf)))
      (when dyninfo


@@ 401,13 402,12 @@ according to DT_NEEDED."
                        needed)))
            runpath))

  (define bv (file->bytevector file #:protection
                               (logior PROT_READ PROT_WRITE)))
  (define port
    (open-file file "r+b"))

  (dynamic-wind
    (const #t)
  (catch #t
    (lambda ()
      (let* ((elf (parse-elf bv))
      (let* ((elf      (parse-elf (get-bytevector-all port)))
             (entries  (dynamic-entries elf (dynamic-link-segment elf)))
             (needed   (filter-map (lambda (entry)
                                     (and (= (dynamic-entry-type entry)


@@ 425,14 425,15 @@ according to DT_NEEDED."
                  "~a: stripping RUNPATH to ~s (removed ~s)~%"
                  file new
                  (lset-difference string=? old new))
          ;; Write to bytevector directly.
          (let ((src (string->utf8 (string-append (string-join new ":")
                                                  "\0"))))
            (bytevector-copy! src 0 bv (dynamic-entry-offset runpath)
                              (bytevector-length src))))
          (seek port (dynamic-entry-offset runpath) SEEK_SET)
          (put-bytevector port (string->utf8 (string-join new ":")))
          (put-u8 port 0))
        (close-port port)
        new))
    (lambda ()
      (munmap bv))))
    (lambda (key . args)
      (false-if-exception (close-port port))
      (apply throw key args))))


(define-condition-type &missing-runpath-error &elf-error
  missing-runpath-error?


@@ 446,18 447,20 @@ according to DT_NEEDED."
  "Set the value of the DT_RUNPATH dynamic entry of FILE, which must name an
ELF file, to PATH, a list of strings.  Raise a &missing-runpath-error or
&runpath-too-long-error when appropriate."
  (define bv (file->bytevector file #:protection
                               (logior PROT_READ PROT_WRITE)))
  (dynamic-wind
    (const #t)
    (lambda ()
      (let* ((elf     (parse-elf bv))
  (define (call-with-input+output-file file proc)
    (let ((port (open-file file "r+b")))
      (guard (c (#t (close-port port) (raise c)))
        (proc port)
        (close-port port))))

  (call-with-input+output-file file
    (lambda (port)
      (let* ((elf     (parse-elf (get-bytevector-all port)))
             (entries (dynamic-entries elf (dynamic-link-segment elf)))
             (runpath (find (lambda (entry)
                              (= DT_RUNPATH (dynamic-entry-type entry)))
                            entries))
             (path    (string->utf8 (string-append (string-join path ":")
                                                   "\0"))))
             (path    (string->utf8 (string-join path ":"))))
        (unless runpath
          (raise (condition (&missing-runpath-error (elf elf)
                                                    (file file)))))


@@ 470,7 473,10 @@ ELF file, to PATH, a list of strings.  Raise a &missing-runpath-error or
          (raise (condition (&runpath-too-long-error (elf #f #;elf)
                                                     (file file)))))

        (bytevector-copy! path 0 bv (dynamic-entry-offset runpath)
                          (bytevector-length path))))
    (lambda ()
      (munmap bv))))
        (seek port (dynamic-entry-offset runpath) SEEK_SET)
        (put-bytevector port path)
        (put-u8 port 0)))))

;;; Local Variables:
;;; eval: (put 'call-with-input+output-file 'scheme-indent-function 1)
;;; End:

M guix/scripts/pack.scm => guix/scripts/pack.scm +6 -7
@@ 5,7 5,7 @@
;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
;;; Copyright © 2018 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
;;; Copyright © 2020-2023, 2025 Maxim Cournoyer <maxim@guixotic.coop>
;;; Copyright © 2020, 2021, 2022, 2023 Maxim Cournoyer <maxim@guixotic.coop>
;;; Copyright © 2020 Eric Bavier <bavier@posteo.net>
;;; Copyright © 2022 Alex Griffin <a@ajgrf.com>
;;; Copyright © 2023 Graham James Addis <graham@addis.org.uk>


@@ 1221,14 1221,12 @@ libfakechroot.so and related ld.so machinery as a fallback."

  (define build
    (with-imported-modules (source-module-closure
                            '((guix build io)
                              (guix build utils)
                            '((guix build utils)
                              (guix build union)
                              (guix build gremlin)
                              (guix elf)))
      #~(begin
          (use-modules (guix build io)
                       (guix build utils)
          (use-modules (guix build utils)
                       ((guix build union) #:select (symlink-relative))
                       (guix elf)
                       (guix build gremlin)


@@ 1262,7 1260,7 @@ libfakechroot.so and related ld.so machinery as a fallback."
            (match (find (lambda (segment)
                           (= (elf-segment-type segment) PT_INTERP))
                         (elf-segments elf))
              (#f #f)                   ;maybe a .so
              (#f #f)                             ;maybe a .so
              (segment
               (let ((bv (make-bytevector (- (elf-segment-memsz segment) 1))))
                 (bytevector-copy! (elf-bytes elf)


@@ 1282,7 1280,8 @@ libfakechroot.so and related ld.so machinery as a fallback."
            #$(if fakechroot?
                  ;; TODO: Handle scripts by wrapping their interpreter.
                  #~(if (elf-file? program)
                        (let* ((bv      (file->bytevector program))
                        (let* ((bv      (call-with-input-file program
                                          get-bytevector-all))
                               (elf     (parse-elf bv))
                               (interp  (elf-interpreter elf))
                               (gconv   (and interp

M tests/debug-link.scm => tests/debug-link.scm +90 -97
@@ 1,6 1,5 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2018 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2025 Maxim Cournoyer <maxim@guixotic.coop>
;;;
;;; This file is part of GNU Guix.
;;;


@@ 21,15 20,12 @@
  #:use-module (guix elf)
  #:use-module (guix build utils)
  #:use-module (guix build debug-link)
  #:use-module (guix build io)
  #:use-module (guix gexp)
  #:use-module (guix modules)
  #:use-module (guix store)
  #:use-module (guix tests)
  #:use-module (guix monads)
  #:use-module (guix derivations)
  #:use-module (gnu packages bootstrap)
  #:use-module ((gnu packages guile) #:select (guile-3.0))
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-26)
  #:use-module (srfi srfi-64)


@@ 44,12 40,15 @@
    (_
     #f)))

(define read-elf
  (compose parse-elf get-bytevector-all))


(test-begin "debug-link")

(unless %guile-executable (test-skip 1))
(test-assert "elf-debuglink, no .gnu_debuglink section"
  (let ((elf (parse-elf (file->bytevector %guile-executable))))
(test-assert "elf-debuglink"
  (let ((elf (call-with-input-file %guile-executable read-elf)))
    (match (call-with-values (lambda () (elf-debuglink elf)) list)
      ((#f #f)                                    ;no '.gnu_debuglink' section
       (pk 'no-debuglink #t))


@@ 57,101 56,95 @@
       (string-suffix? ".debug" file)))))

;; Since we need %BOOTSTRAP-GCC and co., we have to skip the following tests
;; when networking is unreachable because we'd fail to download it.  Since
;; using mmap to load ELF more efficiently, we also need the regular Guile
;; package, as guile-bootstrap cannot resolve dynamic symbols.
(with-external-store store
  (unless (and (network-reachable?) store) (test-skip 1))
  (test-assertm "elf-debuglink"
      ;; Check whether we can compute the CRC just like objcopy, and whether we
      ;; can retrieve it.
      (let* ((code (plain-file "test.c" "int main () { return 42; }"))
             (exp  (with-imported-modules (source-module-closure
                                           '((guix build io)
                                             (guix build utils)
                                             (guix build debug-link)
                                             (guix elf)))
                     #~(begin
                         (use-modules (guix build io)
                                      (guix build utils)
                                      (guix build debug-link)
                                      (guix elf)
                                      (rnrs io ports))
;; when networking is unreachable because we'd fail to download it.
(unless (network-reachable?) (test-skip 1))
(test-assertm "elf-debuglink"
  ;; Check whether we can compute the CRC just like objcopy, and whether we
  ;; can retrieve it.
  (let* ((code (plain-file "test.c" "int main () { return 42; }"))
         (exp  (with-imported-modules '((guix build utils)
                                        (guix build debug-link)
                                        (guix elf))
                 #~(begin
                     (use-modules (guix build utils)
                                  (guix build debug-link)
                                  (guix elf)
                                  (rnrs io ports))

                         (define read-elf
                           (compose parse-elf file->bytevector))
                     (define read-elf
                       (compose parse-elf get-bytevector-all))

                         (setenv "PATH" (string-join '(#$%bootstrap-gcc
                                                       #$%bootstrap-binutils)
                                                     "/bin:" 'suffix))
                         (invoke "gcc" "-O0" "-g" #$code "-o" "exe")
                         (copy-file "exe" "exe.debug")
                         (invoke "strip" "--only-keep-debug" "exe.debug")
                         (invoke "strip" "--strip-debug" "exe")
                         (invoke "objcopy" "--add-gnu-debuglink=exe.debug"
                                 "exe")
                         (call-with-values (lambda ()
                                             (elf-debuglink (read-elf "exe")))
                           (lambda (file crc)
                             (call-with-output-file #$output
                               (lambda (port)
                                 (let ((expected (call-with-input-file "exe.debug"
                                                   debuglink-crc32)))
                                   (write (list file (= crc expected))
                                          port))))))))))
        (mlet* %store-monad ((drv (gexp->derivation "debuglink" exp))
                             (x   (built-derivations (list drv))))
          (call-with-input-file (derivation->output-path drv)
            (lambda (port)
              (return (match (read port)
                        (("exe.debug" #t) #t)
                        (x                (pk 'fail x #f)))))))))
                     (setenv "PATH" (string-join '(#$%bootstrap-gcc
                                                   #$%bootstrap-binutils)
                                                 "/bin:" 'suffix))
                     (invoke "gcc" "-O0" "-g" #$code "-o" "exe")
                     (copy-file "exe" "exe.debug")
                     (invoke "strip" "--only-keep-debug" "exe.debug")
                     (invoke "strip" "--strip-debug" "exe")
                     (invoke "objcopy" "--add-gnu-debuglink=exe.debug"
                             "exe")
                     (call-with-values (lambda ()
                                         (elf-debuglink
                                          (call-with-input-file "exe"
                                            read-elf)))
                       (lambda (file crc)
                         (call-with-output-file #$output
                           (lambda (port)
                             (let ((expected (call-with-input-file "exe.debug"
                                               debuglink-crc32)))
                               (write (list file (= crc expected))
                                      port))))))))))
    (mlet* %store-monad ((drv (gexp->derivation "debuglink" exp))
                         (x   (built-derivations (list drv))))
      (call-with-input-file (derivation->output-path drv)
        (lambda (port)
          (return (match (read port)
                    (("exe.debug" #t) #t)
                    (x                (pk 'fail x #f)))))))))

  (unless (and (network-reachable?) store) (test-skip 1))
  (test-assertm "set-debuglink-crc"
      ;; Check whether 'set-debuglink-crc' successfully updates the CRC.
      (let* ((code  (plain-file "test.c" "int main () { return 42; }"))
             (debug (plain-file "exe.debug" "a"))
             (exp   (with-imported-modules (source-module-closure
                                            '((guix build io)
                                              (guix build utils)
                                              (guix build debug-link)
                                              (guix elf)))
                      #~(begin
                          (use-modules (guix build io)
                                       (guix build utils)
                                       (guix build debug-link)
                                       (guix elf)
                                       (rnrs io ports))
(unless (network-reachable?) (test-skip 1))
(test-assertm "set-debuglink-crc"
  ;; Check whether 'set-debuglink-crc' successfully updates the CRC.
  (let* ((code  (plain-file "test.c" "int main () { return 42; }"))
         (debug (plain-file "exe.debug" "a"))
         (exp   (with-imported-modules '((guix build utils)
                                         (guix build debug-link)
                                         (guix elf))
                  #~(begin
                      (use-modules (guix build utils)
                                   (guix build debug-link)
                                   (guix elf)
                                   (rnrs io ports))

                          (define read-elf
                            (compose parse-elf file->bytevector))
                      (define read-elf
                        (compose parse-elf get-bytevector-all))

                          (setenv "PATH" (string-join '(#$%bootstrap-gcc
                                                        #$%bootstrap-binutils)
                                                      "/bin:" 'suffix))
                          (invoke "gcc" "-O0" "-g" #$code "-o" "exe")
                          (copy-file "exe" "exe.debug")
                          (invoke "strip" "--only-keep-debug" "exe.debug")
                          (invoke "strip" "--strip-debug" "exe")
                          (invoke "objcopy" "--add-gnu-debuglink=exe.debug"
                                  "exe")
                          (set-debuglink-crc "exe" #$debug)
                          (call-with-values (lambda ()
                                              (elf-debuglink
                                               (read-elf "exe")))
                            (lambda (file crc)
                              (call-with-output-file #$output
                                (lambda (port)
                                  (write (list file crc) port)))))))))
        (mlet* %store-monad ((drv (gexp->derivation "debuglink" exp))
                             (x   (built-derivations (list drv))))
          (call-with-input-file (derivation->output-path drv)
            (lambda (port)
              (return (match (read port)
                        (("exe.debug" crc)
                         (= crc (debuglink-crc32 (open-input-string "a"))))
                        (x
                         (pk 'fail x #f))))))))))
                      (setenv "PATH" (string-join '(#$%bootstrap-gcc
                                                    #$%bootstrap-binutils)
                                                  "/bin:" 'suffix))
                      (invoke "gcc" "-O0" "-g" #$code "-o" "exe")
                      (copy-file "exe" "exe.debug")
                      (invoke "strip" "--only-keep-debug" "exe.debug")
                      (invoke "strip" "--strip-debug" "exe")
                      (invoke "objcopy" "--add-gnu-debuglink=exe.debug"
                              "exe")
                      (set-debuglink-crc "exe" #$debug)
                      (call-with-values (lambda ()
                                          (elf-debuglink
                                           (call-with-input-file "exe"
                                             read-elf)))
                        (lambda (file crc)
                          (call-with-output-file #$output
                            (lambda (port)
                              (write (list file crc) port)))))))))
    (mlet* %store-monad ((drv (gexp->derivation "debuglink" exp))
                         (x   (built-derivations (list drv))))
      (call-with-input-file (derivation->output-path drv)
        (lambda (port)
          (return (match (read port)
                    (("exe.debug" crc)
                     (= crc (debuglink-crc32 (open-input-string "a"))))
                    (x
                     (pk 'fail x #f)))))))))

(test-end "debug-link")

M tests/gremlin.scm => tests/gremlin.scm +13 -5
@@ 23,7 23,6 @@
  #:use-module (guix tests)
  #:use-module ((guix utils) #:select (call-with-temporary-directory
                                       target-aarch64?))
  #:use-module (guix build io)
  #:use-module (guix build utils)
  #:use-module (guix build gremlin)
  #:use-module (gnu packages bootstrap)


@@ 45,6 44,9 @@
    (_
     #f)))

(define read-elf
  (compose parse-elf get-bytevector-all))

(define c-compiler
  (or (which "gcc") (which "cc") (which "g++")))



@@ 53,7 55,8 @@

(unless %guile-executable (test-skip 1))
(test-assert "elf-dynamic-info-needed, executable"
  (let ((dyninfo (file-dynamic-info %guile-executable)))
  (let* ((elf     (call-with-input-file %guile-executable read-elf))
         (dyninfo (elf-dynamic-info elf)))
    (or (not dyninfo)                             ;static executable
        (lset<= string=?
                (list (string-append "libguile-" (effective-version))


@@ 137,7 140,9 @@
           (display "int main () { puts(\"hello\"); }" port)))
       (invoke c-compiler "t.c"
               "-Wl,--enable-new-dtags" "-Wl,-rpath=/foo" "-Wl,-rpath=/bar")
       (let* ((dyninfo (file-dynamic-info "a.out"))
       (let* ((dyninfo (elf-dynamic-info
                        (parse-elf (call-with-input-file "a.out"
                                     get-bytevector-all))))
              (old     (elf-dynamic-info-runpath dyninfo))
              (new     (strip-runpath "a.out"))
              (new*    (strip-runpath "a.out")))


@@ 191,7 196,10 @@
           (display "// empty file" port)))
       (invoke c-compiler "t.c"
               "-shared" "-Wl,-soname,libfoo.so.2")
       (let ((dyninfo (file-dynamic-info "a.out")))
	 (elf-dynamic-info-soname dyninfo))))))
       (let* ((dyninfo (elf-dynamic-info
                       (parse-elf (call-with-input-file "a.out"
                                    get-bytevector-all))))
              (soname  (elf-dynamic-info-soname dyninfo)))
	 soname)))))

(test-end "gremlin")