如果我移动一个值进行注册和编辑,它将有所作为

How does it make a difference if I move a value to register and edit it

本文关键字:注册 编辑 有所作为 移动 一个 如果      更新时间:2023-10-16

最近我决定开始使用Inline Assembly和C 进行编码。

我试图像这样零数组长度:

void PrintAsm(void* Array, int Count)
{
    __asm
    {
        cmp Count, 0
            jle Done
            jg LoopArray
        LoopArray :
        mov byte ptr[Array], 0
            inc Array
            dec Count
            jnz LoopArray
            jmp Done
        Done :
    }
}

int main()
{
    char* Array = new char[10]; 
    for (int i(0); i < 10; i++) Array[i] = (char)rand();
    for (int i (0); i < 10; i++) std::cout << (int)Array[i] << " ";
    
    PrintAsm(Array, 10);
    std::cout << "nn";
    for (int i(0); i < 10; i++) std::cout << (int)Array[i] << " ";
    delete[] Array;
    std::cout << "nn";
    system("PAUSE");
    return 0;
}

输出:

41 35 -66 -124 -31 108 -42 -82 -82 -82 -112

41 35 -66 -124 -31 108 -42 -82 -82 -82 -112

直到我实际将数组保存在寄存器中,并且在其他寄存器中的计数才能使用:

void PrintAsm(void* Array, int Count)
{
    __asm
    {
        mov ecx, Array
        mov edx, Count
        cmp edx, 0
        jle Done
        jg LoopArray
    LoopArray:
        mov byte ptr[ecx], 0
        inc ecx
        dec edx
        jnz LoopArray
        jmp Done
    Done:
    }
}
int main()
{
    char* Array = new char[10]; 
    for (int i(0); i < 10; i++) Array[i] = (char)rand();
    for (int i (0); i < 10; i++) std::cout << (int)Array[i] << " ";
    
    PrintAsm(Array, 10);
    std::cout << "nn";
    for (int i(0); i < 10; i++) std::cout << (int)Array[i] << " ";
    delete[] Array;
    std::cout << "nn";
    system("PAUSE");
    return 0;
}

输出:

41 35 -66 -124 -31 108 -42 -82 -82 -82 -112

0 0 0 0 0 0 0 0 0 0 0

代码静止相同,但将其保存在寄存器中,如何修复?

我已经修复了它,但是这让我感到奇怪,这与直接编辑值的编辑有什么不同?

我试图像...

一样零数组长度

相关,这是我使用的,因为GCC的潜在问题及其对volatile预选赛含义的解释。

// g++ -Og -g3 -m64 wipe.cpp -o wipe.exe
// g++ -Og -g3 -m32 wipe.cpp -o wipe.exe
// g++ -Os -g2 -S -m64 wipe.cpp -o wipe.exe.S
// g++ -Os -g2 -S -m32 wipe.cpp -o wipe.exe.S
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
    string s("Hello world");
    cout << "S: " << s << endl;
    char* ptr = &s[0];
    size_t size = s.length();
    if(ptr && size)
    {
        /* Needed because we can't just say to GCC, */
        /*   "give me a register that you choose".  */
        void* dummy;
        __asm__ __volatile__
        (
         "%=:nt"                /* generate a unique label for TOP */
#if (__WORDSIZE == 64)
         "subq $1, %2nt"        /* 0-based index */
#elif (__WORDSIZE == 32)
         "subl $1, %2nt"        /* 0-based index */
#elif (__WORDSIZE == 16)
         "subw $1, %2nt"        /* 0-based index */
#else
# error Unknown machine word size
#endif
         "lea (%1, %2), %0nt"   /* calcualte ptr[idx] */
         "movb $0, (%0)nt"      /* 0 -> ptr[size - 1] .. ptr[0] */
         "jnz %=bnt"            /* Back to TOP if non-zero */
         : "=&r" (dummy)
         :  "r" (ptr), "r" (size)
         : "0", "1", "2", "cc"
         );
    }
#if 0
    cout.setf(ios::hex, ios::basefield);
    cout.fill('0');
    for(size_t i = 0; i < s.length(); i++)
        cout << "0x" << setw(2) << ((int)s[i] & 0xff) << " ";
    cout << endl;
#endif
    cout << "S: " << s << endl;
    return 0;
}

mov byte ptr [Array],0只是将指向数组的地址的下部字节放置为零。更新数组是双重解除 - 一个用于在数组变量中拾取值,一个将一个字节存储在点点的位置。

作为编码,您正在跳过加载值,因此您在数组变量本身中存储零。您首先将其加载到ECX中,然后将其定义。

语法令人困惑[array]并不意味着 *数组,这只是表示数组。编译器让您说Inc Array,但是如果您在调试器中"查看拆卸",它将拆卸为Inc [Array]。[]用于指示地址而不是常数。mov ecx,0x1234用0x1234加载ECX,但mov ecx,[0x1234]将DWORD在地址0x1234中移动到ECX中。在汇编中,给出一个变量名称只需提供变量的"地址",而不会获取内容。通过在[]中放置一个变量,它将解释地址。

内联汇编器无论如何都可以弄清楚[]。因此,在您拥有mov ecx, Array的地方,它确实应该是mov ecx, [Array],并且对程序的运行方式没有影响。

通过使用Dumpbin拆卸您的代码,这可能更清楚。我添加了一些评论以显示您的代码生成的内容。

?PrintAsm@@YAXPAXH@Z (void __cdecl PrintAsm(void *,int)):
  00000000: 55                 push        ebp
  00000001: 8B EC              mov         ebp,esp
  00000003: 81 EC C0 00 00 00  sub         esp,0C0h
  00000009: 53                 push        ebx
  0000000A: 56                 push        esi
  0000000B: 57                 push        edi
  0000000C: 8D BD 40 FF FF FF  lea         edi,[ebp-0C0h]
  00000012: B9 30 00 00 00     mov         ecx,30h
  00000017: B8 CC CC CC CC     mov         eax,0CCCCCCCCh
  0000001C: F3 AB              rep stos    dword ptr es:[edi]
  0000001E: 83 7D 0C 00        cmp         dword ptr [ebp+0Ch],0 ; cmp Count, 0
  00000022: 7E 10              jle         $Done$4
  00000024: 7F 00              jg          $LoopArray$3
$LoopArray$3:
  00000026: C6 45 08 00        mov         byte ptr [ebp+8],0  ; mov byte ptr[Array], 0
  0000002A: FF 45 08           inc         dword ptr [ebp+8]   ; inc Array
  0000002D: FF 4D 0C           dec         dword ptr [ebp+0Ch] ; dec Count
  00000030: 75 F4              jne         $LoopArray$3
  00000032: EB 00              jmp         $Done$4
$Done$4:
  00000034: 5F                 pop         edi
  00000035: 5E                 pop         esi
  00000036: 5B                 pop         ebx
  00000037: 81 C4 C0 00 00 00  add         esp,0C0h
  0000003D: 3B EC              cmp         ebp,esp
  0000003F: E8 00 00 00 00     call        __RTC_CheckEsp
  00000044: 8B E5              mov         esp,ebp
  00000046: 5D                 pop         ebp
  00000047: C3                 ret

当数组用作参数时,该数组的地址将传递在该堆栈上。主::数组的地址通过EB 8传递(在EBP 8处转储内存,您应该看到main :: array的地址);计数在EBP c。

您可以看到0是在EBP 8点的位置存储,而您需要将零存储在 *[EBP 8]