这是indexloc提供的服务,不要输入任何密码
Skip to content

Panic in _startup_runtime in executable built with -default-to-panic-allocator #5336

@imp0s5ible

Description

@imp0s5ible

Context

I use my own allocators for both the regular allocator and the temp allocator, accordingly I use -default-to-panic-allocator to make sure I don't accidentally use the default allocators anywhere. However, doing so results in a crash when the runtime starts up.

Output of odin report

    Odin:    dev-2025-06-nightly:aa0cffb
    OS:      Windows 11 Unknown Edition (000000a1) (version: 23H2), build 22631.5472
    CPU:     AMD Ryzen 9 3950X 16-Core Processor
    RAM:     65456 MiB
    Backend: LLVM 20.1.0

Expected Behavior

_startup_runtime does not panic and my entry point is entered successfully

Current Behavior

_startup_runtime panics

Failure Information

This bug can be triggered if core:log is imported anywhere as this compiles and runs the init_terminal proc which is marked @(init), which then panics due to the panic allocator being set. This is despite the fact that I never use the console logger anywhere and don't need this init to run.

It looks like init_terminal is calling lookup_env which needs to allocate with the temp allocator, but the temp allocator uses the default allocator internally, which is the panic allocator if -default-to-panic-allocator is used. Accordingly, we get a panic.

Steps to Reproduce

  1. Create an odin source file with the following contents:
package repro

import "core:log"

main :: proc() {return}
  1. Build with the following command:
    odin build .\repro.odin -file -default-to-panic-allocator -debug
  2. Run in a debugger to observe the panic

Failure Logs

N/A

Potential solutions

We were discussing this issue on Discord and I thought of a couple of potential solutions, I write them here in case they're of any interest:

Lazy init

This particular @(init) could be replaced by a lazy initialization such as:

if !intrinsics.expect(terminal_initialized, true) do init_terminal()

and then marking init_terminal as @(cold).

Upsides

  • This would allow the user to set up an allocator in time
  • If one is not set up, the resulting call stack would paint a very clear picture of what's gone wrong
  • No additional complexity/features need to be introduced to the language

Downsides

  • This would have to be done in every proc that depends on that init function, with a significant potential for bugs (although the upside of this is it'd be very clear which procs actually do depend on that init)
  • Another downside is multi-threading concerns, there would either need to be locking, atomic load/store or paying the init cost per thread
  • There's a theoretical performance cost as well but I assume it's negligible, especially in the context of printing to the terminal

Static elimination of @(init) procs

Procedures that require (a) certain @(init) function(s) to run could be marked as such with a new attribute e.g. @(needs-init="init_terminal"). In very much the same way that foreign libs are not linked if nothing from them is referenced, if no proc that depends on a specific @(init) proc is ever referenced, then the @(init) proc is also eliminated.
Procs could not be marked @(init) and @(needs-init) at the same time in order to avoid any need for topological sorting or changing the behavior of the order of @(init) procs in any way.

Upsides

  • This would follow the principle of least astonishment - simply importing core:init wouldn't magically inject extra behavior into the runtime that allocates memory and reads environment variables in order to set things up for printing to the terminal if it's never used, but it does inject this behavior if it is used and therefore needed
  • A number of completely unnecessary @(init) procs could be eliminated entirely, although the exact savings would need to be measured

Downsides

  • This would introduce extra complexity in the language/compiler itself
  • It would break a lot of existing code or introduce a second type of init that can be eliminated this way in order to keep compatibility

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions