在调用asm函数之前调用printf与否的神秘副作用?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在调用asm函数之前调用printf与否的神秘副作用?相关的知识,希望对你有一定的参考价值。

该程序必须以用户提供的准确度计算pi。 calculate_pi()函数是用NASM编写的。有人可以向我解释为什么这条线被评论:

//printf("accuracy: %.15f
", precision);       //<- This line

该程序无法正常工作。将奇怪的数字发送到calcuta_pi()函数?如果对此行进行了注释,则会向函数发送一个非常小的值,程序将无限运行。

但如果它不是一个评论的程序正常工作。

#include <stdio.h>
#include <math.h>

extern double calculate_pi(double precision); /* external function declaration */

double calculate_pi(double precision);   /* function prototype */

int main()
{
double precision = 1;

printf("A program that calculates pi, with accuracy provided by the user
");
printf("Give me accuracy
");

while(1)
{
    if (scanf("%lf", &precision) != 1)
    {
        printf("reading error
");

        fseek(stdin,0,SEEK_END);

        continue;
    }
    if(precision<0)
        precision = fabs(precision);

    //printf("accuracy: %.15f
", precision);       //<- This line
    printf("pi: %.15f
", calculate_pi(precision));
}

return 0;
}

这是我的汇编代码:

;   arctg(1)=a
;   tg(arctg(1))=tg(a)
;   atan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9..
;   PI/4 = atan(1) = 1 - 1/3 + 1/5 - 1/7 + 1/9...
;   PI = (4/1) - (4/3) + (4/5) - (4/7) + (4/9) - (4/11) + (4/13) - (4/15) ...


section .text use32

global  _calculate_pi

_calculate_pi:

%idefine    a   [ebp+12]


;ramka stosu
push    ebp
mov ebp, esp

;ustawianie zmiennych
fld qword [const_wynik]
fstp qword [wynik]

fld qword [const_licznik]
fstp qword [licznik]

fld qword [const_mianownik]
fstp qword [mianownik]

.loop:
finit ; inicjalizacja stosu FPU

fld qword [licznik]         ;licznik na stos
fld qword [mianownik]       ;mianownik na stos
fdiv                        ;wynik dzielenia st1/st0
fadd qword [wynik]          ;st0 = wynik dzielenia + [wynik]

fstp qword [wynik]          ;wywalamy z st0 do [wynik]

                            ;zmieniamy mianownik + 2
fld qword [mianownik]       ;mianownik na stos
fadd qword [zwiekszmian]    ;st0 = mianownik + 2
fstp qword [mianownik]          ;wywalamy z st0 do [mianownik]

                            ;zmieniamy licznik *(-1)
fld qword [licznik]         ;licznik na stos
fchs                        ;st0 = -st0 = -licznik
fstp qword [licznik]        ;wywalamy z st0 do [licznik]

                        ;sprawdzanie dokladnosci
fld qword[wynik]        ;wynik na stos
fldpi                   ;pi na stos
fsub                    ;st0 = wynik-pi = st1 - st0
fabs                    ;st0 = |wynik-pi|

fld qword a             ;st0 = zadana dokladnosc

                            ;(Unordered Compare ST(0) to ST(i) and set CPU flags and Pop ST(0))
                            ;Przyrostek p oznacza obniżenie stosu rejestrów koprocesora, przyrostek i oznacza zapisywanie wyników bezpośrednio do flag procesora a nie flag koprocesora
fucomip st0, st1            ;porownanie z dokladnoscia  if(zadana dokladnosc > uzyskana)


jb    .loop   ;only the C0 bit (CF flag) would be set if no error


fld qword [wynik]

        ;zwraca to co w st0
leave   ; LEAVE = mov esp, ebp / pop ebp
ret


section .data:

wynik       dq 4.0
licznik     dq -4.0
mianownik   dq 3.0
zwiekszmian dq 2.0

const_wynik     dq 4.0
const_licznik   dq -4.0
const_mianownik dq 3.0

样本输出:

enter image description here

我正在使用:

  • NASM版本2.11.06于2014年10月20日编译
  • gcc(MinGW.org GCC-6.3.0-1)6.3.0

编译和汇编命令:

nasm -o pi.o -f coff pi.asm
gcc pi.o pi_interface.c -o projekt.exe -Wall -Wextra
答案

我认为你正在错误地访问函数arg,偏移4个字节。当你创建一个堆栈框架时,第一个arg位于[ebp+8],但是你从[ebp+12]加载。 (这适用于在堆栈上传递args的所有调用约定。我认为32位mingw默认为double执行此操作。)

这意味着你正在使用的double值,因为precision的高4字节来自调用者碰巧留在8字节arg插槽上方的堆栈上。这解释了为什么调用者的更改会影响函数的行为,以及为什么你可以获得无限循环:如果你加载的字节碰巧代表一个非常小的double,你的循环永远不会退出。

低4字节(尾数的32个最低有效位)来自调用者通过的前4个字节。

通过查看寄存器并注意到加载的值不是调用者传递的值,您可以通过调试器轻松找到它。另外,@ Ped7g的建议只是尝试在一个简单的asm函数中返回precision也会发现问题。

以上是关于在调用asm函数之前调用printf与否的神秘副作用?的主要内容,如果未能解决你的问题,请参考以下文章

x64 asm如何将函数指针设置为_cdecl C函数并调用它?

C语言 两个[不定参函数]之间如何进行 [参数传递]

printf输出格式总结

ASM 约束副作用

在C中调用函数之前的参数评估顺序[重复]

asm:调用 DLL