Page 1 of 1
How to Change CS Register in GCC Inline Assembly? [Solved]
Posted: Sat Oct 16, 2010 12:38 am
by blobmiester
While I was cleaning up my code, I noticed I was doing the follow for modifying the CS register (after installing the GDT):
Code: Select all
__asm__ volatile ("movb %0, %%al\n"
"movb %%al, (do + 5)\n"
""
"do:\n"
"ljmp $0x08, $flush\n"
""
"flush:"
:: "r" (cs_register)
: "%al");
I don't particularly like the self-modifying code but that is the only thing I could come up with at the time (to be able to set the CS register to a variable). It works. However, I'm wondering if anyone has any suggestions for improving it (or am I going about this the wrong way)?
Thanks for the help.
Oh, and I realize it might be better to use an external ASM function. But... I don't know. I would rather do this in inline assembly. If I can't come up anything else I'll move it to an external file. But I can hope, right?
Re: How to Change CS Register in GCC Inline Assembly?
Posted: Sat Oct 16, 2010 1:15 am
by TylerH
Code: Select all
__asm__ volatile ("ljmp %0, $flush\n"
"flush:"
:: "r" (cs_register)
: "%al");
Does that not work?
Re: How to Change CS Register in GCC Inline Assembly?
Posted: Sat Oct 16, 2010 1:24 am
by blobmiester
Code: Select all
/tmp/ccpk8g0z.s: Assembler messages:
/tmp/ccpk8g0z.s:87: Error: suffix or operands invalid for `ljmp'
Sad Panda. When I add the immediate '$' in front of the '%0' it complains like the following:
Code: Select all
/tmp/ccZC5MeA.s: Assembler messages:
/tmp/ccZC5MeA.s:87: Error: illegal immediate register operand %dl
Apparently the ljmp segment needs to be a constant. Period. No "N" constraint. Nothing. (At least of what I tried). Meh.
Re: How to Change CS Register in GCC Inline Assembly?
Posted: Sat Oct 16, 2010 1:35 am
by xenos
TylerAnon wrote:Code: Select all
__asm__ volatile ("ljmp %0, $flush\n"
"flush:"
:: "r" (cs_register)
: "%al");
This probably won't work because ljmp doesn't allow a general purpose register ("r") in here. But you could try an indirect far jump, i.e. store a far pointer in some variable and use it as a jump target. I'll try to figure out how to do that in inline assembly, but maybe you'll find out yourself in the meantime
EDIT: Try this:
Code: Select all
struct {unsigned int offset; unsigned short segment;} dest;
dest.segment = 0x08; // whatever value you want in CS
asm volatile (
"movl $1f, %0\n"
"ljmp *%0\n"
"1:" :: "m"(dest));
Re: How to Change CS Register in GCC Inline Assembly?
Posted: Sat Oct 16, 2010 6:02 am
by Owen
Code: Select all
asm volatile(
"push %[cs];"
"push $1f;"
"retf;"
"1:"
:: [cs] "nr" (cs)
);
Re: How to Change CS Register in GCC Inline Assembly?
Posted: Sat Oct 16, 2010 4:52 pm
by blobmiester
Owen wrote:Code: Select all
asm volatile(
"push %[cs];"
"push $1f;"
"retf;"
"1:"
:: [cs] "nr" (cs)
);
Damn. That looks awesome. I didn't even think of using retf. Focusing too much on trying to get ljmp to work.
Here is my final code, many thanks:
Code: Select all
__asm__ volatile ("pushw %[cs]\n"
"pushl $1f\n"
"retf\n"
""
"1:"
:: [cs] "nr" (cs_register));
XenOS: the indirect ljmp works too. Thanks.
Re: How to Change CS Register in GCC Inline Assembly? [Solve
Posted: Sun Oct 17, 2010 1:35 am
by xenos
The retf method is probably the most elegant one
The only thing one needs to be careful about is the possibility of stack switches, so the old and new value of CS must have the same privilege level. Of course this is also true for the ljmp method, but in that case you simply get a #GP.
Re: How to Change CS Register in GCC Inline Assembly?
Posted: Mon Oct 18, 2010 3:56 am
by qw
blobmiester wrote:Code: Select all
__asm__ volatile ("pushw %[cs]\n"
"pushl $1f\n"
"retf\n"
""
"1:"
:: [cs] "nr" (cs_register));
Don't use "pushw". Yes, CS is only 16 bits, but "retf" will pop 32 bits for the selector.
Re: How to Change CS Register in GCC Inline Assembly? [Solve
Posted: Tue Oct 19, 2010 12:25 pm
by Owen
Yeah, you have to be careful about that. Pushw will make 16-bit stack slots that absolutely nothing wants, and will also cause alignment hell.