相关疑难解决方法(0)

为什么在调用printf时会覆盖EDX的值?

我写了一个简单的汇编程序:

section .data
str_out db "%d ",10,0
section .text
extern printf
extern exit
global main
main:

MOV EDX, ESP
MOV EAX, EDX
PUSH EAX
PUSH str_out
CALL printf
SUB ESP, 8 ; cleanup stack
MOV EAX, EDX
PUSH EAX
PUSH str_out
CALL printf
SUB ESP, 8 ; cleanup stack
CALL exit
Run Code Online (Sandbox Code Playgroud)

我是NASM汇编程序和GCC,用于将目标文件链接到linux上的可执行文件.

本质上,该程序首先将堆栈指针的值放入寄存器EDX,然后将该寄存器的内容打印两次.但是,在第二次printf调用之后,打印到stdout的值与第一个不匹配.

这种行为似乎很奇怪.当我用EBX替换该程序中每次使用EDX时,输出的整数与预期的完全相同.我只能推断在printf函数调用期间某些时候EDX会被覆盖.

为什么会这样?如何确保我将来使用的寄存器与C lib函数不冲突?

c assembly nasm inline-assembly

6
推荐指数
2
解决办法
754
查看次数

记住 x86-64 System V arg 寄存器顺序的最佳方法是什么?

我经常忘记系统调用中每个参数需要使用的寄存器,每次我忘记时我都会访问这个问题。

x86_64 用户空间函数调用的整数/指针参数的正确顺序是:
%rdi%rsi%rdx%rcx%r8%r9(可变参数函数采用 AL = FP 参数的数量,最多 8)

或者对于系统调用,%rax(系统调用调用号)和相同的参数,除了%r10代替%rcx.

记住这些寄存器而不是每次都用谷歌搜索这个问题的最佳方法是什么?

assembly x86-64 abi cpu-registers calling-convention

5
推荐指数
1
解决办法
646
查看次数

为什么在RISC-V中建议只使用寄存器a0和a1来传递返回值?

RISC-V 调用约定规定寄存器 a0 和 a1 可用于返回值,而不是所有 8 个寄存器 a0~a7。当需要“返回”两个以上的值时,我们可以使用堆栈。为什么?这样做有什么好处吗?

我正在学习 RISC-V 语言,作为计算机体系结构研究的一部分。我注意到我们可以使用所有八个 a0~a7 寄存器将参数传递给函数,但根据一些 RISC-V 调用约定,只有其中两个(a0 和 a1)可用于返回返回值,例如了解 RISC-V 调用约定RISC-V 调用约定。我很困惑为什么约定包含只有 a0 和 a1 应该用于返回的规则。我浏览了上面提到的两篇文章,但没有找到任何解释这一点的内容。在我看来,这些a0~a7寄存器在函数调用之间不被保留,这表明我们可以在函数中自由地使用它们。因此,如果需要,我们可以而且应该使用它们中的任何一个来传递返回值,以方便和高效。总之,有什么理由要求我们限制返回值到a0和a1寄存器吗?

PS我刚刚注意到这个问题Whytworeturnregisters(inmanyprocedurecallsconventions/ABIs),它告诉我连续的寄存器可以用于大数字。然而,我的观点是,为什么我们限制自己在a2~a7中放入更多的返回值,即使看起来没有明显的缺点?或者,如果我使用a2~a7作为返回值,违反约定,会不会很糟糕?

assembly calling-convention riscv

5
推荐指数
1
解决办法
1075
查看次数

如何理解易失性和非易失性寄存器?

CPU寄存器可以通过调用来分类为volatile和non-volatile,word的含义如何volatile暗示分类?

cpu-registers calling-convention volatility

4
推荐指数
2
解决办法
4306
查看次数

x64 参数和返回值调用约定

我调用 Clang 12.0.0 来-Os -march=haswell编译以下 C 程序:

int bar(int);

int foo(int x) {
  const int b = bar(x);
  if (x || b) {
      return 123;
  }
  return 456;
}
Run Code Online (Sandbox Code Playgroud)

生成以下程序集:

foo:                                    # @foo
        push    rbx
        mov     ebx, edi
        call    bar
        or      eax, ebx
        mov     ecx, 456
        mov     eax, 123
        cmove   eax, ecx
        pop     rbx
        ret
Run Code Online (Sandbox Code Playgroud)

https://gcc.godbolt.org/z/WsGoM56Ez

据我了解,foo 的调用者在 RAX/EAX 中设置了 x。然后 foo 调用 bar,这不需要修改 RAX/EAX,因为 x 是作为未修改的输入传递的。

or eax, ebx指令似乎是将输入 x 与 bar 的结果进行比较。该结果如何最终进入 EBX?有何目的mov …

c assembly x86-64 calling-convention

4
推荐指数
1
解决办法
1552
查看次数

在调用约定中使用非易失性寄存器有什么好处?

