unofficial mirror of bug-guix@gnu.org 
 help / color / mirror / code / Atom feed
From: Danny Milosavljevic <dannym@scratchpost.org>
To: 43513@debbugs.gnu.org, <ludo@gnu.org>
Subject: bug#43513: json-c build failure (on armhf-linux) while trying to build u-boot
Date: Mon, 21 Sep 2020 14:22:26 +0200	[thread overview]
Message-ID: <20200921134855.2ed40eb0@scratchpost.org> (raw)
In-Reply-To: <20200919173628.423331da@scratchpost.org>


[-- Attachment #1.1: Type: text/plain, Size: 6193 bytes --]

Hi,

I found the underlying cause of the problem with qemu transparent emulation:

* qemu transparent emulator has 64 bit registers
* the thing it's emulating has 32 bit registers
* The glibc in the distro that is running in the emulator is using getdents64
(on 32 bits!) and then (rightfully) checking whether d_off and the inode number
fit into their own (32 bits/entry) struct, which they don't (the thing they get
from the kernel is 64 bits/entry).

See also https://lore.kernel.org/lkml/20181229015453.GA6310@bombadil.infradead.org/T/
for an analysis.

See also https://sourceware.org/bugzilla/show_bug.cgi?id=23960 for a discussion.

Least-shitty workaround: Use a 32-bit qemu (yes, a qemu compiled on 32 bit)
on a 64 bit machine for transparent emulation of ANOTHER 32-bit machine.
That way, the kernel can know that there's a 32 bit user lurking somewhere up
the call chain that is calling getdents64 and is not actually able to process the
result.  "The truth?  It can't handle the truth."

The right fix: One could also tell all the packages in the emulated
system to use the large file size API (-D_FILE_OFFSET_BITS=64 and co).  In this
case cmake is affected--but it could be any number of things.  I think that that
is the only good fix (we could also add a compile-time check whether <dirent.h>
has been included without anyone specifying -D_FILE_OFFSET_BITS=64--that would
make finding these problems a LOT easier; if possible, emit that compile-time
error only if readdir is actually called anywhere).

For the workaround, we could adapt Guix system's gnu/services/virtualization.scm
so it uses a qemu built for the 32-bit variant of the host architecture if it's
emulating a 32 bit system.

So qemu could be compiled for i686 if the host is x86_64 and the emulated
system is armhf,
qemu could be compiled for armhf if the host is aarch64 and the emulated system
is armhf and so on.

That also means that if a host system is 64 bits and DOES NOT HAVE a 32 bit
pendant target, then the emulation of a 32 bit system is going to be imperfect.

One way to fix that would be to fix the kernel to accept an argument on
the getdents64 syscall (and similar ones) that tells it whether the user wants
to have 64 bit offsets or 32 bit offsets[1].  Right now, from user space, that
is only possible via process personality flags.  And those would switch the
entire qemu executable over to 32 bits, which we don't want (qemu itself has
to do stuff using kernel syscalls, so it needs to be capable of 64 bits
if it itself is 64 bits).  And I think that even that case is not being
handled in the kernel correctly.  So this fix cannot be done.

@Ludo:

Anyway, I have a question on how to replicate what "guix build --target=i686..."
does, in the guix service definition.  How can I make it use
package-cross-derivation instead of package-derivation ?
See attachment.

