'asm'操作数有不可能的限制

问题描述 投票:1回答:2

我正在尝试在Arch linux上编译xen并收到以下错误:

src/stacks.c:342:5: error: 'asm' operand has impossible constraints
 asm volatile(
 ^

以下是导致错误的方法的代码:

void
run_thread(void (*func)(void*), void *data)
{
ASSERT32FLAT();
if (! CONFIG_THREADS || ! ThreadControl)
    goto fail;
struct thread_info *thread;
thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
if (!thread)
    goto fail;

dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
thread->stackpos = (void*)thread + THREADSTACKSIZE;
struct thread_info *cur = getCurThread();
hlist_add_after(&thread->node, &cur->node);
asm volatile(
    // Start thread
    "  pushl $1f\n"                 // store return pc
    "  pushl %%ebp\n"               // backup %ebp
    "  movl %%esp, (%%edx)\n"       // cur->stackpos = %esp
    "  movl (%%ebx), %%esp\n"       // %esp = thread->stackpos
    "  calll *%%ecx\n"              // Call func

    // End thread
    "  movl %%ebx, %%eax\n"         // %eax = thread
    "  movl 4(%%ebx), %%ebx\n"      // %ebx = thread->node.next
    "  movl (%5), %%esp\n"          // %esp = MainThread.stackpos
    "  calll %4\n"                  // call __end_thread(thread)
    "  movl -4(%%ebx), %%esp\n"     // %esp = next->stackpos
    "  popl %%ebp\n"                // restore %ebp
    "  retl\n"                      // restore pc
    "1:\n"
    : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
    : "m"(*(u8*)__end_thread), "m"(MainThread)
    : "esi", "edi", "cc", "memory");
return;

fail:
    func(data);
}

我不确定发生了什么。具有装配知识的人是否可以帮助查看并告知此处是否存在明显问题?

更新:

您可以通过执行以下操作来修复此错误:

  1. 将COMMONCFLAGS + = $(调用cc-option,$(CC), - fstack-check = no,)添加到seabios makefile中(如果你从git AUR构建xen,那么location应该是xen / src / xen-4.5.1 / tools /固件/ seabios-DIR-远程/生成文件)
  2. 转到stacks.c并将movl(%5),%% esp更改为movl%5,%% esp
c gcc assembly archlinux xen
2个回答
3
投票

直接原因可能是您没有通过优化开关直接或间接启用-fomit-frame-pointer。因此,编译器用完寄存器,因为eaxebxecxedx用于参数,esiedi是clobbers而ebp是帧指针。因此,解决方案是确保启用此选项。

显然this code is part of SeaBIOS(感谢Michael Petch找到它)。 __end_thread只有一个函数,而不是一个函数指针,人们会期望存在那个投射魔法。因此,我认为这个结构的重点是解决任何最终的名称修改问题。不幸的是,它为此目的牺牲了一个注册表。如果您知道您的环境不会破坏函数名称,您可以使用这个更简单的版本,它不需要额外的寄存器,并且在使用帧指针的调试版本中也可以正常编译:

asm volatile(
    // Start thread
    "  pushl $1f\n"                 // store return pc
    "  pushl %%ebp\n"               // backup %ebp
    "  movl %%esp, (%%edx)\n"       // cur->stackpos = %esp
    "  movl (%%ebx), %%esp\n"       // %esp = thread->stackpos
    "  calll *%%ecx\n"              // Call func

    // End thread
    "  movl %%ebx, %%eax\n"         // %eax = thread
    "  movl 4(%%ebx), %%ebx\n"      // %ebx = thread->node.next
    "  movl (%4), %%esp\n"          // %esp = MainThread.stackpos
    "  call __end_thread\n"         // call __end_thread(thread)
    "  movl -4(%%ebx), %%esp\n"     // %esp = next->stackpos
    "  popl %%ebp\n"                // restore %ebp
    "  retl\n"                      // restore pc
    "1:\n"
    : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
    : "m"(MainThread)
    : "esi", "edi", "cc", "memory");

1
投票

我完全重写了asm语句。基本问题是该语句要么使用clobbers,要么用作除EBP之外的每个寄存器的输入/输出操作数。禁用优化并使用-fno-omit-frame-pointer时,没有用于存储评估表达式(u8*)__end_thread的结果的寄存器。这是一件好事,因为它是因为如果帧指针可用,它将生成calll (%ebp),这不是这里实际需要的。

以下asm语句使得除EBP之外的每个寄存器都是输出操作数,而不是尝试分配所有未使用的寄存器和clobber。这使编译器可以更自由地分配输入寄存器。

int dummy;
asm volatile("push 1f\n\t"
         "push %%ebp\n\t"
         "mov %%esp, %[cur_stackpos]\n\t"
         "mov %[thread_stackpos], %%esp\n\t"
         "call *%[func]\n\t"
         "mov %p[mainthread_stackpos], %%esp\n\t"
         "mov %[thread], %%eax\n\t"
         "call %c[end_thread]\n\t"
         "mov 4(%[thread]),%%eax\n\t"
         "mov -4(%%eax),%%esp\n\t"
         "pop %%ebp\n\t"
         "pop %%eax\n\t"
         "jmp *%%eax\n\t"
         "1:\n"
         : 
         [data] "+a" (data),
         "=b" (dummy), "=c" (dummy), "=d" (dummy),
         "=S" (dummy), "=D" (dummy)
         :
         [func] "r" (func),
         [cur_stackpos] "m" (cur->stackpos),
         [thread_stackpos] "rm" (thread->stackpos),
         [mainthread_stackpos] "i" (&MainThread.stackpos),
         [thread] "bSD" (thread),
         [end_thread] "i" (__end_thread)
         : 
         "memory", "cc");

我已经为"i"[mainthread_stackpos]操作数使用了[end_thread]约束和操作数修饰符,以确保这些操作数是简单的标签。编译器不能将它们放在寄存器或堆栈中。这有点偏执,使用没有操作数修饰符的"m"约束也可以。至少在编译器执行与*(u8*)__end_thread相同的意外操作之前。说到这一点,我用__end_thread取而代之的是演员阵容和取消引用似乎毫无意义。

我也用ret取代了pop %eax; jmp *%eax声明,因为这应该更快。 ret语句将始终被错误预测,因为地址不会返回堆栈缓冲区,但至少有可能预测jmp *eax。它要么跳转到下一条指令,要么跳到1:switch_stacks标签。

© www.soinside.com 2019 - 2024. All rights reserved.