GNAT returns objects from functions via registers (if small) or via the primary stack. For the latter, the caller of the function typically allocates space for the return object on its primary stack before the call. However, Ada allows functions to return objects of unconstrained types, for example unbounded array types such as String, and unconstrained discriminated record types. In this case the caller does not know the size of the returned object at the point of the call.
To resolve this problem, GNAT provides each task with a secondary stack that objects of unconstrained types are returned on. On native and cross targets using the full run-time, the secondary stack by default is allocated dynamically on the heap. For cross and bareboard platforms, where stack sizes are fixed, secondary stacks are statically allocated and cannot grow at run time. In both cases the secondary stack is allocated independently from the primary stack.
https://docs.adacore.com/gnat_ugx-docs/html/gnat_ugx/gnat_ugx/the_stacks.html