实战汇编语言与 C 语言之间相互调用

1. 引言

众所周知,C 语言相比于汇编语言拥有更为强大的灵活性和抽象能力,但相较于汇编语言,C 语言又缺乏了直接寻址、读写内存的强大能力。
同时,C 语言由于具备更强大的抽象能力,往往会造成生成的机器指令过多,因此,对于嵌入式编程等领域的 C 语言程序设计来说,有一个非常常用的优化方式,就是将 C 语言编译后反汇编为汇编语言,然后通过阅读并精简汇编语言,来实现代码优化的目的。
那么,既然 C 语言、C++ 可以被编译器反汇编为汇编语言,我们是否可以直接通过汇编语言调用 C 语言或者让 C 语言去调用汇编语言呢?答案当然是可以的。
本文,我们就来详细介绍,如何在 linux 环境下实现 C 语言与汇编语言的相互调用。

2. linux 系统调用的实现 — int 80h 中断

2.1. 原理

各个操作系统都有一系列原生实现的系统调用,这些服务运行在操作系统内核,供用户态进程调用,从而实现了用户态进程对更高权限的使用。
此前我们已经介绍过,由于系统调用运行在 ring0 特权级,ring3 特权级的用户态进程必须通过四种调用门之一进行调用:

  1. 调用门
  2. 中断门
  3. 陷阱门
  4. 任务门

利用调用门实现特权级间跳转(上) — 原理篇

linux 系统调用就是通过陷阱门实现的,它的调用过程如下:

  1. 应用程序调用库函数(API)
  2. API 将系统调用号存入 EAX,然后通过中断(int 0x80)调用使系统进入内核态
  3. 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用)
  4. 系统调用完成相应功能,将返回值存入 EAX,返回到中断处理函数
  5. 中断处理函数返回到 API 中
  6. API 将 EAX 返回给应用程序

因此,我们按照上述步骤设置寄存器、触发 80h 号中断就可以实现在汇编语言中调用 linux 系统调用了,具体的参数和系统调用编号见附录。
不过需要注意的是,windows 的 WSL 并不支持。

2.2. 实践

下面我们就来通过汇编实现在屏幕上打印 hello world。
查表可以看到,sys_write 的号码为 4,ebx 参数 fd 为 1 则表示 stdout,ecx、edx 分别是字符串指针和字符串长度。

2.2.1. hello.asm

[section .data]    ; 数据段 
strHello db "Hello World", 0AH
strlen equ $ - strHello

[section .code] ; 代码段
global _start ; 导出程序入口

_start:

mov edx, strlen
mov ecx, strHello
mov ebx, 1 ; stdout
mov eax, 4 ; sys_write
int 80h

mov ebx, 0
mov eax, 1 ; sys_exit
int 80h

2.2.2. Makefile

main:  
nasm -f elf hello.asm -o hello.o
ld -s hello.o -o hello

clean:
rm -rf hello.o hello

2.2.3. 执行结果

运行 ./hello 可以看到打印出了执行结果:

Hello World

3. 汇编与 C 语言的相互调用

3.1. 汇编调用 C 语言程序

上面的程序中,我们使用了 global 关键字,他的目的是导出入口,也就是供链接器识别程序调用的入口。
外部语言也可以通过这个导出的入口调用,我们也可以导出更多的函数供外部调用。
如果汇编程序需要依赖外部的程序入口,可以使用 extern 关键字,他用来导入外部程序入口。
但这里存在两个问题,也就是 C 调用约定:

  1. 参数压栈顺序 — C 语言参数列表中,后面的参数先压栈
  2. 谁来清理堆栈 — 调用者清理

3.2. C 语言调用汇编程序

在 C 语言中调用已经被汇编 global 关键字导出的代码也很简单,和调用其他动态链接库中的函数是一样的,只要显式声明即可直接调用。

3.3. 实践

下面我们就以汇编语言作为入口,调用 C 语言的快速排序程序。
当 C 语言中的快速排序完成时,调用汇编程序,实现结果的打印。

3.3.1. main.asm

extern quick_sort    ; void quick_sort(char *str, int len); 

[section .data]
randstr db "cmyqpdexnlbzfsgtouhirvakjw"
strlen equ $ - randstr

[section .code]
global _start ; 导出 _start 标记,供链接器识别程序入口
global print_text ; 导出打印程序供 C 语言调用

_start:

push dword strlen ; len
push dword randstr ; str
call quick_sort ; 调用快速排序

add esp, 8 ; 清理堆栈

mov ebx, 0
mov eax, 1 ; sys_exit
int 80h

; void print_text(char *str, int len)
print_text:
mov edx, [esp + 8] ; len
mov ecx, [esp + 4] ; str
mov ebx, 1
mov eax, 4 ; sys_write
int 80h
ret

3.3.2. main.c

void print_text(char *str, int len);  
void quick_sort(char *str, int len);
void quick_sort_body(char *str, int start, int end);
void exchange_char(char *str, int i, int j);

void quick_sort(char *str, int len) {
if (str == 0 || len <= 0) {
print_text("params error\n", 13);
return;
}
quick_sort_body(str, 0, len);
print_text(str, len);
print_text("\n", 1);
return;
}

void quick_sort_body(char *str, int start, int end) {
if (end <= start) {
return;
}
int i = start + 1, j = start + 1;
char temp;
while (j < end) {
if (str[j] < str[start]) {
exchange_char(str, i, j);
i++;
}
j++;
}
exchange_char(str, start, i-1);
quick_sort_body(str, start, i-1);
quick_sort_body(str, i, end);
return;
}

void exchange_char(char *str, int i, int j) {
char temp = str[i];
str[i] = str[j];
str[j] = temp;
return;
}

3.3.3. Makefile

main:  
nasm -f elf -o asm.o main.asm
gcc -c -o main.o main.c
ld -s -o main asm.o main.o

clean:
rm -rf asm.o main.o main

3.3.4. 执行结果

执行 ./main 打印出了:

abcdefghijklmnopqrstuvwxyz

4. 微信公众号

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤。

5. 附录1 — 参考资料

https://zh.wikipedia.org/wiki/%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8。
https://blog.csdn.net/xiaominthere/article/details/17287965。

6. 附录2 — 汇编调用系统调用参数

汇编调用 linux 系统调用参数

