This was due to the few base __scanf() functions, used by strto*() and
co. to share their framework with scanf() specifiers, being in the main
scanf() file. Not sure why it ended up pulling the entire file even with
LTO, but now that it's separate there's no issue anymore.
Pulling in the entirety of scanf() is mostly expensive because it
contains specifiers(), including strtod(), which itself computes on
floating point numbers, leading to many libm functions being linked in,
some of them with their internal data tables.
In the end you'd call atoi() and get a 24-kB size increase.
Which is not ✨great✨ :)
A standard libc would normally use the kernel's syscall interface to
connect with the lower level, and this interface would usually imitate
POSIX or a similar style. With the statically-linked unikernel design,
the syscalls would be functions, but the interface wouldn't change much.
However, there are some fundamental differences. For instance, in gint
there aren't separate kernel/userspace memory allocators, there's just a
unified allocator in the kernel. This makes malloc() conceptually a
direct syscall, which pushes the kernel/libc boundary at an unusual
location.
Having a clearly-marked HAL makes it easier to identify where the
boundary is. Code from <time.h> currently has an ad-hoc interface which
should be replaced with clock_gettime(2) in the future.
The HAL offers default implementations but these aren't used by gint
yet, because it's more practical to have undefined references than to
end up with the stubs in an executable. These are provided for future
completeness.
This change does not lift the requirement to recursively link gint with
the libc and the libc with gint.
Also set -Wa,--dsp also on C files because the fxSDK sets it globally
and LTO complains if -Wa/-Xassembler options are not uniform across all
compilation units.
Basically removing it from the __scanf_input structure and specializing
it at format sites. The reason is that pretending it's the end of the
stream after the limit is reached does not work because we have to
return EOF at end of stream but not when the limit is hit. So we have to
handle it explicitly, and since we do, no need to have it in the
structure too.
Mostly an initialization problem. But I also optimized the check by
making the bound a maximal unsigned integer when there is no bound,
since __scanf_peek() is used a lot.
Using the _Atomic types is technically more accurate, but equivalent in
practice (glibc uses a normal int) and a bit of a headache for C++
targets since _Atomic is replaced with std::atomic.
GCC's default stdint.h only defaults to stdint-gcc.h, which we want to
use, when using -ffreestanding. Make our wishes explicit to avoid
needing that flag.