Recently, a friend sent me an email exposing some problems he faced when trying to assemble on Cygwin a code originally targeted at Linux. The problem, as he stated, was that
int 0x80 didn’t perform as expected. Well, plenty of explanations are pertinent:
Cygwin allows to run a collection of Unix tools on Windows, including the GNU development toolchain. However, at its core, cygwin is a library which translates the POSIX system call API into the pertinent Win32 system calls (system calls are often abbreviated as syscalls). Therefore, cygwin is a software layer between applications using POSIX system calls and the Win32 operating systems, which allows porting some Unix applications to Windows. This way you can, for instance, have the Apache daemon working as a Windows service. Other very attractive feature of Cygwin is its interactive environment: you can run your shell quite nicely, and run your Autoconf scripts, for example. However, porting means recompiling. There is no binary compatibility, and your program cannot run in computers without Cygwin (without
CYGWIN1.DLL, more precisely). Furthermore, albeit some progress has been made, Cygwin is relatively slow (it’s a POSIX compatibility layer, after all.) If possible, I prefer to recompile my applications directly with MinGW. For me, this allows for a faster development cycle. Note, though, that Cygwin can compile MinGW-compatible executables. It’s just that, as aforesaid, I prefer to work with MinGW directly. I only work on Windows if I have to develop applications for Windows. But Linux’s development tools are the best, and we can access several of them by using MinGW. I think that Cygwin is best suited for general cross-development and for handling complicated software porting.
System Calls and int 0x80
A system call is a request by an active process for a service performed by the operating system kernel. Remember that a process is an executing (running) instance of a program, and the active process is the process currently using the CPU. The active process may perform a system call to request creation of other process, for instance. Or perhaps the process needs to communicate with a peripheral device. In Linux on x86,
int 0x80 is the assembly language instruction that is used to invoke system calls. int 0x80 is a software interrupt, as it will be raised by a software process, not by hardware devices. Before invoking such interruption, our program has to store the system call number (which allows the operating system to know what service your program is specifically requesting ) in the proper register of the CPU. Every interrupt is a signal to the operating system, notifying it about the occurrence of an event that must be computationally handled.
Application Binary Interface
Generally speaking, an Application Binary Interface (ABI) is the interface between an application program and the operating system. Conceptually, it’s related to the more well-known API concept. But ABIs are a low-level notion, while APIs are more leaned toward the application source code level. If your program uses an specific API, you will be able to compile it on any system which implements that API. Similarly, a program (or more generally, a compiled object code) which uses a specific ABI will run (without the need for recompilation) on any system offering that ABI. Specification of ABIs includes, but is not limited to, details such as:
1. How applications make system calls to the operating system.
2. System call numbers.
3. Calling conventions (how parameters are passed to functions, and how their return values are received).
4. The format of the object code (COFF/PE, ELF, etc.)
To consolidate ideas, notice that a relatively backward-compatible ABI is which has allowed several older applications to run on newer versions of Windows.
Now a few things should be quite clear. First, Cygwin is a software layer at the API level. Second, interrupts are a concept from the ABI level. And it’s obvious that Linux syscalls are entirely different from Windows syscalls. Further, in Cygwin you don’t make raw system calls… it’s CYGWIN1.DLL which does the needed Windows system calls according to your program requirements. The ABI of software compiled with Cygwin is that of Windows systems. You cannot use a Linux ABI’s thing, such as int 0x80, and expect that your program runs fine on Windows.
If you’re programming in assembly,
0x80 only works in Linux. For DOS/Windows, you must use
0x26, etc. That’s the rationale behind my decision for using a function of the C library, printf, in order to avoid this problem in the example code of this post.
The Truth Reflected in the Mirror
You cannot use a Linux ABI’s thing, such as
int 0x80, and expect that your program runs fine on Windows. That’s not entirely true. Under Windows, you can install a vectored exception handler which traps the interruption request, and with some coding translate the trapped information into the Windows equivalent. But that amounts to writing an Linux kernel emulator.
If you are in a hurry, perhaps you’ll prefer the wise path of virtualization. By using a virtualization engine like VMWare and Xen you can install and execute a guest operating system on a host system. Such virtual machines offer a full resources abstraction which allows you to use the guest operating system almost at its full. For some tasks, the coLinux‘s approach may be preferable. Cooperative Linux (coLinux) allows the Windows and Linux kernel to run simultaneously on the same machine, but unlike traditional virtual machines, coLinux shares resources that already exist in the host. coLinux is a very elegant and exciting project, but developers need the GNU development tools. Install them by executing apt-get install gcc
At this point, you should have the GNU assembler (as) installed. Now you can type your programs, and assemble/run them. And you can use
0x80 in your assembly program! For example, I typed the hello, world program of the tutorial by Miyagi, and it assembled and ran just fine 😀