C Kernel
Simon Willcocks (1499) 513 posts |
Can anyone help me with the SharedCLibrary? Reading initmodules.s, around line 240, it seems to expect writable workspace at the bottom of the MiB containing the stack it is given. In my kernel, that puts it at 0xfff00000, and the stack (well above 0xfff8000) doesn’t go down that far! Is this some kind of “common knowledge” that the SVC stack base is at 0xfff00000? Can I temporarily provide a page of writable memory at that address and expect it to work? |
Julie Stamp (8365) 474 posts |
All the non-USR stacks are aligned to a megabyte. This is because modules written in C keep information for locating their static data in a few words at the bottom. They can then find the stack base by masking off R13. So you’ll need a page to be there whenever you’re inside such a module. Other modules (such as TaskWindow) may also attempt to read from that page. |
Simon Willcocks (1499) 513 posts |
Thanks. So, it is “common knowledge”. How long do they keep that information there? Start: USR (with no stack, but memory from 0×8000 to HIMEM) So, I have to allocate separate 1 MiB virtual memory for SVC and IRQ stacks. Thanks again. |
Stuart Swales (8827) 1357 posts |
You only have to ensure that their base (not top) is aligned at a 1 MB multiple, and not that they are 1 MB each. I thought that I’d put a comment in the kernel about this requirement, but apparently not. It goes back to 1987. |
Simon Willcocks (1499) 513 posts |
Got it. Do you know if it’s assumed elsewhere than the SharedCLibrary? (Just thinking of putting a few stacks into a single l2 table.) |
Stuart Swales (8827) 1357 posts |
Don’t know, honestly. This guarantee was given specifically for the SharedCLibrary, but whether there is any other software out there that makes use of this… I thought that it was just so the SCL could set an appropriate stack limit register for module code, but may have evolved to stuff data there temporarily as well. |
Rick Murray (539) 13840 posts |
I think it might just be for C in order to find internal important things, but a lot of stuff is written in C. If I remember correctly (I’m at work), it’s a shift right twenty (?) places followed by a shift left by the same. You’ll spot this all over the place as part of the thunking between the low level API and the C runtime.
Does C in module code also try to load information from an offset of 504 (?) etc? Like I said, I’m at work so I can’t throw a module into Zap and see what the event handler does, for example. |
André Timmermans (100) 655 posts |
I don’t know for RISC OS 5 but I remember that at some point RISC OS Select started to add extra stuff in there for background processes (callbacks, etc) so that it could detect what was really crashing and take the appropriate action instead killing of the current application. And for some reason the AMPlayer module had some trouble because of that while seeking. |
Jon Abbott (1421) 2651 posts |
My suggestion would be a 1MB block for all the stacks that is MB aligned, ensure the page below each stack is unmapped to catch overruns and map a single page at the start of the MB boundary for C. You must ensure a stack can’t overrun into the C area. I don’t recall the size of the C area, might be 256 words. I’m not certain if it’s ever been official documentated, I stumbled upon it by accident when debugging a C Module trying figure out why it was corrupting memory below the stack. I still don’t understand why C couldn’t have had its own dedicated OS provided DA instead of words in Page Zero and hacks off the stack. A throwback to IOC chipset days that’s never been addressed I guess. C is the only code that does this, I’ve seen it in both Modules and Application code. It might be related to elevated C code? |
Simon Willcocks (1499) 513 posts |
OK, so I’ll need to have a virtual MiB of pages for each stack, the second and top pages being unmapped, to detect stack or data overflows, and the top of the stack at 0x???fe000. I take it you meant “C is [NOT] the only code that does this”? It would be nice to tidy things like that up, but that can be done by trapping accesses to the bottom page of the stacks. Later. |
Jon Abbott (1421) 2651 posts |
No, it is ONLY C code that does this. |
Simon Willcocks (1499) 513 posts |
Ah, and only in the SharedCLibrary, itself? I had a quick look and there’s only a few places that clear the bottom 20 bits that I can see. |
André Timmermans (100) 655 posts |
I cannot check at the moment but I suspect that all the CMHG generated veneers use the bottom 20 bits clearing trick to setup the registers correctly so that C code can correctly when called from interrupts, vectors or callbacks. |
Jon Abbott (1421) 2651 posts |
It would be useful if someone “in the know” would document it’s use and what it’s actually doing somewhere. In particular what the full code sequence is and what it’s using the bottom of the stack area for. Ideally I’d like the JIT in ADFFS to check for the code sequence and replace it with a legal request for the address block its after. I’ve not actually studied what it does after the MOV Rx,R13,LSR #20 / LSL, is it using it as a temporary area to store CPSR for example, before switching to USR to call C code? If so, I’m assuming there’s also an exit sequence that does something similar? |
Dave Brown (29) 18 posts |
If I recall correctly, the block contains several pointers to C static data (1 for the C library ‘kernel’, 1 for the C library and 1 for the module itself) |
Rick Murray (539) 13840 posts |
Maybe take a peek at the GCCSDK’s “cmodule”? This might help as well? https://www.riscosopen.org/wiki/documentation/show/Using%20C%20in%20assembler%20components |
Stuart Swales (8827) 1357 posts |
I thought I remembered FileSwitch also used the MB-boundary knowledge for creating temporary buffers, but above the adjusted sp. Yup, grok for the GetLumpOfStack macro (as it’s now known).
It’s ‘cmunge’ in gccsdk (under gcc4/riscos/). The veneers for C module code do set some stuff up down there, more comments would certainly help. The idea of having data stored below sp in the non-USR stacks fills me with horror. Fine for setting up a sl so that code compiled with stack checking (e.g. functions in the SharedCLibrary) can be called, and work out that the stack would overflow, but for data, no. That’s what the module workspace is for. It’s going to get overwritten at some point when a C module calls a SWI. The C compiler really needed more work to generate module code that fit the OS model.
No, C module code is called in the appropriate privileged mode for the operation and platform it’s running on, SVC26 or SVC32. Exception being for module-is-runnable’s main() function which is USR26 or USR32. |
Rick Murray (539) 13840 posts |
Yup. My bad. It’s cmunge…
There are numerous things I could have done without knowing. ;)
I think part of the problem is that the OS is heavily based on an assembler friendly API with utterly no concept of runtime linking, and C is an alien concept. I’ll remind you that the Arthur PRM noted that they (Acorn? The authors?) anticipated that all major applications would be written as modules. With hindsight, there’s so much wrong with that that it’s impossible to say that out loud without giggling or, at least, eyeball rolling. But it gives you an idea of what C was up against.
I think some of the veneers or service call thingies may also enter in the appropriate IRQ mode. |
Stuart Swales (8827) 1357 posts |
Almost no expense was given to supporting development for Arthur BITD. We said we needed compilers to produce (a) ROM-able (b) PIC that could © run in privileged modes back in 1986 and were ignored on all counts, as they knew better. Hell, they even used the wrong register as sp in APCS until what, end 1988? Or in fact sometime in 1989, as SharedCLibrary 1.02 (07 Dec 1988) on my RISC OS 2 App1 disc only has one SWI, to support APCS-A. Relocatable module support came sometime 90/91? [Edit: APCS-R and module support were both added with C Release 3, August 1989, still after RISC OS 2 was launched to the public (and 11 months after RISC OS 2.00 went off for ROMming), so references to APCS-A here and in the PRM as a purely Arthur-thing are misleading; PipeDream 3.00 (which required RISC OS 2, not Arthur) would have been compiled with C Release 2 (July 1988) using APCS-A.]
I think that may have been the case in the dim and distant past, ’cos I nearly wrote the same, but a cursory glance at cmunge shows it changing to SVC. I nearly sobbed when I saw the vector veneer.
And even more so when an interrupt routine is triggered within that SWI handler calling another SWI that might end up in a HAL call. Yum yum SVC stack. As I said, the idea of putting data down there is truly daft. I thought BITD when the SharedCLibrary wanted to set a sl in a non-USR stack that the SharedCLibrary stack extension routines and friends would just do something appropriate (i.e. fail) when called in non-USR mode rather than trying to examine it as if it were a fully valid stack chunk. |
Rick Murray (539) 13840 posts |
Oh, that historical “it uses R12” incarnation? I’ve seen it mentioned but don’t think I’ve ever run into code that did such a thing. I wasn’t trying to disassemble Edit in the RISC OS 2 days…
Was that because they were betting the farm on ARX?
Refer again to the aforementioned PRM quote. Writing major application software as a module and having it run in the RMA is… actually bordering on being a bit brain damaged.
Yup. Just wasted an hour pulling apart the object file that cmhg creates.
Makes for interesting reading the source code of cmunge’s writefile (where the interesting stuff happens). More than a few cases of “This code is weird but this is what cmhg does”.
Doesn’t the kernel habitually “flatten” it for <Many$Reasons>? |
Stuart Swales (8827) 1357 posts |
Oh yes. And they’d have produced an A302.5 if we’d bowed to pressure. [Edit: From Sarah Walker on stardot – Biggest A305 serial number I’ve seen is 1002053, compared to biggest A310 serial number of 1022931. So the A305 production run was pretty small, though still larger than the A440 (1000357).]
I don’t remember the emphasis on application-as-module under Arthur that you have; the program environment chapter in the Arthur PRM vol. 1 does list the first two instances of starting applications as modules, but the latter two are standard load application program into application space and run there. [Edit: OK, it’s in Arthur PRM vol 2 “However, it is clear that any major piece of software written for the A-series machines should be implemented as a module, so this chapter is included as a guide for the writer of such software.” Dunno how that passed proof-reading, especially given the only way to create a module back then was in assembler. Otherwise why would we have implemented FF8s? Software developers did have the early C compiler available (with static AnsiLib); PipeDream 2 was written in C for Arthur. BCPL too.]
If it’s going to be unable to return up the SVC call stack, then of course. Like when you OS_GenerateError and pass control to the USR mode Error handler, or run an application. |
Jon Abbott (1421) 2651 posts |
Are we saying that as well as making assumptions about the stack MB alignment, C is also making assumptions about the stack size and actually writing data into the end of the stack? I’d assumed it was writing to a special page below the stack. Does anyone have an example of the full veneer? I’ll have a trawl through some C game dumps if not. |
Julie Stamp (8365) 474 posts |
Doing
will output the assembly source of whatever veneers you request in test.cmhg. (This only works on cmunge, not cmhg.) The way modules written in C access their static data is summarised in the entry for SharedCLibrary_LibInitModule at PRM 4-267. |
Jon Abbott (1421) 2651 posts |
Here’s a sequence that I nabbed from VCHIQ many years ago:
So there’s two words at the end of the stack and SL is set to (MB boundary)+&21C |
Stuart Swales (8827) 1357 posts |
Yes, it preserves and replaces those two words (at what I’d call the bottom of the SVC stack!) and +&21C == +10_540 (SL_Lib_Offset in the PRM). Note that on entry to the veneer, it could be in IRQ32/26 (vectors, generic) or SVC32/26 (SWI handlers etc.), forcing the call into C code as SVCxx before setting up the stack chunk, restoring to the callers’ mode on exit. See cmunge for the gory details as the veneers are now usually 26/32-bit agnostic; I think they followed CMHG closely. |