[英]Why doesn't this simple assembly program return a number?
Why doesn't this simple assembly program return a number? 为什么这个简单的汇编程序不返回数字?
I'm trying to get it to print 12
hex, but its printing gibberish. 我正在尝试使它打印
12
进制,但其打印乱码。
global _start
section .text
_start:
mov eax, 0x4
mov ebx, 0x1
mov ecx, var1
mov edx, 2
int 0x80
;exit the program gracefully
mov eax, 0x1
mov ebx, 0x5
int 0x80
section .data
var1: db 0x12
The primary stumbling block you have run into is attempting to write the value of 0x12
(ASCII 18) to stdout
. 您遇到的主要绊脚石试图将
0x12
(ASCII 18)的值写入stdout
。 A quick check of the ASCII chart will reveal that this value is non-printable. 快速检查ASCII图表将发现此值不可打印。 In assembly, you can only write characters to
stdout
. 在汇编中,您只能将字符写入
stdout
。 What that means is when faced with writing a numeric value , you have to separate the value into its digits, convert the digits to their ASCII representation (by adding '0'
or 0x30
or 48
decimal) to the value. 这就是说,当面对一个数字值时 ,您必须将该值分成数字,然后将数字转换为ASCII表示形式(通过将
'0'
或0x30
或48
位十进制加)。
It's no different than manually converting a number to a string in C or any other language. 这与以C或任何其他语言将数字手动转换为字符串没有什么不同。 You basically divide the original number by
10
repeatedly, saving the remainder each time as the digit to a buffer, then writing the buffer out in reverse order to write the ASCII representation of the number to stdout
(there are many example on SO). 您基本上可以将原始数字重复除以
10
,每次将剩余的数字作为数字保存到缓冲区中,然后以相反的顺序写出缓冲区,以将数字的ASCII表示形式写到stdout
(SO上有很多示例)。
Before looking at an example that will separate all digits into a buffer and write them to stdout
, let's just cover some basics in your example and get it to print something (anything). 在看一个将所有数字分隔到缓冲区并将它们写入
stdout
的示例之前,让我们仅介绍示例中的一些基础知识并使其打印任何内容。 The first concept in assembly you need to cement in your brain is that all labels (variables) in assembly point to a memory address not a numeric value. 您需要牢记在脑中的汇编的第一个概念是,汇编中的所有标签(变量)都指向一个内存地址,而不是数字值。 When you assign a label in assembly, eg:
在装配中分配标签时,例如:
var1 db 0x12
you are storing a single byte of data whose value is 12-hex
at the memory location pointed to by var1
. 您将 在
var1
指向的存储位置中存储一个单字节数据,其值为12-hex
。 var1
is a pointer to that memory location. var1
是指向该内存位置的指针。 In nasm, if you want to operate on/or reference the value at that location, you must dereference the pointer to the address by enclosing the pointer in [ ]
(just like dereferencing a pointer in C with *var1
). 在nasm中,如果要在该位置上操作/或引用该值 ,则必须通过将指针括在
[ ]
来将指针解除引用到地址(就像在C中使用*var1
解除引用指针一样)。
The next concept to cement, is that sys_write (syscall 4)
expects a beginning memory address, not a value, stored in ecx
. 巩固的下一个概念是
sys_write (syscall 4)
期望在ecx
存储的起始内存地址而不是值。 It will then write edx
bytes of data to the file descriptor stored in ebx
(1 - stdout
). 然后,它将
edx
数据字节写入ebx
(1- stdout
)中存储的文件描述符中。 mov
ing the address of var1
into edx
will allow us to dereference the value at that address by operating on [edx]
. mov
荷兰国际集团的地址var1
到edx
将使我们能够在解引用该地址的值通过对操作[edx]
To convert individual digits to their ASCII value you add '0'
(or 0x30) to them, but what will the result be if we just add '0'
to [edx]
? 要将单个数字转换为ASCII值,请在其上添加
'0'
(或0x30),但是如果仅在[edx]
添加'0'
,结果将是什么? (present value 0x12
(18) + 0x30
(48) = 0x42
(66) -- which just happens to be ASCII 'B'
) (当前值
0x12
(18)+ 0x30
(48)= 0x42
(66)-恰好是ASCII'B 'B'
)
(we will aslo cheat and append a newline
( 0xa
) to the end of var1
and make it 2-bytes so we don't have to mess with a separate call) (我们还将作弊,并在
var1
的末尾添加一个newline
( 0xa
),并将其0xa
2个字节,这样我们就不必再为单独的调用弄乱了。)
Putting this together with your example would result in: 将其与您的示例放在一起将导致:
section .text
global _start
_start:
mov eax, 4 ; linux sys_write
mov ebx, 1 ; stdout
mov ecx, var1 ; mov address of var1 to ecx
add byte [ecx], 0x30 ; add '0' to first byte of var1
mov edx, 2 ; number of chars to print
int 0x80 ; syscall
mov eax, 0x1 ; __NR_exit 1
xor ebx, ebx ; exit code of 0
int 0x80
section .data
var1 db 0x12, 0xa ; ASCII 18 (non-printable) with newline
Compiling and running the program will result in: 编译并运行该程序将导致:
$ ./convert
B
Now we can move on to a full example that will separate each digit for the value stored at var1
and then print each digit of 0x12
(decimal 18) to stdout
followed by a newline
. 现在我们来看一个完整的示例,它将存储在
var1
处的值的每个数字分开,然后将0x12
(十进制18)的每个数字打印到stdout
然后newline
。 (outputting a conversion to the hex representation is left to you -- there are several examples you can search the web and find, int 80h.org comes to mind.) (输出转换为十六进制表示形式的工作就交给您了-您可以在网上搜索几个示例, 然后就会想到int 80h.org 。)
A brief example in nasm would be: nasm中的一个简单示例是:
section .text
global _start
_start:
mov edi, result ; address of buffer in destination index
xor eax, eax ; zero out eax
mov al, [var1] ; put the value of var1 in al to divide
mov bl, 10 ; base 10 divisor to find remainder
; separate remainder digits into result buffer
remloop:
div bl ; divide current value by 10
mov [edi], ah ; move the remainder to result
cmp al, 0 ; is the quotient zero?
je printchar ; if it is we are done
xor ah, ah
inc edi ; move offset in result string (note digits
jmp remloop ; of answer are stored in reverse order)
printchar:
add byte [edi], 0x30 ; add ascii '0' to digit to get printable char
mov eax, 4 ; linux sys_write
mov ebx, 1 ; stdout
mov ecx, edi ; point to current digit
mov edx, 1 ; number of chars to print
int 0x80 ; syscall
dec edi ; point to next digit
cmp edi, result ; are we past the final digit?
jge printchar ; if not, keep printing to stdout
; print newline
mov eax, 4 ; linux sys_write
mov ebx, 1 ; stdout
mov ecx, newline ; address of newline
mov edx, 1 ; number of chars to print
int 0x80 ; syscall
; exit the program gracefully
mov eax, 0x1 ; __NR_exit 1
mov ebx, 0x5 ; exit code of 5
int 0x80
section .data
var1 db 0x12 ; ASCII 18 (non-printable)
result times 8 db 0 ; 8 byte buffer for result
newline db 0xa ; newline character
If you build the code: 如果您构建代码:
nasm -f elf -o convert.o convert.asm
ld -m elf_i386 -o convert convert.o
You can then display the ASCII output of 0x12
or 18
ASCII: 然后,您可以显示
0x12
或18
ASCII的ASCII输出:
$ ./convert
18
For an excellent web reference for Assembly see The Art of Assembly Language Programming . 有关汇编的出色Web参考,请参见汇编语言编程的艺术 。 Read it.
阅读。 All of it.
所有的。 It's that good.
就是那样 While it is primarily written for 8086, all principles are 100% applicable to current assembly programming.
尽管它主要是为8086编写的,但所有原理都可100%适用于当前的汇编编程。 The only differences are register sizes, calling conventions and syscall numbers for x86_64.
唯一的区别是x86_64的寄存器大小,调用约定和系统调用号。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.