With the move to dynamic executables, crt0's _start was only ever
calling libc's __init_libc, which only ran libc's init_array list. Now
make crt0 itself (which is statically linked into every executable) call
it's own init_array list and have ld.so call every other image's ctor
lists.
I had defined hook-quit in the GDB script to kill the inferior. But if
the inferior had already terminated, then trying to quit GDB only
printed an error. Now hook-quit is smarter and checks if the inferior is
running before trying to kill it.
The mkj6romfs.py script always wrote uncompessed directory info to the
initrd image, even if compressed was smaller - but it would write the
metadata as if it had compressed it.
This commit adds the 6s shell, and a bunch of supporting work for it.
Major changes include:
- New shell.yaml manifest to give 6s control of the TTY instead of
srv.logger
- Changes to mailbox syscalls to add max handles array size separate
from input size. Also reversed the meaning of the similar data size
argument in those syscalls. (Using the second arg as the max array
size and the first as the current valid size allows for the auto
verify code to verify handles properly, and simplifies user-side
code.)
- New util::unique_ptr smart pointer class similar to std::unique_ptr
- New ipc::message format that uses util::unique_ptr to manage ownership
and lifetimes and avoid extra copying.
- The service locator protocol now supports multiple handles per entry
- Channels got a major overhaul. They are now split into two VMAs, each
containing a mutex, a condition, and a util::bip_buffer. The order of
the VMAs determines which end of the pipe you're on. (ie, the creator
swaps them before handing them to the other thread.) Their API also
changed to be similar to that of util::bip_buffer, to avoid extra
copies.
- util::bip_buffer now keeps its state and its buffer together, so that
there are no pointers. This allows multiple processes to share them in
shared memory, like in channels.
- The UART driver changed from keeping buffers for the serial ports to
just keeping a channel, and the serial port objects read/write
directly from/to the channel.
Known issues:
- The shell doesn't actually do anything yet. It echos its input back to
the serial line and injects a prompt on new lines.
- The shell is one character behind in printing back to the serial line.
The UART driver would constantly hang in unpredictable spots. Turns out
it could get into a situation where it was stuck in a loop unable to
read more from the receive channel, and/or write to the serial port
buffer. Now we use a ring buffer to read as much as possible from the
receive channel, write as much as possible to the serial port buffer,
and move on without looping.
The enum_bitfields system never worked quite right, and always had edge cases where name
resolution for the SFINAE would fail. Move everything over to use util::bitset, which can
be constexpr and boils down to inline integer bitops in release mode.
Improved util::bitset itself, moving the array-backed base implementation into a new
util::sized_bitset, and making the single-inttype backed implementation the base case.
Also added a distinction between | or |= (which work with real bit values) and + or +=
(which work with bit indexes).
Added a release config, and fixed a few spots where optimizations broke things:
- Clang was generating incorrect code for run_ctor_list in libc's init.cpp (it
ignored a check for the end of the list)
- my rep movsb memcpy implementation used incorrect inline asm constraints, so
it was returning a pointer to the end of the copied range instead of the start.
Since this function was just inline asm anyway, I rewrote it in asm by hand in
a new memutils.s file.
User code can now set the log area and severity of log messages. This also updates the j6_log
syscall to take these parameters, but removes all calls to it except through j6::syslog().
Added a new IPC log category, and logging of mailbox calls in it. Also added
some logs in init's service locator to help track down the bug fixed in the
previous commit.
Now the init args are a linked list - this also means ld.so can use its
own plus those of the program (eg, SLP and VFS handles). __init_libj6
now adds the head of the list to its global init_args structure, and the
j6_find_init_handle function can be used to find a handle in those args
for a given proto.
This fixes situations like the logger using the wrong mailbox for the
service locator and never finding the uart driver.
Now when loading a process, init will push all the loader args, align
the stack so that the next pointer will have correct alignment, then
push a pointer to the loader args.
Also:
* _ldso_start will pop all loader args off of the stack before jumping
to the loaded program's entrypoint, as if it had never run.
* The sentinel arg structures have a size instead of being all zeros, so
that they can be popped off properly when done.
time.h and wctype.h had "#error not yet implemented" in them. Now time.h is correct (though the functions
are only declared), and wctype.h exists enough to define its types. Also a dlsym stub was added that just
returns 0.
Update init to use the new xoroshiro RNG to create a random load address for dynamic executables.
Also pass an address past the end of the loader in the loader_arg to use when loading dynamic
dependencies.
ld.so will now go through all DT_NEEDED entries in the dynamic table and load and relocate
those shared libraries as well. Lazy linking of functions via the PLT is not yet supported,
all PLT entries are looked up ahead of time by ld.so.
Passing a size of 0 in to vma_resize will now not attempt to alter the VMA size, but will
still put the size into the passed-in pointer. Using this allows querying the size of a
VMA without changing it.
Stacks were not 16-byte aligned when coming into j6::thread::init_proc,
make sure it aligns stacks for SSE by adding the `force_align_arg_pointer`
attribute.
While waiting for the main panicing CPU to finish, don't spin the CPU
without using `asm("pause")` to lower power consumption. Some panics can
get stuck in this state and oh man do my fans spin up.
In the CRT startup code, when linked in a PIC executable, jumps to
`__init_libj6`, `__init_libc`, `main`, and `exit` were not linked
correctly. They needed a bit more support for looking up the GOT, and
getting the symbol address out of it.
Now libutil has a `got.inc` file for inclusion in asm code that needs to
reference symbols from the GOT.
This change allows the `vma_map` and `vma_create_map` syscalls to map to
addresses other than the one specified, and therefore makes the address
parameter to those syscalls `inout` in order to return the mapped
address.
Also add the `exact` flag for specifying that mapping needs to be done
at the exact address given. If the mapping collides with another, the
new `j6_err_collision` error is returned.
Added an `API` macro in `j6/api.h` that expands to mark the given
declaration as a default-visible symbol. Also change `format` and
`vformat` to non-template functions, and make calls to `main`, `exit`,
and the library init functions in `_start` GOT-relative.
The `driver_main` sinature was an alternate signature for `main`
implemented with weak symbols, but it causes linking issues when not
statically linked, and drivers are going to work differently soon
anyway. Just get rid of it for now.
Bonnibel will now build dynamic libraries when they're dependencies for
non-statically linked modules. It will also copy those shared libraries
into the initrd image for programs being copied into the image.
This commit includes a number of changes to enable loading of PIE
executables:
- The loader in srv.init checks for a `PT_INTERP` segment in the program
its loading, and if it exists, loads the specified interpreter and
passes control to it instead of the program itself.
- Added ld.so the dynamic linker executable and set it as the
interpreter for all user-target programs.
- Program initial stack changed again to now contain a number of
possible tagged structures, including a new one for ld.so's arguments,
and for passing handles tagged with protocol ids.
- Added a stub for a new VFS protocol. Unused so far, but srv.init will
need to serve VFS requests from ld.so once I transition libraries to
shared libs for user-target programs. (Right now all executables are
PIE but statically linked, so they only need internal relocations.)
- Added 16 and 8 bit variants of `util::bitset`. This ended up not being
used, but could be useful.
Going back to letting mailboxes use variable-length data. Note that this
requires extra copies, so shared memory channels should be used for
anything in the hot path. But this allows better RPC over mailboxes and
other flexibility.
Other changes:
- added a j6::proto::sl::client class to act as a service locator
client, instead of duplicating that code in every program.
- moved protocol ids into j6/tables/protocols.inc so that C++ clients
can easily have their own API
This change allows for parameters in definition files to have the "list"
option while also needing the handle verification. The verify function
will now iterate through the list checking capabilities and types on
every valid handle in the list.
Remove the `-lc++`, `-lc++abi`, and `-lunwind` options from the user
target, where they should be handled automatically. (ie, we're not using
`-nostdlib` or its bretheren.)
Add extra info to the NASM `global` directive to specify the sizes of
these symbols, mostly so they look right in `nm` or `readelf` and don't
trick me into thinking something is wrong.