linux系统如何打印”Hello World”
1. fprintf
使用fprintf输出到stdout
#include <stdio.h>
int main(int argc, char *argv[]) {
fprintf(stdout, "Hello World!\n");
return 0;
}
2. write
使用write将字符串输出到STDOUT_FILENO
#include <unistd.h>
int main(int argc, char *argv) {
const char msg[] = "Hello World!\n";
write(STDOUT_FILENO, msg, sizeof(msg)-1);
return 0;
}
3. syscall
使用glibc提供的syscall进行系统调用SYS_write将字符串输出到STDOUT_FILENO
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <sys/syscall.h> /* For SYS_xxx definitions */
int main(int argc, char *argv[]) {
const char msg[] = "Hello World!\n";
syscall(SYS_write, STDOUT_FILENO, msg, sizeof(msg)-1);
return 0;
}
汇编格式对比
Intel Code | AT&T Code |
---|---|
mov eax,1 | movl $1,%eax |
mov ebx,0ffh | movl $0xff,%ebx |
int 80h | int $0x80 |
mov ebx, eax | movl %eax, %ebx |
mov eax,[ecx] | movl (%ecx),%eax |
mov eax,[ebx+3] | movl 3(%ebx),%eax |
mov eax,[ebx+20h] | movl 0x20(%ebx),%eax |
add eax,[ebx+ecx*2h] | addl (%ebx,%ecx,0x2),%eax |
lea eax,[ebx+ecx] | leal (%ebx,%ecx),%eax |
sub eax,[ebx+ecx*4h-20h] | subl -0x20(%ebx,%ecx,0x4),%eax |
4. nasm格式汇编
使用nasm格式汇编进行int 80h系统调用,nasm_write.s内容如下
; nasm -f elf64 nasm_write.s && ld -s -o nasm_write nasm_write.o
section .data
hello db 'Hello world!', 10 ; ‘Hello world!’加一个换行符(10)
len equ $-hello ; 字符串长度
section .text
global _start
_start:
mov eax, 4 ; 系统调用sys_write
mov ebx, 1 ; 文件描述符,标准输出
mov ecx, hello ; 设置字符串偏移地址到ecx
mov edx, len ; 设置字符串长度
int 80h ; 中断0x80, kernel系统调用syscall
mov eax, 1 ; exit的系统调用(sys_exit)
mov ebx, 0 ; 退出值我为0
int 80h
编译
nasm -f elf64 nasm_write.s && ld -s -o nasm_write nasm_write.o
5. int 80h
在Linux x86和Linux x86_64系统上可以使用int $0x80制造中断0x80进行系统调用。调用参数:
Syscall # | Param 1 | Param 2 | Param 3 | Param 4 | Param 5 | Param 6 |
---|---|---|---|---|---|---|
eax | ebx | ecx | edx | esi | edi | ebp |
Return value |
---|
eax |
在arch/x86/include/asm/unistd_32.h中,exit和write的syscall number为
#define __NR_exit 1
#define __NR_write 4
AT&T格式汇编通过int $0x80进行系统调用
# as -o gas_int.o gas_int.s && ld -s -o gas_int gas_int.o
.data # section声明
msg: .ascii "Hello World!\n"
len = . - msg # 字符串长度
.text
.global _start
_start:
movl $4, %eax
movl $1, %ebx
movl $msg, %ecx
movl $len, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
编译:
as -o gas_int.o gas_int.s && ld -s -o gas_int gas_int.o
6. syscall
在x86_64上有专用指令进行系统调用
Syscall # | Param 1 | Param 2 | Param 3 | Param 4 | Param 5 | Param 6 |
---|---|---|---|---|---|---|
rax | rdi | rsi | rdx | r10 | r8 | r9 |
Return value |
---|
rax |
在arch/x86/include/asm/unistd_64.h中相应的syscall number
#define __NR_write 1
#define __NR_exit 60
文件gas_syscall.s
# as -o gas_syscall.o gas_syscall.s && ld -s -o gas_syscall gas_syscall.o
.data
msg: .ascii "Hello World!\n"
len = . - msg # 字符串长度
.text
.global _start
_start:
movq $1, %rax
movq $1, %rdi
movq $msg, %rsi
movq $len, %rdx
syscall
movq $60, %rax
movq $0, %rdi
syscall
编译
as -o gas_syscall.o gas_syscall.s && ld -s -o gas_syscall gas_syscall.o
7. 内联方式调用syscall
gcc -nostdlib不连接glibc库
/**
* 在c语言中内联汇编,使用AT&T风格
* 编译: gcc -nostdlib inline.c -o inline
*/
/**
* @brief 通过系统调用打印字符串
*/
void my_printf(char *s, int len) {
long ret;
__asm__ volatile(
"int $0x80" /* 系统调用 */
: "=a" (ret) /* 返回值eax("a") */
: "0"(4), /* 系统调用号sys_write */
"b"(1), /* 参数ebx,文件句柄1=标准输出 */
"c"(s), /* 参数ecx, 字符串 */
"d"(len) /* 参数edx, 字符长度 */
);
}
/**
* @brief 程序入口
*/
void _start() {
/* main body of program: call main(), etc */
char *s = "Hello world!\n";
my_printf(s, 13);
/* exit system call */
asm("movl $1,%eax;"
"xorl %ebx,%ebx;"
"int $0x80"
);
}
参考
+ Hello, world!
+ Linux System Call Table
+ Linux Assembly HOWTO 6.2. Hello, world!
+ X86 Assembly/Interfacing with Linux
+ X86 Assembly/NASM Syntax
+ What is better “int 0x80” or “syscall”?
+ Linux C中内联汇编的语法格式及使用方法(Inline Assembly in Linux C)