我是@coding的初学者,这是我关于堆栈溢出的第一个问题,虽然你的一些很好的答案已经给我带来了一些方法..
尝试此assembler-tutorial时,我在运行程序后遇到分段错误。我试着评论每一行,并注意到当我"调用printString"时,程序崩溃了。在第30行。
当我尝试使用gdb调试时(哎呀,我真的不知道我在那里做什么......)我在函数内部得到一个错误" iterateChar"在我的lenString调用中。 (在我的函数文件中 - baseOperators.asm - 第50行)
我怀疑,不知怎的,我搞砸了eax寄存器中的信息,但我不知道为什么,发生了什么以及如何解决这个问题。我的代码看起来非常类似于asmtutor.com上的教程16中的那个代码 - 我认为这样,并且无论出于何种原因都可以。请帮忙。
(我正在编译" $ 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
my 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中的print函数:
;------------------------------------------
; 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
我感谢任何tipps,
亲切的问候
答案 0 :(得分:1)
如果程序是以正常的,传统的和合法的方式启动的,那么堆栈上已经有始终一个参数:程序本身的路径。因此,第一个POP(pop ecx
)至少得到1.另外两个参数的值为3.将ECX寄存器减1或将其与1进行比较:
...
argumentsLoop:
cmp ecx, 1h
jz argumentsEnd ; See footnote ¹
...
第一个命令行参数的地址位于堆栈的第三个位置。你必须弹出程序路径的地址:
...
_start:
pop ecx ; Get the arguments count
mov edx, 0
pop eax ; Pop away the program path
...
函数atoi
将ASCII字符串转换为整数。函数printString
打印 - 顾名思义 - 只打印字符串,而不是整数。请改用printInteger
:
...
argumentsEnd:
mov eax, edx
call printInteger
...
¹可以在没有任何参数(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
...
答案 1 :(得分:0)
你的答案充满了专业知识,帮助我重新学到了很多东西。谢谢。 rkhb的回答使我的代码工作,实现了这些变化:
_start:
pop ecx ; Get the arguments count
mov edx, 0
pop eax ; Pop away the program path
argumentsLoop:
cmp ecx, 1h
jz argumentsEnd
...
argumentsEnd:
mov eax, edx
call printInteger