Working within the confines of a poorly architected system is always a problem, but there is nothing you have said that prevents you from launching a persistent procedure and referencing it anywhere in the call stack. Commonly used procedures are a natural for this use.
E.g., many years ago I had an application whose core dated literally from version 2. One of the indicators of that was that there was a big chunk of shared variables that were included everywhere to provide context information like user name, fiscal period, login, etc. Few of the values were referenced in any body of code, but they were all defined everywhere. I created a persistent procedure, launched and populated it in the menu system, and made it a super. I populated both the shared variables so that everything that hadn't been changed still worked the way it always had. But, for any new function or any function which needed any serious rework, I cut out the shared variables and substituted calls to the super. Making it a super made that particularly easy since I didn't even have to pass in a handle. The result was that I only referenced the values which the particular program actually needed and anything not needed was not defined. Much, much cleaner and something I could implement one program at a time.
Similarly, I would find programs where there were a bunch of interacting programs that needed access to some common information and logic, but only in the set of programs related to one menu choice. Put that info and logic in a persistent procedure in the top level function and make it super, but not session super, and the info and logic could be freely accessed by any of the programs in that overall function, but the logic to implement it was all in one place and the interface between programs was limited to the information which needed to be passed. Much cleaner and clearer and easier to maintain.
Encapsulate, encapsulate, encapsulate.