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.
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
....