Question

I have the following assembly code, which is meant to be a simple implementation of the C-function sprintf(). So far, it works fine in parsing %c and %%, and I am now working on implementing %s, it can be found below under the label found_string. I am trying to save the %ecx register (which is used in the main loop), by doing pushl %eax, so I can use it to iterate through my parameter string without disturbing the main loop at all, and then popl %eax when I am done.

.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

Unfortunately, this code segfaults once it enters found_string. I've tried using the %eax register as well, but I honestly have no idea why it fails. Am I doing the save/restore procedure incorrectly? What would be a better method of doing this?

This is the C-code I compile it with:

#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;
}

If I remove any %s from the format string, the function works fine.

Was it helpful?

Solution

Your restore %ecx for usage in main loop line is never gonna be reached because earlier you have this:

   cmpb $0,(%ecx)
   jz back_loop

Presumably you want to create a label for the restore %ecx... block, and jump there instead. It could look something like this:

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top