Inspiration
The most common first question about a new platform is "can I bring my existing code?" For QNX,
the answer is usually yes, but you only find out how one compile error at a time. Every
Linux-ism turns into a build-fail, search, patch, rebuild loop: epoll, a glibc-only header,
a missing -lsocket. Some breakages are subtle and specific to QNX 8. SA_RESTART
doesn't exist. <limits.h> deliberately leaves IOV_MAX undefined. An empty
#define _XOPEN_SOURCE hits an #error. Even code that already supports QNX 7 breaks,
because the syspage layout changed.
We wanted the tool we would have liked on day one of a port: one command that lists every blocker up front, explains it with the QNX equivalent, fixes the mechanical ones, then builds and runs the result on QNX without hardware.
What it does
qnx-port-doctor is a command-line tool with five subcommands:
scanwalks a C/C++ tree and matches 45 rules in five families: Linux-only headers and APIs (epoll, inotify, prctl, eventfd, signalfd, timerfd,linux/*), glibc-isms, Linux/procand sysfs paths, link-flag differences (-lrt/-ldl; QNX'slibsocket/libregex), and QNX 8 quirks plus QNX 7 → 8 migration issues. Each finding comes with a concrete QNX fix.- Context-aware: a naive grep drowns you in false positives, because portable projects
already wrap their Linux code. The scanner understands
#ifdef __linux__/HAVE_*guards, files only compiled under Automakeifor CMake platform branches, and per-platform sources (osdep-linux.c,os390-syscalls.h). Those findings becomeinfowith an explanation. QNX quirks are still reported in code that will compile on QNX. On tmux this turns 30+ raw hits into exactly the 8 lines where the QNX build actually fails. fixwrites a reviewable unified diff (patch -p1-ready, idempotent) for six auto-fixable rules. Every edit is valid on all platforms: guarded fallbacks (#ifndef SA_RESTART), version-qualified feature macros, spelled-outstrptimeformats, and the QNX 8 syspage rename.buildcross-compiles a single file, a plain Makefile, a CMake project (it generates the QNX toolchain file) or an Autotools project (--autotoolssets thex86_64-pc-nto-qnx8.0.0/aarch64-unknown-nto-qnx8.0.0host, QNX libraries, pkg-config sysroot and an optional compat header).runboots the QNX 8 QEMU quickstart image headless, copies the binary (plus extra files or stdin) in over ssh, runs it and returns its output and exit code. That makes "does it work on QNX?" one command, scriptable in CI.
Reports come as text, Markdown, JSON or SARIF 2.1.0, so findings show up as GitHub code-scanning alerts. Rules live in a YAML file, and teams can add their own.
How we built it
- Pure Python 3 (one dependency, PyYAML), MIT-licensed, 20 unit tests.
- The rule engine is regex rules over source and build files. A small preprocessor-aware
pass tracks
#if/#elif/#else/#endifand positive and negated conditions, and a build-system pass parses Automake conditionals,PLATFORM_DEPENDENTsource lists and CMakeif(CMAKE_SYSTEM_NAME …)blocks. Rules carry aguardableflag: Linux-only APIs are harmless inside a Linux guard, but QNX quirks likeSA_RESTARTmatter wherever the code is compiled for QNX. - Fixers are small, conservative, line-level transformations. Their output is checked
with
patch -p1and re-scanned to prove idempotence. - build/run wrap the QNX SDP 8.0 toolchain (
qcc) and the official QEMU quickstart image.runuses QEMU user networking with an ssh port forward and-snapshot, so every run starts from a clean disk. - Every rule that came out of a real port was validated on QNX: we built the code with
qcc, ran it in QEMU, and confirmed the behaviour. For example, a tiny C probe showed that QNXstrptime("%FT%T")returns NULL.
Real-world results
All built with QNX SDP 8.0 for x86_64 and run on QNX 8 in QEMU:
| Project | Build system | Blockers found → fixed | Result on QNX 8 |
|---|---|---|---|
| jq 1.8.1 | Autotools | empty _XOPEN_SOURCE (auto-fixed) |
jq's own suite: 509/522 (remaining: module fixtures not copied, one real strptime difference) |
| libevent 2.1.12 | Autotools | SA_RESTART (auto-fixed) |
static library used by tmux |
| tmux 3.5a | Autotools | SA_RESTART ×4, IOV_MAX ×4 (auto-fixed), -lsocket -lregex |
sessions, split panes and capture all work |
| cJSON 1.7.18 | CMake | none | 18/18 test suites pass |
| libuv 1.49.2 | CMake | QNX 7-era syspage API (new_cpuinfo auto-fixed, qtime->nsec manual) |
24/26 selected tests pass |
jq, cJSON and libuv also cross-build for aarch64le. jq (509/522) and cJSON (18/18) give the same test results on QNX 8 for Arm in QEMU, and run picks the target from the binary's ELF header.
Challenges we ran into
- False positives were the real enemy. The first version flagged dozens of lines in tmux and libevent that never compile on QNX. Making the scanner understand preprocessor guards and build-system conditionals (Automake, CMake, per-platform file naming) took most of the iteration. So did deciding which rules a guard should not silence. Our libevent regression test catches exactly that mistake.
- QNX 8 is not QNX 7. libuv ships a QNX backend, yet it doesn't compile on QNX 8:
new_cpuinfoandqtime->nsecare gone. We added a QNX 7 → 8 migration rule family. - Running QNX headless in a container. The quickstart image crashes very early under
nested KVM ("Too early for per-cpu data"), so
runfalls back to software emulation (about 30 s boot). A truncated image unpack showed up as a "corrupted"/datafilesystem, which took some digging to track down. - Toolchain drift. CMake 4 rejects many older
CMakeLists.txtfiles, sobuildsetsCMAKE_POLICY_VERSION_MINIMUMfor you.
Accomplishments that we're proud of
- Five well-known open-source projects ported and actually running on QNX 8, each verified with its own tests where available.
- On tmux, libevent and jq the scanner's warnings and errors match exactly the lines a QNX build fails on, with zero false alarms at warning level.
fixpatches small enough to send upstream.- A codelab-style tutorial ("Port your first open-source project to QNX 8") that a new QNX developer can follow end to end.
What we learned
- The QNX differences that break most ports are few and learnable. The table at the end of our tutorial covers the majority of what we hit.
- POSIX leaves room for interpretation (
IOV_MAX,SA_RESTART,strptime), and QNX makes different, valid choices than glibc. - Context matters more than rule count: one well-placed guard check removes more noise than ten new rules add.
What's next for qnx-port-doctor
- Real Arm hardware (Raspberry Pi 4) as a
runtarget, plus KVM-accelerated runs. - Meson support, plus compile-database (
compile_commands.json) mode for exact include paths. - More QNX 7 → 8 migration rules, and fixers for socket and link flags in CMake.
- A GitHub Action wrapping
scan -f sarif, and an upstream patch submission for libuv's QNX 8 support. - A rule-contribution guide so the QNX community can grow the database.
Credits
Demo video narration: Kokoro-82M by hexgrad (Apache-2.0), voice af_heart, synthesized locally. Music: Spiral Maps of Light, original track by Aleksej Sg.
Built With
- autotools
- cmake
- python
- qemu
- qnx
- sarif
- yaml
Log in or sign up for Devpost to join the conversation.