dockerarmqemuapple-m1debian-buster

How do I detect QEMU emulation from within a Docker container?


From within a docker container (in my case running a Debian Busty based image) how can I detect whether it's running under QEMU emulation (as happens on ARM Macs for AMD64 images)?

From the non-docker perspective I've seen suggestion that cpuinfo might surface this, but it doesn't yield anything directly QEMU related when run from inside my container:

$ docker run -it --entrypoint /bin/bash debian-buster-based-amd64-image

WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested

root@c93f6a8ec754:/app# cat /proc/cpuinfo
processor   : 0
BogoMIPS    : 48.00
Features    : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer : 0x00
CPU architecture: 8
CPU variant : 0x0
CPU part    : 0x000
CPU revision    : 0

processor   : 1
BogoMIPS    : 48.00
Features    : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer : 0x00
CPU architecture: 8
CPU variant : 0x0
CPU part    : 0x000
CPU revision    : 0

# ...etc

Docker with QEMU doesn't support a feature some functionality within my container depends on (inotify for file system events) - I'm looking to switch behaviour inside the container to avoid a crash that occurs when a library attempts to use it.


Solution

  • There are more ways to detect that the container is running under the emulation, however the most reliable way is to use identify if the entry point is emulated.

    When a container is created, the entry point will become the PID 1. The mechanism that Docker uses for the qemu emulation will detect that the entry point is for a different architecture and will involve the emulator to emulate the architecture. You can read more about the mechanism used in this post.

    Since the entry point will be emulated, the process name will be replaced with the qemu-xxxx where the xxxx is the architecture that will be emulated. We can identify if our entry pint process was substituted for qemu if we call ps -uax as in the following example:

    docker run -it --entrypoint /bin/bash amd64/ubuntu                                                                                                                                  
    WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
    root@043bd4f57ca8:/# ps -uax
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.7  0.1 149280 13804 pts/0    Ssl  07:08   0:00 /usr/bin/qemu-x86_64 /bin/bash /bin/bash
    root        22  0.0  0.1 150404  9564 ?        Rl+  07:04   0:00 ps -uax
    

    One easy way to utilise this knowlige to detect that we are emulated would be to get the pmap fro the PID 1 and check there is qemu in the response (the count of qemu is bigger than 0)

    root@043bd4f57ca8:/# pmap 1 | grep qemu | wc -l
    5
    

    The pmap will report the memory map of the process and from which binary/library the memory segment was created and the result in this case look like:

    root@043bd4f57ca8:/# pmap 1
    1:   /usr/bin/qemu-x86_64 /bin/bash /bin/bash
    0000000000200000    724K r---- qemu-x86_64
    00000000002c4000   1652K r-x-- qemu-x86_64
    0000000000470000    236K rw--- qemu-x86_64
    00000000004ba000     84K rw--- qemu-x86_64
    00000000004cf000    128K rw---   [ anon ]
    0000000017053000      4K -----   [ anon ]
    0000000017054000     16K rw---   [ anon ]
    0000004000000000    180K r---- bash
    000000400002d000    708K r---- bash
    00000040000de000    220K r---- bash
    0000004000115000     16K r---- bash
    0000004000119000     36K rw--- bash
    0000004000122000    460K rw---   [ anon ]
    000000400112c000      4K -----   [ anon ]
    000000400112d000   8192K rw---   [ anon ]
    000000400192d000      4K r---- ld-2.31.so
    000000400192e000    140K r---- ld-2.31.so
    0000004001951000     32K r---- ld-2.31.so
    0000004001959000      4K -----   [ anon ]
    000000400195a000      4K r---- ld-2.31.so
    000000400195b000      4K rw--- ld-2.31.so
    000000400195c000     12K rw---   [ anon ]
    0000004001961000     56K r---- libtinfo.so.6.2
    000000400196f000     60K r---- libtinfo.so.6.2
    000000400197e000     56K r---- libtinfo.so.6.2
    000000400198c000     16K r---- libtinfo.so.6.2
    0000004001990000      4K rw--- libtinfo.so.6.2
    0000004001991000      8K rw---   [ anon ]
    0000004001993000      4K r---- libdl-2.31.so
    0000004001994000      8K r---- libdl-2.31.so
    0000004001996000      4K r---- libdl-2.31.so
    0000004001997000      4K r---- libdl-2.31.so
    0000004001998000      4K rw--- libdl-2.31.so
    0000004001999000    136K r---- libc-2.31.so
    00000040019bb000   1504K r---- libc-2.31.so
    0000004001b33000    312K r---- libc-2.31.so
    0000004001b81000     16K r---- libc-2.31.so
    0000004001b85000      8K rw--- libc-2.31.so
    0000004001b87000     28K rw---   [ anon ]
    0000004001b90000     12K r---- libnss_files-2.31.so
    0000004001b93000     28K r---- libnss_files-2.31.so
    0000004001b9a000      8K r---- libnss_files-2.31.so
    0000004001b9c000      4K r---- libnss_files-2.31.so
    0000004001b9d000      4K rw--- libnss_files-2.31.so
    0000004001b9e000     24K rw---   [ anon ]
    0000ffff78916000   1808K rw---   [ anon ]
    0000ffff78ada000 131068K rwx--   [ anon ]
    0000ffff80ad9000      4K -----   [ anon ]
    0000ffff80ada000   1196K rw---   [ anon ]
    0000ffff80c05000      8K -----   [ anon ]
    0000ffff80c07000    140K rw---   [ anon ]
    0000ffff80c2a000      8K r----   [ anon ]
    0000ffff80c2c000      4K r-x--   [ anon ]
    0000ffffd61bc000    132K rw---   [ stack ]
     total           149536K
    

    To note, this works as well as in case when we want to emulate ARM under Intel as it can be seen in this example:

    root@9bd90133ebee:/# ps -uax
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.6  0.1 151208 13560 pts/0    Ssl  06:57   0:00 /usr/bin/qemu-aarch64 /usr/bin/bash
    
    root@9bd90133ebee:/# pmap 1
    1:   /usr/bin/qemu-aarch64 /usr/bin/bash
    0000000000200000   1408K r---- qemu-aarch64
    0000000000360000   3304K r-x-- qemu-aarch64
    000000000069a000    408K rw--- qemu-aarch64
    0000000000700000     48K rw--- qemu-aarch64
    000000000070c000    136K rw---   [ anon ]
    0000000001d5a000      4K -----   [ anon ]
    0000000001d5b000     12K rw---   [ anon ]
    0000005500000000   1132K r---- bash
    ....