Depending on which kind of "hard" you mean. Some problems are straightforward but very involved to fix, while others are incredibly difficult to investigate but easy to fix once found.<p>My first significant contribution to FOSS was to port OpenOffice.org to work without the then-proprietary Java, so that it could go into Debian main (and other distros with similar requirements). At the time, OO.o took 8 hours to build, or 3 hours with the wonders of ccache, and I was hacking on the build system itself, so incremental builds were often broken. (And the first thing OO.o built was its own implementation of make.) So over the course of a month or so, I would hack on it, rebuild to see it get a bit further, and repeat until it finally built without error. The net result was dozens of patches submitted and merged into Debian and ooo-build, and the 1.1.0-2 changelog entry listed here, which made it all worth it: <a href="http://metadata.ftp-master.debian.org/changelogs/main/libr/libreoffice/libreoffice_4.3.3-2+deb8u1_changelog" rel="nofollow">http://metadata.ftp-master.debian.org/changelogs/main/libr/l...</a> ('The "Wohoo-we-are-going-to-main" release')<p>The most <i>challenging</i> problems were two different mysterious crashes in BITS (biosbits.org), a Python environment running at the firmware level. Because of the environment, a crash means a sudden unexplained reboot, with no diagnostic information.<p>First, I was trying to debug a crash in the initial CPU bringup code, which brought the CPU from 16-bit real mode to 32-bit mode. After extensive investigation, including assembly output of characters to the serial port to indicate how far the code got, and hand-comparison of disassembled code with the original, it finally turned out to be a bug in the GNU assembler, mis-assembling an expression with a forward-referenced symbol when in .intel_syntax mode. The forward reference ended up becoming an unresolved relocation (with a 0 placeholder) instead of the intended compile-time constant, resulting in a wild pointer. It was one of the rare instances where the bug really was in the toolchain, combined with an environment that makes debugging a challenge.<p>The other such bug, in the 64-bit version of the same environment, involved GCC compiling struct assignments into SSE instructions that assume aligned addresses, and GRUB not actually aligning its stack for SSE because it never actually used SSE itself and didn't happen to use struct assignments. Debugging that one involved a quick hack of a general-protection-fault handler that hex-dumped the bytes of code around the instruction pointer, searching for those bytes in the compiled code, and matching that back up with the disassembly and source code.<p>Most recently, I debugged a race condition in a build system, where disk image manipulation (done by syslinux and mtools) was failing to obtain an flock file lock. The kernel doesn't actually have any way to find out who holds the lock, so I ended up instrumenting the flock syscall to print the conflicting lock holder. Turns out that udev took a file lock on the loopback device as soon as it showed up.