【问题标题】:Operand type mismatch for `push' [duplicate]'push'的操作数类型不匹配[重复]
【发布时间】:2018-11-02 00:02:52
【问题描述】:

我需要有关使用 ASSEMBLY 部件的 C 代码的帮助。 GCC编译程序集有问题,错误:

$ make
gcc -Wall -g -std=c99 -pedantic   -c -o sthread.o sthread.c
sthread.c: In function ‘sthread_create’:
sthread.c:159:57: warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arith]
     t->context = __sthread_initialize_context(t->memory + DEFAULT_STACKSIZE, f, arg);
                                                         ^
gcc -Wall -g -std=c99 -pedantic   -c -o queue.o queue.c
as -g  -o glue.o glue.s
glue.s: Assembler messages:
glue.s:32: Error: operand type mismatch for `push'
<wbudowane>: polecenia dla obiektu 'glue.o' nie powiodły się
make: *** [glue.o] Błąd 1

有问题的代码:

__sthread_switch:
    # preserve CPU state on the stack, with exception of stack pointer, instruction pointer first, reverse order
    pushq %rip  #line 32
    pushf
    pushq %rdi
    pushq %rsi
    pushq %rbp
    pushq %rbx
    pushq %rdx
    pushq %rcx
    pushq %rax

    # Call the high-level scheduler with the current context as an argument
    movq    %rsp, %rdi
    movq    scheduler_context, %rsp
    call    __sthread_scheduler

【问题讨论】:

  • 相关:You cannot access the instruction pointer directly。你想达到什么目的?为什么需要访问 RIP?有关访问 RIP 的替代方法,请参阅链接的问题。
  • rip 不是通用寄存器,也没有专用的push rip 指令。
  • 您通常会在 call 指令中获得相当于 push rip 的内容。进入函数时,调用者的rip已经保存在栈上,

标签: c gcc assembly x86-64


【解决方案1】:

使用X86_64,您无法推送%rip,实际上您根本无法直接访问它。

如果你还需要做,你可以做

leaq 0(%rip), %rax # Or any other GPR that is free
pushq %rax 

callq . + 5          # no label, hard-code instruction length
# or
callq 1f ;  1:       # with a local numbered label

虽然我不确定为什么你会想要隐藏%rip,如果你从这里恢复它,执行将从推送指令继续。这有什么价值吗?您需要重新考虑线程切换逻辑。

【讨论】:

  • call rel32 rel32=0 与push rip 相同。事实证明,比 PPro 更新的 CPU 是特殊情况,因此它可能比 lea/push 更有效。
  • @PeterCordes 我以前写过。编辑它是因为不确定,根据我的理解也会与返回预测混淆。但无论如何,这里正在发生某种上下文切换。我猜应该没问题
  • @PeterCordes 你能检查一下我现在添加的指令是否正确吗?我有点不确定。
  • @PeterCordes callq *(%rip) 是否进行间接调用?
  • 是的,这就是* 的意思。它组装到ff 15 00 00 00 00 call QWORD PTR [rip+0x0]。没有*call (%rip) 只会警告indirect call without '*'。我不确定在没有标签或硬编码指令长度的情况下编码call +0 的任何“干净”方式。当然你可以用.byte 0xe8, 0,0,0,0手动编码。
【解决方案2】:

要推送 RIP,只需执行 call 零位移即可。

call next_insn
next_insn:

所以call 的跳转部分是无操作的,所以你只是得到了推送返回地址(即当前 RIP)的效果。

有趣的事实:call rel32=0 is a special case and doesn't unbalance the return address predictor stack 在比 PPro 更新的 CPU 上。所以call next_insn / pop eax 在 32 位模式下很有用,相当于lea (%rip), %rax

它仍然是一个分支指令,并且仍然解码为多个微指令(不像 GPR 的 push 是 1 个微融合微指令),所以 lea (%rip), %raxpush %rax 可能效率更高。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-08
    • 2023-03-27
    • 1970-01-01
    相关资源
    最近更新 更多