gpt4 book ai didi

assembly - 入门教程 : Assembler, 段错误

转载 作者:行者123 更新时间:2023-12-02 22:03:30 27 4
gpt4 key购买 nike

我是一个初学者@coding,这是我在堆栈溢出上的第一个问题,尽管你的一些很好的答案已经让我取得了一些进展..

尝试此操作时 assembler-tutorial运行程序后我遇到段错误。我尝试注释掉每一行,并注意到当我在第 30 行“调用 printString”时程序崩溃了。

当我尝试使用 gdb 进行调试时(哎呀,我真的不知道我在做什么..),我在 lenString 调用中的函数“iterateChar”中收到错误。 (来 self 的函数文件 - baseOperators.asm - 第 50 行)

我怀疑,不知何故我弄乱了 eax 寄存器中的信息,但我不知道为什么,发生了什么以及如何解决这个问题。我的代码看起来与 asmtutor.com 上的教程 16 中的代码非常相似 - 我对它进行了 c&p 编辑,并且无论出于何种原因,它都有效。请帮忙。

(我使用“$ nasm -f elf assemblerTutorial.asm”+“$ ld -m elf_i386 assemblerTutorial.o -o assemblerTutorial)进行编译

;------------------------------------------
; my Assembler learning Environment

;%include "calculate.asm"
%include "baseOperators.asm"
%include "print.asm"

SECTION .text

global _start

_start:

pop ecx
mov edx, 0

argumentsLoop:
cmp ecx, 0h
jz argumentsEnd
pop eax

call atoi

add edx, eax
dec ecx
jmp argumentsLoop

argumentsEnd:
mov eax, edx
call printString
call breakLine
call quit

我的baseOperators.asm:

;------------------------------------------
; int atoi(Integer number)
; Ascii to integer function (atoi)
atoi:
push ebx ; preserve ebx on the stack to be restored after function runs
push ecx ; preserve ecx on the stack to be restored after function runs
push edx ; preserve edx on the stack to be restored after function runs
push esi ; preserve esi on the stack to be restored after function runs
mov esi, eax ; move pointer in eax into esi (our number to convert)
mov eax, 0 ; initialise eax with decimal value 0
mov ecx, 0 ; initialise ecx with decimal value 0

.conversionLoop:
xor ebx, ebx ; resets both lower and uppper bytes of ebx to be 0
mov bl, [esi+ecx] ; move a single byte into ebx register's lower half
cmp bl, 48 ; compare ebx register's lower half value against ascii value 48 (char value 0)
jl .conversionEnd ; jump if less than to label finished
cmp bl, 57 ; compare ebx register's lower half value against ascii value 57 (char value 9)
jg .conversionEnd ; jump if greater than to label finished
cmp bl, 10 ; compare ebx register's lower half value against ascii value 10 (linefeed character)
je .conversionEnd ; jump if equal to label finished
cmp bl, 0 ; compare ebx register's lower half value against decimal value 0 (end of string)
jz .conversionEnd ; jump if zero to label finished
sub bl, 48 ; convert ebx register's lower half to decimal representation of ascii value
add eax, ebx ; add ebx to our interger value in eax
mov ebx, 10 ; move decimal value 10 into ebx
mul ebx ; multiply eax by ebx to get place value
inc ecx ; increment ecx (our counter register)
jmp .conversionLoop ; continue multiply loop

.conversionEnd:
mov ebx, 10 ; move decimal value 10 into ebx
div ebx ; divide eax by value in ebx (in this case 10)

pop esi ; restore esi from the value we pushed onto the stack at the start
pop edx ; restore edx from the value we pushed onto the stack at the start
pop ecx ; restore ecx from the value we pushed onto the stack at the start
pop ebx ; restore ebx from the value we pushed onto the stack at the start
ret


;------------------------------------------
; int lenString(String message)
; String length calculation function
lenString:
push ebx
mov ebx, eax

iterateChar:
cmp byte [eax], 0
jz finalize
inc eax
jmp iterateChar

