Ad

Having Trouble Outputting Both Strings On My Printf Made With Syscalls

Working on an college - Assembly Language C course -assignment where I need to create a basic printf function using only system calls. Whenever there is a '%' I need to check the next character to determine how to implement either a character or a string.

If there is a 'c', replace it with a character, if there is a 's', replace it with a string. If there is another '%', output that. The professor stated that he knows this is a tough assignment, so it's okay if the solution is partially implemented, but i'm so close to a full solution, so I wanted to push ahead.

I've done extensive work on this code, and made comments on mostly every line, so I want to emphasize this is for purposes of me learning. I can print out one string 'woot woot' using mov eax, [abp + 12] or the second 'woot woot' using mov eax, [ebp + 16], but I cannot find a solution to print both. This is my dilemma.

Thank you for your time, and happy coding!

Here is a link to the assignment for clarification: https://imgur.com/h9tP89j

This is my sample output:

Hello world
str3 is 'woot woots', isn't that cool?
A is a char, but so is %,, s again!

Here is my code:

  4 segment .data
  5
  6     str1    db  "Hello world", 10, 0
  7     str2    db  "str3 is '%s', isn't that cool?", 10, 0
  8     str3    db  "woot woot", 0
  9     str4    db  "%c is a char, but so is %%, %s again!", 10, 0
 10
 11 segment .bss
 12
 13
 14 segment .text
 15     global  asm_main
 16
 17 asm_main:
 18     push    ebp
 19     mov     ebp, esp
 20     ; ********** CODE STARTS HERE **********
 21
 22     ;; EVERYTHING UP UNTIL THE PRINTF FUNCTION DOES NOT CHANGE AT ALL
 23
 24     ;   eax (syscall-number) What do we want done?  3 is Read, 4 is Write
 25     ;   ebx (other-info) Usually when do you want the thing done? Or printed?
 26     ;   0 is if you want to type something yourself, 1 is if you want to print something
 27     ;   ecx (other-info) Usually this is where you would put the string to be printed (example: str1)
 28     ;   edx (other-info) How long is the data that needs to be printed? You can ignore the null character
 29     ;   int 0x80 = Turn on the kernel and do the thing
 30
 31     push str1       ; push string 1 - 4 bytes
 32     call printf     ; call function
 33     add esp, 4      ; str1 is a dword with 4 bytes
 34
 35     push str3       ; push string 3 - 4 bytes
 36     push str2       ; push string 2 - 4 bytes
 37     call printf     ; call function
 38     add esp, 8      ; str3 and str2 is 8 bytes total
 39
 40     push str3       ; push string 3 - 4 bytes
 41     push 'A'        ; Push A character - it's still a dword so 4 bytes
 42     push str4       ; push string 4 - 4 bytes
 43     call printf     ; call function
 44     add esp, 8      ; two arguments, 8 bytes total
 45
 46     ; *********** CODE ENDS HERE ***********
 47     mov     eax, 0
 48     mov     esp, ebp
 49     pop     ebp
 50     ret
 51
 52 printf:
 53     push    ebp             ; Prologue - every function starts with this
 54     mov     ebp, esp        ; Prologue - and this
 55
 56     mov     edx, -1         ; this is a counter to walk through each string slowly
 57     mov     edi, -1
 58     loop:
 59     inc     edx     ; increment counter for each loop
 60     mov     esi, edx        ; constantly update this reserve to preserve counter, for use with offsetedx
 61     mov     eax, DWORD [ebp + 8]        ; set eax to the dword  pointer at ebp + 8
 62     cmp     BYTE [eax + edx], 0     ; compare the byte in the string with a null terminator
 63     je      loopEnd     ; if there is a null terminator, jump to the end of the loop
 64
 65         percentCheck:       ; each time we come up to a %, we want to check the next character to see how to proceed
 66         cmp     BYTE [eax + edx], 37    ; compare the current byte with a 37, which is is a '%' on the ascii table
 67         jne continue        ; if there is no percentage, we can continue walking through the string
 68         inc     edx         ; move to the next byte
 69
 70             charCheck:
 71             cmp     BYTE [eax + edx], 99    ; compare the byte with a 99, which is 'c' on the ascii table
 72             jne     stringCheck     ; if there is no 'c', move to the next check
 73             mov     eax, 4      ; syscall write operation
 74             mov     ebx, 1      ; syscall for printing to screen
 75             lea     ecx, [ebp + 12]     ; pointer is possibly on the character. If not...?
 76
 77                     offsetCheck:        ; my idea is to check for the byte where ecx is pointing to see if there's an 'A'
 78                     je      offsetEnd       ; if it is, then output that bad boy!
 79                     add     ebp, 4      ; if not, then add to the stack to adjust for the offset
 80                     lea     ecx, [ebp]  ; now point ecx to the new pointer on the stack 
 81                     jmp     offsetCheck     ; run it again to make sure you are poiting to the 'A' character
 82                     offsetEnd:
 83
 84             int     0x80        ; make the kernel do the thing
 85             jmp     loop        ; re-run the loop
 86
 87             stringCheck:        ; this loop is a little tricky, as we need to be able to point to the correct string to output instead of the 's', but w$
 88             cmp     BYTE [eax + edx], 115       ; compare the byte with a 115, which is an 's' on the ascii table
 89             jne     continue        ;  if there is no 's', just let the string keep going
 90             mov     edx, -1     ; to calculate string length, just use the walktrhough loop again
 91                 offsetedx:
 92                 inc     edx     ; edx is our counter here
 93 ;               mov     edi, edx
 94                 mov     eax, DWORD [ebp + 8]        ; set eax to the dword pointer at ebp + 8 again
 95                 cmp     BYTE [eax + edx], 0     ; checking for a null terminator
 96                 je      offsetedxEnd        ; if there is a null terminator, assume we have reached the end of the string we wanted to drop in, and proc$
 97
 98                 mov     eax, 4      ; syscall write operation
 99                 mov     ebx, 1      ; syscall for printing to screen
