簡體   English   中英

為什么此匯編代碼失敗?

[英]Why does this assembly code fail?

我正在嘗試創建一個程序集,該程序將整個字符串旋轉一個字節,但是當我嘗試測試此代碼時,當旋轉的時間小於8倍時,它似乎工作正常,但除此以外,還有其他事情似乎不再工作了,我花了無數個小時試圖弄清為什么它運氣不好,這是代碼:

_rotateLeft:
    pushl %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    movl    12(%ebp), %esi  #load number of bytes
    subl    $1, %esi        #subtract 1 because we want a number to add to the address of the first char to get the last char.
    addl    %esi, %eax      #adding esi to get the last char in the string (most significant byte)
    movl    $1, %edx
    movl    $0, %edi
    testb   $0x80, (%eax)
    cmove   %edi, %edx      #testing the msb to see if its one or zero so we can rotate it back
    shlb    $1, (%eax)      #shift the most significant byte
    testb   $0x80, -1(%eax) #test the one before it to see if the msb is 0 or 1 so we can move the msb to the most significant byte (to simulate the feel that all string is being shifted)
    jz  L4                  #if 0 then there is not need to put 0 in the lsb because shift already did that for us
    L5:
    orb $1, (%eax)          #if 1 then or it with the most significant byte to turn the lsb to a 1 without changing the whole byte
    L4:
    decl    %esi            #decrement our counter
    decl    %eax            #decrement eax to get the next byte (moving from most significant to least significant)
    shlb    $1, (%eax)      
    movl    $1, %ecx
    movl    $0, %edi
    testb   $0x80, -1(%eax)
    cmove   %edi, %ecx      #if the one before it is a 0 then let ecx equal zero other wise ecx is 1
    orb     %cl, (%eax) 
    cmpl    $1, %esi        #we don't want it to reach the last byte that would be done after
    jne     L4
    decl %eax               #get the last byte
    shlb    $1, (%eax)
    orb     %dl, (%eax)     #or it with the value obtained in line 26
    leave
    ret

這是它應該與之鏈接的C代碼:

#include <stdio.h>

#include <stdlib.h> /* _fmode */
#include <fcntl.h> /*  _O_BINARY */

// The following is from http://oldwiki.mingw.org/index.php/binary
unsigned int _CRT_fmode = _O_BINARY; // Needed to put I/O in binary mode

#define BUFFER_SIZE 2048