finalize:
sub eax, ebx
pop ebx
ret


;------------------------------------------
; void breakLine()
; Break a line - linefeed
breakLine:
push eax ; push eax on the stack
mov eax, 0x0a ; move linefeed into eax - 0x0a = 0Ah

push eax ; linefeed on stack to get adress
mov eax, esp ; move adress of current pointer into eax
call printString
pop eax
pop eax
ret ; return


;------------------------------------------
; void exit()
; Exit program and restore resources
quit:
mov eax, 1 ; invoke SYS_EXIT (kernel opcode 1)
mov ebx, 0 ; return 0 status on exit - 'No Errors'
int 0x80 ; 0x80=80h
ret

以及 print.asm 中的打印函数:

;------------------------------------------
; void printInteger (Integer number)
; Integer printing function (itoa)
printInteger:
push eax ; preserve eax on the stack to be restored after function runs
push ecx ; preserve ecx on the stack to be restored after function runs
push edx ; preserve edx on the stack to be restored after function runs
push esi ; preserve esi on the stack to be restored after function runs
mov ecx, 0 ; counter of how many bytes we need to print in the end

divideLoop:
inc ecx ; count each byte to print - number of characters
mov edx, 0 ; empty edx
mov esi, 10 ; mov 10 into esi
idiv esi ; divide eax by esi
add edx, 48 ; convert edx to it's ascii representation - edx holds the remainder after a divide instruction
push edx ; push edx (string representation of an intger) onto the stack
cmp eax, 0 ; can the integer be divided anymore?
jnz divideLoop ; jump if not zero to the label divideLoop

printLoop:
dec ecx ; count down each byte that we put on the stack
mov eax, esp ; mov the stack pointer into eax for printing
call printString ; call our string print function
pop eax ; remove last character from the stack to move esp forward
cmp ecx, 0 ; have we printed all bytes we pushed onto the stack?
jnz printLoop ; jump is not zero to the label printLoop

pop esi ; restore esi from the value we pushed onto the stack at the start
pop edx ; restore edx from the value we pushed onto the stack at the start
pop ecx ; restore ecx from the value we pushed onto the stack at the start
pop eax ; restore eax from the value we pushed onto the stack at the start
ret

;------------------------------------------
; void printString(String message)
; String printing function
printString:
push edx
push ecx
push ebx
push eax

call lenString

mov edx, eax ; nbytes - number of bytes to write (len), one for each letter plus the zero terminating byte
pop eax

mov ecx, eax ; buffer - move the memory address of our message string into ecx
mov ebx, 1 ; fd - filedescriptor, write to the STDOUT file
mov eax, 4 ; invoke SYS_WRITE (with fd, buf, nbytes / kernel opcode 4)

int 0x80 ; prozessor interupt 0x80 jump to system call, stack clean, 0x80=80h

pop ebx
pop ecx
pop edx
ret

感谢任何提示,

亲切的问候

最佳答案

  1. 如果程序以正常、传统且合法的方式启动,则堆栈上始终已有一个参数:程序本身的路径。因此,第一个 POP (pop ecx) 至少为 1。如果再有两个参数,该值将为 3。将 ECX 寄存器减 1 或将其与 1 进行比较:

    ...
    argumentsLoop:
    cmp ecx, 1h
    jz argumentsEnd ; See footnote ¹
    ...
  2. 第一个命令行参数的地址位于堆栈的第三个位置。你必须弹出程序路径的地址:

    ...
    _start:

    pop ecx ; Get the arguments count
    mov edx, 0
    pop eax ; Pop away the program path
    ...

    Here是一篇由 Gunner 撰写的优秀文章.

  3. 函数 atoi 将 ASCII 字符串转换为整数。函数printString 顾名思义,只打印字符串,而不打印整数。使用 printInteger 代替:

    ...
    argumentsEnd:
    mov eax, edx
    call printInteger
    ...
<小时/>

