Read Me
ibcs-us
=======
iBCS (Intel Binary Compatibility System) runs programs i386
from the following Unix systems:
xenix, isc, sco, solaris, svr4, uw7 and wyse.
Documentation: ibcs-us(1), and this file.
All documentation is readable online at the home page:
http://ibcs-us.sourceforge.net/
iBCS isn't well maintained. It's likely everything except the
personality I use (PER_SCOSVR3) has bit rotted, and even PER_SCOSVR3
will be broken in places. There is no point asking me fix it for
you as I hate the thing, but if you know something about C you may
be able to fix it yourself if you have a platform it does work on.
See "Developer Notes" below.
Dependencies
------------
- gcc (32 or 64bit), the glibc-i386 headers (the libc6-dev-i386
package on Debian), and
- the linux-libc headers for i386 (the linux-libc-dev:i386
package on Debian).
Building & Installing
---------------------
Building:
cd src-directory
make
Not building:
Ibcs-us is a statically linked program with no dependencies.
This means the compiled version in the Debian .deb package will
run on on all systems ibcs-us works on. To extract the relevant
bits from the .deb in the download area on sourceforge use the
following steps:
ar x ibcs-us_4.1.5-1_amd64.deb
tar xJf data.tar.xz ./usr/bin ./usr/share/man
This will create the following files will be in the current
directory:
usr/bin/ibcs-us - the executable program
usr/share/man/man1/ibcs-us.1 - the man page.
Installing:
cp build/ibcs /install/location/.
# either:
sudo setcap cap_sys_rawio+ep /install/location/ibcs-us
# or:
sudo chown root. /install/location/ibcs-us
sudo chmod u+s /install/location/ibcs-us
Developer Notes
---------------
ibcs-us is a port of the old ibcs64 system to user space. (ibcs64
is a set of kernel modules, but the kernel kept ripping stuff out
it depended on making continued maintenance difficult.) In fact
everything all bar the "ibcs" and "include" directories is just the
original source code for the ibcs64 kernel modules with a few
alterations guarded by "#ifdef _KSL_IBCS_US" to customise it to the
new API supplied by ibcs-us. That API is provided by the code in
the "ibcs" directory. Naturally enough it seeks to emulate as
closely as possible the API those modules were written for, which is
the Linux 2.6.32 kernel API. That API is emulated using Linux
user space syscall's.
Although ibcs-us is a Linux user space program written in C, it
isn't a normal one. It doesn't use libc as the kernel it is
emulating provides it's own versions of the libc functions that
may conflict with the libc versions, it is statically linked, and
it is 32 bit (ie strictly x86, not amd64). When it runs it is
loaded at high memory addresses to avoid the usual addresses the
emulated programs are loaded into. Even so, it can be compiled on
64bit Linux, and debugged in the usual way with 64 bit gdb. An
embedded C programmer should feel right at home.
The usual technique for getting an emulation working is to trace the
system calls using --trace=~0 option of ibcs-us and look for a
system call that seems suspicious. If that fails and you have a way
of running the emulated program running in a working environment
compare the similar trace produced in the working to the one
produced by ibcs-us: the system calls and return values should be
near identical. Find when the two diverge and fix it.
Beware a program often refuses to run because there is something
different about it's environment. It may be looking for terminfo
files in a different place, or libraries or configuration files,
or assuming all process id's will always be smaller than 32768.
The --map option of ibcs-us will allow you to work around file
names changing. Again, this is easiest to find using the trace
techniques described above.
If you are curious about how it works, the secret sauce is in
ibcs/sysent.c. The emulated personalities all use the x86 "lcall"
instruction to make syscalls. If you can trap those syscalls the
rest is simply a matter of programming, as all you have to do is
emulate whatever they did. The lcall instruction is is very similar
to normal assembler call instruction, the only difference being it
goes via the x86 CPU's LDT table. Since the entire point of a
"call gate" in the LDT table is to redirect such calls to where you
need them to go the obvious solution is to use the modify_ldt()
Linux syscall to do just that. Sadly modify_ldt() seems completely
borked when it comes to call gates, even though it appears to have
been provided for the x286emul module of this very program. Since
it doesn't work ibcs-us reverts to trapping the SIGSEGV's these
lcall instructions generate, and patches the lcall in the .text
segment to do a normal call instead. It's a distinctly second
class solution because the .text segments can't be write protected,
but there doesn't seem to be much choice.
Linux 2.6.32 has the concept of a "personality" which was set when
the program was loaded. (Linux 5.0 still has the personality(2)
syscall, but it as atrophied to the point to being near useless.)
In Linux 2.6.32 the personality was used to select an "exec_domain".
All syscall's were fed to the function pointed to by
exec_domain.handler. Naturally the default exec_domain was Linux,
and it implemented the native Linux syscall API. iBCS modules
supplied the others. ibcs-us emulates that system. There is a
source directory for each exec_domain (eg, per-sco, per-svr4, and so
on), and each provides it's own syscall table the lcall redirect
passes control to.
These exec_domain syscall handlers do the grunt work of emulating
the old API in terms of the Linux 2.6.32 kernel API, which they
think they are invoking when they use the SYS() macro. But the
SYS() macro now re-directs them to ibcs/linux26-compat.c, which
emulates the Linux 2.6.32 kernel API using Linux user space API.
The x286 directory contains the old 286 Xenix emulation. It is
not currently part of ibcs-us. If the modify_ldt() syscall
worked for call gates it could conceivably be hacked back into
shape and added to ibcs-us.
--
Russell Stuart
2019-05-31