您的位置:首页 > 运维架构 > Linux

linux下64位汇编的系统调用(4)

2015-04-12 10:12 218 查看
经过上一篇的铺垫貌似可以很轻松的用汇编写出mmap的代码来,可仔细一看,还是有不少问题需要解决:

1.系统调用mmap如果出错并不直接返回MAP_FAILED(-1),而是一个“类似”值;C库中的mmap函数对其做了包装,使其最终返回-1;如果我们直接调用mmap syscall,则这些事必须自己来做。

2.C库函数如果出错会设置errno的值,而在汇编中没法直接用:

extern errno


的方法使用外部的值,连接时会报错:

/usr/bin/ld: errno: TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches non-TLS reference in p.o
/lib/x86_64-linux-gnu/libc.so.6: error adding symbols: 错误的值


C语言的解决办法很简单,直接把:

extern int errno;
//替换为
#include <errno.h>


但nasm下这招没法使;我们先看一下errno对应的C代码:

#define errno() *errnofunc()

int *errnofunc()
{
int *errnoptr = get_thread_data(ERRNOPTR);
return errnoptr;
}


可以看到其调用另一个函数,在nasm中我们可以大致这么写:

extern __errno_location
call __errno_location
mov rax,qword [rax]


不过貌似也不太对 :( ,不过我们可以在mmap系统调用后自己操作errno的值,以下代码将填充变量errno的值并且如果出错将修正mmap的返回值为-1:

section .data
errno dq 0

;mmap syscall 之后
cmp rax,0xfffffffffffff001
jb next
push rax
neg rax
mov [errno],rax
pop rax
or rax,-1
next:
;处理mmap返回后的逻辑


3 可以看到C代码中调用mmap参数压栈,是将第4个参数放到rcx里,但是在C库mmap函数里又将rcx赋值给r10;这正应了前面调用规则里的内核系统调用第4个参数是放在r10里,而不是rcx里的哦;所以汇编中我们直接放在r10里即可,不用先转到rcx里了。

最后的代码如下:

section .data
errno dq 0
addr dq 0

MAP_FAILED equ -1
;MAP_LEN equ 40960
MAP_LEN equ 0xffffffffffffffff
PROT_READ_WRITE equ 3
MAP_SHARED_ANON equ 33
;MAP_SHARED_ANON equ 0x20

section .text
;extern errno
extern __errno_location
extern strerror
extern printf
;if use ld
;global _start
;if use gcc
global main
;_start:
main:
and rsp,~0xffff            ;堆栈对齐 equ 0xffffffffffff0000

mov rax,9               ;mmap NO
mov rdi,0               ;map address
mov rsi,MAP_LEN             ;map size
mov rdx,PROT_READ_WRITE
mov r10,MAP_SHARED_ANON
mov r8,-1               ;忽略fd
mov r9,0                ;offset
syscall
cmp rax,0xfffffffffffff001
jb next
push rax
neg rax
mov [errno],rax
pop rax
or rax,-1
next:
cmp rax,MAP_FAILED
;cmp rax,0
;js map_failed
je map_failed

mov [addr],rax
mov rsi,[addr]
mov rdi,msg_success
call printf

mov rax,11
mov rdi,addr
mov rsi,MAP_LEN
syscall
jmp exit

map_failed:
;mov rdi,0xb
;call __errno_location
;mov rax,qword [rax]
;mov rsi,[errno]

mov rdi,[errno]
call strerror
mov rsi,rax
mov rdi,msg_failed
call printf

exit:
mov rax,60      ;exit NO
mov rdi,0       ;error_code
syscall

msg_success: db "map successed , addr at %p",0ax,0
msg_failed: db "map failed ,due to %s",0ax,0


编译连接:

nasm -f elf64 p.s
gcc -o p p.o


如果mmap成功结果如下:

wisy@wisy-pad:~/src/asm_src/nasm_src/linux$ ./p
map successed , addr at 0x7fbe5c94e000


可以用strace查看其返回的syscall:

mmap(NULL, 40960, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) = 0x7fc6b2397000


如果mmap出错则会显示出错原因:

//将MAP_LEN 设为超大的值
./p
map failed ,due to Cannot allocate memory
//传递给flags错误的参数
./p
map failed ,due to Invalid argument
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: