I've done some adaptations to druntime for another C library that
isn't currently supported. Obtaining the FILE* structure of the
clib is done via a function call rather than global variables.
However this function call is never triggered when issuing a
writeln function call. The FILE* structure is a pointer that
points to a completely wrong location.
Reading core.stdc.stdio.stdin and core.stdc.stdio.stdout
explicitly will trigger the function call and the correct
addresses can be read.
The function writeln seems to obtain the std FILE* structures is
"@property ref File makeGlobal(StdFileHandle _iob)()" in
std.stdio.d.
// Undocumented but public because the std* handles are aliasing
it.
@property ref File makeGlobal(StdFileHandle _iob)()
{
__gshared File.Impl impl;
__gshared File result;
// Use an inline spinlock to make sure the initializer is
only run once.
// We assume there will be at most uint.max / 2 threads
trying to initialize
// `handle` at once and steal the high bit to indicate that
the globals have
// been initialized.
static shared uint spinlock;
import core.atomic : atomicLoad, atomicOp, MemoryOrder;
if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2)
{
for (;;)
{
if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max
/ 2)
break;
if (atomicOp!"+="(spinlock, 1) == 1)
{
with (StdFileHandle)
assert(_iob == stdin || _iob == stdout ||
_iob == stderr);
impl.handle = mixin(_iob);
result._p = &impl;
atomicOp!"+="(spinlock, uint.max / 2);
break;
}
atomicOp!"-="(spinlock, 1);
}
}
return result;
}
This seems do some atomic operation preventing the D File class
for stdio not to be initialized several times. I'm not quite sure
if this is global or per thread but I guess it is for the entire
process. For some reason the std File classes are never
initialized at all. Another problem is that the function that is
called to obtain the clib stdin/out use a structure that is lazy
initialized per thread, so it must be called at least a first
time for each thread in order to get the correct stdin/out.
Removing the atomic operations so that the File initialization is
done every time, then it works.
Question is if "File makeGlobal(StdFileHandle _iob)()" is correct
when it comes to ensure compatibility among all the clib versions
out there. Not too seldom are clib global variables really
functions, like errno is often a function rather than a variable.
The internal code the the clib (Newlib) does not have this
"optimization" but always get stdin/out using this function call.