注意:

The Funtoo Linux project has transitioned to "Hobby Mode" and this wiki is now read-only.

Difference between revisions of "Frankenchroot"

From Funtoo
Jump to navigation Jump to search
 
(42 intermediate revisions by 2 users not shown)
Line 1: Line 1:
=''Funtoo binfmt_misc user mode Qemu chroot Guide: aka (Franken chroot)''=
{{Subpages|Required Packages,Live NFS Frankenchroot, Manual Binary Format Setup}}


==''Setting Up Your Filesystem''==
== Introduction ==


A ''frankenchroot'' is the act of chrooting into a directory on a host system (typically x86-64bit) which contains a foreign architecture (such as arm-32bit or arm-64bit) and having it all magically work thanks to the power of QEMU. While this may seem like it should not work at all (thus the name "frankenchroot", like the Frankenstein monster,) it actually happens to work -- and work well -- and it is a very useful trick. This page will guide you through the process of setting up a frankenchroot on your own system.


=== Benefits of Frankenchroot ===


===''Exporting Your Remote Mounts with NFS''===
There are several benefits to setting up a frankenchroot. The primary benefit is that it is a compelling alternative to the two other options one has available for building stuff on lower-powered (in terms of CPU performance as well as power utilization) embedded CPUs. Until now, the only options available have been compiling the software on the embedded CPU itself, or setting up a cross-compile environment, which can be complex and problematic.


Building on the embedded CPU can be fine, but for many embedded systems and packages can be slow, and it's common to run into limitations related to available CPU power and RAM, as well as IO speed on embedded storage. By using a frankenchroot, these limitations can be completely eliminated.


{{file|name=/etc/exports|desc=NFS file systems being exported|body=
== Types of Frankenchroot ==
# /etc/exports: NFS file systems being exported.  See exports(5).
/
192.168.0.0/24(insecure,no_root_squash,nohide,rw,sync,no_subtree_check)
/boot/
192.168.0.0/24(insecure,no_root_squash,nohide,rw,sync,no_subtree_check)
/dev/
192.168.0.0/24(insecure,no_root_squash,nohide,rw,sync,no_subtree_check,fsid=77)
}}


===''Mounting Your NFS Exports Locally and Binding Pseudo Filesystems''===
=== Simple Frankenchroot ===


{{console|body=
A "simple" frankenchroot is very similar to doing a regular chroot. One scenario would look like this. Let's say you wanted to test and arm-32bit stage3 tarball on your fast x86 workstation. You would create a directory, extract the stage3 into this directory, perform some setup to enable frankenchroot, and then you could chroot into this arm-32bit environment and it would work just like you chrooted into a native x86 stage3. You could then perform some test {{c|emerge}} commands which would take advantage of all your x86 CPU cores, memory and IO.
###i## mount foo.local:/ /mnt/piroot
###i## mount -t proc /proc /mnt/piroot/proc
###i## mount --rbind /sys /mnt/piroot/{sys,dev}
###i## mount --make-rslave /mnt/piroot/{sys,dev}
###i## mount -t devpts none /mnt/piroot/dev/pts
}}


{{note|
=== Live Frankenchroot ===
You can safely ignore setting up NFS exports and mount locally if you so desire. Mounting over NFS just makes
for a more versatile setup in the authors opinion}}


{{tip|
There is another cool thing you can do with a frankenchroot, and that is to set up a ''live'' frankenchoot. In this configuration, you would use something like a Raspberry Pi 3 and export its filesystems using NFS. These filesystems would then get mounted on a host x86 system via NFS. The host x86 system would then frankenchroot into this NFS-based filesystem -- even though the Raspberry Pi 3 is booted and running! In fact, it is quite possible -- and safe -- to run an emerge command on your x86 live frankenchroot while your Raspberry Pi does other things.
(For Better Performance!) mount a tmpfs on top of /mnt/piroot/var/tmp/portage
{{console|body=
###i## mount -v -t tmpfs -o size=8G,mode=775,uid=portage,gid=portage,nr_inodes=0 tmpfs /mnt/piroot/var/tmp/portage
}}


}}
The benefit here is that you can use your x86 system to {{c|emerge}} lots of packages, and it doesn't even require shutting down your Raspberry Pi and removing the MicroSD card, which can be inconvenient and requires the Raspberry Pi to be turned off.


==''Local Configuration''==
== Frankenchroot Setup ==