100                 mov     ecx, DWORD [ebp + 12]       ; having trouble figuring out how to dymically set this to the right place. What to compare ecx to? $
101                 cmp     edi, -1
102                 je      continueoffset
103                     inc     edi     ; trying to increment edi so on the next check, I can set ecx to run the second 'woot woot' output
104                     mov     ecx, DWORD [ebp + 4]        ; this will output the sencond woot woot, but I can't get it to make the adjustment
105
106                 continueoffset:
107                 mov     edx, 9
108 ;               mov     edi, ecx
109                 int     0x80
110 ;;              inc     edi
111 ;               mov     edx, edi
112 ;               jmp     offsetedx
113                 offsetedxEnd:
114
115 ;           int     0x80            ; let the kernel do its thing
116             mov     edx, esi        ; make sure to put edx back to what it was supposed to be so the top loop isn't screwed up
117             jmp     loop            ; re-run the loop
118
119     continue:
120     mov     eax, 4      ; SYS_write - Print the thing out
121     mov     ebx, 1      ; STDOUT (terminal) - Write to screen
122     mov     ecx, DWORD [ebp + 8]    ; sets pointer to format string
123
124     add     ecx, edx        ; added counter to pointer, which ecx is pointing to
125     mov     edx, 1          ;; Want edx to only output 1 character, but after the output, we need it restored to original count
126
127     int     0x80            ; kernel please grant me your strength
128     mov     edx, esi        ; Extra important since we need edx to be walking through the string, so it needs to be restored to where it was
129     jmp      loop           ; run that loop back again
130
131     loopEnd:
132
133     mov     esp, ebp        ; Epilogue - every function ends with this
134     pop     ebp             ; Epilogue - and this
135     ret                     ; Epilogue - also this
Ad

Answer

When using stack frames, you should not modify the EBP register outside the function prologue/epilogue.

Since all of your registers are being used, you need a local variable on the stack which always points to the address of the next unused vararg parameter. This variable should be initialized to ebp+12, because that is the address of the second parameter of printf, which is the first vararg parameter. After using that parameter, you should increment that local variable by 4, to make it point to that next vararg parameter.

As long as this variable always points to the next unused vararg parameter, you should have no trouble finding the next parameter.

In order to allocate space for such a local variable, you need to allocate 4 bytes on the stack. You can do this with a push instruction or a sub esp, 4 instruction. This can be done immediately after the function prologue.

Ad
source: stackoverflow.com
Ad