[英]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上,某些寄存器被視為“非易失性”,這意味着您的函數需要保留其值。 通常,這是通過將寄存器壓入堆棧並在返回之前彈出寄存器來完成的。 您要破壞的非易失性寄存器是esi
和edi
。 可能是您的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.