2013-05-08 177 views
0

我有以下彙編代碼,它是C函數sprintf()的簡單實現。到目前爲止,它在解析%c%%時工作正常,而我現在正在執行%s,可以在標籤found_string下找到它。我試圖通過執行pushl %eax來保存%ecx寄存器(在主循環中使用),所以我可以使用它遍歷我的參數字符串,而不會干擾主循環,然後在完成時使用popl %eax嘗試重新使用寄存器x86彙編時發生Segfault

.globl sprinter 

.data 

.escape_string: .string "%" 
.char_string: .string "c" 
.decimal_string: .string "d" 
.octet_string: .string "o" 
.string_string: .string "s" 
.string_hexadecimal: .string "x" 
.num_escape: .long 0 
.num_characters: .long 0 

.text 

sprinter: 
    pushl %ebp    
    movl %esp,%ebp   
    movl $0,.num_characters # set num_characters to '0', otherwise successive runs of sprinter() will just keep incrementing this number 
    movl 8(%ebp),%edx  # %edx = result-string 
    movl 12(%ebp),%ecx  # %ecx = format-string 
    addl $8,%ebp   # remove the parameters from the stack 

movb .escape_string,%bl # add escape character to %bl and keep it there forever since we use it so much! 

loop: 
    cmpb $0, (%ecx)   # if end of string reached 
    jz exit     # exit loop 

    cmpb %bl,(%ecx)   # if escape character found 
    je found_escape_char # jump to subprodecure to handle the escape character 

    movb (%ecx), %al  # copy current character to %al 
    movb %al, (%edx)  # copy current character to #edx (result-string) 

    back_loop:    # this is where we return from the subprocedure 
     incl %ecx   # increment %ecx since we read a character from it 
     incl %edx   # increment %edx since we wrote a character to it 
     incl .num_characters 
     jmp loop   # continue loop 

found_escape_char: 
    # let's see if the next character is a 'c' 
    movb .char_string,%al 
    cmpb %al,1(%ecx) 
    je found_char 
    # ...or, let's see if the next character is a '%' 
    movb .escape_string, %al 
    cmpb %al,1(%ecx) 
    je found_percent 
    # ...or, let's see if the next character is an 's' 
    movb .string_string, %al 
    cmpb %al,1(%ecx) 
    je found_string 

    # ...or if we didn't match anything, just write it to the result string for now (e.g. we print "%b", "%n" or other invalid codes to the result) 
    movb (%ecx), %al 
    movb %al, (%edx)  # copy current character to #edx (result-string) 

    jmp back_loop   # back into main loop 

found_percent: 
    incl %ecx    # skip the "operand" character we just found 

    movb %al,(%edx)   # write percent sign to result 

    jmp back_loop   # back into main loop 

found_char: 
    incl %ecx    # skip the "operand" character we just found 
    movb 8(%ebp),%al 
    movb %al,(%edx) 
    addl $4,%ebp   # remove the parameter we consumed from the stack 
    jmp back_loop   # back into main loop 

found_string: 
    pushl %ecx    # save %ecx, because we use it in the main loop 
    movl 8(%ebp),%ecx  # put the string parameter into %ecx 
    string_loop:   # this is the exact same loop as above in 'loop:', and that one works fine 
     cmpb $0,(%ecx) 
     jz back_loop 
     movb (%ecx),%al  # copy current character to %al 
     movb %al,(%edx)  # copy current character to %edx (result-string) 
     incl %ecx   # increment %ecx since we read a character from it 
     incl %edx   # increment %edx since we wrote a character to it 
     jmp string_loop 
    popl %ecx    # restore %ecx for usage in main loop 
    addl $4,%ebp   # remove the parameter we consumed from the stack 

    jmp back_loop   # back into main loop 



exit: 
    movl $0,(%edx)   # write null character to finish off the result string 

    # return number of characters printed 
    movl .num_characters, %eax 
    popl %ebp 
    ret 

不幸的是,此代碼段錯誤一旦進入found_string。我也嘗試過使用%eax寄存器,但我真的不知道它爲什麼會失敗。我是否正確執行保存/恢復過程?什麼會是更好的方法呢?

這是C代碼我編譯:

#include <stdio.h> 
extern int sprinter (unsigned char* res, unsigned char* string, ...); 
int main (void) 
{ 
    unsigned char t[2000]; 
    int n = sprinter(t, "this is a char: %c, this is a percent symbol: %%, this is a string: %s", 'A',"a string"); 
    printf("numchars: %d\n",n); 
    printf("result: %s\n",t); 
    return 0; 
} 

如果我從格式字符串刪除任何%s,功能工作正常。

回答

2

是永遠不會達到你restore %ecx for usage in main loop線因爲早期你有這樣的:

cmpb $0,(%ecx) 
    jz back_loop 

大概你要創建的restore %ecx...塊的標籤,並跳轉到那裏來代替。它可能看起來像這樣:

found_string: 
    pushl %ecx   # save %ecx, because we use it in the main loop 
    movl 8(%ebp),%ecx # put the string parameter into %ecx 
string_loop:   # this is the exact same loop as above in 'loop:', and that one works fine 
    cmpb $0,(%ecx) 
    jz found_string_end 
    movb (%ecx),%al  # copy current character to %al 
    movb %al,(%edx)  # copy current character to %edx (result-string) 
    incl %ecx   # increment %ecx since we read a character from it 
    incl %edx   # increment %edx since we wrote a character to it 
    jmp string_loop 
found_string_end: 
    popl %ecx   # restore %ecx for usage in main loop 
    addl $4,%ebp  # remove the parameter we consumed from the stack 
    jmp back_loop  # back into main loop 
+0

哇,太簡單了!我不知道爲什麼我沒有看到。我一直在扯着我的頭髮。非常感謝! – ponycat 2013-05-08 23:58:24