nuttx/libs/libc/stdlib/lib_qsort.c
Marco Casaroli 43694933ce libc, sched: Resolve FDPIC descriptors at module callback entry points.
The base firmware and an FDPIC module disagree about what a function
pointer is.  Firmware is not built FDPIC, so to it a pointer is a code
address and it branches there.  A module passes the address of a two word
descriptor instead, because its code and data are placed independently and
a bare code address would leave the callee unable to find its own data.  A
firmware routine that takes a callback therefore branches into the
module's data segment and faults.

So the ten entry points that can be handed a callback by a module resolve
the descriptor before storing or branching to it: qsort, bsearch,
pthread_create, signal, sigaction, task_create and task_create_with_stack,
task_spawn, pthread_once, scandir, and mq_notify and timer_create with
SIGEV_THREAD.

Which one resolves matters as much as that one does.  Resolving twice would
take an already resolved code address for a descriptor and read two words
from the instruction stream, so each pointer is resolved exactly once, at
the outermost point that sees it.  signal() passes its argument through
untouched because sigaction() and then nxsig_action() will resolve it,
which covers a module calling sigaction() directly as well.  qsort() is
split so that the public entry resolves and the recursive implementation
does not.  scandir() resolves its filter but not its comparison function,
which it hands to qsort().

Whether a caller is a module at all is asked of the PIC base register,
which up_initial_state() sets only for a task that has a D-Space.  A plain
kernel task therefore reads zero and is left alone.

SIGEV_THREAD is the case the register cannot answer, because the callback
runs later on a work queue worker that carries no module's base at all.
The base is captured instead when the notification is registered, in the
module's own context, and installed around the call.

All of it is behind CONFIG_FDPIC, which defaults off.  Built for
mps3-an547:picostest both ways; with it off the entry points compile to
what they were.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-09-25 10:46:48 -03:00

320 lines
9.8 KiB
C