eax Name Source ebx ecx edx esx edi
1 sys_exit kernel/exit.c int
2 sys_fork arch/i386/kernel/process.c struct pt_regs
3 sys_read fs/read_write.c unsigned int char * size_t
4 sys_write fs/read_write.c unsigned int const char * size_t
5 sys_open fs/open.c const char * int int
6 sys_close fs/open.c unsigned int
7 sys_waitpid kernel/exit.c pid_t unsigned int * int
8 sys_creat fs/open.c const char * int
9 sys_link fs/namei.c const char * const char *
10 sys_unlink fs/namei.c const char *
11 sys_execve arch/i386/kernel/process.c struct pt_regs
12 sys_chdir fs/open.c const char *
13 sys_time kernel/time.c int *
14 sys_mknod fs/namei.c const char * int dev_t
15 sys_chmod fs/open.c const char * mode_t
16 sys_lchown fs/open.c const char * uid_t gid_t
18 sys_stat fs/stat.c char * struct __old_kernel_stat *
19 sys_lseek fs/read_write.c unsigned int off_t unsigned int
20 sys_getpid kernel/sched.c
21 sys_mount fs/super.c char * char * char *
22 sys_oldumount fs/super.c char *
23 sys_setuid kernel/sys.c uid_t
24 sys_getuid kernel/sched.c
25 sys_stime kernel/time.c int *
26 sys_ptrace arch/i386/kernel/ptrace.c long long long long
27 sys_alarm kernel/sched.c unsigned int
28 sys_fstat fs/stat.c unsigned int struct __old_kernel_stat *
29 sys_pause arch/i386/kernel/sys_i386.c
30 sys_utime fs/open.c char * struct utimbuf *
33 sys_access fs/open.c const char * int
34 sys_nice kernel/sched.c int
36 sys_sync fs/buffer.c
37 sys_kill kernel/signal.c int int
38 sys_rename fs/namei.c const char * const char *
39 sys_mkdir fs/namei.c const char * int
40 sys_rmdir fs/namei.c const char *
41 sys_dup fs/fcntl.c unsigned int
42 sys_pipe arch/i386/kernel/sys_i386.c unsigned long *
43 sys_times kernel/sys.c struct tms *
45 sys_brk mm/mmap.c unsigned long
46 sys_setgid kernel/sys.c gid_t
47 sys_getgid kernel/sched.c
48 sys_signal kernel/signal.c int __sighandler_t
49 sys_geteuid kernel/sched.c
50 sys_getegid kernel/sched.c
51 sys_acct kernel/acct.c const char *
52 sys_umount fs/super.c char * int
54 sys_ioctl fs/ioctl.c unsigned int unsigned int unsigned long
55 sys_fcntl fs/fcntl.c unsigned int unsigned int unsigned long
57 sys_setpgid kernel/sys.c pid_t pid_t
59 sys_olduname arch/i386/kernel/sys_i386.c struct oldold_utsname *
60 sys_umask kernel/sys.c int
61 sys_chroot fs/open.c const char *
62 sys_ustat fs/super.c dev_t struct ustat *
63 sys_dup2 fs/fcntl.c unsigned int unsigned int
64 sys_getppid kernel/sched.c
65 sys_getpgrp kernel/sys.c
66 sys_setsid kernel/sys.c
67 sys_sigaction arch/i386/kernel/signal.c int const struct old_sigaction * struct old_sigaction *
68 sys_sgetmask kernel/signal.c
69 sys_ssetmask kernel/signal.c int
70 sys_setreuid kernel/sys.c uid_t uid_t
71 sys_setregid kernel/sys.c gid_t gid_t
72 sys_sigsuspend arch/i386/kernel/signal.c int int old_sigset_t
73 sys_sigpending kernel/signal.c old_sigset_t *
74 sys_sethostname kernel/sys.c char * int
75 sys_setrlimit kernel/sys.c unsigned int struct rlimit *
76 sys_getrlimit kernel/sys.c unsigned int struct rlimit *
77 sys_getrusage kernel/sys.c int struct rusage *
78 sys_gettimeofday kernel/time.c struct timeval * struct timezone *
79 sys_settimeofday kernel/time.c struct timeval * struct timezone *
80 sys_getgroups kernel/sys.c int gid_t *
81 sys_setgroups kernel/sys.c int gid_t *
82 old_select arch/i386/kernel/sys_i386.c struct sel_arg_struct *
83 sys_symlink fs/namei.c const char * const char *
84 sys_lstat fs/stat.c char * struct __old_kernel_stat *
85 sys_readlink fs/stat.c const char * char * int
86 sys_uselib fs/exec.c const char *
87 sys_swapon mm/swapfile.c const char * int
88 sys_reboot kernel/sys.c int int int void *
89 old_readdir fs/readdir.c unsigned int void * unsigned int
90 old_mmap arch/i386/kernel/sys_i386.c struct mmap_arg_struct *
91 sys_munmap mm/mmap.c unsigned long size_t
92 sys_truncate fs/open.c const char * unsigned long
93 sys_ftruncate fs/open.c unsigned int unsigned long
94 sys_fchmod fs/open.c unsigned int mode_t
95 sys_fchown fs/open.c unsigned int uid_t gid_t
96 sys_getpriority kernel/sys.c int int
97 sys_setpriority kernel/sys.c int int int
99 sys_statfs fs/open.c const char * struct statfs *
100 sys_fstatfs fs/open.c unsigned int struct statfs *
101 sys_ioperm arch/i386/kernel/ioport.c unsigned long unsigned long int
102 sys_socketcall net/socket.c int unsigned long *
103 sys_syslog kernel/printk.c int char * int
104 sys_setitimer kernel/itimer.c int struct itimerval * struct itimerval *
105 sys_getitimer kernel/itimer.c int struct itimerval *
106 sys_newstat fs/stat.c char * struct stat *
107 sys_newlstat fs/stat.c char * struct stat *
108 sys_newfstat fs/stat.c unsigned int struct stat *
109 sys_uname arch/i386/kernel/sys_i386.c struct old_utsname *
110 sys_iopl arch/i386/kernel/ioport.c unsigned long
111 sys_vhangup fs/open.c
112 sys_idle arch/i386/kernel/process.c
113 sys_vm86old arch/i386/kernel/vm86.c unsigned long struct vm86plus_struct *
114 sys_wait4 kernel/exit.c pid_t unsigned long * int options struct rusage *
115 sys_swapoff mm/swapfile.c const char *
116 sys_sysinfo kernel/info.c struct sysinfo *
117 sys_ipc (*Note) arch/i386/kernel/sys_i386.c uint int int int void *
118 sys_fsync fs/buffer.c unsigned int
119 sys_sigreturn arch/i386/kernel/signal.c unsigned long
120 sys_clone arch/i386/kernel/process.c struct pt_regs
121 sys_setdomainname kernel/sys.c char * int
122 sys_newuname kernel/sys.c struct new_utsname *
123 sys_modify_ldt arch/i386/kernel/ldt.c int void * unsigned long
124 sys_adjtimex kernel/time.c struct timex *
125 sys_mprotect mm/mprotect.c unsigned long size_t unsigned long
126 sys_sigprocmask kernel/signal.c int old_sigset_t * old_sigset_t *
127 sys_create_module kernel/module.c const char * size_t
128 sys_init_module kernel/module.c const char * struct module *
129 sys_delete_module kernel/module.c const char *
130 sys_get_kernel_syms kernel/module.c struct kernel_sym *
131 sys_quotactl fs/dquot.c int const char * int caddr_t
132 sys_getpgid kernel/sys.c pid_t
133 sys_fchdir fs/open.c unsigned int
134 sys_bdflush fs/buffer.c int long
135 sys_sysfs fs/super.c int unsigned long unsigned long
136 sys_personality kernel/exec_domain.c unsigned long
138 sys_setfsuid kernel/sys.c uid_t
139 sys_setfsgid kernel/sys.c gid_t
140 sys_llseek fs/read_write.c unsigned int unsigned long unsigned long loff_t * unsigned int
141 sys_getdents fs/readdir.c unsigned int void * unsigned int
142 sys_select fs/select.c int fd_set * fd_set * fd_set * struct timeval *
143 sys_flock fs/locks.c unsigned int unsigned int
144 sys_msync mm/filemap.c unsigned long size_t int
145 sys_readv fs/read_write.c unsigned long const struct iovec * unsigned long
146 sys_writev fs/read_write.c unsigned long const struct iovec * unsigned long
147 sys_getsid kernel/sys.c pid_t
148 sys_fdatasync fs/buffer.c unsigned int
149 sys_sysctl kernel/sysctl.c struct __sysctl_args *
150 sys_mlock mm/mlock.c unsigned long size_t
151 sys_munlock mm/mlock.c unsigned long size_t
152 sys_mlockall mm/mlock.c int
153 sys_munlockall mm/mlock.c
154 sys_sched_setparam kernel/sched.c pid_t struct sched_param *
155 sys_sched_getparam kernel/sched.c pid_t struct sched_param *
156 sys_sched_setscheduler kernel/sched.c pid_t int struct sched_param *
157 sys_sched_getscheduler kernel/sched.c pid_t
158 sys_sched_yield kernel/sched.c
159 sys_sched_get_priority_max kernel/sched.c int
160 sys_sched_get_priority_min kernel/sched.c int
161 sys_sched_rr_get_interval kernel/sched.c pid_t struct timespec *
162 sys_nanosleep kernel/sched.c struct timespec * struct timespec *
163 sys_mremap mm/mremap.c unsigned long unsigned long unsigned long unsigned long
164 sys_setresuid kernel/sys.c uid_t uid_t uid_t
165 sys_getresuid kernel/sys.c uid_t * uid_t * uid_t *
166 sys_vm86 arch/i386/kernel/vm86.c struct vm86_struct *
167 sys_query_module kernel/module.c const char * int char * size_t size_t *
168 sys_poll fs/select.c struct pollfd * unsigned int long
169 sys_nfsservctl fs/filesystems.c int void * void *
170 sys_setresgid kernel/sys.c gid_t gid_t gid_t
171 sys_getresgid kernel/sys.c gid_t * gid_t * gid_t *
172 sys_prctl kernel/sys.c int unsigned long unsigned long unsigned long unsigned long
173 sys_rt_sigreturn arch/i386/kernel/signal.c unsigned long
174 sys_rt_sigaction kernel/signal.c int const struct sigaction * struct sigaction * size_t
175 sys_rt_sigprocmask kernel/signal.c int sigset_t * sigset_t * size_t
176 sys_rt_sigpending kernel/signal.c sigset_t * size_t
177 sys_rt_sigtimedwait kernel/signal.c const sigset_t * siginfo_t * const struct timespec * size_t
178 sys_rt_sigqueueinfo kernel/signal.c int int siginfo_t *
179 sys_rt_sigsuspend arch/i386/kernel/signal.c sigset_t * size_t
180 sys_pread fs/read_write.c unsigned int char * size_t loff_t
181 sys_pwrite fs/read_write.c unsigned int const char * size_t loff_t
182 sys_chown fs/open.c const char * uid_t gid_t
183 sys_getcwd fs/dcache.c char * unsigned long
184 sys_capget kernel/capability.c cap_user_header_t cap_user_data_t
185 sys_capset kernel/capability.c cap_user_header_t const cap_user_data_t
186 sys_sigaltstack arch/i386/kernel/signal.c const stack_t * stack_t *
187 sys_sendfile mm/filemap.c int int off_t * size_t
190 sys_vfork arch/i386/kernel/process.c struct pt_regs

7. 附录 — 系列目录

实现一个操作系统

实战汇编语言与 C 语言之间相互调用》来自互联网,仅为收藏学习,如侵权请联系删除。本文URL:http://www.bookhoes.com/4026.html