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

经过上一篇的铺垫貌似可以很轻松的用汇编写出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
时间: 2024-11-03 07:49:24

linux下64位汇编的系统调用(4)的相关文章

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

知道了syscall调用号之后还不算完,还要搞清楚2件事: 1 每种调用号需要传递哪些参数: 2 调用如何传递参数以及结果如何返回: 第一个问题的答案是: 在linux系统中某个程序执行时进行的系统调用可以通过strace命令来查看,solaris中对应的命令为dtrace,而mac os x中可以通过类似的dtruss命令来查看.当进程已经处于 D 状态(uninterruptible sleep)时,strace 也帮不上忙.这时候可以通过: cat /proc/<PID>/syscall

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

现在基本上系统都是64位了,而64位系统下的汇编和32位有了较大的变化,无论是系统调用的接口还是C标准库的接口都和32位汇编有所不同:下面简单谈一下在64位linux下如何利用汇编直接调用系统调用. 需要准备的有: 1.一台linux机器:我的系统是ubuntu 14.10 x64版: 2.还要一个汇编器:我没有使用gas,而是使用了跨平台的nasm: 3.一个调试器:这里选择的不多,要不就是gdb,要不就是lldb,不过这里选择前者. 4.一个编辑器,linux下有很多优秀的语法编辑器,这里用

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

背景知识基本交代清楚了,下面我们实际写一个小例子看一下.代码的功能很简单,显示一行文本,然后退出.我们使用了syscall中的write和exit调用,查一下前面的调用号和参数,我们初步总结如下: write(即sys_write)调用号为1,需传递3个参数 unsigned int fd const char *buf size_t count exit(sys_exit)调用号为60,只需传递一个错误码 int error_code 如果该值为0表示程序执行成功. 因为以上两个调用最多的也只

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

看到这里大家都基本知道了如何进行linux下的汇编系统调用:不过有些童鞋可能会问:那些C库中函数里为我们解决的额外汇编代码你是怎么知道的? 好吧,我承认:我是通过逆向知道的,这貌似有点犯规的嫌疑- 比如举个例子,那上一篇里的mmap C库函数来说,首先写一个C代码: #include <stdlib.h> #include <stdio.h> #include <stdbool.h> #include <sys/mman.h> #include <sy

Mac OS X下64位汇编与Linux下64位汇编的一些不同

1 首先系统调用号大大的不同:mac64和linux32的系统调用号也不同(虽然局部可能有相同) 2 mac64的系统调用号在: /usr/include/sys/syscall.h 可以查到,但是调用的时候其值要加上0x2000000,可以写一个宏处理: %define mk64 0x2000000+ 使用方式如下: mov rax,mk64 1 ;exit NO mov rdi,0 ;error_code syscall 3 如果在mac64下汇编要与C库相链接,所有extern符号名前要加

linux下32位汇编调用规则

传递给系统调用的参数必须安装参数顺序一次放到寄存器中,当系统调用完成后,返回值放在eax中: 当系统调用参数<=5个时: eax中存放系统调用的功能号,传递给系统调用的参数顺序依次放到寄存器:ebx,ecx,edx,esi,edi中 当系统调用参数>5个时: eax中存放系统调用的功能号,全部参数应依次放在一块连续的内存区域中,同时在寄存器ebx中保存指向该内存区域的指针(内存块的首地址):linux采用的是C调用模式,这意味着如果要以stack来实现这个连续的内存块(当然可以用其他方式实现,

Linux GCC 64位编程技巧

                                 linux GCC 64位编程技巧 64位系统的优势? 既然要采用64位系统,首先要知道64位系统的优势所在.对于技术人员来说,完全没有必要去看那些厂家拿出的厚厚的说明书.或者某个研究机构抛出的一堆的数字,64位系统的优势总结起来很简单:内存大.速度快! 内存大 与32位系统相比,64位系统的地址空间大大增大,达到了18PB,18PB究竟是多大呢?说出来有点吓人:4G内存的40亿倍!这么大的空间,不要说内存了,就是整个磁盘的数据都

Red Hat Enterprise Linux 7 64位安装mysql-5.7.17详细步骤

1.下载mysqlhttps://dev.mysql.com/downloads/mysql/下载你需要的对应的x86架构的 32或者64位的mysql的版本 2.将下载的mysql传到linux的桌面 3.释放安装包: tar -xvf MySQL-5.7.4-m14-0.2.m14.el7.x86_64.rpm-bundle.tar 4.下面开始安装显示出有冲突,应该卸载掉原有的软件mariadb-devel 和 mariadb卸载后开始安装 5.光驱挂载上系统镜像命令行输入mount 进入

Linux下文件操作的主要系统调用,注意要点及说明!

基本上所有的操作系统都会有对文件的操作,也叫文件I/O.除开可以C函数库中基本操作外,所有操作系统都会针对各自的特点重新提供一些操作文件的API,下面描述一下linux下的主要文件操作API. 一.函数名称及格式. creat   函数(相信很多的初学者都会在这个名字上吃点小亏)- 创建文件 int creat(const char* pathname,mode_t mode); /* 返回值 :若成功返回为只写打开的文件描述符,若错误则返回 -1*/ open  函数 - 打开文件 int o