为什么当我使用 __asm__ 时 gcc 不能正确写入内存
Why is gcc not writing to memory correctly when I use __asm__
我有一段代码调用了BIOS文本输出函数,INT 0x10, AH=0x13
:
static void __stdcall print_raw(uint8_t row, uint8_t col, uint8_t attr,
uint16_t length, char const *text)
{
__asm__ __volatile__ (
"xchgw %%bp,%%si\n\t"
"int [=11=]x10\n\t"
"xchgw %%bp,%%si\n\t"
:
: "d" (row | (col << 8)), "c" (length), "b" (attr), "a" (0x13 << 8), "S" (text)
);
}
我还有一个可以在屏幕上打印数字的函数:
void print_int(uint32_t n)
{
char buf[12];
char *p;
p = buf + 12;
do {
*(--p) = '0' + (n % 10);
n /= 10;
} while (p > buf && n != 0);
print_raw(1, 0, 0x0F, (buf + 12) - p, p);
}
我花了很多时间试图弄清楚为什么屏幕上什么也没有出现。我深入研究并查看了为 print_int
:
生成的代码
.globl print_int
.type print_int, @function
print_int:
.LFB7:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl , %ecx
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %esi
pushl %ebx
.cfi_offset 6, -12
.cfi_offset 3, -16
leal -8(%ebp), %esi
leal -20(%ebp), %ebx
subl , %esp
movl 8(%ebp), %eax
.L4:
xorl %edx, %edx
decl %esi
divl %ecx
testl %eax, %eax
je .L6
cmpl %ebx, %esi
ja .L4
.L6:
leal -8(%ebp), %ecx
movl 64, %eax
movb , %bl
movl , %edx
subl %esi, %ecx
#APP
# 201 "bootsect.c" 1
xchgw %bp,%si
int [=13=]x10
xchgw %bp,%si
# 0 "" 2
#NO_APP
addl , %esp
popl %ebx
.cfi_restore 3
popl %esi
.cfi_restore 6
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
如果您仔细观察 L4
处的循环,您会发现它从未将任何内容存储到 buf
中。它省略了说明!它只是将它分解并且从不将任何字符存储到缓冲区中。
优化器在使用 __asm__
语句时可能会导致此类错误代码。你需要非常小心你的约束。在这种情况下,编译器没有 "see" 我通过 esi
中的指针访问内存,如 "S" (text)
输入约束中指定的那样。
解决方案是在 __asm__
语句的 clobber 部分添加一个 "memory"
clobber:
__asm__ __volatile__ (
"xchgw %%bp,%%si\n\t"
"int [=10=]x10\n\t"
"xchgw %%bp,%%si\n\t"
:
: "d" (row | (col << 8)), "c" (length), "b" (attr), "a" (0x13 << 8), "S" (text)
: "memory"
);
这会告诉编译器您依赖于内存值,并且您可能会更改内存值,因此在汇编语句执行之前确保内存是最新的并确保不依赖于它可能缓存在寄存器中的任何内存值。有必要防止编译器在我的代码中省略对 buf
的存储。
我有一段代码调用了BIOS文本输出函数,INT 0x10, AH=0x13
:
static void __stdcall print_raw(uint8_t row, uint8_t col, uint8_t attr,
uint16_t length, char const *text)
{
__asm__ __volatile__ (
"xchgw %%bp,%%si\n\t"
"int [=11=]x10\n\t"
"xchgw %%bp,%%si\n\t"
:
: "d" (row | (col << 8)), "c" (length), "b" (attr), "a" (0x13 << 8), "S" (text)
);
}
我还有一个可以在屏幕上打印数字的函数:
void print_int(uint32_t n)
{
char buf[12];
char *p;
p = buf + 12;
do {
*(--p) = '0' + (n % 10);
n /= 10;
} while (p > buf && n != 0);
print_raw(1, 0, 0x0F, (buf + 12) - p, p);
}
我花了很多时间试图弄清楚为什么屏幕上什么也没有出现。我深入研究并查看了为 print_int
:
.globl print_int
.type print_int, @function
print_int:
.LFB7:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl , %ecx
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %esi
pushl %ebx
.cfi_offset 6, -12
.cfi_offset 3, -16
leal -8(%ebp), %esi
leal -20(%ebp), %ebx
subl , %esp
movl 8(%ebp), %eax
.L4:
xorl %edx, %edx
decl %esi
divl %ecx
testl %eax, %eax
je .L6
cmpl %ebx, %esi
ja .L4
.L6:
leal -8(%ebp), %ecx
movl 64, %eax
movb , %bl
movl , %edx
subl %esi, %ecx
#APP
# 201 "bootsect.c" 1
xchgw %bp,%si
int [=13=]x10
xchgw %bp,%si
# 0 "" 2
#NO_APP
addl , %esp
popl %ebx
.cfi_restore 3
popl %esi
.cfi_restore 6
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
如果您仔细观察 L4
处的循环,您会发现它从未将任何内容存储到 buf
中。它省略了说明!它只是将它分解并且从不将任何字符存储到缓冲区中。
优化器在使用 __asm__
语句时可能会导致此类错误代码。你需要非常小心你的约束。在这种情况下,编译器没有 "see" 我通过 esi
中的指针访问内存,如 "S" (text)
输入约束中指定的那样。
解决方案是在 __asm__
语句的 clobber 部分添加一个 "memory"
clobber:
__asm__ __volatile__ (
"xchgw %%bp,%%si\n\t"
"int [=10=]x10\n\t"
"xchgw %%bp,%%si\n\t"
:
: "d" (row | (col << 8)), "c" (length), "b" (attr), "a" (0x13 << 8), "S" (text)
: "memory"
);
这会告诉编译器您依赖于内存值,并且您可能会更改内存值,因此在汇编语句执行之前确保内存是最新的并确保不依赖于它可能缓存在寄存器中的任何内存值。有必要防止编译器在我的代码中省略对 buf
的存储。