[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[MiNT] Super() bug
Miro Kropáček wrote:
Do you know more about this? I don't quite understand the comment in
mintlib header, the binding seems right to me (pushing long as
parameter), what can be done differently? What makes calling under
FreeMiNT and TOS different?
Tested right now with Steem and TOS 1.62.
David had the same trouble on a real Falcon, and my fix solved his problem.
1) When a program starts, it is in user mode. To switch to supervisor mode,
it is well known we have to do the following:
clr.l -(sp)
move.w #0x20,-(sp)
trap #1
addq.l #6,sp
move.l d0,oldssp
Note that the clr instruction means "don't modify the stack", and it works well.
Then d0 is the old SSP. We must not use it, just keep it transmit it when
returning into user mode.
2) To return in supervisor mode:
move.l oldssp,-(sp)
move.w #0x20,-(sp)
trap #1
addq.l #6,sp
This should just return to user mode, without modifying the stack.
Here we have 2 cases:
A) If the stack has the same value than it was at 1) (when we have switched
to supervisor mode) it just works as expected. Most of the times, in
assembler sources, it is the case, because the programmer switches to
supervisor mode, does some things, when returns to the user mode.
So most of the times, there is no bug.
B) After switching to supervisor mode, the program still has the right to
use the stack for local variables or whatever, right ?
This scenario is not theoretical, it is used by GCC when optimizing the
stack usage. Or it could happen if the return to user mode happens in an
inner function.
So let's subtract some bytes to sp. Then return to the user mode.
*Rule:* If the stack is different than the value it was at 1), the stack
value after the trap is totally bogus !!
Actually, it seems to be the value of the stack as it was at 1) minus 4.
Of course that stack must absolutely not be used.
Solution: Since the trap trashes SP, backup SP before the trap and restore
it afterwards. Something like:
move.l sp,a3
move.l oldssp,-(sp)
move.w #0x20,-(sp)
trap #1
move.l a3,sp
That way, the potentially bad SP returned after the trap is immediately
reset to the right value, and everything is good.
I have been using that trick for a long time, it works flawlessly.
Unfortunately, this trick must not be used in the rare case (never used ?)
when Super() is used to switch to supervisor mode using a specified stack.
Of course the old SP must not be restored.
So we can't simply change the Super() binding.
So I propose a new and safe binding named SuperToUser() to be always used
when returning to user mode, and only in that case. See the attached file.
Oh, I used an inline function, but since the MiNTLib uses macros we should
convert that one to a macro as well.
Final note, this problem does not appear with FreeMiNT, because it is
probably more clever and does not trash the stack when it is supposed to be
unchanged.
--
Vincent Rivière
static inline
void SuperToUser(void* ssp)
{
register long spbackup;
__asm__ volatile
(
"move.l sp,%0\n\t"
"move.l %1,-(%%sp)\n\t"
"move.w #0x20,-(%%sp)\n\t"
"trap #1\n\t"
"move.l %0,sp"
: "=&r"(spbackup) /* outputs */
: "g"(ssp) /* inputs */
: "d0", "d1", "d2", "a0", "a1", "a2" /* clobbered regs */
);
}