从汇编中的系统调用打印 return 值
Print return value from a syscall in assembly
目标是打印时间和日期,但到目前为止我什至无法打印时间戳(纪元时间)。
详情:
System: Linux
Assembler: NASM (Intel syntax)
Arch: x86_64
版本 1:
time_t t = time(NULL);
printf("%ld\n");
time.nasm:
global _start
section .text
_start:
mov rax, 201 ; sys_time
xor rdi, rdi
syscall
mov rsi, rax ; store return value in rsi (arg2) for sys_write
mov rax, 1 ; sys_write
mov rdi, 1
mov rdx, 64 ; How to get the proper size here?
syscall
jmp exit
exit:
mov rax, 60
xor rdi, rdi
syscall
版本 2:
time_t t;
time(&t);
printf("%ld\n");
time.nasm:
global _start
section .text
_start:
mov rax, 201 ; sys_time
mov rdi, time
syscall
mov rax, 1
mov rdi, 1
mov rsi, time
mov rdx, 64 ; How to get the proper size here?
syscall
jmp exit
exit:
mov rax, 60
xor rdi, rdi
syscall
section .data
time: dq 0x0
编译链接:
nasm -g -f elf64 time.nasm -o time.o && ld time.o -o time && ./time
问题:
- 如何在两种解决方案中获取 return 值?
- 有没有办法获取已经格式化的日期和时间?
为此编写了一个 200 多行的汇编程序。它首先打印时间戳,然后打印格式化的日期和时间。它的所有功能都遵循 System-V x64 调用约定。
global _start
section .rodata
strings: ; '\n', ' ', '/', ':'
db 0x1, 0xa, 0x1, 0x20
db 0x1, 0x2f, 0x1, 0x3a
weekdays: ; weekday strings
db 0x3, 0x54, 0x68, 0x75
db 0x3, 0x46, 0x72, 0x69
db 0x3, 0x53, 0x61, 0x74
db 0x3, 0x53, 0x75, 0x6e
db 0x3, 0x4d, 0x6f, 0x6e
db 0x3, 0x54, 0x75, 0x65
db 0x3, 0x57, 0x65, 0x64
months: ; length of months
db 0x1f, 0x1c, 0x1f, 0x1e
db 0x1f, 0x1e, 0x1f, 0x1f
db 0x1e, 0x1f, 0x1e, 0x1f
section .text
_start:
push rbx ; align stack
mov rax, 201 ; sys_time
xor rdi, rdi
syscall
mov rbx, rax
; you may uncomment the following line and put an arbitary timestamp to test it out
; mov rbx, 0
mov rdi, rbx
call print_num ; print unix timestamp
mov rdi, strings
call sys_print ; new line
mov rdi, rbx
call print_time ; print formatted date
pop rbx ; since we are exiting, we don't need this pop actually
mov rax, 60 ; sys_exit
xor rdi, rdi
syscall
leap_year: ; rsi + (year in rdi is leap)
mov rax, rdi
mov rcx, 4
xor rdx, rdx
div rcx
test rdx, rdx ; return 0 if year % 4
jnz func_leap_year_ret_0
mov rax, rdi
mov rcx, 100
xor rdx, rdx
div rcx
test rdx, rdx ; return 1 if year % 100
jnz func_leap_year_ret_1
mov rax, rdi
mov rcx, 400
xor rdx, rdx
div rcx
test rdx, rdx ; return 0 if year % 400
jnz func_leap_year_ret_0
func_leap_year_ret_1:
lea rax, [rsi + 1]
ret
func_leap_year_ret_0:
mov rax, rsi
ret
year_length: ; length of year in rdi
mov rsi, 365
jmp leap_year
month_length: ; length of month (year in rdi, month in rsi)
push r15
push r14
push r13
mov r14, rsi ; back up month in r14, will be used as index
cmp rsi, 1
setz r15b
movzx r13, r15b
xor rsi, rsi
call leap_year
and r13, rax
movzx rax, byte [r14 + months]
add rax, r13
pop r13
pop r14
pop r15
ret
print_time: ; print time_t in rdi
push r15
push r14
push r13
push r12
mov r14, 1970 ; 1970-01-01T00:00:00Z
xor r15, r15
mov rcx, 60
mov rax, rdi
xor rdx, rdx
div rcx
push rdx ; push #5
xor rdx, rdx
div rcx
push rdx ; push #6
mov rcx, 24
xor rdx, rdx
div rcx
push rdx ; push #7, the last one
mov r12, rax
mov r13, rax
func_print_time_loop_1_start:
mov rdi, r14
call year_length
cmp r13, rax
jb func_print_time_loop_2_start
sub r13, rax
inc r14
jmp func_print_time_loop_1_start
func_print_time_loop_2_start:
mov rdi, r14
mov rsi, r15
call month_length
cmp r13, rax
jb func_print_time_loop_end
sub r13, rax
inc r15
jmp func_print_time_loop_2_start
func_print_time_loop_end:
; print time
mov rdi, [rsp]
call print_num
mov rdi, strings + 6
call sys_print
mov rdi, [rsp + 8]
call print_num
mov rdi, strings + 6
call sys_print
mov rdi, [rsp + 16]
call print_num
; print " "
mov rdi, strings + 2
call sys_print
; print weekday
mov rax, r12
mov rcx, 7
xor rdx, rdx
div rcx
lea rdi, [rdx * 4 + weekdays]
call sys_print
; print " "
mov rdi, strings + 2
call sys_print
; print date
mov rdi, r15
inc rdi
call print_num
mov rdi, strings + 4
call sys_print
mov rdi, r13
inc rdi
call print_num
mov rdi, strings + 4
call sys_print
mov rdi, r14
call print_num
; print new line
mov rdi, strings
call sys_print
add rsp, 24
pop r12
pop r13
pop r14
pop r15
ret
print_num: ; print number in rdi
mov r8, rsp
sub rsp, 24 ; 21 bytes for local storage, with extra 3 bytes to keep stack aligned
xor r9, r9
mov rax, rdi
mov rcx, 10
func_print_num_loop_start:
dec r8
xor rdx, rdx
div rcx
add dl, 48
mov [r8], dl
inc r9b
test rax, rax
jnz func_print_num_loop_start
func_print_num_loop_end:
dec r8
mov [r8], r9b
mov rdi, r8
call sys_print
add rsp, 24 ; deallocate local storage, restore rsp
ret
sys_print: ; print a string pointed by rdi
movzx rdx, byte [rdi]
lea rsi, [rdi + 1]
mov rdi, 1 ; stdout
mov rax, 1 ; write
syscall
ret
print_num
函数打印寄存器 rdi
中的任意数字。如果你想知道我如何打印一个数字你可以看看那个函数。
print_time
是计算和打印日期和时间的地方。
这是输出,以及使用 asctime(gmtime(time_t t))
打印格式化日期和时间的 C 程序的输出
$ ./time && ./ct
1608515228
1:47:8 Mon 12/21/2020
Unix time: 1608515228
C library returns: Mon Dec 21 01:47:08 2020
(最后两行来自C程序)
您也可以在第 34 行中放置任何时间戳来进行测试。
我的解决方案很幼稚:
- 先算出总天数,可以使用 time/60/60/24 找到(然后您会在这一步得到 hour/min/sec)。
- 然后算出年份。我通过逐年减去一年中的天数来做到这一点。我设计了一个函数来计算任何一年的天数作为我的辅助函数。
- 查找年月日。和步骤2差不多,我设计了一个计算任意一年任意月份天数的函数作为我的辅助函数
编辑:
按照几个人的要求,将整个程序粘贴到这个答案中。
为了打印整数部分,我在这里使用了@PeterCordes 的实现:
目标是打印时间和日期,但到目前为止我什至无法打印时间戳(纪元时间)。
详情:
System: Linux
Assembler: NASM (Intel syntax)
Arch: x86_64
版本 1:
time_t t = time(NULL);
printf("%ld\n");
time.nasm:
global _start
section .text
_start:
mov rax, 201 ; sys_time
xor rdi, rdi
syscall
mov rsi, rax ; store return value in rsi (arg2) for sys_write
mov rax, 1 ; sys_write
mov rdi, 1
mov rdx, 64 ; How to get the proper size here?
syscall
jmp exit
exit:
mov rax, 60
xor rdi, rdi
syscall
版本 2:
time_t t;
time(&t);
printf("%ld\n");
time.nasm:
global _start
section .text
_start:
mov rax, 201 ; sys_time
mov rdi, time
syscall
mov rax, 1
mov rdi, 1
mov rsi, time
mov rdx, 64 ; How to get the proper size here?
syscall
jmp exit
exit:
mov rax, 60
xor rdi, rdi
syscall
section .data
time: dq 0x0
编译链接:
nasm -g -f elf64 time.nasm -o time.o && ld time.o -o time && ./time
问题:
- 如何在两种解决方案中获取 return 值?
- 有没有办法获取已经格式化的日期和时间?
为此编写了一个 200 多行的汇编程序。它首先打印时间戳,然后打印格式化的日期和时间。它的所有功能都遵循 System-V x64 调用约定。
global _start
section .rodata
strings: ; '\n', ' ', '/', ':'
db 0x1, 0xa, 0x1, 0x20
db 0x1, 0x2f, 0x1, 0x3a
weekdays: ; weekday strings
db 0x3, 0x54, 0x68, 0x75
db 0x3, 0x46, 0x72, 0x69
db 0x3, 0x53, 0x61, 0x74
db 0x3, 0x53, 0x75, 0x6e
db 0x3, 0x4d, 0x6f, 0x6e
db 0x3, 0x54, 0x75, 0x65
db 0x3, 0x57, 0x65, 0x64
months: ; length of months
db 0x1f, 0x1c, 0x1f, 0x1e
db 0x1f, 0x1e, 0x1f, 0x1f
db 0x1e, 0x1f, 0x1e, 0x1f
section .text
_start:
push rbx ; align stack
mov rax, 201 ; sys_time
xor rdi, rdi
syscall
mov rbx, rax
; you may uncomment the following line and put an arbitary timestamp to test it out
; mov rbx, 0
mov rdi, rbx
call print_num ; print unix timestamp
mov rdi, strings
call sys_print ; new line
mov rdi, rbx
call print_time ; print formatted date
pop rbx ; since we are exiting, we don't need this pop actually
mov rax, 60 ; sys_exit
xor rdi, rdi
syscall
leap_year: ; rsi + (year in rdi is leap)
mov rax, rdi
mov rcx, 4
xor rdx, rdx
div rcx
test rdx, rdx ; return 0 if year % 4
jnz func_leap_year_ret_0
mov rax, rdi
mov rcx, 100
xor rdx, rdx
div rcx
test rdx, rdx ; return 1 if year % 100
jnz func_leap_year_ret_1
mov rax, rdi
mov rcx, 400
xor rdx, rdx
div rcx
test rdx, rdx ; return 0 if year % 400
jnz func_leap_year_ret_0
func_leap_year_ret_1:
lea rax, [rsi + 1]
ret
func_leap_year_ret_0:
mov rax, rsi
ret
year_length: ; length of year in rdi
mov rsi, 365
jmp leap_year
month_length: ; length of month (year in rdi, month in rsi)
push r15
push r14
push r13
mov r14, rsi ; back up month in r14, will be used as index
cmp rsi, 1
setz r15b
movzx r13, r15b
xor rsi, rsi
call leap_year
and r13, rax
movzx rax, byte [r14 + months]
add rax, r13
pop r13
pop r14
pop r15
ret
print_time: ; print time_t in rdi
push r15
push r14
push r13
push r12
mov r14, 1970 ; 1970-01-01T00:00:00Z
xor r15, r15
mov rcx, 60
mov rax, rdi
xor rdx, rdx
div rcx
push rdx ; push #5
xor rdx, rdx
div rcx
push rdx ; push #6
mov rcx, 24
xor rdx, rdx
div rcx
push rdx ; push #7, the last one
mov r12, rax
mov r13, rax
func_print_time_loop_1_start:
mov rdi, r14
call year_length
cmp r13, rax
jb func_print_time_loop_2_start
sub r13, rax
inc r14
jmp func_print_time_loop_1_start
func_print_time_loop_2_start:
mov rdi, r14
mov rsi, r15
call month_length
cmp r13, rax
jb func_print_time_loop_end
sub r13, rax
inc r15
jmp func_print_time_loop_2_start
func_print_time_loop_end:
; print time
mov rdi, [rsp]
call print_num
mov rdi, strings + 6
call sys_print
mov rdi, [rsp + 8]
call print_num
mov rdi, strings + 6
call sys_print
mov rdi, [rsp + 16]
call print_num
; print " "
mov rdi, strings + 2
call sys_print
; print weekday
mov rax, r12
mov rcx, 7
xor rdx, rdx
div rcx
lea rdi, [rdx * 4 + weekdays]
call sys_print
; print " "
mov rdi, strings + 2
call sys_print
; print date
mov rdi, r15
inc rdi
call print_num
mov rdi, strings + 4
call sys_print
mov rdi, r13
inc rdi
call print_num
mov rdi, strings + 4
call sys_print
mov rdi, r14
call print_num
; print new line
mov rdi, strings
call sys_print
add rsp, 24
pop r12
pop r13
pop r14
pop r15
ret
print_num: ; print number in rdi
mov r8, rsp
sub rsp, 24 ; 21 bytes for local storage, with extra 3 bytes to keep stack aligned
xor r9, r9
mov rax, rdi
mov rcx, 10
func_print_num_loop_start:
dec r8
xor rdx, rdx
div rcx
add dl, 48
mov [r8], dl
inc r9b
test rax, rax
jnz func_print_num_loop_start
func_print_num_loop_end:
dec r8
mov [r8], r9b
mov rdi, r8
call sys_print
add rsp, 24 ; deallocate local storage, restore rsp
ret
sys_print: ; print a string pointed by rdi
movzx rdx, byte [rdi]
lea rsi, [rdi + 1]
mov rdi, 1 ; stdout
mov rax, 1 ; write
syscall
ret
print_num
函数打印寄存器 rdi
中的任意数字。如果你想知道我如何打印一个数字你可以看看那个函数。
print_time
是计算和打印日期和时间的地方。
这是输出,以及使用 asctime(gmtime(time_t t))
$ ./time && ./ct
1608515228
1:47:8 Mon 12/21/2020
Unix time: 1608515228
C library returns: Mon Dec 21 01:47:08 2020
(最后两行来自C程序)
您也可以在第 34 行中放置任何时间戳来进行测试。
我的解决方案很幼稚:
- 先算出总天数,可以使用 time/60/60/24 找到(然后您会在这一步得到 hour/min/sec)。
- 然后算出年份。我通过逐年减去一年中的天数来做到这一点。我设计了一个函数来计算任何一年的天数作为我的辅助函数。
- 查找年月日。和步骤2差不多,我设计了一个计算任意一年任意月份天数的函数作为我的辅助函数
编辑:
按照几个人的要求,将整个程序粘贴到这个答案中。
为了打印整数部分,我在这里使用了@PeterCordes 的实现: