GCC baremetal inline-assembly SI register not playing nicely with pointers

醉酒当歌 提交于 2019-12-13 16:10:03

问题


Well, this is obviously a beginner's question, but this is my first attempt at making an operating system in C (Actually, I'm almost entirely new to C.. I'm used to asm) so, why exactly is this not valid? As far as I know, a pointer in C is just a uint16_t used to point to a certain area in memory, right (or a uint32_t and that's why it's not working)? I've made the following kernel ("I've already made a bootloader and all in assembly to load the resulting KERNEL.BIN file):

kernel.c

void printf(char *str)
{
__asm__(
"mov    si, %0\n"
"pusha\n"
"mov    ah, 0x0E\n"
".repeat:\n"
"lodsb\n"
"cmp    al, 0\n"
"je .done\n"
"int    0x10\n"
"jmp    .repeat\n"
".done:\n"
"popa\n"
:
: "r" (str)
);
return;
}

int main()
{
char *msg = "Hello, world!";
printf(msg);
__asm__("jmp $");
return 0;
}

I've used the following command to compile it kernel.c:

gcc kernel.c -ffreestanding -m32 -std=c99 -g -O0 -masm=intel -o kernel.bin

which returns the following error:

kernel.c:3: Error: operand type mismatch for 'mov'

Why exactly might be the cause of this error?


回答1:


As Michael Petch already explained, you use inline assembly only for the absolute minimum of code that cannot be done in C. For the rest there is inline assembly, but you have to be extremely careful to set the constraints and clobber list right.

Let always GCC do the job of passing the values in the right register and just specify in which register the values should be.

For your problem you probably want to do something like this

#include <stdint.h>

void print( const char *str )
{
  for ( ; *str; str++) {
    __asm__ __volatile__("int $0x10" : : "a" ((int16_t)((0x0E << 8) + *str)), "b" ((int16_t)0) : );
  }
}

EDIT: Your assembly has the problem that you try to pass a pointer in a 16 bit register. This cannot work for 32 bit code, as 32 bit is also the pointer size. If you in case want to generate 16 bit real-mode code, there is the -m16 option. But that does not make GCC a true 16 bit compiler, it has its limitations. Essentially it issues a .code16gcc directive in the code.




回答2:


You can't simply use 16bit assembly instructions on 32-bit pointers and expect it to work. si is the lower 16bit of the esi register (which is 32bit).

gcc -m32 and -m16 both use 32-bit pointers. -m16 just uses address-size and operand-size prefixes to do mostly the same thing as normal -m32 mode, but running in real mode.

If you try to use 16bit addressing in a 32bit application you'll drop the high part of your pointers, and simply go to a different place.

Just try to read a book on intel 32bit addressing modes, and protected mode, and you'll see that many things are different on that mode.

(and if you try to switch to 64bit mode, you'll see that everything changes again)

A bootloader is something different as normally, cpu reset forces the cpu to begin in 16bit real mode. This is completely different from 32bit protected mode, which is one of the first things the operating system does. Bootloaders work in 16bit mode, and there, pointers are 16bit wide (well, not, 20bits wide, when the proper segment register is appended to the address)



来源:https://stackoverflow.com/questions/39482526/gcc-baremetal-inline-assembly-si-register-not-playing-nicely-with-pointers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!