void rotateLeft(char *, int);
int main(int argc, char **argv) {
    char buffer[BUFFER_SIZE];
    int bytesRead;

    while( (bytesRead = read(0, buffer, BUFFER_SIZE)) > 0) {

        rotateLeft(buffer, bytesRead);
        write(1, buffer, bytesRead); // Does not add newline

    }   

    return 0;

此代碼在輸入為32位時,旋轉32次后應輸出相同的輸入,但正如我所說,在8次后失敗,這是一個小腳本,用於測試代碼:

gcc -Wall -g rotateLeftMain.c rotateLeft.s -o rotateLeftMain
awk 'BEGIN {printf "2345"}' >junkIn.txt

for i in `seq 1 32`;
do
    ./rotateLeftMain.exe < junkIn.txt > junkOut.txt
    mv junkOut.txt junkIn.txt
done    

cat junkIn.txt

我想我了解您的代碼在做什么。 在您的邏輯中我沒有發現任何錯誤。

但是,您犯的一個錯誤是您的匯編函數未遵循x86調用約定。 在x86上,某些寄存器被視為“非易失性”,這意味着您的函數需要保留其值。 通常,這是通過將寄存器壓入堆棧並在返回之前彈出寄存器來完成的。 您要破壞的非易失性寄存器是esiedi 可能是您的main功能是將局部變量存儲在這些寄存器之一中,並且當您破壞該寄存器時,匯編函數之后的write操作未獲得正確的參數。 我不確定這是否可以解決您的整個問題,但這是您需要解決的一件事。

好的,我只是運行了您的程序,它對我有用。 我對其進行了修改,以便在打印前后打印出來。 這可以幫助您確定它在哪里破裂:

#include <stdio.h>

#include <stdlib.h> /* _fmode */
#include <fcntl.h> /*  _O_BINARY */

// The following is from http://oldwiki.mingw.org/index.php/binary
unsigned int _CRT_fmode = _O_BINARY; // Needed to put I/O in binary mode

#define BUFFER_SIZE 2048

void rotateLeft(char *, int);
int main(int argc, char **argv)
{
    unsigned char buffer[BUFFER_SIZE];
    int bytesRead;

    while( (bytesRead = read(0, buffer, BUFFER_SIZE)) > 0) {
        fprintf(stderr, "before = %02x %02x %02x %02x\n",
            buffer[0], buffer[1], buffer[2], buffer[3]);
        rotateLeft(buffer, bytesRead);
        fprintf(stderr, "after  = %02x %02x %02x %02x\n",
            buffer[0], buffer[1], buffer[2], buffer[3]);
        write(1, buffer, bytesRead); // Does not add newline
    }   

    return 0;
}

和程序集(稍作修改以添加推入式和彈出式):

_rotateLeft:
    .globl _rotateLeft
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %esi
    pushl   %edi
    movl    8(%ebp), %eax
    movl    12(%ebp), %esi  #load number of bytes
    subl    $1, %esi        #subtract 1 because we want a number to add to the address of the first char to get the last char.
    addl    %esi, %eax      #adding esi to get the last char in the string (most significant byte)
    movl    $1, %edx
    movl    $0, %edi
    testb   $0x80, (%eax)
    cmove   %edi, %edx      #testing the msb to see if its one or zero so we can rotate it back
    shlb    $1, (%eax)      #shift the most significant byte
    testb   $0x80, -1(%eax) #test the one before it to see if the msb is 0 or 1 so we can move the msb to the most significant byte (to simulate the feel that all string is being shifted)
    jz  L4                  #if 0 then there is not need to put 0 in the lsb because shift already did that for us
    L5:
    orb $1, (%eax)          #if 1 then or it with the most significant byte to turn the lsb to a 1 without changing the whole byte
    L4:
    decl    %esi            #decrement our counter
    decl    %eax            #decrement eax to get the next byte (moving from most significant to least significant)
    shlb    $1, (%eax)      
    movl    $1, %ecx
    movl    $0, %edi
    testb   $0x80, -1(%eax)
    cmove   %edi, %ecx      #if the one before it is a 0 then let ecx equal zero other wise ecx is 1
    orb     %cl, (%eax) 
    cmpl    $1, %esi        #we don't want it to reach the last byte that would be done after
    jne     L4
    decl %eax               #get the last byte
    shlb    $1, (%eax)
    orb     %dl, (%eax)     #or it with the value obtained in line 26
    popl    %edi
    popl    %esi
    leave
    ret

最后是輸出:

before = 32 33 34 35
after  = 64 66 68 6a
before = 64 66 68 6a
after  = c8 cc d0 d4
before = c8 cc d0 d4
after  = 91 99 a1 a9
before = 91 99 a1 a9
after  = 23 33 43 53
before = 23 33 43 53
after  = 46 66 86 a6
before = 46 66 86 a6
after  = 8d cc 0c 4d
before = 8d cc 0c 4d
after  = 1a 99 19 9a
before = 1a 99 19 9a
after  = 35 32 33 34

對那些對此問題感到好奇的人,我和OP進行了討論,似乎他的shell可能不喜歡stdin / stdout中的0x1a或0x19字符。 在第8次迭代中,它拒絕讀取正確的輸入“ 1a 99 19 9a”。 0x1a的ascii含義是“替代”,0x19的含義是“介質的末尾”。 因此,也許“中端”關閉了管道或其他東西。 一切對我來說都很完美(Win32上的Cygwin bash)。

最后一件事是,我讓OP嘗試使用“ 6789”來避免獲得0x1a和0x19,並且他的程序運行良好。 那是另一個指示,表明他的設置中的某些內容不喜歡這些字節。

編輯:修改后的回復,已理解原始代碼。

您的代碼所做的不是普通的輪換。 它有效地獲取字符串中每個字節的最左位,並將其傳輸到下一個字節的最右端(在最后一個字節處回繞)。

因此,像這樣:

ab     c de     f gh     i   
00111011 10100010 11110000

 b     cg e     fa h     id
 01110111 01000100 11100001

       cgh      fab      ide   
  11101111 10001000 11000010

....

通常,這種准旋轉不等同於普通旋轉。 但是,如果存在8n個准旋轉,則結果恰好等於整個字符串的8n個標准右旋。

使用rclb指令可以非常有效地實現准旋轉。

部件:

.text
.globl bitDrift

bitDrift:
    pushl %ebp
    movl %esp, %ebp
    movl 8(%ebp), %eax     # %eax holds the position of the current byte
    movl 12(%ebp), %ecx    # %ecx holds the number of bytes
    movl %eax, %edx        # %edx holds the position of the first byte
    clc                    # set the carry bit to zero
nextd:
    rclb $1, (%eax)        # left-rotate the current byte, putting the 
                           # existing carry bit in the vacant position and
                           # putting the bit rotated out into the carry bit
    incl %eax              # move to the next byte
    loop nextd             # decrement the count in %ecx and keep going 
                           # until it is 0
    adcb $0, (%edx)         # add the carry bit from the last rotation to 
                           # the first byte
    pop %ebp
    ret

C代碼(您也應該很好;這只是為了我可以測試):

#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE 2048

void bitDrift(char *, int);

int main(int argc, char **argv) {
    unsigned char buffer[BUFFER_SIZE];
    buffer[0] = 0x32;
    buffer[1] = 0x33;
    buffer[2] = 0x34;
    buffer[3] = 0x35;
    int i;
    for (i=0; i<33; i++) {
        printf("%02x %02x %02x %02x\n", buffer[0],
               buffer[1], buffer[2], buffer[3]);
        bitDrift(buffer, 4);
    }
}

gcc -g rot.c rot.S -o rot組裝。

輸出:

32 33 34 35
64 66 68 6a
c8 cc d0 d4
91 99 a1 a9
23 33 43 53
46 66 86 a6
8d cc 0c 4d
1a 99 19 9a
35 32 33 34
6a 64 66 68
d4 c8 cc d0
a9 91 99 a1
53 23 33 43
a6 46 66 86
4d 8d cc 0c
9a 1a 99 19
34 35 32 33
68 6a 64 66
d0 d4 c8 cc
a1 a9 91 99
43 53 23 33
86 a6 46 66
0c 4d 8d cc
19 9a 1a 99
33 34 35 32
66 68 6a 64
cc d0 d4 c8
99 a1 a9 91
33 43 53 23
66 86 a6 46
cc 0c 4d 8d
99 19 9a 1a
32 33 34 35

該字符串中有32位,並且該字符串在旋轉32次后重復。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM