c-在使用系统调用的printf上输出这两个字符串时出现问题



在一所大学的汇编语言C课程中,我需要只使用系统调用来创建一个基本的printf函数。每当出现"%"时,我都需要检查下一个字符,以确定如何实现字符或字符串。

如果有"c",请将其替换为字符;如果有"s",请用字符串替换。如果还有另一个"%",请输出它。这位教授表示,他知道这是一项艰巨的任务,所以如果解决方案得到部分实施也没关系,但我离完整的解决方案很近,所以我想继续推进。

我对这段代码做了大量的工作,并对每一行都做了评论,所以我想强调这是为了我的学习我可以使用mov eax,[abp+12]打印出一个字符串"woot woot",或者使用mov eax,[ebp+16]打印出第二个"woot woot",但我找不到同时打印这两个字符串的解决方案这是我的两难选择。

感谢您抽出时间,祝您编码愉快!

以下是任务链接以供澄清:https://imgur.com/h9tP89j

这是我的样本输出:

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

这是我的代码:

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

使用堆栈帧时,不应在函数prologue/epilogue之外修改EBP寄存器。

由于所有寄存器都在使用中,因此堆栈上需要一个本地变量,该变量始终指向下一个未使用的vararg参数的地址。这个变量应该初始化为ebp+12,因为这是printf的第二个参数的地址,也是第一个vararg参数。使用该参数后,应该将该局部变量增加4,使其指向下一个vararg参数。

只要这个变量总是指向下一个未使用的vararg参数,您就可以毫不费力地找到下一个参数。

为了为这样一个局部变量分配空间,您需要在堆栈上分配4个字节。可以使用push指令或sub esp, 4指令执行此操作。这可以在函数prolog之后立即完成。

最新更新