Linux ELF semi-compatibility
Posted: Fri Feb 23, 2024 3:12 pm
I have a somewhat unusual desire.
I want to produce a standalone Linux ELF executable that works fine under Linux, but also works under PDOS/386 (after I make appropriate enhancements).
PDOS/386 (http://pdos.org) already supports a 32-bit MSDOS-like API (you can quibble this), and also runs certain Win32 programs. I see no barrier to adding (limited) OS/2 2.0 support to it as well. But now I am onto Linux.
PDOS/386 is already running a "hello world" Linux ELF binary as proof of concept, with just write and exit syscalls implemented (with INT 80H).
One thing that was a barrier for a long time was the fact that Linux/Unix programs expect the stack to have all the parameters to be split up and on the stack in an unusual manner, and I do not want PDOS/386 to do that. However, it turns out that you can open /proc/<pid>/command and retrieve the command line parameters without needing to inspect the stack.
One thing that may provide inspiration is 16-bit OS/2 1.x with its "family API" so that a single executable runs under OS/2 and MSDOS, without simply providing a complete second copy of the program to put two programs for 2 systems into a single executable. I don't want to do that. I only want a single version of the code.
Note that in order to get my MSDOS programs working properly under both MSDOS and PDOS/86, I did in fact do this:
/* user pressing ESC will always be 1 character on MSDOS,
but not PDOS */
if (p[0] == 0x1b)
{
if (genuine == -1)
{
genuine = (__magic() == 0x1234);
}
if (!genuine)
{
pending[0] = 0x1b;
numpending = 1;
return;
}
/* genuine PDOS will get the second ESC next read */
}
That differing codepath irks me, but it's pretty minor, and the only instance, but it does demonstrate that I am willing to introduce conditional execution, even though I don't like it.
Next issue for the Linux campaign is that PDOS/386 is DOS/Windows-centric, and as such, I want to use CRLF terminators for text files - but only when run on PDOS/386. When the exact same binary is run on Linux, I expect just NL.
The neatest solution to me would be for Linux (and POSIX) to have an O_TEXT flag for open(). I was thinking of just using 0x8000 0000 for that flag, and hope that Linux doesn't stop me now or in the future. And/or get the POSIX committee to formally endorse that. Note that there is prior art for this - Cygwin uses such a flag too, but I can't just use the same value they used, as Linux uses that bit for something else.
I only wish to run "properly written" (TM) Linux executables. And "properly written" is something I'm just going to make up right now. And so my C library (PDPCLIB) will be the only thing that conforms to those rules, and uses the right (TBD) flag for the open syscall etc. Maybe others will start conforming to "the rules" in future or maybe they won't - I don't particularly care so long as I can build MY executables to "the rules" and have them run on both systems.
Another thing to note is that I don't really want this to be PDOS/386-specific. I'm more after a rival to POSIX. Mini-POSIX or whatever. So in the same way that Linus says that Linux is a POSIX-compatible OS, I want to say that PDOS is a Mini-POSIX compatible OS.
Also note that I don't like fork() and exec() in POSIX. It puts a burden on an OS (in this case, PDOS/386), that I don't want to either exist or implement. posix_spawn() would be another possibility, but I would need to introduce another syscall for that (what number?), potentially replacing the unused (by PDOS/386) fork() syscall.
With that in mind, an alternative to getting an official O_TEXT would be to perhaps do a uname syscall. I could look at the system name, but that's not very nice, because I don't want to do a strcmp to either Linux or PDOS, as I'm really after a "mini-posix" flag. I could make the uname syscall return 0x1234 instead of 0, and make that an indicator of mini-posix.
Once I know that I am on a mini-posix system (likely, but not necessarily, PDOS/386), I can add an O_TEXT of 0x8000 0000 to the open() syscalls (noting that on genuine Linux, I would not add the flag until such point that it is officially endorsed - if that ever happens).
Ideally I would like the other way around to be supported too - ie update Windows kernel32.dll (at least the version on PDOS/386), so that a "text" flag can be passed to CreateFile() so that an appropriate OS (PDOS/386 at least) can strip off the CRs that the application (or PDPCLIB) inserted before the NLs.
But first things first. When PDOS/386 is running a Linux ELF, it will get the text flag on the open syscall, and then when the application does a write(), the OS (PDOS/386) will add CRs before the NLs (the same as Cygwin does - and perhaps configurable in the future, as Cygwin is also).
So - within those parameters - and to achieve that goal (I am aware that most people don't have the same goal as me) - any suggestions?
Thanks. Paul.
I want to produce a standalone Linux ELF executable that works fine under Linux, but also works under PDOS/386 (after I make appropriate enhancements).
PDOS/386 (http://pdos.org) already supports a 32-bit MSDOS-like API (you can quibble this), and also runs certain Win32 programs. I see no barrier to adding (limited) OS/2 2.0 support to it as well. But now I am onto Linux.
PDOS/386 is already running a "hello world" Linux ELF binary as proof of concept, with just write and exit syscalls implemented (with INT 80H).
One thing that was a barrier for a long time was the fact that Linux/Unix programs expect the stack to have all the parameters to be split up and on the stack in an unusual manner, and I do not want PDOS/386 to do that. However, it turns out that you can open /proc/<pid>/command and retrieve the command line parameters without needing to inspect the stack.
One thing that may provide inspiration is 16-bit OS/2 1.x with its "family API" so that a single executable runs under OS/2 and MSDOS, without simply providing a complete second copy of the program to put two programs for 2 systems into a single executable. I don't want to do that. I only want a single version of the code.
Note that in order to get my MSDOS programs working properly under both MSDOS and PDOS/86, I did in fact do this:
/* user pressing ESC will always be 1 character on MSDOS,
but not PDOS */
if (p[0] == 0x1b)
{
if (genuine == -1)
{
genuine = (__magic() == 0x1234);
}
if (!genuine)
{
pending[0] = 0x1b;
numpending = 1;
return;
}
/* genuine PDOS will get the second ESC next read */
}
That differing codepath irks me, but it's pretty minor, and the only instance, but it does demonstrate that I am willing to introduce conditional execution, even though I don't like it.
Next issue for the Linux campaign is that PDOS/386 is DOS/Windows-centric, and as such, I want to use CRLF terminators for text files - but only when run on PDOS/386. When the exact same binary is run on Linux, I expect just NL.
The neatest solution to me would be for Linux (and POSIX) to have an O_TEXT flag for open(). I was thinking of just using 0x8000 0000 for that flag, and hope that Linux doesn't stop me now or in the future. And/or get the POSIX committee to formally endorse that. Note that there is prior art for this - Cygwin uses such a flag too, but I can't just use the same value they used, as Linux uses that bit for something else.
I only wish to run "properly written" (TM) Linux executables. And "properly written" is something I'm just going to make up right now. And so my C library (PDPCLIB) will be the only thing that conforms to those rules, and uses the right (TBD) flag for the open syscall etc. Maybe others will start conforming to "the rules" in future or maybe they won't - I don't particularly care so long as I can build MY executables to "the rules" and have them run on both systems.
Another thing to note is that I don't really want this to be PDOS/386-specific. I'm more after a rival to POSIX. Mini-POSIX or whatever. So in the same way that Linus says that Linux is a POSIX-compatible OS, I want to say that PDOS is a Mini-POSIX compatible OS.
Also note that I don't like fork() and exec() in POSIX. It puts a burden on an OS (in this case, PDOS/386), that I don't want to either exist or implement. posix_spawn() would be another possibility, but I would need to introduce another syscall for that (what number?), potentially replacing the unused (by PDOS/386) fork() syscall.
With that in mind, an alternative to getting an official O_TEXT would be to perhaps do a uname syscall. I could look at the system name, but that's not very nice, because I don't want to do a strcmp to either Linux or PDOS, as I'm really after a "mini-posix" flag. I could make the uname syscall return 0x1234 instead of 0, and make that an indicator of mini-posix.
Once I know that I am on a mini-posix system (likely, but not necessarily, PDOS/386), I can add an O_TEXT of 0x8000 0000 to the open() syscalls (noting that on genuine Linux, I would not add the flag until such point that it is officially endorsed - if that ever happens).
Ideally I would like the other way around to be supported too - ie update Windows kernel32.dll (at least the version on PDOS/386), so that a "text" flag can be passed to CreateFile() so that an appropriate OS (PDOS/386 at least) can strip off the CRs that the application (or PDPCLIB) inserted before the NLs.
But first things first. When PDOS/386 is running a Linux ELF, it will get the text flag on the open syscall, and then when the application does a write(), the OS (PDOS/386) will add CRs before the NLs (the same as Cygwin does - and perhaps configurable in the future, as Cygwin is also).
So - within those parameters - and to achieve that goal (I am aware that most people don't have the same goal as me) - any suggestions?
Thanks. Paul.