Guest C++ runtime¶
The SDK supplies application-linked libc++, compiler-rt and native adapters
for ARMv5T and ARMv6. Guest libraries use C++20, AAPCS soft-float, 16-bit
wchar_t, and disabled exceptions and RTTI. Use the SDK's headers, generated
configuration and selected archive together; host libraries are not guest
runtime dependencies.
Select a runtime profile¶
| CMake target | Facilities and requirements |
|---|---|
Symbian::Runtime |
Core libc++ containers, ownership, clocks, allocation and compiler helpers |
Symbian::Streams |
Core runtime plus classic C/POSIX locale and string-stream formatting |
Symbian::Threads |
Threaded runtime profile; firmware must provide the selected libpthread.dll imports |
Symbian::AbseilStatusOr |
Status, StatusOr, Cord, time and flat hash maps with the matching streams runtime |
Symbian::NativeAtomics64 |
Alternative runtime using native EUSER 64-bit atomics; requires matching firmware exports |
Symbian::Stackless |
Future/Task composition, native request owners and event/worker executors |
Symbian::Fibers |
ARM-backed fibers and an explicitly pumped, thread-affine scheduler |
Link the runtime selected by your component targets. Do not link both
Symbian::Runtime and Symbian::Streams into one image: their libc++
configurations must match their archives. A DLL using an Abseil-based component
sets RUNTIME_TARGET Symbian::Streams; see
native library targets.
The default architecture for new projects is ARMv6. Select ARMv5T when the target requires it. The modern runtime requires EKA2; the EKA1 process profile is separate.
Allocation and failure policy¶
Startup creates the Symbian thread heap before calling application code.
Ordinary, array, sized, nothrow and over-aligned allocation are provided.
Ordinary allocation failure terminates with KErrNoMemory (-4); nothrow
allocation returns null. Zero-size allocation requests at least one byte, and
sizes above the native signed-size limit are rejected. Container growth does
not turn allocation failure into a recoverable StatusOr.
For a download scratch buffer whose allocation failure should be recoverable, choose nothrow allocation explicitly and enforce an application limit:
#include <cstddef>
#include <memory>
#include <new>
#include "absl/status/statusor.h"
absl::StatusOr<std::unique_ptr<std::byte[]>> AllocateDownloadBuffer(
std::size_t bytes) {
if (bytes == 0 || bytes > 64 * 1024) {
return absl::InvalidArgumentError("Buffer must be 1–65536 bytes");
}
auto buffer =
std::unique_ptr<std::byte[]>(new (std::nothrow) std::byte[bytes]);
if (!buffer) {
return absl::ResourceExhaustedError("No download buffer");
}
return buffer;
}
This handles this allocation only; later container or Status payload allocations still follow the ordinary allocation policy.
The default SYMBIAN_RUNTIME_MIMALLOC=ON profile uses mimalloc 3.5.3 with
process-owned RChunk storage, reserve/commit/decommit support and pthread
cleanup. It reserves 8 MiB per virtual arena and defaults to a 64 MiB total
chunk address budget. Configure SYMBIAN_MIMALLOC_ADDRESS_BUDGET_MIB from
16 to 512. A source build needs SYMBIAN_MIMALLOC_SOURCE pointing to the
pinned checkout. SYMBIAN_RUNTIME_MIMALLOC=OFF uses the original RHeap
adapter. The native 64-bit atomic runtime uses that adapter and cannot be
combined with the mimalloc profile.
Thread registration must pair entry and exit. Direct RThread::Create threads
must remain unregistered unless their owner supplies both; a native thread
exit does not necessarily run pthread cleanup. General C++ thread_local,
ELF TLS relocations and DLL TLS destruction are unsupported.
Fatal libc++ preconditions and invalid no-exceptions thread operations exit
with KErrArgument (-6). These exits are runtime contract failures, rather
than recoverable application argument errors. Check ownership and input ranges
before calling operations such as std::thread::join().
Standard library and C services¶
The core archive supplies strings, vectors, smart pointers, hash containers,
threads, mutexes, condition variables, clocks and error categories from pinned
LLVM sources. Function-section linking includes only reachable functions.
Compiler-rt supplies integer division/remainder, ARM EABI wrappers, aligned
memory helpers, soft-float arithmetic/conversions/comparisons and ARMv5-safe
wide-arithmetic helpers. The 32-bit EABI divide-by-zero hook exits with
KErrArgument; do not rely on recoverable divide-by-zero behavior.
Memory copies use the selected EUSER imports. Some operations also require
firmware C/POSIX libraries: hash-table growth can import ceilf from
libm.dll, formatting uses vsnprintf, error messages use strerror_r, and
thread yield uses sched_yield. The SDK's stdarg_e.h preserves Clang's ARM
variadic-call convention; use it ahead of the historical OpenC header.
Symbian::Streams supports classic locale, C/POSIX locale names and
std::ostringstream. It does not provide arbitrary named locales, file
streams, general wide I/O, filesystem, random-device or timezone services.
std::promise and std::future require an exception ABI that this profile
does not supply; use the SDK Future/Task APIs instead.
Clocks and atomics¶
std::chrono::system_clock reads the firmware real-time clock. The SDK steady
clock uses the nanokernel counter and its HAL-reported period, with the ordinary
tick and its period as fallback. It extends the 32-bit count and clamps
out-of-order cross-thread samples. Keep reads less than half a counter wrap
apart (about 24.9 days at a 1 ms period); suspension behavior is device-specific.
Use absl::Time for absolute real-world deadlines and absl::Duration for
relative delays. Native timer owners convert an accepted absolute deadline
once to monotonic waiting, so later wall-clock corrections do not move it.
A native fast counter is optional for profiling; check its frequency, direction
and power cost before using it.
32-bit compiler atomics call ordered EUSER operations. Default 64-bit atomics
serialize through a process-owned RFastLock. Symbian::NativeAtomics64
uses native exports instead: RM-807, RM-675 and RM-609 profiles provide the
selected exports; RM-243 and RM-346 do not. Other firmware needs a compatible
export table and implementation. Use an object's standard is_lock_free()
query; compiler target macros can disagree with the linked runtime.
Storage, relocations and lifetime¶
EXE code and writable data relocate independently. The converter supports
initialized .data, zero-initialized .bss and typed pointers to either
mapping, with a combined data/BSS limit of 1 MiB. Startup runs .init_array
after heap setup and finalizers before exit. DLL startup runs process-attach
constructors; dynamic load and close use RLibrary. Thread-safe local-static
guards, TLS and general DLL teardown ordering are unsupported.
The local GOT is word-aligned, limited to 1,024 words, and requires retained
R_ARM_GOT_PREL symbol coverage. Imported function pointers resolve through
validated PLT entries. Imported data objects, including exception typeinfo,
are unsupported. Internal or hidden PC-relative references across the code/data
mappings are rejected; keep DLL globals default-visible when GOT references
are needed. Unsupported sections and relocations cause conversion errors.
Historical Symbian placement-new declarations conflict with modern libc++
<new>. Keep original platform headers in separate translation units and
cross into modern code through a narrow adapter. Never pass modern strings
or containers to frozen OS DLL C++ interfaces; use native descriptors at
those interfaces. See C++ application guidance.
Asynchronous operations¶
Use SDK concurrency for Futures, Tasks, timers, property watches, channels and fibers. Native handles, request statuses and buffers remain owned until completion or cancellation drainage. Inline continuations can run on the completing thread; dispatch UI work to its event owner and blocking work to a worker. The runtime provides no preemption for a blocked fiber or an indefinitely waiting worker.
Build the runtime from source¶
Follow source preparation for platform headers and the matching Clang/LLD tools. Keep external LLVM sources outside version control:
git clone --depth 1 --branch llvmorg-23.1.2 --filter=blob:none --sparse \
https://github.com/llvm/llvm-project.git research/upstream/llvm-project
git -C research/upstream/llvm-project sparse-checkout set libcxx libcxxabi \
compiler-rt cmake runtimes llvm/cmake
The SDK source build applies its target patches and generates the matching libc++ configuration. Use SDK installation to export headers, archives and import proxies. The runtime guide shows a complete example.