33

How can I test or check C++ code for endian-independence? It's already implemented, I would just like to verify that it works on both little- and big-endian platforms.

I could write unit tests and run them on the target platforms, but I don't have the hardware. Perhaps emulators?

Are there compile time checks that can be done?

sourcenouveau
  • 29,356
  • 35
  • 146
  • 243
  • 4
    @0A0D, this is not a dup. That question was on how to detect endianness of current platform. OP wants to check if his code works correctly on both BE and LE platforms. – Kirill V. Lyadvinsky Jun 20 '11 at 14:30
  • @0A0D/@Chris: you need to re-read the question – Paul R Jun 20 '11 at 14:30
  • 1
    @0A0D: This is not about detecting the endianness of the platform. The question is how to be sure whether some give code depends on endianness. – sharptooth Jun 20 '11 at 14:31
  • @sharptooth: My apologies, I misread the question. –  Jun 20 '11 at 14:32
  • 4
    @Chris None of the results on Google first page for "test endianness" address my question. – sourcenouveau Jun 20 '11 at 14:32
  • @emddudley: to avoid lmgtfy links now and in the future, you should express in your question that googling didn't help. In this case, it only turns up how to detect endianess at runtime, not how to test your software on different endian:ity hardware. – Macke Jun 20 '11 at 14:34
  • Damned filter bubble. Google knows I'm a developer and shows those results to me first. Sorry. – Chris Eberle Jun 20 '11 at 14:35
  • @emddudley: Maybe the question should be changed to express a desire to ensure endian interoperability. That's what tripped me up initially. –  Jun 20 '11 at 14:37
  • 2
    @Chris: nope chris. Google knows _I_ am a developer, and the top links (all that I've seen, and I looked down the list) were about determining endianness, not testing code endian-safe-ness – sehe Jun 20 '11 at 14:37
  • 1
    @0A0D I added some clarification that I'm looking to verify, not implement. – sourcenouveau Jun 20 '11 at 15:28
  • @emddudley: How are you transferring the data between machines ? I believe TCP/IP uses big endian. –  Jun 20 '11 at 16:21
  • @0A0D: I'm primarily on Windows, so I'm byteswapping from little- to big-endian. The message protocol I have is actually mixed-endian... – sourcenouveau Jun 20 '11 at 16:50
  • Depending on the target platforms you might also want to check for alignment problems. – starblue Jun 20 '11 at 20:51

7 Answers7

17

If you have access to an x86-based Mac then you can take advantage of the fact that Mac OS X has PowerPC emulation built in as well as developer tool support for both x86 (little endian) and PowerPC (big endian). This enables you to compile and run a big and little endian executable on the same platform, e.g.

$ gcc -arch i386 foo.c -o foo_x86 # build little endian x86 executable
$ gcc -arch ppc foo.c -o foo_ppc  # build big endian PowerPC executable

Having built both big endian and little endian executables you can then run whatever unit tests you have available on both, which will catch some classes of endianness-related problems, and you can also compare any data generated by the executables (files, network packets, whatever) - this should obviously match.

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • 1
    Rather expensive solution of the problem if you have no Mac. Downvote not mine. – Kirill V. Lyadvinsky Jun 20 '11 at 14:33
  • 2
    @Kirill (and anonymous down-voter): note that I specifically said "if you have access to a Mac" - there are many ways of getting access to a Mac for a period of testing without actually buying one (although as has already been pointed out, a cheap second-hand Mac Mini costs very little on eBay). – Paul R Jun 20 '11 at 15:26
  • @Paul: do you know if it is possible to compile for the PPC architecture on an x86 machine as the inverse? Maybe I didn't word this right... –  Jun 20 '11 at 16:01
  • @Paul: Also, see OP's edit. Using your methodology, how can he verify? –  Jun 20 '11 at 16:03
  • @0A0D: you can both *compile* and *run* both x86 and ppc code on an Intel Mac, so you would just run the unit tests and any other validation on the same machine that you just built the executables on. – Paul R Jun 20 '11 at 16:31
  • @Paul: What if all you have is an x86 machine ? Can you also compile for PPC? –  Jun 20 '11 at 16:33
  • @0A0D: yes, this is what I said in my answer and in my last comment - Intel Macs can *build* and *run* ppc code as well as x86 code. I do this all the time for testing legacy cross-platform code. – Paul R Jun 20 '11 at 16:35
  • @Paul: How does compiling for two different architectures ensure that the data is represented the same on both architectures? –  Jun 20 '11 at 16:44
  • @0A0D: the general idea is that you run whatever unit tests you have available on both executables, which will catch some classes of endianness-related problems, and you also compare any data generated by the executables - this should obviously match. – Paul R Jun 20 '11 at 17:34
  • @Paul: I think your last comment should be added to your answer because that really gets to the root of the OP's problem, even if it seems obvious. –  Jun 20 '11 at 17:57
  • @0A0D: yes, OK, I thought it was obvious too, but I'll add it if you think it might help – Paul R Jun 20 '11 at 17:59
  • 2
    Probably the cheapest way to get access to a Mac is to simply go to an Apple Store. You can try out the Macs there and compile your stuff. Just make sure the employees don't see you. :) –  Jul 15 '11 at 22:43
  • @WTP: good idea - I don't suppose the employees will mind, so long as you're not messing up the display machines or preventing potential customers from using them. – Paul R Jul 16 '11 at 07:17
  • @Paul R: I don't think they like it if you use the command line, plug in your USB drive and start a long compiling process waiting till it's finished. If you need to install a compiler you have to ask them to enter the administrator account's password in order to do so. :) –  Jul 17 '11 at 10:54
  • @WTP: probably best to take a portable drive with a complete bootable OS, developer tools and your projects, and boot from that - that way the drive on the store machine isn't touched. – Paul R Jul 17 '11 at 11:01
  • I guess this isn't possible anymore, since rosetta was dropped? –  Jul 08 '14 at 08:35
  • @Erik: yes, sadly the later versions of OS X no longer have Rosetta, so PowerPC cross-development is no longer an option on current x86 Macs. – Paul R Jul 08 '14 at 09:34
12

You can set up an execution environment in the opposite endianness using qemu. For example if you have access to little-endian amd64 or i386 hardware, you can set up qemu to emulate a PowerPC Linux platform, run your code there.

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
7

I read a story that used Flint (Flexible Lint) to diagnose this kind of errors.

Don't know the specifics anymore, but let me google the story back for you:

http://www.datacenterworks.com/stories/flint.html

An Example: Diagnosing Endianness Errors

On a recent engagement, we were porting code from an old Sequent to a SPARC, and after the specific pointer issues we discussed in the Story of Thud and Blunder, we needed to look for other null pointer issues and also endian-ness errors.

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
7

I would suggest adapting a coding technique that avoids the problem all together.

First, you have to understand in which situation an endianess problem occurs. Then either find an endianess-agnostic way to write this, or isolate the code.

For example, a typical problem where endianess issues can occur is when you use memory accesses or unions to pick out parts of a larger value. Concretely, avoid:

long x;
...
char second_byte = *(((char *)&x) + 1);

Instead, write:

long x;
...
char second_byte = (char)(x >> 8)

Concatenation, this is one of my favorites, as many people tend to think that you can only do this using strange tricks. Don't do this:

union uu
{
  long x;
  unsigned short s[2];
};
union uu u;
u.s[0] = low;
u.s[1] = high;
long res = u.x;       

Instead write:

long res = (((unsigned long)high) << 16) | low
Francesco
  • 3,200
  • 1
  • 34
  • 46
Lindydancer
  • 25,428
  • 4
  • 49
  • 68
1

I could write unit tests and run them on the target platforms, but I don't have the hardware.

You can setup your design so that unit tests are easy to run independent of actually having hardware. You can do this using dependency injection. I can abstract away things like hardware interfaces by providing a base interface class that the code I'm testing talks to.

class IHw
{
public:
    virtual void SendMsg1(const char* msg, size_t size) = 0;
    virtual void RcvMsg2(Msg2Callback* callback) = 0;
     ...
};

Then I can have the concrete implementation that actually talks to hardware:

class CHw : public IHw
{
public:
    void SendMsg1(const char* msg, size_t size);
    void RcvMsg2(Msg2Callback* callback);
};

And I can make a test stub version:

class CTestHw : public IHw
{
public:
    void SendMsg1(const char* msg, size_t);
    void RcvMsg2(Msg2Callback* callback);
};

Then my real code can us the concrete Hw, but I can simulate it in test code with CTestHw.

class CSomeClassThatUsesHw
{
public:
   void MyCallback(const char* msg, size_t size)
   {
       // process msg 2
   }
   void DoSomethingToHw()
   {
       hw->SendMsg1();
       hw->RcvMsg2(&MyCallback);
   }
private:
    IHw* hw; 
}
Doug T.
  • 64,223
  • 27
  • 138
  • 202
  • Sometimes I can write my code such that it does not rely on a specific endianness and will work on all hardware platforms. I prefer it over hardware-specific implementations, where possible, since it means maintaining less code. – sourcenouveau Jun 20 '11 at 14:35
  • Sure, but dependency injecting your endianness-conversions?? Isn't that going a bit far? – Rup Jun 20 '11 at 14:36
1

I personally use Travis to test my software hosted on github and it supports running on multiple architectures [1], including s390x which is big endian.

I just had to add this to my .travis.yml:

arch:
  - amd64
  - s390x  # Big endian arch

It's probably not the only CI proposing this, but that's the one I was already using. I run both unit tests and integrated test on both systems which gives me some reasonable confidence that it works fine no matter the endianness.

It's no silver bullet though, I'd like to have an easy way to test it manually too just to ensure there's no hidden error (e.g I'm using SDL, colors could be wrong. I'm using screenshot to validate the output but the code for taking screenshot could have errors compensating the display problem, so the tests could pass with the display being wrong).

[1] https://blog.travis-ci.com/2019-11-12-multi-cpu-architecture-ibm-power-ibm-z

Colin Pitrat
  • 1,992
  • 1
  • 16
  • 28
0

IMO, the only answer that comes close to being correct is Martin's. There are no endianness concerns to address if you aren't communicating with other applications in binary or reading/writing binary files. What happens in a little endian machine stays in a little endian machine if all of the persistent data are in the form of a stream of characters (e.g. packets are ASCII, input files are ASCII, output files are ASCII).

I'm making this an answer rather than a comment to Martin's answer because I am proposing you consider doing something different from what Martin proposed. Given that the dominant machine architecture is little endian while network order is big endian, many advantages arise if you can avoid byte swapping altogether. The solution is to make your application able to deal with wrong-endian inputs. Make the communications protocol start with some kind of machine identity packet. With this info at hand, your program can know whether it has to byte swap subsequent incoming packets or leave them as-is. The same concept applies if the header of your binary files has some indicator that lets you determine the endianness of those files. With this kind of architecture at hand, your application(s) can write in native format and can know how to deal with inputs that are not in native format.

Well, almost. There are other problems with binary exchange / binary files. One such problem is floating point data. The IEEE floating point standard doesn't say anything about how floating point data are stored. It says nothing regarding byte order, nothing about whether the significand comes before or after the exponent, nothing about the storage bit order of the as-stored exponent and significand. This means you can have two different machines of the same endianness that both follow the IEEE standard and you can still have problems communicating floating point data as binary.

Another problem, not so prevalent today, is that endianness is not binary. There are other options than big and little. Fortunately, the days of computers that stored things in 2143 order (as opposed to 1234 or 4321 order) are pretty much behind us, unless you deal with embedded systems.

Bottom line: If you are dealing with a near-homogenous set of computers, with only one or two oddballs (but not too odd), you might want to think of avoiding network order. If the domain has machines of multiple architectures, some of them very odd, you might have to resort to the lingua franca of network order. (But do beware that this lingua franca does not completely resolve the floating point problem.)

David Hammen
  • 32,454
  • 9
  • 60
  • 108