nuttx/libs/libc/stdlib/lib_bsearch.c
Marco Casaroli ea78802f8d binfmt/fdpic: Add an FDPIC ELF module loader.
Lets a NOMMU target execute downloadable modules in place from
memory-mapped NOR flash, so a module's text and rodata never consume
RAM.  It is the consumer of xipfs: the loader maps a module's read-only
segment with MAP_XIP_STRICT, which resolves to a direct flash pointer or
fails with -ENXIO rather than falling back to a RAM copy, and pins the
extent for as long as the module is loaded so the defragmenter cannot
relocate code that is executing.

Only the writable segment is copied to RAM, once per running instance.
The loader follows DT_NEEDED so a module can use shared libraries, each
object getting its own GOT and its own data.

FDPIC is what makes this possible: text is position-independent and each
LOAD segment is placed independently, with the text-to-data offset
communicated at load time through function descriptors and the GOT.  A
descriptor is a pair -- entry pointer plus data base -- so a module
function handed back to the firmware carries the data base it needs.
r9 holds that base at runtime, per the ARM FDPIC ABI.

Reserving r9 across the base firmware is what allows a firmware routine
to call back into module code and still arrive with the module's data
base intact.  Toolchain.defs puts --fixed-r9 in ARCHCPUFLAGS rather than
CFLAGS, because almost every board Make.defs assigns CFLAGS with ':='
after including it, which would discard the flag; ARCHCPUFLAGS is
re-expanded by that same assignment and so survives it.  The CONFIG_PIC
--fixed-r10 case is skipped under FDPIC, since reserving both registers
would cost one for nothing.

The DT_NEEDED walk is depth capped.  fdpic_loaddepends() recursed once per
link of a dependency chain with a path buffer on each frame and nothing to
stop it, so a malformed module set overflowed the stack of whichever task
called the loader instead of being rejected.  A dependency *cycle* was never
the hazard -- an object joins the load's list before its own dependencies are
walked, so a library naming something already loaded finds it there and stops
-- what was unbounded is a chain of distinct names, which the list cannot
bound, hence an explicit cap rather than cycle detection.

A module's .rofixup section is skipped, and the file header records why.
.rofixup is the FDPIC self-relocation list a static executable's crt0 walks
to derive its own GOT when no loader is present.  A module links -shared
-nostartfiles, so no crt0 runs, and the built objects hold exactly one entry
there -- the address of the GOT itself, which this loader computes and
installs at every entry into module code anyway.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-01 12:23:27 +02:00

148 lines
6.5 KiB
C

/****************************************************************************
* libs/libc/stdlib/lib_bsearch.c
*
* SPDX-License-Identifier: BSD-3-Clause
* SPDX-FileCopyrightText: 1990 The Regents of the University of California.
* SPDX-FileCopyrightText: 1993 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdlib.h>
#ifdef CONFIG_FDPIC
# include <nuttx/binfmt/fdpic.h>
#endif
#include <assert.h>
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bsearch
*
* Description:
* The bsearch() function will search an array of nel objects, the initial
* element of which is pointed to by 'base', for an element that matches
* the object pointed to by 'key'. The size of each element in the array
* is specified by 'width'. If the nel argument has the value zero, the
* comparison function pointed to by 'compar' will not be called and no
* match will be found.
*
* The comparison function pointed to by 'compar' will be called with two
* arguments that point to the 'key' object and to an array element, in
* that order.
*
* The application will ensure that the comparison function pointed to by
* 'compar 'does not alter the contents of the array. The implementation
* may reorder elements of the array between calls to the comparison
* function, but will not alter the contents of any individual element.
*
* The implementation will ensure that the first argument is always a
* pointer to the 'key'.
*
* When the same objects (consisting of width bytes, irrespective of their
* current positions in the array) are passed more than once to the
* comparison function, the results will be consistent with one another.
* That is, the same object will always compare the same way with the key.
*
* The application will ensure that the function returns an integer less
* than, equal to, or greater than 0 if the key object is considered,
* respectively, to be less than, to match, or to be greater than the
* array element. The application will ensure that the array consists of
* all the elements that compare less than, all the elements that compare
* equal to, and all the elements that compare greater than the key
* object, in that order.
*
* (Based on description from OpenGroup.org).
*
* Returned Value:
* The bsearch() function will return a pointer to a matching member of
* the array, or a null pointer if no match is found. If two or more
* members compare equal, which member is returned is unspecified.
*
* Notes from the NetBSD version:
* The code below is a bit sneaky. After a comparison fails, we divide
* the work in half by moving either left or right. If 'lim' is odd,
* moving left simply involves halving 'lim': e.g., when 'lim' is 5 we
* look at item 2, so we change 'lim' to 2 so that we will look at items
* 0 & 1. If 'lim' is even, the same applies. If 'lim' is odd, moving
* right again involves halving 'lim', this time moving the base up one
* item past 'middle': e.g., when 'lim' is 5 we change base to item 3 and
* make 'lim' 2 so that we will look at items 3 and 4. If 'lim' is
* even, however, we have to shrink it by one before halving: e.g.,
* when 'lim' is 4, we still looked at item 2, so we have to make 'lim'
* 3, then halve, obtaining 1, so that we will only look at item 3.
*
****************************************************************************/
FAR void *bsearch(FAR const void *key, FAR const void *base, size_t nel,
size_t width, CODE int (*compar)(FAR const void *,
FAR const void *))
{
FAR const void *middle; /* Current entry being tested */
FAR const char *lower; /* The lower limit of the search region */
size_t lim; /* The number of elements in the region */
int cmp; /* Boolean comparison result */
DEBUGASSERT(key != NULL);
DEBUGASSERT(base != NULL || nel == 0);
DEBUGASSERT(compar != NULL);
#ifdef CONFIG_FDPIC
/* See qsort(): an FDPIC caller passes a descriptor, not a code address */
compar = (CODE int (*)(FAR const void *, FAR const void *))
fdpic_callback((FAR void *)compar);
#endif
for (lim = nel, lower = (const char *)base; lim != 0; lim >>= 1)
{
middle = lower + (lim >> 1) * width;
cmp = (*compar)(key, middle);
if (cmp == 0)
{
return (FAR void *)middle;
}
if (cmp > 0)
{
/* key > middle: move right (else move left) */
lower = (FAR const char *)middle + width;
lim--;
}
}
return NULL;
}