===''Editing make.conf''===
Up-to-date Frankenchroot setup steps can be found on the [https://code.funtoo.org/bitbucket/users/drobbins/repos/fchroot/browse code.funtoo.org frankenchroot page].
 
First, add the following to {{f|/etc/portage/make.conf}}:
{{file|name=/etc/portage/make.conf|desc=Portage make.conf file|body=
QEMU_USER_TARGETS="aarch64 arm"
FEATURES="-sandbox -ipc-sandbox -usersandbox candy"
}}
 
===''Editing package.use''===
 
Then, edit {{f|/etc/portage/package.use}}:
 
{{file|name=/etc/portage/package.use|desc=Portage package.use file|body=
app-emulation/qemu static-user
dev-libs/glib static-libs
sys-apps/attr static-libs
sys-libs/zlib static-libs
dev-libs/libpcre static-libs
}}
 
{{warning|
You ''WILL'' need to have updated to Funtoo-1.3-release and emerged the latest {{Package|dev-libs/glib}} ebuild as it has fixed the static library creation needed to compile and run Qemu.
}}
 
==='''Emerging Qemu'''===
 
Finally, emerge {{Package|app-emulation/qemu}}:
{{console|body=
###i## emerge -a app-emulation/qemu
}}
 
===''Building and Installing the Wrapper Binary''===
 
{{file|name=qemu-arm-wrapper.c|lang=C|desc=qemu arm wrapper|body=
    /*
    * Call QEMU binary with additional "-cpu cortex-a7" argument.
    *
    * Copyright (c) 2018 sakaki <sakaki@deciban.com>
    * License: GPL v3.0+
    *
    * Based on code from the Gentoo Embedded Handbook
    * ("General/Compiling_with_qemu_user_chroot")
    */
 
    #include <string.h>
    #include <unistd.h>
 
    int main(int argc, char **argv, char **envp) {
        char *newargv[argc + 3];
 
        newargv[0] = argv[0];
        newargv[1] = "-cpu";
        newargv[2] = "cortex-a7";
 
        memcpy(&newargv[3], &argv[1], sizeof(*argv) * (argc -1));
        newargv[argc + 2] = NULL;
        return execve("/usr/local/bin/qemu-arm", newargv, envp);
    }
}}
 
{{console|body=
###i## gcc -static -O3 -s -o qemu-arm-wrapper qemu-arm-wrapper.c
}}
 
{{console|body=
###i## cp -av /usr/bin/qemu-arm /mnt/piroot/usr/local/bin/qemu-arm
###i## cp -av qemu-arm-wrapper /mnt/piroot/usr/local/bin/qemu-arm-wrapper
}}
 
===''Setting up binfmt_misc and Starting the Service''===
 
{{console|body=
 
###i## echo ":arm:M::\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-arm-wrapper:" > /proc/sys/fs/binfmt_misc/register
 
}}
 
{{console|body=
###i## rc-service binfmt restart
}}
 
{{console|body=
###i## rc-service binfmt status
}}
 
 
 
{{tip|
The following code will create the binfmt_misc register string for the arch you are emulating in usermode Qemu. Usage is as followed:
{{console|body=
###i## chmod +x masky
###i## masky /path/to/foreign/binary
}}
 
* Paste the output to /proc/sys/fs/binfmt_misc/register
* Change "arm" in the following code to whatever you want the name of the binfmt_misc name to be when it is registered
* Change /usr/local/bin/qemu-arm-wrapper to the name of your wrapper
 
{{file|name=masky|lang=python|desc=masky|body=
 
#!/usr/bin/python3
import sys
import struct
import string
import codecs
 
printable_chars = set(string.printable)
printable_chars = set()
def print_out_hexstring(hexstring):
    to_process = hexstring
    while len(to_process):
        ascii_value = chr(int(to_process[:2], 16))
        to_process = to_process[2:]
        if ascii_value in printable_chars:
            sys.stdout.write(ascii_value)
        else:
            sys.stdout.write("\\x" + "{0:02x}".format(ord(ascii_value)))
 
chunk_as_hexstring = ""
with open(sys.argv[1], 'rb') as f:
    for x in range(0,19):
      chunk_as_hexstring += f.read(1).hex()
mask_as_hexstring = "fffffffffffffffcfffffffffffffffffeffff"
mask = int(mask_as_hexstring, 16)
chunk = int(chunk_as_hexstring, 16)
out_as_hexstring = hex(chunk & mask)[2:]
sys.stdout.write(":arm:M::")
print_out_hexstring(out_as_hexstring)
sys.stdout.write(":")
print_out_hexstring(mask_as_hexstring)
sys.stdout.write(":/usr/local/bin/qemu-arm-wrapper:\n")
}}
 
}}
 
==''Chrooting and Letting the Magic Happen''==
 
===Entering Franken Chroot===
 
{{console|body=
###i## mv -v /mnt/piroot/etc/resolv.conf{,.orig}
###i## cp -v -L /etc/resolv.conf /mnt/piroot/etc/
###i## env -i HOME=/root TERM=$TERM /bin/chroot /mnt/piroot /bin/bash -l
###i## export PS1="(Franken_Chroot) $PS1"
}}
 
===Exiting Franken Chroot and Final Thoughts===
 
{{console|body=
###i## rm /etc/resolv.conf
###i## mv -v /etc/resolv.conf{.orig,}
###i## exit
###i## umount -v /mnt/piroot/var/tmp/portage
###i## umount -lR /mnt/piroot
###i## sync
}}


== How It Works ==


A frankenchroot works by leveraging a static QEMU executable as the "handler" for any binaries that are built for your embedded device. Then, when you are in the chroot and the kernel attempts to run one of these binaries, it will call QEMU to execute the binary, and QEMU will interpret the machine code in the binary and run it transparently. The "handler" mechanism used is the Linux kernel {{c|binfmt_misc}} functionality.


So, the steps in the frankenchroot work as follows:


# Linux kernel encounters foreign binary.
# Linux kernel sees that this is a foreign binary and that a static binary QEMU is registered to "handle" it.
# Linux kernel runs QEMU and essentially passes the foreign binary to QEMU as an argument. QEMU runs fine since it is compiled for your workstation's architecture rather than the foreign architecture. So QEMU runs happily on your local CPU.
# QEMU does its thing and interprets the contents of the foreign binary so that it performs all calculations and actions as if it were running on native hardware. Any kernel calls contained in the foreign binary are either translated by QEMU so that they can run using your host's kernel or emulated.


[[Category:HOWTO]]
[[Category:HOWTO]]
[[Category:Official Documentation]]

Latest revision as of 00:54, January 14, 2020

Introduction

A frankenchroot is the act of chrooting into a directory on a host system (typically x86-64bit) which contains a foreign architecture (such as arm-32bit or arm-64bit) and having it all magically work thanks to the power of QEMU. While this may seem like it should not work at all (thus the name "frankenchroot", like the Frankenstein monster,) it actually happens to work -- and work well -- and it is a very useful trick. This page will guide you through the process of setting up a frankenchroot on your own system.

Benefits of Frankenchroot

There are several benefits to setting up a frankenchroot. The primary benefit is that it is a compelling alternative to the two other options one has available for building stuff on lower-powered (in terms of CPU performance as well as power utilization) embedded CPUs. Until now, the only options available have been compiling the software on the embedded CPU itself, or setting up a cross-compile environment, which can be complex and problematic.

Building on the embedded CPU can be fine, but for many embedded systems and packages can be slow, and it's common to run into limitations related to available CPU power and RAM, as well as IO speed on embedded storage. By using a frankenchroot, these limitations can be completely eliminated.

Types of Frankenchroot

Simple Frankenchroot

A "simple" frankenchroot is very similar to doing a regular chroot. One scenario would look like this. Let's say you wanted to test and arm-32bit stage3 tarball on your fast x86 workstation. You would create a directory, extract the stage3 into this directory, perform some setup to enable frankenchroot, and then you could chroot into this arm-32bit environment and it would work just like you chrooted into a native x86 stage3. You could then perform some test emerge commands which would take advantage of all your x86 CPU cores, memory and IO.

Live Frankenchroot

There is another cool thing you can do with a frankenchroot, and that is to set up a live frankenchoot. In this configuration, you would use something like a Raspberry Pi 3 and export its filesystems using NFS. These filesystems would then get mounted on a host x86 system via NFS. The host x86 system would then frankenchroot into this NFS-based filesystem -- even though the Raspberry Pi 3 is booted and running! In fact, it is quite possible -- and safe -- to run an emerge command on your x86 live frankenchroot while your Raspberry Pi does other things.

The benefit here is that you can use your x86 system to emerge lots of packages, and it doesn't even require shutting down your Raspberry Pi and removing the MicroSD card, which can be inconvenient and requires the Raspberry Pi to be turned off.

Frankenchroot Setup

Up-to-date Frankenchroot setup steps can be found on the code.funtoo.org frankenchroot page.

How It Works

A frankenchroot works by leveraging a static QEMU executable as the "handler" for any binaries that are built for your embedded device. Then, when you are in the chroot and the kernel attempts to run one of these binaries, it will call QEMU to execute the binary, and QEMU will interpret the machine code in the binary and run it transparently. The "handler" mechanism used is the Linux kernel binfmt_misc functionality.

So, the steps in the frankenchroot work as follows:

  1. Linux kernel encounters foreign binary.
  2. Linux kernel sees that this is a foreign binary and that a static binary QEMU is registered to "handle" it.
  3. Linux kernel runs QEMU and essentially passes the foreign binary to QEMU as an argument. QEMU runs fine since it is compiled for your workstation's architecture rather than the foreign architecture. So QEMU runs happily on your local CPU.
  4. QEMU does its thing and interprets the contents of the foreign binary so that it performs all calculations and actions as if it were running on native hardware. Any kernel calls contained in the foreign binary are either translated by QEMU so that they can run using your host's kernel or emulated.