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

2022-06-27 15:28:27 浏览数 (1)

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

代码语言:javascript复制
[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

代码语言:javascript复制
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

代码语言:javascript复制
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

代码语言:javascript复制
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 errorn", 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

代码语言:javascript复制
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

5. 附录1 — 参考资料

https://zh.wikipedia.org/wiki/系统调用。 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. 附录 — 系列目录

实现一个操作系统

0 人点赞