mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
The FDPIC counterpart of examples/nxflatxip, kept separate from it: that example is about NXFLAT, and mixing the two module formats into one program would leave neither demonstrating anything clearly. Four subcommands, each showing one property of the loader. 'qsort' is the simplest case it has -- one self-contained module, two concurrent instances, one shared copy of the text in flash and a private copy of the data each, with the pin count showing the extent held in place while they run and released after. 'solib' adds a shared library, so two objects are mapped and each instance still gets its own copy of both objects' data. 'cxx' is the same in C++, which additionally requires global constructors to have run in dependency order before main. 'jmprel' is a module whose imports are all in DT_JMPREL rather than DT_REL. The modules are embedded as headers rather than built as part of the app: linking one needs arm-uclinuxfdpiceabi binutils, which the tree does not require, so the headers are committed and both this example and testing/fs/xipfs build with a plain toolchain. Their sources are in modules/, with a makefile that rebuilds every header from them on an explicit 'make regen NUTTX_DIR=...' and is never invoked by the application build. It drives nuttx/tools/fdpic and writes the headers this example needs alongside the ones testing/fs/xipfs needs, so one source tree serves both and the two cannot drift apart. CPU is cortex-m3 there deliberately: a v7-M module runs on the v8-M targets too, so one set of blobs serves both the RP2350 and mps2-an500, while a cortex-m33 build produces blobs the Cortex-M7 cannot execute at all. This demonstrates; it does not assert. The assertions are in the fdpic and reject sections of apps/testing/fs/xipfs. Verified on a Pimoroni Pico Plus 2: all four subcommands, and nxflatxip unaffected alongside them. Also verified on mps2-an500 under QEMU, which is a Cortex-M7 -- a different core generation from the RP2350's Cortex-M33. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
160 lines
4.2 KiB
C++
160 lines
4.2 KiB
C++
/****************************************************************************
|
|
* apps/examples/fdpicxip/modules/libshape.cpp
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* A C++ shared library, executed in place out of flash.
|
|
*
|
|
* The point of this file is g_registry. It is a global object with a real
|
|
* constructor, and the constructor is the only thing that ever writes
|
|
* m_magic. If the loader does not walk DT_INIT_ARRAY, the object simply
|
|
* stays in .bss and every field reads back zero -- the library still loads,
|
|
* still links, and still runs, and quietly answers wrong. That is why the
|
|
* demo checks for a magic rather than just printing a total: the failure
|
|
* this is here to catch does not announce itself.
|
|
*
|
|
* m_total is the second half: it lives in the library's writable segment,
|
|
* which is copied once per running instance, so two tasks sharing this
|
|
* library's flash-resident code still count independently.
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
|
|
#define SHAPE_MAGIC 0x5ba9e0
|
|
|
|
class Registry
|
|
{
|
|
public:
|
|
Registry() : m_magic(SHAPE_MAGIC), m_total(0), m_adds(0)
|
|
{
|
|
m_marker[0] = '\0';
|
|
syslog(LOG_INFO, " [libshape] Registry() ran\n");
|
|
}
|
|
|
|
/* Runs last of the two objects, because destruction mirrors construction
|
|
* and this library was constructed first. So a marker written here is
|
|
* evidence that the whole DT_FINI_ARRAY chain ran, not just the module's
|
|
* half -- which is why the marker is written from the library rather than
|
|
* from the module that knows the path.
|
|
*/
|
|
|
|
~Registry()
|
|
{
|
|
syslog(LOG_INFO, " [libshape] ~Registry() ran after %d adds\n", m_adds);
|
|
|
|
if (m_marker[0] != '\0')
|
|
{
|
|
char text[16];
|
|
int len;
|
|
int fd;
|
|
|
|
len = snprintf(text, sizeof(text), "%d", m_total);
|
|
|
|
fd = open(m_marker, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
if (fd >= 0)
|
|
{
|
|
/* xipfs wants the size declared up front so it can reserve one
|
|
* contiguous extent.
|
|
*/
|
|
|
|
if (ftruncate(fd, len) >= 0)
|
|
{
|
|
write(fd, text, len);
|
|
}
|
|
|
|
close(fd);
|
|
}
|
|
else
|
|
{
|
|
syslog(LOG_INFO, " [libshape] marker %s failed\n", m_marker);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Where ~Registry() should record its final total. Optional: the demo
|
|
* does not set one and simply logs instead.
|
|
*/
|
|
|
|
void marker(const char *path)
|
|
{
|
|
strlcpy(m_marker, path, sizeof(m_marker));
|
|
}
|
|
|
|
bool constructed() const
|
|
{
|
|
return m_magic == SHAPE_MAGIC;
|
|
}
|
|
|
|
int add(int by)
|
|
{
|
|
m_adds++;
|
|
m_total += by;
|
|
return m_total;
|
|
}
|
|
|
|
int total() const
|
|
{
|
|
return m_total;
|
|
}
|
|
|
|
private:
|
|
int m_magic;
|
|
int m_total;
|
|
int m_adds;
|
|
char m_marker[64];
|
|
};
|
|
|
|
static Registry g_registry;
|
|
|
|
/* C linkage, because the module imports these by name and the loader
|
|
* matches dynamic symbols as plain strings.
|
|
*/
|
|
|
|
extern "C"
|
|
{
|
|
int shape_add(int by);
|
|
int shape_total(void);
|
|
bool shape_constructed(void);
|
|
void shape_marker(const char *path);
|
|
}
|
|
|
|
int shape_add(int by)
|
|
{
|
|
return g_registry.add(by);
|
|
}
|
|
|
|
void shape_marker(const char *path)
|
|
{
|
|
g_registry.marker(path);
|
|
}
|
|
|
|
int shape_total(void)
|
|
{
|
|
return g_registry.total();
|
|
}
|
|
|
|
bool shape_constructed(void)
|
|
{
|
|
return g_registry.constructed();
|
|
}
|