Testing C++ code on PowerPC with QEMU
PowerPC is not a commonly used platform, but it’s quite useful for testing proper handling of endianness (as the vast majority of the current modern platforms is little-endian, but original PPC is big-endian). Let me provide simple instructions how to test C++ code on the PowerPC platform.
First, let’s install all the required dependencies (using Debian as an example):
$ sudo apt-get install qemu-system-ppc g++-powerpc-linux-gnu sshfs
Second, we need to get a proper image for QEMU. There are few QCOW2 ready-to-use images on https://people.debian.org/~aurel32/qemu/. They are quite outdated, but easy to use, and still helpful for testing. For PPC we’re going to use standard (headless) Debian Wheezy image:
$ curl -O https://people.debian.org/~aurel32/qemu/powerpc/debian_wheezy_powerpc_standard.qcow2
We’re going to use SSHFS for data sharing, therefore we’ll start the image with the port forwarding (binding port 60022 on the host with port 22 on the guest). Surely, if the SSH daemon on the guest is listening to the different port, the port binding option should be updated accordingly.
$ qemu-system-ppc -hda debian_wheezy_powerpc_standard.qcow2 -nic user,hostfwd=tcp::60022-:22
The root credentials: root
/root
, the user credentials: user
/user
.
Next, mount the user’s home on the guest with some directory on the host:
$ mkdir ppc-home
$ sshfs user@127.0.0.1:/home/user ppc-home -C -p 60022
To unmount after you done, please use the corresponding FUSE mount command:
$ fusermount3 -u ppc-home
Let’s compile and run within the emulated PPC platform the simple program that shows the various information about the platform.
#include <iostream>
void platform() {
#ifdef __BYTE_ORDER__
std::cout << "__BYTE_ORDER__\t\t= " << __BYTE_ORDER__ << " (";
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
std::cout << "__ORDER_BIG_ENDIAN__";
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
std::cout << "__ORDER_LITTLE_ENDIAN__";
#else
std::cout << "unknown";
#endif
std::cout << ")" << std::endl;
#else
std::cout << "__BYTE_ORDER__ is undefined" << std::endl;
#endif
#ifdef __FLOAT_WORD_ORDER__
std::cout << "__FLOAT_WORD_ORDER__\t= " << __FLOAT_WORD_ORDER__ << " (";
#if __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__
std::cout << "__ORDER_BIG_ENDIAN__";
#elif __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__
std::cout << "__ORDER_LITTLE_ENDIAN__";
#else
std::cout << "unknown";
#endif
std::cout << ")" << std::endl;
#else
std::cout << "__FLOAT_WORD_ORDER__ is undefined" << std::endl;
#endif
std::cout << "sizeof(bool)\t\t= " << sizeof(bool)
<< " (" << 8 * sizeof(bool) << " bits)" << std::endl;
std::cout << "sizeof(char)\t\t= " << sizeof(char)
<< " (" << 8 * sizeof(char) << " bits)" << std::endl;
std::cout << "sizeof(wchar_t)\t\t= " << sizeof(wchar_t)
<< " (" << 8 * sizeof(wchar_t) << " bits)" << std::endl;
std::cout << "sizeof(short)\t\t= " << sizeof(short)
<< " (" << 8 * sizeof(short) << " bits)" << std::endl;
std::cout << "sizeof(int)\t\t= " << sizeof(int)
<< " (" << 8 * sizeof(int) << " bits)" << std::endl;
std::cout << "sizeof(long)\t\t= " << sizeof(long)
<< " (" << 8 * sizeof(long) << " bits)" << std::endl;
std::cout << "sizeof(long long)\t= " << sizeof(long long)
<< " (" << 8 * sizeof(long long) << " bits)" << std::endl;
std::cout << "sizeof(float)\t\t= " << sizeof(float)
<< " (" << 8 * sizeof(float) << " bits)" << std::endl;
std::cout << "sizeof(double)\t\t= " << sizeof(double)
<< " (" << 8 * sizeof(double) << " bits)" << std::endl;
std::cout << "sizeof(void*)\t\t= " << sizeof(void*)
<< " (" << 8 * sizeof(void*) << " bits)" << std::endl;
}
int main() {
platform();
}
We use the cross-compilation:
$ cd ppc-home
$ curl -O https://gitlab.com/nuald/nuald-blogspot-com/-/raw/master/site/adoc/platform.C
$ powerpc-linux-gnu-g++ platform.C -o ppc -static
On the guest system, you’ll be able to run the binary now:
$ cd
$ uname -a > uname.txt
$ ./ppc > platform.txt
The generated text file will be available on the host to read:
$ cat uname.txt
Linux debian-powerpc 3.2.0-4-powerpc #1 Debian 3.2.51-1 ppc GNU/Linux
$ cat platform.txt
__BYTE_ORDER__ = 4321 (__ORDER_BIG_ENDIAN__)
__FLOAT_WORD_ORDER__ = 4321 (__ORDER_BIG_ENDIAN__)
sizeof(bool) = 1 (8 bits)
sizeof(char) = 1 (8 bits)
sizeof(wchar_t) = 4 (32 bits)
sizeof(short) = 2 (16 bits)
sizeof(int) = 4 (32 bits)
sizeof(long) = 4 (32 bits)
sizeof(long long) = 8 (64 bits)
sizeof(float) = 4 (32 bits)
sizeof(double) = 8 (64 bits)
sizeof(void*) = 4 (32 bits)
Please compare with the modern platform:
$ uname -a
Linux amber 5.11.16-arch1-1 #1 SMP PREEMPT Wed, 21 Apr 2021 17:22:13 +0000 x86_64 GNU/Linux
$ g++ platform.C -o modern && ./modern
__BYTE_ORDER__ = 1234 (__ORDER_LITTLE_ENDIAN__)
__FLOAT_WORD_ORDER__ = 1234 (__ORDER_LITTLE_ENDIAN__)
sizeof(bool) = 1 (8 bits)
sizeof(char) = 1 (8 bits)
sizeof(wchar_t) = 4 (32 bits)
sizeof(short) = 2 (16 bits)
sizeof(int) = 4 (32 bits)
sizeof(long) = 8 (64 bits)
sizeof(long long) = 8 (64 bits)
sizeof(float) = 4 (32 bits)
sizeof(double) = 8 (64 bits)
sizeof(void*) = 8 (64 bits)
As you could see, PowerPC has different endianess, long
and void*
sizes (as it’s a 32-bit platform).
While for some languages it won’t make a lot of difference (for example, Java is always big-endian
and use fixed sizes for its primitive types), for C++ it’s quite important and worth testing,
especially if you’re targeting a variety of platforms. Hope it helps you with your projects, and happy hacking.
Comments
Post a Comment