GCC Embedded Assembly for Writing mips Instructions (Godson Suitable)

Sometimes embedded assembly is needed to implement some functions in c language projects. However, many methods of using embedded assembly on the network are introduced with x86 as an example. This article will take Mips instructions as an example to introduce the demo of embedded assembly format.

I. GCC Embedded Assembly Format

__asm__(
Assembly statement template
 Output section
 Input section
 Destruction Description
);

"Asm" means that the following code is an embedded assembly, or can be replaced by "asm".

It can be seen that the embedded assembly consists of four parts: Assembly statement template, output part, input part and damage description part. The Assembly statement template is indispensable, and the other three parts are optional. Each part is separated by ":".

Following is an example of the use of embedded assembly.

DEMO: Implementing File Creation and Writing

In this example, I wrote the test.c file, and implemented the embedded assembly of open, close, write and other three system functions using embedded assembly. The code is as follows:

void exit(){
/* sys_exit (unsigned long error_code) */
    asm(
	"li	$2,5058\n\t"  //sys_exit syscall id is 5058 -> v0
	"li	$4,0\n\t"     //fixme: exit code           -> a0
	"syscall   \n\t");
}


int open(const char* fileName,unsigned long flags, unsigned long mode){
/* sys_open (const char *filename, unsigned long flags, unsigned long mode) */
    int fd = 0;

    asm(
	"li	$2,5002\n\t"         //sys_open syscall id is 5002 -> v0
	"move	$4,%1\n\t"       //filename            -> a0
	"move	$5,%2\n\t"       // flags            -> a1
	"move	$6,%3\n\t"       // mode            -> a2
	"syscall      \n\t"
	"sw     $2,%0\n\t"
	:"=m"(fd)                         //The output corresponds to% 0
	:"r"(fileName),"r"(flags),"r"(mode)  //Input corresponds to% 1% 2% 3 
	:"$2","$4","$5","$6");
    return fd;
}

void close(int fd){
/* sys_close		(int fd) */
    asm(
	"li	$2,5003\n\t"  //sys_close syscall id is 5003 -> v0
	"move	$4,%0\n\t"     //fixme: exit code           -> a0
	"syscall      \n\t"
	:
	:"r"(fd)
	:"$2","$4");
}


void write(int fd,const void *buf, unsigned long count){
/* sys_write (int fd, const void *buf, unsigned long count) */
    asm(
	".set noreorder \n\t"      
	"li	$2,5001\n\t"      //sys_write syscall id is 5001 -> v0
	"move	$4,%0\n\t"         //stdio file id is 0           -> a0
	"move	$5,%1\n\t"       // fixme: *str value            -> a1
	"move   $6,%2\n\t"        //length(*str) is 13           -> a2
	"syscall      \n\t"
	:
	:"r"(fd),"r"(buf),"r"(count)
	:"$2","$4","$5","$6");
}

int main(){
    const char* fileName = "/home/sunguoyun/file.txt";
    unsigned long flags = 0x00000002 | 0x00000100; //O_RDWR | O_CREAT
    unsigned long mode = 0644;
    int fd = open(fileName,flags,mode);
    write(fd,"hello,world\n",12);
    close(fd);
    exit();
}

In the above example, no gcc libraries are referenced, because the required functions have been implemented using syscall's system call instructions. The open function implements four parts of the embedded assembly. Each line has instructions.

The function of the whole program is very simple. The entry function is main(). It implements opening the / home/sunguoyun/file.txt file (if it does not exist), and writing the "hello,world" string to the file.

Now compile and run with the following command:

$ gcc -fno-builtin test.c -o test
$ ./test 
$ cat /home/sunguoyun/file.txt 
hello,world

We use the GCC command to compile the test.c file and generate the final binary executable test. The function of - fno-builtin is to avoid the conflict between close file in test.c file and the same name built-in function of gcc.
After executing with the'. / test'command, you can see through the cat command that the file.txt file has been created, which is exactly what you just wrote to "hello,world".

Tags: network C

Posted on Mon, 02 Sep 2019 23:01:52 -0700 by dfowler