我是绿色与asm编程.我一直试图解决一些任务作为学习的方式.两个任务运行良好,但两个是失败的分割故障.我已经通过了很多文章,但我不能把我的头周围.
这是我的C驱动程序
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
extern int is_palindrome(char *s);
extern void palindrome_check();
int is_palindrome(char *s){
int len=strlen(s);
for(int i=0;i<len/2;i++){
if(s[i] != s[len -i -1]){
return 0;
}
}
return 1;
}
int main() {
int choice;
int result1;
char str2[1024];
do {
printf("\nMenu:\n");
printf("1) Test if a string is a palindrome (from C to ASM)\n");
printf("2) Test if a string is a palindrome (from ASM to C)\n");
printf("Enter your choice: ");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("Enter a string: ");
scanf("%s",str2);
result1=is_palindrome(str2);
if(result1==1){
printf("It is a palindrome");
}
else {
printf("It is NOT a palindrome");}
break;
case 2:
palindrome_check();
break;
}
}
return 0;
}
字符串
这是我的函数. asm
BITS 32
extern is_palindrome
GLOBAL is_palindrome
GLOBAL palindrome_check
SECTION .bss
buf1 resb 1024
buf resb 1024
SECTION .data
prompt db "Please enter a string:",0x0A
lenprompt equ $ - prompt
palindrome_msg db "It is a palindrome",0x0A
lenpamsg equ $ - palindrome_msg
not_palindrome_msg db "It is NOT a palindrome",0x0A
lenntpamsg equ $ - not_palindrome_msg
SECTION .text
palindrome_check:
mov eax,4
mov ebx,1
mov ecx,prompt
mov edx,lenprompt
int 0x80
mov eax,3
mov ebx,0
mov ecx,buf
mov edx,1024
int 0x80
push ecx
call is_palindrome_c
add esp,4
cmp eax,1
jne .palindrome_failed
mov eax,4
mov ebx,1
mov ecx,palindrome_msg
mov edx,lenpamsg
int 0x80
.palindrome_failed:
mov eax,4
mov ebx,1
mov ecx,not_palindrome_msg
mov edx,lenntpamsg
int 0x80
is_palindrome:
mov eax,[esp+4]
mov [buf1],eax
;Calculate the length of the string
xor eax,eax
xor edi,edi
mov esi,buf1
len_loop:
inc eax
inc esi
cmp BYTE[esi],10
je set_len
jmp len_loop
set_len:
dec eax
mov [len1],eax
;For the palindrome check function
mov ecx,eax
mov ebx,buf1
call is_palindrome_asm
is_palindrome_asm:
mov eax,[len1]
mov edi,eax
xor ecx,ecx
mov esi,ebx
palindrome_loop:
cmp ecx,edi
jae .done
mov al,[esi+ecx]
mov bl,[esi+edi]
cmp al,bl
jne .not_palindrome
inc ecx
dec edi
jmp palindrome_loop
.not_palindrome:
mov eax,0
ret
.done:
mov eax,1
ret
型
为了组装文件,我使用了以下命令:
nasm -g -f elf32 -F dwarf -o functions.o functions.asm
gcc -g -Wall -static -m32 -o backandforth backandforth.c functions.o
型
我已经编辑了关于提供的更正代码。任何方向都会有帮助。请注意,我说我是绿色,我给了GEF调试器安装,但我不能弄清楚错误是什么。palindrome_check不再退出分段错误,但我传递的每个字符串都不是回文。我已经测试了c函数is_palindome_c,并确认它工作正常。
2条答案
按热度按时间ivqmmu1c1#
你熟悉“范围”这个词吗?因为你的问题似乎就在那里。
让我们先从C代码开始:
字符串
如果你调用codeA,你希望输出是什么样的?codeB先打印,所以它会打印6。但是因为它将
x
设置为6,这意味着codeA也会打印6吗?不,当然不是。C 'scopes'每个例程都有自己的x副本,所以codeB的x不会覆盖codeA的副本。
然而,在汇编程序中情况有些不同,寄存器没有作用域,如果你将ebx寄存器设置为5,然后调用一个将其设置为6的例程,当它返回时,它仍然是6。
很吓人,对吧?如果每次你调用一个子程序,它都有可能改变你所有寄存器的值,这真的会造成混乱。为了安全起见,似乎你必须在调用一个函数之前保存所有寄存器,然后在返回时把它们都放回去。
虽然这可能会起作用,但它会 * 真的 * 减慢你的代码。如果你调用的函数实际上没有覆盖任何寄存器,这将是一个巨大的和毫无意义的时间浪费。
因此,做这些决定的人决定在调用函数时要遵循一些约定(恰当地称为“调用约定”)。他们会定义一些寄存器,被调用的例程可以随意更改这些寄存器。如果被调用者更改了任何其他寄存器,它必须在返回之前放回原始值。
有许多不同的调用约定。考虑到你正在编写32位汇编代码并从C调用它,C代码的编译器将假设你使用的是
cdecl
约定。我在上面链接了它的描述,但在这里它又出现了。该链接的关键点是:寄存器EAX、ECX和EDX是调用方保存的,其余的是被调用方保存的。
所以当
main
调用palindrome_check
时,main是调用者,palindrome_check是被调用者。那么当palindrome_check这样做时会发生什么:型
调用者已经保存了eax、ecx和edx,所以你可以随意修改它们。但是ebx呢?你(被调用者)正在修改它,这意味着当你返回时,调用者将不会得到它期望的ebx值。你违反了规则,现在糟糕的事情正在发生。
所以,你能做什么?毕竟,你需要使用ebx来打印字符串。这就是int 0x80的工作原理。所以你需要做的是保存旧值,通常在被调用例程的顶部,如下所示:
型
然后,在例程的底部,就在返回之前,您可以使用以下命令恢复原始值:
型
看看你的代码,你还修改了其他的“被调用者保存”寄存器(比如edi),它们也需要被保存。
这里面有一点小技巧,所以我保存你不必回来问下一个明显的问题。
如果您推送多个寄存器:
型
然后,当你
pop
他们,你需要做的是以相反的顺序:型
修改“被调用者保存”寄存器(可能)是导致代码崩溃的原因。
你还有另一个问题(我在上面提到过),再看看codeA,它完成printf之后会发生什么?它到达例程的末尾并返回,对吗?
但是汇编程序并不这样做,它没有“例程的结束”,缩进代码也没有任何效果,所以在它打印完第一条消息后,它只是继续执行下一条指令。
可能你需要的是一个
jmp
到例程的结尾,在那里你有你的pop
指令,后面跟着ret
。sc4hvdpw2#
你的 palindrome_check 和 is_palindrome 代码是两个不同的函数,你应该清楚地将它们分开。也许用途:
字符串
palindrome_check
根据 is_palindrome_c 的结果,您应该只显示一条消息。目前您允许'is'代码在'is not'代码中失败!一个可能的快速修复方法是跳过“is not”代码,使用
ret
指令退出 palindrome_check 函数。(在这两种情况下,您也可以只使用int 0x80
后跟ret
。但考虑到调用约定希望您保留EBX,我建议如下:
型
is_palindrome
mov esi,buf1
,但遗憾的是,这并不检索指针。在NASM中,此指令更倾向于加载 * buf 1 * 标签的地址。要获得存储在 * buf 1 * 的内容,您需要像mov esi, [buf1]
中那样解引用(就像你一开始存储时所做的那样)。这可能是你的分段错误的原因!型
直接在标签下面的
call
是不需要的。它只是使 is_palindrome_asm 运行两次。在这个程序中,它似乎是无害的,但为什么你要这样做呢?以下是我的回文检查器版本。
我认为空字符串和一个字符的字符串是回文。随意改变.
型
[edit]
我自己不使用C,但是C不存储零终止的字符串吗?因此,你的 is_palindrome 不应该查找10(C将其作为输入结束信号使用),而是查找0。
经过一些分析,我得到了这个更快的版本,它只使用EAX、ECX和EDX。本地标签 .a:、.b: 和 .c: 将以16字节对齐。
型
值得注意的是,在 .OK: 的情况下使用
mov al, 1
比mov eax, 1
快8%。我用一个合理大小的字符串'abcdefgfedcba'进行测试,并让调用者检查EAX:型