[英]How to print a number in assembly NASM?
Suppose that I have an integer number in a register, how can I print it? 假设我在寄存器中有一个整数,我该如何打印? Can you show a simple example code?
你能展示一个简单的示例代码吗?
I already know how to print a string such as "hello, world". 我已经知道如何打印一个字符串,如“你好,世界”。
I'm developing on Linux. 我正在Linux上开发。
You have to convert it in a string; 你必须用字符串转换它; if you're talking about hex numbers it's pretty easy.
如果你在谈论十六进制数字,这很容易。 Any number can be represented this way:
任何数字都可以这样表示:
0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3
So when you have this number you have to split it like I've shown then convert every "section" to its ASCII equivalent. 因此,当你有这个数字时,你必须像我展示的那样拆分它,然后将每个“部分”转换为它的ASCII等价物。
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we're interested in in the first four bits then AND the result with 0xf to isolate it from the rest. 获得这四个部分很容易完成一些魔术,特别是右移以在前四位移动我们感兴趣的部分,然后用0xf将结果与其余部分隔离。 Here's what I mean (soppose we want to take the 3):
这就是我的意思(我们希望采用3):
0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003
Now that we have a single number we have to convert it into its ASCII value. 现在我们只有一个数字,我们必须将其转换为ASCII值。 If the number is smaller or equal than 9 we can just add 0's ASCII value (0x30), if it's greater than 9 we have to use a's ASCII value (0x61).
如果数字小于或等于9,我们可以添加0的ASCII值(0x30),如果它大于9,我们必须使用ASCII值(0x61)。
Here it is, now we just have to code it: 在这里,现在我们只需编写代码:
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: I know this is 16 bit code but I still use the old TASM :P PS:我知道这是16位代码,但我仍然使用旧的TASM:P
PPS: this is Intel syntax, converting to AT&T syntax isn't difficult though, look here . PPS:这是英特尔语法,虽然转换为AT&T语法并不困难,但请看这里 。
If you're already on Linux, there's no need to do the conversion yourself. 如果您已经在Linux上,则无需自行进行转换。 Just use printf instead:
只需使用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
Note that printf
uses the cdecl calling convention so we need to restore the stack pointer afterwards, ie add 4 bytes per parameter passed to the function. 请注意,
printf
使用cdecl调用约定,因此我们需要在之后恢复堆栈指针,即每个传递给函数的参数添加4个字节。
Linux x86-64 with printf 带有printf的Linux x86-64
main.asm 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
Then: 然后:
nasm -f elf64 -o main.o main.asm
gcc -no-pie -o main.out main.o
./main.out
Output: 输出:
0x12345678
Notes: 笔记:
sub rsp, 8
: How to write assembly language hello world program for 64 bit Mac OS X using printf? sub rsp, 8
: 如何使用printf为64位Mac OS X编写汇编语言hello world程序? xor eax, eax
: Why is %eax zeroed before a call to printf? xor eax, eax
: 为什么在调用printf之前%eax归零? -no-pie
: plain call printf
doesn't work in a PIE executable ( -pie
), the linker only automatically generates a PLT stub for old-style executables. -no-pie
:普通call printf
在PIE可执行文件( -pie
)中不起作用,链接器只为旧式可执行文件自动生成PLT存根。 Your options are: 你的选择是:
call printf wrt ..plt
to call through the PLT like traditional call printf
call printf wrt ..plt
来调用PLT,就像传统的call printf
call [rel printf wrt ..got]
to not use a PLT at all, like gcc -fno-plt
. call [rel printf wrt ..got]
根本不使用PLT,比如gcc -fno-plt
。
Like GAS syntax call *printf@GOTPCREL(%rip)
. 像GAS语法
call *printf@GOTPCREL(%rip)
。
Either of these are fine in a non-PIE executable as well, and don't cause any inefficiency unless you're statically linking libc. 这些中的任何一个在非PIE可执行文件中都是正常的,并且除非您静态链接libc,否则不会导致任何低效率。 In which case
call printf
can resolve to a call rel32
directly to libc, because the offset from your code to the libc function would be known at static linking time. 在这种情况下,
call printf
可以直接解析为call rel32
到libc,因为在静态链接时,从代码到libc函数的偏移量是已知的。
See also: Can't call C standard library function on 64-bit Linux from assembly (yasm) code 另请参见: 无法从汇编(yasm)代码调用64位Linux上的C标准库函数
If you want hex without the C library: Printing Hexadecimal Digits with Assembly 如果您想要不带C库的十六进制 : 使用汇编打印十六进制数字
Tested on Ubuntu 18.10, NASM 2.13.03. 在Ubuntu 18.10,NASM 2.13.03上测试。
It depends on the architecture/environment you are using. 这取决于您使用的架构/环境。
For instance, if I want to display a number on linux, the ASM code will be different from the one I would use on windows. 例如,如果我想在linux上显示一个数字,ASM代码将与我在Windows上使用的代码不同。
Edit: 编辑:
You can refer to THIS for an example of conversion. 您可以参考THIS获取转换示例。
I'm relatively new to assembly, and this obviously is not the best solution, but it's working. 我对装配比较陌生,这显然不是最好的解决方案,但它有效。 The main function is _iprint, it first checks whether the number in eax is negative, and prints a minus sign if so, than it proceeds by printing the individual numbers by calling the function _dprint for every digit.
主要功能是_iprint,它首先检查eax中的数字是否为负数,如果是,则打印减号,而不是通过为每个数字调用函数_dprint来打印单个数字。 The idea is the following, if we have 512 than it is equal to: 512 = (5 * 10 + 1) * 10 + 2 = Q * 10 + R, so we can found the last digit of a number by dividing it by 10, and getting the reminder R, but if we do it in a loop than digits will be in a reverse order, so we use the stack for pushing them, and after that when writing them to stdout they are popped out in right order.
这个想法如下,如果我们有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 ; край
Because you didn't say about number representation I wrote the following code for unsigned number with any base(of course not too big), so you could use it: 因为你没有说过数字表示,我用任何基数(当然不是太大)写了下面的无符号数代码,所以你可以使用它:
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
It's not optimised for numbers with base of power of two and doesn't use printf
from libc
. 它没有针对基数为2的数字进行优化,并且不使用来自
libc
printf
。
The function print
outputs the number with a new line. 功能
print
输出一个新行的数字。 The number string is formed on stack. 数字串形成在堆栈上。 Compile by nasm.
由nasm编译。
Output: 输出:
clockz
https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.