Re: Some questions on reentrancy, __DYNAMIC_REENT__ and _impure_ptr

Jeff Johnston via Newlib <[email protected]>
Newsgroups gmane.comp.lib.newlib
Message-ID <CAOox84ssV=gHTRC7JqebdsHh4soT-xkzCAunjMd7RHnDRRjznQ@mail.gmail.com>
On Thu, Apr 1, 2021 at 12:58 AM Nick <[email protected]> wrote:

> Hi,
>
> I've been trying to enable reentrancy of newlib on a home brew kernel
> for the x86 platform and have some questions on how various pieces all
> fits together.
>
> Implemented __getreent () to return a private copy of struct reent, and
> also hard coded __DYNAMIC_REENT__ and GETREENT_PROVIDED in sys/config.h
> to rule out any issue of passing in via build CFLAGS or the CFLAGS in
> configure.host. Things including errno seem to work but not totally
> making sense.
>
> As many library functions are still accessing the reent structure using
> _impure_ptr instead of calling my __getreent () function, for example,
> the CHECK_INIT (_REENT, fp) at the beginning of __swsetup_r (struct
> _reent *ptr, register FILE * fp).
>
> Questions:
>
> 1. Are the library functions expected to still use _impure_ptr instead
> of calling __getreent () when both __DYNAMIC_REENT__ and
> GETREENT_PROVIDED are hard coded in sys/config.h?
>
>
No, the dynamic reent system redefines the _REENT macro in sys/reent.h to
call
__getreent().  Something is going wrong in your build.  You can try
compiling a file with -dD to see what is defined by the preprocessor to see
what went wrong.  If you have specified --enable-newlib-multithread=no or
--disable-newlib-multithread, then the dynamic reent system will not be
used.

If so, how do they provide reentrancy? Since _impure_ptr is a global
> pointer visible to all threads and threads can easily step on each
> other's toes trying to change fields in the reent structure pointed to
> by _impure_ptr concurrently.
>
>
If not, what other MACROs or changes should I make so that all the
> library functions all use __getreent () instead of _impure_ptr? Is it
> okay to set _impure_ptr to a bad value such as NULL in this case, in
> order to catch any unintended access?
>
>
Note there is a global impure ptr reference used for std I/O which is shared
between threads.

2. in the documentation on https://sourceware.org/newlib/, the following
> is mentioned as needed for syscalls stubs to return errno:
>
> #include <errno.h>
> #undef errno
> extern int errno;
>
> If I do include this part, all the syscalls stubs seem to do when they
> assign values to errno is setting the global int errno; inside reent.c.
> As user code built against the library don’t read out that integer but
> instead calls __(), errno set by syscall stubs can't be read out by user
> code.
>
> If on the other hand I don’t include this part before my syscall stubs,
> the errno set by them do seem to work as they also set the copy in reent
> structures. What might I have missed here?
>
>
Look at lib/include/reent.h for explanation of reentrancy and syscalls.
You need to specify true
_r versions of the syscalls that place the errno result into the reentrancy
structure.  Otherwise,
you can have a collision in writing to the global errno value before it
gets transferred unless
you use a lock mechanism to ensure syscalls don't happen at the same time.


3. There were some old discussions about manually changing _impure_ptr
> at each context switch. But I’m wondering about the validity of such a
> method since it seems like a really clumsy maneuver for kernel code at
> CPL0 to reach into user space belonging to different binaries to change
> a global pointer. What's more, if manually changing _impure_ptr at each
> context switch is needed, then what would be the purpose of
> __DYNAMIC_REENT__, GETREENT_PROVIDED and implementing a __getreent () to
> get a thread local version?
>
>
See Dave's response on that.

4. Is _global_impure_ptr thread safe? It is a bit concerning as it seems
> to be pointing to the same copy of impure_data that some libraries calls
> would access, and even if I try to change _impure_ptr at each context
> switch, some threads might still be accessing _global_impure_ptr
> concurrently?
>
>
There are locks that are performed for various I/O functions.  You will
need to provide
the low-level lock support ... i.e. provide a lock.c implementation (see
misc/lock.c, libc/include/sys/lock.h).


> 5. There were also old discussions about having to provide mutex for
> malloc, is this still the case for newer versions of newlib like 4.10?
>
>
There is a mallock lock system which again uses the low-level lock.c
implementation which you need to provide
as part of 4.

Thanks!
> Nick
>
>

-- Jeff J.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.