I was asleep when I posted this, so I left out the example code. Don't know why, but I worked out more complete code than the example I failed to give. To keep chatter off the list, I posted it in my blog, if anyone is interested. (Probably not?)
http://defining-computers.blogspot.jp/2015/03/splitting-return-addresses-out-of.html -- Joel Rees On Mon, Mar 30, 2015 at 1:08 AM, Joel Rees <[email protected]> wrote: > On Sun, Mar 29, 2015 at 2:50 PM, Philip Guenther <[email protected]> wrote: >> On Sun, 29 Mar 2015, Joel Rees wrote: >>> Is there any good reason for interleaving the return addresses with data >>> on the data/parameter stack in C? I know it's the tradition, from back >>> when it was all we could hope for to have one page per process, but that >>> has not been the case for many years, I think. >> >> It's easy, efficient, > > I don't agree with those two assertions, but I'm not really looking at > easy or efficient. I'm thinking about return pointers getting walked > on. > >> doesn't burn another register for a second stack, > > I've often pondered on whether the entire reason for having a frame > pointer is that you have to have something to lead you around the > return pointer. > > Still, there is no reason to keep the frame link in a register. Dump > the saved parameter stack pointer on the flow-of-control stack, paired > with the return pointer, and you're done. If you think even that is > necessary. Or you could save the previous top of parameter stack below > the local variables and temporaries on the parameter stack, which is a > little more fragile, if you really want to make it appear to be a > linked list. > >> and the ISA (instruction set architecture) may have direct support for it. > > Looking at all the funky instructions and addressing modes the 68020 > and 80286 and 80386 directly supported, but got left behind as CPU > advanced, direct instruction support really isn't an argument, to me. > >> For example, in both i386 and x86_64 the 'call' and 'ret' instructions >> work with the same stack pointer, %sp, as the various 'push' and 'pop' >> instructions. > > Yeah, it does feel like a waste not to use the instructions provided, > doesn't it? > >> If you use %sp for the stack with return addresses so you >> can use 'call' and 'ret', then what is stack pointer for your >> arguments/locals stack? > > Well, if we link the frame on one of the stacks instead of in a > register, the erstwhile frame pointer register isn't doing anything in > particular any more. > >> On i386, you're crying for registers already; >> losing another would be bad. > > Back when SP and BP were sixteen bits, and pointed into the same > segment unless overridden, there was definitely something unsettling > about using them independently, but if you consider that the 6809 was > even more restricted in stack space when you used SP and UP for two > separate stacks, you remember that, even now, most stacks aren't that > big anyway. > > But that was then, this is now, and %sp and %bp aren't sixteen bits > any more. We can define regions that cause access exceptions to help > us know when our stacks and heaps collide. And are we still even using > the segment registers? > > I know older MMUs are kind of tight on the number of page tables you > can keep active, but are they that tight? This is one of my questions, > by the way. > >> Hmm, wasn't there a shipping processor which explicitly have two stacks >> something like this? I have a vague memory that it may have been itanium, >> but that could be a hallucination. > > My impression was that the register stack backing store was assumed to > be allocated with the rest of the procedure frame in the memory stack, > but the documentation seems to have been written by lawyers who really > didn't want their end users to be able to pierce the patent haze. I > couldn't say for sure, and I never had actual hardware to check > against, and I really don't want to dig into that dinosaur any more. > > Intel has always made things harder than necessary. > >>> Adding code to the program preamble to reserve space for another stack >>> with mmap shouldn't be hard at all. Default address separation of about >>> a quarter to a half a gig should be reasonable in 32 bit address space, >>> at any rate. New compiler switches would be needed to tune the >>> separation. I'm pretty sure openbsd has the means to keep a largish >>> no-access region between the stacks. >> >> Ugh, knobs are bad if more than a tiny fraction of program have to use >> them. > > You won't hear me argue with you there. But default to a quarter gig > of unallocated logical address space below each stack and only a tiny > fraction of programs would need to change that. > >>> The call protocol itself should be simpler, although I might expect some >>> debate about which stack to push frame pointers to when pushing frame >>> pointers. The problem, I think, is in convincing the compiler to refrain >>> from moving the frame pointer to the stack pointer on function entry. >>> Maybe. >> >> Simpler? > > Well, yeah, I think so. > >> I doubt it. To support exception processing and debugger >> unwinding of calls and displaying variables from them you'll need some way >> to successively peel call frames off *both* stacks. > > On the caller side, push your arguments, perform an unadorned call. > Something like > > SUB SP, parameterbytes :-( > On the called side, if the frame is being maintained, save the > parameter stack pointer on the flow-of-control stack. Do your stuff. > When you're done, if the compiler knows what it has on the stack > anyway, you don't restore the parameter stack pointer, so you just > drop the saved one. Perform an unadorned return. > > You don't even need instruction set support of any kind to keep you > out of trouble. > >>> To those on the list who are intimate with the compiler(s), how difficult >>> would it be to change the function call protocol to push the program >>> counter to a separate stack from the parameters and locals? >> >> Heh, you're talking about creating a new ABI. For difficultly level, look >> at the x32 ABI in Linux. It's an alternative ABI for x86-long-mode, >> changing relatively few things from the amd64/x86_64 ABI, and it still >> took a huge effort. > > This is what I'm asking about. > > I have the impression that some of the stack smash mitigation > techniques in use in openbsd add a similar amount of complexity to the > call protocol. > >> You might want to grab a copy of the ELF ABI for a CPU you're interested >> in, read through it and see what sort of changes would be necessary for >> supporting a two-stack model. Example code sequences for argument >> passing, relocations, stack unrolling, registers set on process entry,... > > Considering the various conventions already in use, I'm not sure it > really changes the ABI significantly, but I'll go back the the ELF > docs and see if I'm missing something. > >> Oooh, and then threads come into play, where programs expect to be able to >> specify a single size for the stack, so maybe you should have the two >> stacks grow towards each other from opposite ends of the allocated stack >> memory? > > I'm hoping that the abomination of lightweight processes sharing stack > space can be avoided with an overall saner run-time. Clean up the > run-time enough and we shouldn't need that kind of threads. > > But, yeah, if we have to have that kind of threads, we'd end up having > to determine the maximum call depth for every independent thread. But > don't we have to do that anyway? > > The flow-of-control stack, at any rate, is dead easy. One address per > nested call, or, if tracking the frames, a pair of addresses per > nested call. > >>> Or am I speculating about a different world, still? >> >> Tomorrow is a different world, but only slightly so. > > Which is my question put a different way -- whether this is too much > of a jump from current practice. And I guess, in the end, I'll > probably be the one who will need to get familiar enough with one of > the compilers to answer this question. > > Just looking for some input and pointers from those who are already > familiar with the compilers. Thanks for helping me unpack my questions > a bit. > > And since you suggest looking back over the ELF docs, I will. > > -- > Joel Rees > > Be careful when you look at conspiracy. > Look first in your own heart, > and ask yourself if you are not your own worst enemy. > Arm yourself with knowledge of yourself, as well.

