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

Popular posts from this blog

Web application framework comparison by memory consumption

Trac Ticket Workflow

Shellcode detection using libemu