IRIX porting: GCC extensions, missing functions and more fun

It's a UNIX system, I know this!
I’ve spun up an IRIX machine at home to replace my long dead Linux based file server / general network management box. (It’s a 2-node IP45 Origin rack, so 8 sockets of R14000 lovin’) I don’t really like to have more than 1 active server and 1 active desktop at home at a time for simplicity / heat / space reasons. I’ve been an avid IRIX user since 1990, in the golden days of the Personal IRIS and 4D/480. It was my first UNIX and will always have a special place in my heart. SGI has retired the platform and it will go End of Support in 2013, and the final generation of IRIX hardware is becoming outdated on a GFLOP/watt basis now, and thusly affordable!
IRIX is POSIX, POSIX2 compliant, really strictly. It was based on SysV and later got several BSD extensions added, but anything that isn’t mandated with POSIX probably isn’t there unless they wanted it within the halls of SGI. It’s profiling support, native debugging tools and general sysadmin usability are still unmatched. I can still install and configure an SGI box in my sleep, I will definitely be sad the day the final IRIX hardware is thoroughly useless performance wise.
Enough of the background though, I didn’t want to turn this into a sappy rant over a dead platform. I’ve been porting stuff to IRIX for a decade at least, I find as an exercise it’s very enjoyable. I’ve often found myself porting stuff over only to never use it or even nuke it for the sake of the exercise itself. I find it generally makes you a more ‘aware’ C (and UNIX) programmer, especially of stuff outside of the realm of Linux and GCC. I ran into a really cool one late last night. I’ve been working with a bunch of caching engines, on all layers of the equation (object, code, front end), and while I do all of my actual performance testing in Linux land, I’ve been doing some IRIX ports for shiggles. I ran into in a pretty basic mmap which is what spurred me to write this article:
return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
Pretty basic, MAP_PRIVATE specifies that modifications to our memory map are private, that is our changes won’t make it to the file descriptor, it’s copy on write. MAP_ANONYMOUS (at least to me) always seemed like a given too, in Linux/BSD/Solaris it ignores the file descriptors and just gives you a zero’d memory. OS’ all seem to implement this differently, just reading the OS X man page it looks like you can pass flags to the Mach VM about tags and its purgability. IRIX (and HP-UX it seems) completely lacks it. No biggie! Sure enough we can accomplish the same thing thusly!
static int devZero;
devZero = open(“/dev/zero”, 2);
return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, devZero, 0);
Easy peasy. Open a file descriptor to /dev/zero, kernel then knows what to do. Worked like a charm, fast in memory caching was a go!

Ok so this is an OS X portability issue, but IRIX lacks daemon() too. The error was just too good to not include in a portability article :P
Let’s talk about some other Linux/IRIX/UNIX/whateverisms. I’m just going to skim the surface but here we go….
You also run into lots of cases where IRIX’s libc does not support many ‘givens’ in the Linux (and even FreeBSD and even sometimes Solaris’) libc. I was working on porting varnish to IRIX on Sunday morning, and while we’re still in the syscall tracing phase due to some misbehaving of its internal code compiler, but I’ll use it as an example as it had two of the most common functions that are easy substitutions I see all the time:
setenv() setenv was added to UNIX version 7 back in 1979, somehow IRIX completely lacks it. It’s pretty easy to throw putenv in for most cases though. Looking at the varnish source we swap:
AZ(setenv("TZ", "UTC", 1));
for
AZ(putenv("TZ=UTC"));
Same goes for wait4(), it’s part of System 4 but not specified by POSIX so IRIX completely lacks it. Thankfully it’s what waitpid() is implemented with on many platforms (BSD), so if the *rusage isn’t specified, we can do a direct swap thusly:
r = wait4(v->pid, &status, 0, NULL);
becomes
r = waitpid(v->pid, &status, 0);
Varnish needed a few other tweaks, stuff like IOV_MAX isn’t specified in any IRIX headers, but can be gleaned from sysconfig (1024). CLOCK_MONOTONIC is not defined, but thankfully they had Solaris conditionals around it that became Solaris and IRIX conditionals. Fun.
Onto GCCisms. GCC is a great compiler suite. Supports a ton of software/hardware, but it will never been the fastest on any platform. On SGI’s we have MIPSpro and on PC’s you have Intel’s really awesome compiler suite. (We used to use Intel’s suite at Cedara for our x86 medical imaging software, strictly based on its killer performance). I tend to try and port stuff to MIPSpro unless you run into too many bad GCCisms. Sometimes I think that GCC being the defacto ‘nix compiler, has lead people (likely CS students) to believe thats just “the way things are”.
Zero length arrays i.e.
char contents[0];
Ok so it is a clever way to throw a place holder into a struct, but this could be totally handled with a char *contents; pointer too.. Not portable though…
Variable length arrays i.e.
char str[strlen (s1) + strlen (s2) + 1];
Again, its a clever way to do really basic memory handling without thinking about it, but ugh. C99 does support variable length arrays, but GCC allows them in C89 and C++ code too, just to muck shit up. Don’t use these outside of C99 code, it will not be portable.
There are so many more, but these are the most common I tend to see. GNU has a guide on their extensions to the C language, and totally there is lots of really useful stuff there, but just know that it breaks portability. This rant would have a lot more meaning if people used much other than GCC, but nowadays even embedded development and video game consoles are moving toward GCC. At this point in time I honestly don’t see another compiler suite beating GCC but who knows…
Code, port, hack. Expect more cooking stuff soon :P