假设我在寄存器中有一个整数,我该如何打印?你能展示一个简单的示例代码吗?
我已经知道如何打印一个字符串,例如“hello,world”。
我正在Linux上开发。
答案 0 :(得分:12)
你必须用字符串转换它;如果你在谈论十六进制数字,这很容易。任何数字都可以这样表示:
0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3
所以当你有这个号码时,你必须像我所示的那样拆分它然后将每个“部分”转换成它的ASCII等价物。
获得这四个部分很容易完成一些魔术,特别是右移以在前四位移动我们感兴趣的部分,然后用0xf将结果与其余部分隔离。这就是我的意思(我们希望采用3):
0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003
现在我们只有一个数字,我们必须将其转换为ASCII值。如果数字小于或等于9,我们可以添加0的ASCII值(0x30),如果它大于9,我们必须使用ASCII值(0x61)。
现在我们只需编写代码就可以了:
mov si, ??? ; si points to the target buffer
mov ax, 0a31fh ; ax contains the number we want to convert
mov bx, ax ; store a copy in bx
xor dx, dx ; dx will contain the result
mov cx, 3 ; cx's our counter
convert_loop:
mov ax, bx ; load the number into ax
and ax, 0fh ; we want the first 4 bits
cmp ax, 9h ; check what we should add
ja greater_than_9
add ax, 30h ; 0x30 ('0')
jmp converted
greater_than_9:
add ax, 61h ; or 0x61 ('a')
converted:
xchg al, ah ; put a null terminator after it
mov [si], ax ; (will be overwritten unless this
inc si ; is the last one)
shr bx, 4 ; get the next part
dec cx ; one less to do
jnz convert_loop
sub di, 4 ; di still points to the target buffer
PS:我知道这是16位代码,但我仍然使用旧的TASM:P
PPS:这是英特尔语法,转换为AT& T语法并不困难,看看here。
答案 1 :(得分:12)
如果您已经使用Linux,则无需自行进行转换。只需使用printf代替:
;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf
main:
mov eax, 0xDEADBEEF
push eax
push message
call printf
add esp, 8
ret
message db "Register = %08X", 10, 0
请注意,printf
使用cdecl calling convention,因此我们需要在之后恢复堆栈指针,即每个传递给函数的参数添加4个字节。
答案 2 :(得分:4)
Linux x86-64 with printf
main.asm中
default rel ; make [rel format] the default, you always want this.
extern printf, exit ; NASM requires declarations of external symbols, unlike GAS
section .rodata
format db "%#x", 10, 0 ; C 0-terminated string: "%#x\n"
section .text
global main
main:
sub rsp, 8 ; re-align the stack to 16 before calling another function
; Call printf.
mov esi, 0x12345678 ; "%x" takes a 32-bit unsigned int
lea rdi, [rel format]
xor eax, eax ; AL=0 no FP args in XMM regs
call printf
; Return from main.
xor eax, eax
add rsp, 8
ret
然后:
nasm -f elf64 -o main.o main.asm
gcc -no-pie -o main.out main.o
./main.out
输出:
0x12345678
注意:
sub rsp, 8
:How to write assembly language hello world program for 64 bit Mac OS X using printf? xor eax, eax
:Why is %eax zeroed before a call to printf? -no-pie
:普通call printf
无法在PIE可执行文件(-pie
)中工作,链接器只会自动为旧式可执行文件生成PLT存根。您的选择是:
call printf wrt ..plt
通过PLT调用传统call printf
call [rel printf wrt ..got]
根本不使用PLT,例如gcc -fno-plt
。
与GAS语法call *printf@GOTPCREL(%rip)
类似。
其中任何一个在非PIE可执行文件中都可以,并且除非你静态链接libc,否则不会导致效率低下。在这种情况下,call printf
可以直接解析为call rel32
到libc,因为在静态链接时,从代码到libc函数的偏移量是已知的。
如果你想要没有C库的hex:Printing Hexadecimal Digits with Assembly
在Ubuntu 18.10,NASM 2.13.03上测试。
答案 3 :(得分:1)
答案 4 :(得分:0)
我对装配比较陌生,这显然不是最好的解决方案, 但它正在发挥作用。主要功能是_iprint,它首先检查是否 eax中的数字是负数,如果是,则打印减号,而不是它继续 通过调用函数_dprint来打印单个数字 每一个数字。这个想法如下,如果我们有512而不是它等于:512 =(5 * 10 + 1)* 10 + 2 = Q * 10 + R,所以我们可以通过除以它来找到数字的最后一位数10,和 得到提醒R,但是如果我们在一个循环中完成它而不是数字将在一个 反向顺序,所以我们使用堆栈来推送它们,然后在那之后 把它们写到stdout,它们会以正确的顺序弹出。
; Build : nasm -f elf -o baz.o baz.asm
; ld -m elf_i386 -o baz baz.o
section .bss
c: resb 1 ; character buffer
section .data
section .text
; writes an ascii character from eax to stdout
_cprint:
pushad ; push registers
mov [c], eax ; store ascii value at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; copy c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; writes a digit stored in eax to stdout
_dprint:
pushad ; push registers
add eax, '0' ; get digit's ascii code
mov [c], eax ; store it at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; pass the address of c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; now lets try to write a function which will write an integer
; number stored in eax in decimal at stdout
_iprint:
pushad ; push registers
cmp eax, 0 ; check if eax is negative
jge Pos ; if not proceed in the usual manner
push eax ; store eax
mov eax, '-' ; print minus sign
call _cprint ; call character printing function
pop eax ; restore eax
neg eax ; make eax positive
Pos:
mov ebx, 10 ; base
mov ecx, 1 ; number of digits counter
Cycle1:
mov edx, 0 ; set edx to zero before dividing otherwise the
; program gives an error: SIGFPE arithmetic exception
div ebx ; divide eax with ebx now eax holds the
; quotent and edx the reminder
push edx ; digits we have to write are in reverse order
cmp eax, 0 ; exit loop condition
jz EndLoop1 ; we are done
inc ecx ; increment number of digits counter
jmp Cycle1 ; loop back
EndLoop1:
; write the integer digits by poping them out from the stack
Cycle2:
pop eax ; pop up the digits we have stored
call _dprint ; and print them to stdout
dec ecx ; decrement number of digits counter
jz EndLoop2 ; if it's zero we are done
jmp Cycle2 ; loop back
EndLoop2:
popad ; pop registers
ret ; bye
global _start
_start:
nop ; gdb break point
mov eax, -345 ;
call _iprint ;
mov eax, 0x01 ; sys_exit
mov ebx, 0 ; error code
int 0x80 ; край
答案 5 :(得分:0)
因为您没有说数字表示形式,所以我为无符号数字编写了以下代码,并带有任意基数(当然不要太大),因此您可以使用它:
IProgress<long>
它并未针对2的幂的底数进行优化,并且未使用BITS 32
global _start
section .text
_start:
mov eax, 762002099 ; unsigned number to print
mov ebx, 36 ; base to represent the number, do not set it too big
call print
;exit
mov eax, 1
xor ebx, ebx
int 0x80
print:
mov ecx, esp
sub esp, 36 ; reserve space for the number string, for base-2 it takes 33 bytes with new line, aligned by 4 bytes it takes 36 bytes.
mov edi, 1
dec ecx
mov [ecx], byte 10
print_loop:
xor edx, edx
div ebx
cmp dl, 9 ; if reminder>9 go to use_letter
jg use_letter
add dl, '0'
jmp after_use_letter
use_letter:
add dl, 'W' ; letters from 'a' to ... in ascii code
after_use_letter:
dec ecx
inc edi
mov [ecx],dl
test eax, eax
jnz print_loop
; system call to print, ecx is a pointer on the string
mov eax, 4 ; system call number (sys_write)
mov ebx, 1 ; file descriptor (stdout)
mov edx, edi ; length of the string
int 0x80
add esp, 36 ; release space for the number string
ret
中的printf
。
函数libc
用新行输出数字。数字字符串在堆栈上形成。通过nasm编译。
输出:
print