---
title: VM C Environment
description: Understand the freestanding execution model, memory model, and
  runtime constraints of C programs on the Thru VM.
source_url:
  html: https://thru.org/docs/sdks/c-reference/vm-c-environment/
  md: https://thru.org/docs/sdks/c-reference/vm-c-environment.md
---

# VM C Environment

Use this page when you need to reason about what kind of C environment a Thru program actually runs in.

## What environment you are in

Thru C programs run inside the Thru VM, not as normal hosted user-space processes.

That means:

- there is no conventional `main`
- there is no argv or environment-variable interface
- transaction, account, block, and shadow-stack data come from VM-mapped segments
- control flow ends through `tsdk_return`, `tsdk_revert`, or `tsys_exit`

## Memory model

Most of the public SDK reads are borrowed pointers into VM-managed memory:

- `tsdk_get_txn()`
- `tsdk_get_account_meta(...)`
- `tsdk_get_account_data_ptr(...)`
- `tsdk_get_current_block_ctx()`
- `tsdk_get_shadow_stack()`

Treat these as borrowed views over runtime-managed memory, not heap allocations you own.

## Hosted vs freestanding assumptions

The SDK is designed for freestanding builds targeting RISC-V. The toolchain links against [picolibc](https://github.com/picolibc/picolibc), a lightweight C library for embedded targets.

What the build files clearly establish:

- programs are compiled as freestanding C, not hosted user-space binaries
- the Thru VM machine config uses a picolibc sysroot
- normal process and OS facilities are not part of the program model

What the build files do **not** guarantee:

- that every familiar libc helper is available in every installed toolchain
- that POSIX-style APIs are present
- that you can treat the environment like a normal Linux process

Practical takeaway:

- prefer SDK helpers first
- assume extra libc usage needs to be validated against the installed toolchain
- keep dependencies on non-SDK runtime facilities narrow and explicit

### Facilities you should not assume exist

| Category | Notes |
| - | - |
| File I/O | No `fopen`, `fread`, `printf` (use `tsdk_printf` instead) |
| Dynamic memory | No `malloc`, `free`, `calloc`, `realloc` |
| Process/OS | No `exit`, `fork`, `getenv`, `signal` |
| Networking | No sockets or network calls |
| Threads | No `pthread`, no concurrency primitives |
| Hosted runtime behavior | No argv, environment variables, filesystem process model, or normal process startup/teardown |

### Floating-point expectations

The default Thru VM build target does not enable the RISC-V floating-point ISA extensions in its `-march` flags.

Practical takeaway:

- do not assume a full normal libc or POSIX environment
- prefer SDK helpers and simple explicit code
- be careful with patterns that rely on hosted-process facilities
- prefer integer math unless you have validated that floating-point code is appropriate for your target and toolchain
- use stack allocation or the anonymous segment syscalls for temporary memory

## Temporary memory

For simple programs, stack allocation is often enough.

If you need structured scratch-space carving, the SDK exposes:

- `TSDK_LAYOUT_INIT`
- `TSDK_LAYOUT_APPEND`
- `TSDK_LAYOUT_FINI`
- `TSDK_SCRATCH_ALLOC_INIT`
- `TSDK_SCRATCH_ALLOC_APPEND`
- `TSDK_SCRATCH_ALLOC_FINI`

These are safer than ad hoc pointer arithmetic when you need multiple aligned temporary regions.

## Limits

| Limit | Value | Defined as |
| - | - | - |
| Max account data size | 16 MB | `TSDK_ACCOUNT_DATA_SZ_MAX` |
| `tsdk_printf` buffer | 1024 bytes per call | Internal fixed buffer |
| Max CPI call depth | 17 frames | `TSDK_SHADOW_STACK_FRAME_MAX` |

## Logging and output

There is no normal stdout or stderr channel in the usual process sense.

Use:

- `tsdk_printf(...)` for formatted debugging output (capped at 1024 bytes per call)
- `tsys_log(...)` for raw log bytes
- `tsys_emit_event(...)` for event payloads intended for downstream consumers

## Alignment and raw byte parsing

When reading from raw instruction bytes or any layout with uncertain alignment:

- prefer `TSDK_LOAD`
- prefer `TSDK_STORE`

Direct casts are easier to get wrong in variable-length or tightly packed payloads.

## Notes

- If a task sounds like “normal C application code,” the C SDK is probably the wrong layer.
- If a task sounds like “VM entrypoint, account mutation, CPI, proof parsing, or instruction decoding,” this environment model is the right one.