With the attachment and the service definition

            (service qemu-binfmt-service-type
             (qemu-binfmt-configuration
              (platforms (lookup-qemu-platforms "arm"))
              (guix-support? #t)))

in order to emulate ARM on x86_64 I eventually get:

>Building qemu-minimal for i686

(That's what I want!)

>[...]
>starting phase `configure'
>
>ERROR: pkg-config binary 'i686-unknown-linux-gnu-pkg-config' not found

pkg-config has not been cross-compiled...

That's because it's not using package-cross-derivation, I guess.

>command "./configure" "--cc=/gnu/store/rn75fm7adgx3pw5j8pg3bczfqq1y17lk-gcc-7.5.0/bin/gcc" "--host-cc=/gnu/store/rn75fm7adgx3pw5j8pg3bczfqq1y17lk-gcc-7.5.0/bin/gcc" "--disable-debug-info" "--enable-virtfs" "--prefix=/gnu/store/80ljf47lrh8arrzjmkrrqxghc0k67b3s-qemu-minimal-5.1.0" "--sysconfdir=/etc" "--cross-prefix=i686-unknown-linux-gnu-" "--target-list=i386-softmmu,x86_64-softmmu" failed with status 1

I think that "--cc" should use ,(cc-for-target) at all times.

Better this:

diff --git a/gnu/packages/virtualization.scm b/gnu/packages/virtualization.scm
index 53e9dde125..bf712afd4a 100644
--- a/gnu/packages/virtualization.scm
+++ b/gnu/packages/virtualization.scm
@@ -227,7 +227,7 @@
                (setenv "LDFLAGS" "-lrt")
                (apply invoke
                       `("./configure"
-                        ,(string-append "--cc=" (which "gcc"))
+                        ,(string-append "--cc=" ,(cc-for-target))
                         ;; Some architectures insist on using HOST_CC
                         ,(string-append "--host-cc=" (which "gcc"))
                         "--disable-debug-info" ; save build space

[1] A way for userspace to tell the kernel that is to use getdents instad of
getdents64 on 32 bits, like it used to.  But they don't want to do that anymore.

Another way would be for qemu to translate a syscall getdents64 from the guest
to getdents on the host if the machine they are emulating is 32 bits.  But that
would mean that getdents on a 64 bit host would still have to act like it's
32 bits and translate the d_off accordingly.  That's not guaranteed[2].

Or even using the old readdir syscall.

[2] Linux-5.8.8:

[... CONFIG_COMPAT ...]
SYSCALL_DEFINE3(getdents, unsigned int, fd,
                struct linux_dirent __user *, dirent, unsigned int, count)
{
        struct fd f;
        struct getdents_callback buf = {
                .ctx.actor = filldir,
                .count = count,
                .current_dir = dirent
        };
        int error;

        f = fdget_pos(fd);
        if (!f.file)
                return -EBADF;

        error = iterate_dir(f.file, &buf.ctx);
        if (error >= 0)
                error = buf.error;
        if (buf.prev_reclen) {
                struct linux_dirent __user * lastdirent;
                lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;

                if (put_user(buf.ctx.pos, &lastdirent->d_off))
                        error = -EFAULT;
                else
                        error = count - buf.count;
        }
        fdput_pos(f);
        return error;
}
It's not guaranteed.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: services-qemu-transparent-emulation-make-more-exact.patch --]
[-- Type: text/x-patch, Size: 11665 bytes --]

diff --git a/gnu/services/virtualization.scm b/gnu/services/virtualization.scm
index 20e104f48c..77f15ba07b 100644
--- a/gnu/services/virtualization.scm
+++ b/gnu/services/virtualization.scm
@@ -23,6 +23,7 @@
   #:use-module (gnu bootloader grub)
   #:use-module (gnu image)
   #:use-module (gnu packages admin)
+  #:use-module (gnu packages cross-base)
   #:use-module (gnu packages ssh)
   #:use-module (gnu packages virtualization)
   #:use-module (gnu services base)
@@ -554,12 +555,13 @@ potential infinite waits blocking libvirt."))
 
 ;; Platforms that QEMU can emulate.
 (define-record-type <qemu-platform>
-  (qemu-platform name family magic mask)
+  (qemu-platform name family register-width magic mask)
   qemu-platform?
-  (name     qemu-platform-name)                   ;string
-  (family   qemu-platform-family)                 ;string
-  (magic    qemu-platform-magic)                  ;bytevector
-  (mask     qemu-platform-mask))                  ;bytevector
+  (name     qemu-platform-name)                                 ;string
+  (family   qemu-platform-family)                               ;string
+  (register-width    qemu-platform-register-width)              ;int, in bits
+  (magic    qemu-platform-magic)                                ;bytevector
+  (mask     qemu-platform-mask))                                ;bytevector
 
 (define-syntax bv
   (lambda (s)
@@ -576,123 +578,123 @@ potential infinite waits blocking libvirt."))
 ;;; 'scripts/qemu-binfmt-conf.sh' in QEMU.
 
 (define %i386
-  (qemu-platform "i386" "i386"
+  (qemu-platform "i386" "i386" 32
                  (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00")
                  (bv "\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %i486
-  (qemu-platform "i486" "i386"
+  (qemu-platform "i486" "i386" 32
                  (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00")
                  (bv "\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %alpha
-  (qemu-platform "alpha" "alpha"
+  (qemu-platform "alpha" "alpha" 64
                  (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90")
                  (bv "\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %arm
-  (qemu-platform "arm" "arm"
+  (qemu-platform "arm" "arm" 32
                  (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %armeb
-  (qemu-platform "armeb" "arm"
+  (qemu-platform "armeb" "arm" 32
                  (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %sparc
-  (qemu-platform "sparc" "sparc"
+  (qemu-platform "sparc" "sparc" 32 ; FIXME check
                  (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %sparc32plus
-  (qemu-platform "sparc32plus" "sparc"
+  (qemu-platform "sparc32plus" "sparc" 32 ; FIXME check
                  (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x12")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %ppc
-  (qemu-platform "ppc" "ppc"
+  (qemu-platform "ppc" "ppc" 32
                  (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %ppc64
-  (qemu-platform "ppc64" "ppc"
+  (qemu-platform "ppc64" "ppc" 64
                  (bv "\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %ppc64le
-  (qemu-platform "ppc64le" "ppcle"
+  (qemu-platform "ppc64le" "ppcle" 64
                  (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15\x00")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\x00")))
 
 (define %m68k
-  (qemu-platform "m68k" "m68k"
+  (qemu-platform "m68k" "m68k" 32
                  (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04")
                  (bv "\xff\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 ;; XXX: We could use the other endianness on a MIPS host.
 (define %mips
-  (qemu-platform "mips" "mips"
+  (qemu-platform "mips" "mips" 32
                  (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %mipsel
-  (qemu-platform "mipsel" "mips"
+  (qemu-platform "mipsel" "mips" 32
                  (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %mipsn32
-  (qemu-platform "mipsn32" "mips"
+  (qemu-platform "mipsn32" "mips" 32 ; FIXME check
                  (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %mipsn32el
-  (qemu-platform "mipsn32el" "mips"
+  (qemu-platform "mipsn32el" "mips" 32 ; FIXME check
                  (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %mips64
-  (qemu-platform "mips64" "mips"
+  (qemu-platform "mips64" "mips" 64
                  (bv "\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %mips64el
-  (qemu-platform "mips64el" "mips"
+  (qemu-platform "mips64el" "mips" 64
                  (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %riscv32
-  (qemu-platform "riscv32" "riscv"
+  (qemu-platform "riscv32" "riscv" 32 ; FIXME
                  (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %riscv64
-  (qemu-platform "riscv64" "riscv"
+  (qemu-platform "riscv64" "riscv" 64
                  (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %sh4
-  (qemu-platform "sh4" "sh4"
+  (qemu-platform "sh4" "sh4" 32
                  (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %sh4eb
-  (qemu-platform "sh4eb" "sh4"
+  (qemu-platform "sh4eb" "sh4" 32
                  (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %s390x
-  (qemu-platform "s390x" "s390x"
+  (qemu-platform "s390x" "s390x" 64
                  (bv "\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
 (define %aarch64
-  (qemu-platform "aarch64" "arm"
+  (qemu-platform "aarch64" "arm" 64
                  (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff")))
 
 (define %hppa
-  (qemu-platform "hppa" "hppa"
+  (qemu-platform "hppa" "hppa" 32
                  (bv "\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x0f")
                  (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff")))
 
@@ -712,12 +714,48 @@ potential infinite waits blocking libvirt."))
   qemu-binfmt-configuration make-qemu-binfmt-configuration
   qemu-binfmt-configuration?
   (qemu        qemu-binfmt-configuration-qemu
-               (default qemu))
+               (default qemu-minimal))
   (platforms   qemu-binfmt-configuration-platforms
                (default '()))                     ;safest default
   (guix-support? qemu-binfmt-configuration-guix-support?
                  (default #f)))
 
+(define (register-width system)
+  (match (%current-system)
+   ("i686-linux" 32)
+   ("armhf-linux" 32)
+   ("aarch64-linux" 64)
+   ("x86_64-linux" 64)))
+
+(define (closest-cross-compiled-qemu qemu target-bits)
+  "Cross-compile QEMU for the given TARGET-BITS platform that is closest to
+the actual host architecture, if possible.  This is in order to prevent
+https://lore.kernel.org/lkml/20181229015453.GA6310@bombadil.infradead.org/T/"
+  (define (cross-compiled-qemu target)
+    (package
+      (inherit qemu)
+      (arguments
+       (substitute-keyword-arguments (package-arguments qemu)
+        ((#:configure-flags flags)
+         `(cons ,(string-append "--cross-prefix=" target "-")
+                ,flags))))
+      (native-inputs
+        `(("cross-gcc" ,(cross-gcc target))
+          ("cross-binutils" ,(cross-binutils target))
+          ,@(package-native-inputs qemu)))))
+  (match target-bits
+   (64 qemu)
+   (32 (match (register-width (%current-system))
+        (32 qemu)
+        (64 (match (%current-system)
+             ("x86_64-linux"
+              (cross-compiled-qemu (nix-system->gnu-triplet "i686-linux")))
+             ("aarch64-linux"
+              (cross-compiled-qemu "arm-linux-gnueabihf"))
+             (_ (begin
+                   ;; TODO: Print warning
+                   qemu))))))))
+
 (define (qemu-platform->binfmt qemu platform)
   "Return a gexp that evaluates to a binfmt string for PLATFORM, using the
 given QEMU package."
@@ -732,12 +770,13 @@ given QEMU package."
           (bytevector->u8-list bv))))
 
   (match platform
-    (($ <qemu-platform> name family magic mask)
+    (($ <qemu-platform> name family register-width magic mask)
      ;; See 'Documentation/binfmt_misc.txt' in the kernel.
      #~(string-append ":qemu-" #$name ":M::"
                       #$(bytevector->binfmt-string magic)
                       ":" #$(bytevector->binfmt-string mask)
-                      ":" #$(file-append qemu "/bin/qemu-" name)
+                      ":" #$(file-append (closest-cross-compiled-qemu qemu register-width)
+                                         "/bin/qemu-" name)
                       ":"                         ;FLAGS go here
                       ))))
 

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

  parent reply	other threads:[~2020-09-21 12:23 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-19 15:36 bug#43513: json-c build failure (on armhf-linux) while trying to build u-boot Danny Milosavljevic
2020-09-19 21:05 ` Danny Milosavljevic
2020-09-21 12:22 ` Danny Milosavljevic [this message]
2020-09-21 12:23   ` Danny Milosavljevic
2020-09-21 12:44   ` Danny Milosavljevic
2020-09-25 10:13   ` Ludovic Courtès
2020-09-25 11:13     ` Danny Milosavljevic
2020-09-25 11:18       ` Danny Milosavljevic
2020-09-25 16:00         ` Ludovic Courtès
2020-09-25 16:25           ` Danny Milosavljevic
2020-09-26 10:53             ` Danny Milosavljevic
2020-09-26 17:20               ` Andreas Enge
2020-09-27  9:50               ` Andreas Enge
2020-09-27 11:32                 ` Danny Milosavljevic
2020-09-29 10:25               ` Ludovic Courtès
2020-09-29 10:43                 ` Danny Milosavljevic
2020-09-29 11:05                   ` Danny Milosavljevic
2020-09-30  9:10                   ` Ludovic Courtès
2020-09-30 11:27                     ` Danny Milosavljevic
2020-09-30 12:17                       ` Andreas Enge
2020-10-01 16:18                         ` Bengt Richter
2020-09-25 16:02       ` Ludovic Courtès
2020-09-25 16:23         ` Danny Milosavljevic
2020-09-25 16:37           ` Danny Milosavljevic

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://guix.gnu.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200921134855.2ed40eb0@scratchpost.org \
    --to=dannym@scratchpost.org \
    --cc=43513@debbugs.gnu.org \
    --cc=ludo@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).