1 可以在不使用任何参数 (argc = 0) 或使用约定未涵盖的 argv[0] 的情况下启动程序(请参阅 execve(2) )。我写了一个例子来演示它:

get_argv.asm:

SECTION  .data
LineFeed dw 10
nullstr db '(null)',0
argcstr db 'argc = '
argcstr1 db '---------------',0
argvstr db 'argv['
argvstr1 db '---------------',0
argvstr2 db '] = ',0

SECTION .text
global _start

_start:
push ebp
mov ebp, esp

mov eax, [ebp + 4] ; argc
mov edi, argcstr1
call EAX_to_DEC ; Convert EAX to a string pointed by EDI

mov esi, argcstr
call PrintString
mov esi, LineFeed
call PrintString

xor ecx, ecx

.J1:
mov eax, ecx
mov edi, argvstr1
call EAX_to_DEC ; Convert EAX to a string pointed by EDI

mov esi, argvstr
call PrintString
mov esi, argvstr2
call PrintString
mov esi, [ebp+8+4*ecx] ; argv[ECX]
call PrintString
test esi, esi
jz .J2
mov esi, LineFeed
call PrintString
add ecx, 1
jmp .J1
.J2:

.exit:
mov esi, LineFeed
call PrintString

mov esp, ebp
pop ebp

mov eax, 1 ; SYS_EXIT
xor ebx, ebx ; Exit code = 0 = no error
int 0x80 ; Call Linux kernel

PrintString: ; ARG: ESI Pointer to ASCIZ string
pusha

test esi, esi
jne .J0
mov esi, nullstr

.J0:

mov eax, 4 ; SYS_WRITE
mov ebx, 1 ; STDOUT
mov ecx, esi

xor edx, edx ; Count of bytes to send
.J1:
cmp byte [esi], 0 ; Look for the terminating null
je .J2
add edx, 1
add esi, 1
jmp .J1

.J2:
int 0x80 ; Call Linux kernel

popa
ret

EAX_to_DEC: ; ARG: EAX integer, EDI pointer to string buffer
push ebx
push ecx
push edx

mov ebx, 10 ; Divisor = 10
xor ecx, ecx ; ECX=0 (digit counter)
.J1: ; First Loop: store the remainders
xor edx, edx ; Don't forget it!
div ebx ; EDX:EAX / EBX = EAX remainder EDX
push dx ; Push the digit in DL (LIFO)
add cl, 1 ; = inc cl (digit counter)
or eax, eax ; AX == 0?
jnz .J1 ; No: once more
mov ebx, ecx ; Store count of digits
.J2: ; Second loop: load the remainders in reversed order
pop ax ; get back pushed digits
or al, 00110000b ; to ASCII
mov [edi], al ; Store AL to [EDI] (EDI is a pointer to a buffer)
add edi, 1 ; = inc edi
loop .J2 ; until there are no digits left
mov byte [edi], 0 ; ASCIIZ terminator (0)
mov eax, ebx ; Restore Count of digits

pop edx
pop ecx
pop ebx
ret ; RET: EAX length of string (w/o last null)

start_get_argv.c:

#include <stdio.h>
#include <unistd.h>

int main ( int argc, char *argv[] )
{
char* asmprog = "./get_argv";

puts ("execute me\n");
printf ("argc = %d\n",argc);
for (int i=0; i <= argc; ++i)
{
printf ("argv[%d]=%s\n",i,argv[i]);
}

printf ("\nexecve %s\n\n",asmprog);
fflush (0);
execve (asmprog, NULL, NULL);

return 0;
}

在同一目录中构建这两个文件并运行 ./start_get_argv。调用的 ./get_argv 将报告 argc = 0 和 argv[0] = (null)。空指针意味着“数组末尾”。处理这种情况很简单:如果 argc 小于或等于 1,则退出:

...
argumentsLoop:
cmp ecx, 1h
jbe argumentsEnd
...

关于assembly - 入门教程 : Assembler, 段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48227295/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com