[PATCH v2 1/1] manual: document audit interface

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Mon Sep 28 12:26:11 GMT 2026



On 25/09/26 17:47, DJ Delorie wrote:
> [initial reply only, for now...]
> 
> On Fri, Sep 25, 2026 at 3:50 PM Adhemerval Zanella Netto
> <adhemerval.zanella@linaro.org> wrote:
>> The patch seems to have been line-wrapped by the mailer (the menu context
>> line and the la_objsearch, la_objopen, and la_symbind @deftypefun lines),
>> so it does not apply with git am and/or git-pw.
> 
> Sorry.  I'm on a new system and haven't figured out how to send email
> via anything other than pasting into a gmail window yet.
> 
>> It might be worth adding that this means each auditor also gets its
>> own copy of libc (separate malloc heap, stdio, etc.).
> 
> I have an alternate intro from Ben that uses "its own libstdc++" for
> the example.
> 
>> We had some issue regarding TLS, so we need to check what is the
>> current support for this.
> 
> I submitted a patch for that,it's still pending.
> https://inbox.sourceware.org/libc-alpha/xno6fh4ed4.fsf@greed.delorie.com/

Right, I will try to take care of it (not sure this week due Cauldron).

> 
>>> +@item DT_DEPAUDIT
>>> +Similarly for the DT_DEPAUDIT dynamic tag.  This is an advisory tag
>>> +that hints that auditors are requested not by the object, but by one
>>> +of the shared objects it links against.
>>
>> I am not sure if 'advisory' is the correct word here, since ld.so loads
>> DT_DEPAUDIT exactly like DT_AUDIT. Maybe extend that DT_DEPAUDIT is ignore
>> if not present.
> 
> I knew that.  I'll try to word it more clearly.
> 
>>> +to ensure the module supports the API version the dynamic loader is
>>> +using.  Auditors which do not are unloaded.  Once an auditor is
>>> +loaded, it may audit the loading of further auditors@footnote{The
>>> +current implementation sends events for the searching and loading, but
>>> +nothing significant after that.  This may be intentional or it may be
>>> +a bug, and auditors should assume auditing auditors is unsupported but
>>> +plan for such events.}.  Each auditor is loaded into its own namespace,
>>> +that is, any symbols exported by the audit will not be visible to the
>>> +main program or other auditors.
>>
>> This could be more concrete: earlier auditors get la_objsearch and
>> la_objopen for later auditors and their dependencies (with the lmid of
>> the new auditor's namespace), but no la_activity, la_symbind, or
>> la_objclose for them.
> 
> Given the code that decides this, I'm inclined to think that the
> intra-auditor auditing is very buggy and I hesitate to be more
> concrete for something that might (should?) change.  BTW, la_activity
> and la_symbind *do* get called, just not all the times you'd expect.

Far enough, I take is not an usual setup and it does has some corner cases
issues (I will send a couple of fixes I found).

> 
>>> +is to clean it up in @code{la_objclose()} @emph{and} set the word to
>>> +zero at that time.  With that, all callbacks would need to check for a
>>> +zero word and assume the related object has been or is being closed.
>>
>> It is initialized when the link map is created (_dl_new_object), and
>> the difference is visible. For dlmopen (LM_ID_NEWLM, ...), la_activity
>> (LA_ACT_ADD) is called with the new object's word before its
>> la_objopen runs.  So callbacks can see the default value (the link map
>> pointer) before la_objopen has had a chance to change it.
> 
> Sigh, yeah.  The API for this part is a mess.
> 
>>> +
>>> +@deftypefun {unsigned int} la_version (unsigned int version)
>>
>> I would add '@standards{GNU, link.h}'. Same for all other functions.
> 
> I considered that, but libc doesn't provide these functions, and there
> are scripts that parse the @standards lines, and I didn't want to
> cause problems.

I don't expect this would case an issue with script, and link.h does
export such prototypes. But it is a good question whether we should
document interfaces that glibc does not really have proper symbols.

> 
>>> +This mandatory callback is called when the auditor is loaded.  It is
>>> +passed the API version the dynamic linker is using.  It should return
>>> +the API version the module is using, or zero to unload the module.  If
>>> +the module and the dynamic linker are using incompatible API versions,
>>> +the auditor is unloaded.  The expectation is that the module will
>>> +return the @code{LAV_CURRENT} value from @samp{<link.h>}.
>>
>> This could be a bit more precise: returning 0 unloads the module silently
>> (it is only reported with LD_DEBUG=files), while returning a version
>> ld.so does not accept prints an error and unloads it.  The generic
>> check accepts any nonzero value up to LAV_CURRENT, but aarch64 and
>> loongarch require exactly LAV_CURRENT (which we might revise since it
>> seems a deviation).
> 
> They require exact values for a reason.  In the future, each arch will
> end up with a range of versions that are "compatible" and each auditor
> may look for a range it "supports".
> 
>>> +Note that @code{la_version()} may return values other than LAV_CURRENT
>>> +if it has knowledge of what the various versions provide and can adapt
>>> +to the @var{version} given, but doing this is outside the scope of
>>> +this document.  This may be complicated by LAV_CURRENT being different
>>> +for different architectures.
>>
>> Should we list the architecture we its differs?
> 
> I see no reason to.  There's no reason why each arch couldn't have its
> own unique value.  If we wanted a consistent API value, we should have
> bumped them all at once, but we didn't, and if we did, we'd need more
> complicated compatibility logic anyway.

Fair enough.

> 
>>> +@item LA_SER_SECURE
>>> +Unused in Linux.
>>> +
>>
>> I think it is unused on Hurd as well, same for LA_SYMB_STRUCTCALL.
> 
> I suppose I could just say "Unused."
> 
>>> +@deftypefun uintptr_t la_symbind (Elf_Sym *sym, unsigned int ndx,
>>> uintptr_t *refdata, uintptr_t *defdata, unsigned int *flags, const
>>> char *symname)
>>
>> Neither la_symbind nor Elf_Sym exists, and the @deftypefun name ends
>> up in the info dir entries as a "GNU C library function".  Maybe:
>>
>>   @deftypefun uintptr_t la_symbind32 (Elf32_Sym *@var{sym}, ...)
>>   @deftypefunx uintptr_t la_symbind64 (Elf64_Sym *@var{sym}, ...)
>>
>> which also removes the need for the @findex lines.
> 
> I was trying to keep it from getting messy, but sure.
> 
>>> +The callback may modify the frame size used for @code{la_pltexit} via
>>> +the @var{framesizep} pointer.  The call will use the largest requested
>>> +frame size.  If no auditors modify this, @code{la_pltexit()} will not
>>> +be called.
>>
>> It would help to say what the value means: the number of bytes of the
>> caller's stack (the stack-passed arguments) that the trampoline copies
>> so it can call the function and regain control for la_pltexit.  Any
>> value >= 0 enables la_pltexit (0 is fine when no arguments are passed
>> on the stack), and a value that is too small makes the callee read
>> garbage stack arguments.  The default, -1, means no la_pltexit.
> 
> I couldn't figure out what the value meant, though.  Thanks for the
> info.



More information about the Libc-alpha mailing list