/****************************************************************************
* libs/libc/stdlib/lib_qsort.c
*
* SPDX-License-Identifier: BSD-3-Clause
* SPDX-FileCopyrightText: 2007, 2009, 2011 Gregory Nutt.
* SPDX-FileCopyrightText: 1993 The Regents of the University of California.
* SPDX-FileCopyrightText: 1990 The Regents of the University of California.
* SPDX-FileContributor: Gregory Nutt <gnutt@nuttx.org>
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdlib.h>
#ifdef CONFIG_FDPIC
# include <nuttx/fdpic.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define swapcode(TYPE, parmi, parmj, n) \
{ \
long i = (n) / sizeof(TYPE); \
register FAR TYPE *pi = (FAR TYPE *)(parmi); \
register FAR TYPE *pj = (FAR TYPE *)(parmj); \
do { \
register TYPE t = *pi; \
*pi++ = *pj; \
*pj++ = t; \
} while (--i > 0); \
}
#define SWAPINIT(a, width) \
swaptype = (uintptr_t)a % sizeof(long) || \
width % sizeof(long) ? 2 : width == sizeof(long) ? 0 : 1;
#define swap(a, b) \
if (swaptype == 0) \
{ \
long t = *(FAR long *)(a); \
*(FAR long *)(a) = *(FAR long *)(b); \
*(FAR long *)(b) = t; \
} \
else \
{ \
swapfunc(a, b, width, swaptype); \
}
#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static inline void swapfunc(FAR char *a, FAR char *b, int n, int swaptype);
static inline FAR char *med3(FAR char *a, FAR char *b, FAR char *c,
CODE int (*compar)(FAR const void *,
FAR const void *));
/****************************************************************************
* Private Functions
****************************************************************************/
static inline void swapfunc(FAR char *a, FAR char *b, int n, int swaptype)
{
if (swaptype <= 1)
{
swapcode(long, a, b, n)
}
else
{
swapcode(char, a, b, n)
}
}
static inline FAR char *med3(FAR char *a, FAR char *b, FAR char *c,
CODE int (*compar)(FAR const void *,
FAR const void *))
{
return compar(a, b) < 0 ?
(compar(b, c) < 0 ? b : (compar(a, c) < 0 ? c : a)) :
(compar(b, c) > 0 ? b : (compar(a, c) < 0 ? a : c));
}
/****************************************************************************
* Public Function
****************************************************************************/
/****************************************************************************
* Name: qsort
*
* Description:
* The qsort() function will sort an array of 'nel' objects, the initial
* element of which is pointed to by 'base'. The size of each object, in
* bytes, is specified by the 'width" argument. If the 'nel' argument has
* the value zero, the comparison function pointed to by 'compar' will not
* be called and no rearrangement will take place.
*
* 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.
*
* 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, they will define a total ordering on the array.
*
* The contents of the array will be sorted in ascending order according
* to a comparison function. The 'compar' argument is a pointer to the
* comparison function, which is called with two arguments that point to
* the elements being compared. The application will ensure that the
* function returns an integer less than, equal to, or greater than 0,
* if the first argument is considered respectively less than, equal to,
* or greater than the second. If two members compare as equal, their
* order in the sorted array is unspecified.
*
* (Based on description from OpenGroup.org).
*
* Returned Value:
* The qsort() function will not return a value.
*
* Notes from the original BSD version:
* Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
*
****************************************************************************/
static void qsort_internal(FAR void *base, size_t nel, size_t width,
CODE int(*compar)(FAR const void *,
FAR const void *))
{
FAR char *pa;
FAR char *pb;
FAR char *pc;
FAR char *pd;
FAR char *pl;
FAR char *pm;
FAR char *pn;
int swaptype;
int swap_cnt;
int d;
int r;
loop:
SWAPINIT(base, width);
swap_cnt = 0;
if (nel < 7)
{
for (pm = (FAR char *)base + width;
pm < (FAR char *)base + nel * width;
pm += width)
{
for (pl = pm;
pl > (FAR char *)base && compar(pl - width, pl) > 0;
pl -= width)
{
swap(pl, pl - width);
}
}
return;
}
pm = (FAR char *)base + (nel / 2) * width;
if (nel > 7)
{
pl = base;
pn = (FAR char *)base + (nel - 1) * width;
if (nel > 40)
{
d = (nel / 8) * width;
pl = med3(pl, pl + d, pl + 2 * d, compar);
pm = med3(pm - d, pm, pm + d, compar);
pn = med3(pn - 2 * d, pn - d, pn, compar);
}
pm = med3(pl, pm, pn, compar);
}
swap(base, pm);
pa = pb = (FAR char *)base + width;
pc = pd = (FAR char *)base + (nel - 1) * width;
for (; ; )
{
while (pb <= pc && (r = compar(pb, base)) <= 0)
{
if (r == 0)
{
swap_cnt = 1;
swap(pa, pb);
pa += width;
}
pb += width;
}
while (pb <= pc && (r = compar(pc, base)) >= 0)
{
if (r == 0)
{
swap_cnt = 1;
swap(pc, pd);
pd -= width;
}
pc -= width;
}
if (pb > pc)
{
break;
}
swap(pb, pc);
swap_cnt = 1;
pb += width;
pc -= width;
}
if (swap_cnt == 0)
{
/* Switch to insertion sort */
for (pm = (FAR char *)base + width;
pm < (FAR char *)base + nel * width;
pm += width)
{
for (pl = pm;
pl > (FAR char *)base && compar(pl - width, pl) > 0;
pl -= width)
{
swap(pl, pl - width);
}
}
return;
}
pn = (FAR char *)base + nel * width;
r = MIN(pa - (FAR char *)base, pb - pa);
vecswap(base, pb - r, r);
r = MIN(pd - pc, pn - pd - width);
vecswap(pb, pn - r, r);
if ((r = pb - pa) > width)
{
qsort_internal(base, r / width, width, compar);
}
if ((r = pd - pc) > width)
{
/* Iterate rather than recurse to save stack space */
base = pn - r;
nel = r / width;
goto loop;
}
}
/****************************************************************************
* Name: qsort
*
* Description:
* Public entry point. Resolves the comparison function once, then hands
* an ordinary pointer to the implementation, which recurses.
*
****************************************************************************/
void qsort(FAR void *base, size_t nel, size_t width,
CODE int(*compar)(FAR const void *, FAR const void *))
{
#ifdef CONFIG_FDPIC
/* An FDPIC module passes the address of a function descriptor, not a
* code address.
*/
compar = (CODE int (*)(FAR const void *, FAR const void *))
fdpic_callback((FAR void *)compar);
#endif
qsort_internal(base, nel, width, compar);
}