How to call another subroutine from a function by push-ing arguments onto the stack before making the call.
The routine copies a substring into the main string if there is enough room before the null byte of the main string and then returns zero, otherwise it aborts the function and returns 1.
Before calling a routine from within a function one pushes a required value onto the stack (if it needs one). This is used by the called routine - is this case the address of the string. By pushing a value onto the stack one has moved all previous stack values down one. So after returning from the subroutine one would normally pop to re-establish the previous stack state. However by using RET num in the called subroutine the pop-ing is done automatically. One can pop a maximum of 16 bytes using this method. As we only push one long in this routine we use _asm("ret $4"); to pop 4 bytes.
When calling an asm routine from C do not use RET num unless you have declared the asm routine __stdcall. This tells the compiler that the function will clean up the stack. Without _stdcall the compiler will generate code to clean up the stack too, so it will be cleaned twice and will result in a crash.
Example.
size_t __declspec(naked) __stdcall str_len(char * s)
#include <stdio.h>
size_t __declspec(naked) str_len(char * s)
{
_asm("movl 4(%esp),%ecx"); // first arg is at 4(%esp)
_asm("jecxz exit1");
// check for '0' NULL in ecx
_asm("movl $-1,%eax");
// -1 to start
_asm("L1:");
_asm("inc %eax");
_asm("cmpb $0,(%ecx,%eax)"); // compares '0' with the byte pointed to
// by ecx with an offfset of eax.
_asm("jne L1");
// not finished yet?
_asm("exit1:");
_asm("ret $4");
// ret in eax (pop 4 bytes before returning)
}
int __declspec(naked) str_insert(char * str, char * substr, int pos)
{
_asm ("pushl %esi");
_asm ("pushl %edi");
_asm ("movl 12(%esp), %edi"); // address of str
_asm ("movl 16(%esp), %esi"); // address of substr
_asm ("movl 20(%esp), %edx"); // start pos
_asm("pushl %edi");
// push address of main string
_asm("call _str_len");
// find length of string, result will be in eax
_asm("movl %eax,%edx");
// save length of main string
_asm("pushl %esi");
// push address of sub string
_asm("call _str_len");
// find length of substring, result will be in eax
_asm("movl %eax,%ecx");
// save length of substring
_asm("addl 20(%esp),%eax"); // position +
length of substring
_asm("cmpl %edx,%eax");
// is pos + sublen to big?
_asm("jg error");
_asm("movl %ecx,%edx");
// save substring length again
_asm("addl 20(%esp),%edi"); // start address + pos
_asm ("cld");
// inc for string instructions (edi)
_asm ("shr $2, %ecx");
// divide sublen for dwords
_asm ("rep");
_asm ("movsl");
// copy dwords
_asm ("movl %edx, %ecx"); // size of sub string in ECX
_asm ("andl $3, %ecx");
// divide for bytes
_asm ("rep");
_asm ("movsb");
// copy bytes
_asm("jmp noerr");
_asm("error:");
_asm("movl $1,%eax");
// return 1
_asm("jmp exit");
_asm("noerr:");
_asm("xorl %eax,%eax");
// return 0
_asm("exit:");
_asm ("popl %edi");
_asm ("popl %esi");
_asm("ret");
// result is in eax
}
int main(void)
{
char s[] = "The way was tough on all but the most hardy";
char s1[] = "tardy";
printf(" %s\n", s);
int ret = str_insert(s, s1, 12);
printf("%s: %s\n", ret ? "Failed" : "Success",s);
return 0;
}