我正在编写一个 JIT 编译器,我惊讶地发现在 Win64 调用约定中,如此多的 x86-64 寄存器是非易失性的(被调用者保留的)。在我看来,非易失性寄存器只是在所有可以使用这些寄存器的函数中做更多的工作。在数值计算的情况下尤其如此,您希望在叶函数中使用许多寄存器,例如某种高度优化的矩阵乘法。但是,例如,16 个 SSE 寄存器中只有 6 个是易失性的,因此如果您需要使用更多,您将有很多溢出要做。

所以是的,我不明白。这里有什么权衡?

cpu-registers calling-convention

3
推荐指数
1
解决办法
1393
查看次数

在iOS上使用Gas的ARM64?

我已经将一些汇编功能移植到了64位ARM,并且它们在Android上也能正常工作,但是当我尝试在Xcode中编译相同的文件时,我发现clang使用了不同的语法(与官方ARM不同)文档)。

我发现一些脚本可以将源文件从一种格式转换为另一种格式,但这不是理想的解决方案(当源文件包含预处理器定义时,这些脚本似乎不起作用)。

我可以简单地在Xcode中使用gas还是将clang配置为接受gas语法?如果不是,那么clang汇编器文档在哪里?

更新-2015年9月

看来该问题已由XCode 7(新的clang版本?)解决了:现在,我可以导入为Android编写的程序集源文件,并且它们进行编译时无需进行任何更改。

assembly android gnu-assembler ios arm64

3
推荐指数
1
解决办法
1094
查看次数

在堆栈帧创建之前或之后推送寄存器之间有什么区别吗?

假设我有一个名为 func 的函数:

PROC func:
    ;Bla bla
    ret
ENDP func
Run Code Online (Sandbox Code Playgroud)

现在,假设我使用 register axbx例如,为了保存它们的初始值,我将它们推送到函数内部的堆栈中。

现在的问题是:在创建堆栈帧之前推送寄存器之间是否有很大的不同:

PROC func:
    push bp
    push ax
    push bx
    mov bp, sp
    ;Bla bla
    ret
ENDP func
Run Code Online (Sandbox Code Playgroud)

还是之后?

PROC func:
    push bp
    mov bp, sp
    push ax
    push bx
    ;Bla bla
    ret
ENDP func
Run Code Online (Sandbox Code Playgroud)

我应该在我的程序中使用什么?一种方法比另一种更好或更“正确”吗?因为我目前使用第一种方法。

x86 assembly callstack stack-frame

3
推荐指数
1
解决办法
351
查看次数

如何在函数调用期间保护寄存器不被覆盖?

我正在尝试为以下函数编写汇编代码:

#include <iostream>
void f(int x) {
    if (x > 0) {
        std::cout << x << std::endl;
        f(x-1);
        std::cout << x << std::endl;
    }
}
int main() {
    f(1);
}
Run Code Online (Sandbox Code Playgroud)

该函数脚本的输出是1 1。我尝试为所谓的“低成本计算机”汇编器编写汇编代码,这是Anthony Dos Reis为他的书“C and C++ under the hood”发明的计算机。我写的汇编代码是:

startup   jsr main
          halt             ; back to operating system
;==============================================================
                           ; #include <stdio.h>
greater   dout
          nl
          sub r1, r0, 1
          push lr
          push fp
          mov fp, sp
          push r1
          jsr f
          add sp, sp, 1
          dout
          nl …
Run Code Online (Sandbox Code Playgroud)

c c++ assembly calling-convention

3
推荐指数
1
解决办法
1021
查看次数

为什么使用 %ebx 寄存器会导致我的汇编代码出现分段错误

我正在处理一小段 i386 汇编代码,并在使用 %ebx 寄存器时遇到分段错误。我希望能了解一些关于为什么会发生这种情况的见解。

我有一个非常简单的汇编函数的两个版本。该函数的目的是将两个整数参数相加。当我使用 %ecx 寄存器时,该函数可以正常工作。但是,当我切换到使用 ebx 寄存器时,会导致分段错误。这是两个版本的代码:

版本 1(导致分段错误):

.globl addArgs
.text
addArgs:
    movl 4(%esp), %eax
    movl 8(%esp), %ebx
    addl %ebx, %eax
    ret
Run Code Online (Sandbox Code Playgroud)

我缺少有关 ebx 寄存器的特定信息吗?

所以下面的版本可以完美运行。

版本 2(工作正常):

.globl addArgs
.text
addArgs:
    movl 4(%esp), %eax
    movl 8(%esp), %ecx
    addl %ecx, %eax
    ret
Run Code Online (Sandbox Code Playgroud)

我正在 32 位 x86 系统上编译我的代码并使用 GCC 进行编译。

在这种情况下,下面是使用该函数的 C 代码:

#include <stdio.h>

int addArgs();

int main(){
    int a = 1, b = 2;
    printf("%d\n",  addArgs(a, b));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这就是我编译它的方式:

.globl addArgs
.text
addArgs: …
Run Code Online (Sandbox Code Playgroud)

c x86 assembly cpu-registers calling-convention

3
推荐指数
1
解决办法
92
查看次数