Spivoxity / obc-3

Oxford Oberon-2 compiler
38 stars 7 forks source link

C primitive errno fails to link #34

Closed ghost closed 4 years ago

ghost commented 4 years ago

Attempting to surface the C primitive errno as procedure fails to link. The following output is produced:

obc -C -o s platform.k types.k sockets.k s.k
/usr/bin/ld: errno: TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches non-TLS reference in /tmp/ccG09i5l.o
/usr/bin/ld: /lib/x86_64-linux-gnu/libc.so.6: error adding symbols: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:3: all] Error 1

The error is reproducible via the following code :

PROCEDURE err(): INTEGER IS "errno";

ghost commented 4 years ago

Quite possibly there is a different syntax here that is required?

Spivoxity commented 4 years ago

There are two errors here: first errno is some kind of variable, not a function, so it is not directly accessible via the primitive mechanism of the Keiko machine. Second, errno is actually defined as a macro in the C library, and "man errno" sternly warns us to "#include <errno.h>" and not try writing our own extern declaration for it.

The error message you're getting refers to TLS, which in this context means Thread Local Storage, not Transport Layer Security. In fact, errno is defined in the GNU C library as

# define errno (*__errno_location ())

This use of a global variable is, of course, a wart on the design of the C library that becomes painful as soon as multiple threads enter the picture.

The simple and portable solution is to introduce a simple C function get_errno, defined by

#include <errno.h>
int get_errno(void) { return errno; }

and then arrange to call that function as a primitive from Oberon. Other solutions (such as exposing the function __errno_location) are not likely to work because on amd64 that function will return a 64-bit pointer that is not addressible from the Keiko virtual machine.

ghost commented 4 years ago

Thanks so much Mike. I'll close this issue and update my project accordingly. It lives here and seems to be running well enough. It needs a lot more work to be considered safe and sound for more than localhost use as a learning project. I'll update it with get_errno as you suggest.

Spivoxity commented 4 years ago

Looking at your project, it's as I suspected: you've managed to live without adding C code so far, but this change will drive you to use -C and link a custom runtime every time. Since errno is guaranteed by ISO C, I see no reason not to add get_errno as above as an unused support routine in the runtime. I've done that and pushed a new revision. Sadly, the actual values of errno are not guaranteed to be consistent between platforms, and that affects portability.

You should be careful, by the way, with any routine (such as fopen for example) that allocates and returns a pointer to storage. Typically, such storage will not be addressible from Keiko. There are things we can do about that if the need arises.

ghost commented 4 years ago

I've done as you suggested and created a simple errno.c containing a get_errno implementation and linked it using -C. Thanks again. Working with Oberon-2 is a great pleasure. I look forward to your forthcoming Oberon-64 implementation which I presume will be the canonical 64-bit implementation. :wink:

Spivoxity commented 4 years ago

On amd64, the JIT already uses the extended register set and SSE for floating point. The only thing that is not 64-bit is the heap, which uses 32-bit pointers. There's a good reason for that, the same reason why both Lua and the most common VMs for Java use 32-bit pointers: it is because interpreting 64-bit pointers would seriously slow down the garbage collector. Google "Java compressed object pointers" to understand the issues.