レジスタに整数があるとしたら、どうすればそれを出力できますか?簡単なサンプルコードを見せてもらえますか?
「hello、world」などの文字列を出力する方法はすでに知っています。
私はLinuxで開発しています。
すでに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呼び出し規約 を使用するため、後でスタックポインターを復元する必要があることに注意してください。つまり、関数に渡されるパラメーターごとに4バイトを追加します。
文字列に変換する必要があります。 16進数について話している場合、それは非常に簡単です。この方法で任意の数を表すことができます。
0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3
したがって、この番号がある場合は、私が示したように分割する必要があり、すべての「セクション」をASCII同等のものに変換します。
4つの部分の取得は、ビットマジックを使用して簡単に実行できます。特に、最初の4ビットに関心のある部分を右に移動してから、結果を0xfでANDし、残りの部分から分離します。これが私が意味することです(私たちが3を使いたいとは思っていません):
0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003
これで、単一の数値が得られたので、その数値をASCII値に変換する必要があります。数値が9以下の場合は、0を追加できますASCII値(0x30)、それが9より大きい場合、aの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:これはIntel構文ですが、AT&T構文への変換は難しくありません。 ここ を参照してください。
Linux x86-64と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
_: printfを使用して64ビットMac OS X用のアセンブリ言語のHello Worldプログラムを記述する方法xor eax, eax
_: printfを呼び出す前に%eaxがゼロになるのはなぜですか?_-no-pie
_:プレーン_call printf
_はPIE実行可能ファイル(_-pie
_)では機能しません。リンカーは、古いスタイルの実行可能ファイル用のPLTスタブのみを自動的に生成します。オプションは次のとおりです。
_call printf wrt ..plt
_は、従来の_call printf
_のようにPLTを介して呼び出す
_call [rel printf wrt ..got]
_のように、PLTをまったく使用しない場合は_gcc -fno-plt
_。
GAS構文call *printf@GOTPCREL(%rip)
のように。
これらはいずれも、PIE以外の実行可能ファイルでも問題なく、libcを静的にリンクしている場合を除いて、非効率を引き起こしません。この場合、コードからlibc関数へのオフセットは静的リンク時に既知であるため、_call printf
_は_call rel32
_をlibcに直接解決できます。
Cライブラリなしで16進数が必要な場合: 16進数をアセンブリで印刷
Ubuntu 18.10、NASM 2.13.03でテスト済み。
使用しているアーキテクチャ/環境によって異なります。
たとえば、Linuxで数値を表示したい場合、ASMコードはWindowsで使用するものとは異なります。
編集:
変換の例については [〜#〜] this [〜#〜] を参照してください。
私はアセンブリに比較的慣れていないので、これは明らかに最良の解決策ではありませんが、機能しています。主な関数は_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 ; край
数値表現については言わなかったので、基数が無ければ(もちろん大きすぎない)符号なし数値用に次のコードを書いたので、それを使用できます。
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
2の累乗の基数を持つ数値に対して最適化されておらず、printf
のlibc
を使用しません。
関数print
は、数値を改行して出力します。数値文字列はスタック上に形成されます。 nasmでコンパイルします。
出力:
clockz