I'm writing a program to print binary string of a hardcoded word. Here is how it looks like currently:
section .text global _start extern _print_binary_content _start: push word [word_to_print] ; pushing word. Can we push just one byte? call _print_binary_content mov rax, 60 mov rdi, 0 syscall section .data word_to_print: dw 0xAB0F
SYS_BRK_NUM equ 0x0C BITS_IN_WORD equ 0x10 SYS_WRITE_NUM equ 0x01 STD_OUT_FD equ 0x01 FIRST_BIT_BIT_MASK equ 0x01 ASCII_NUMBER_OFFSET equ 0x30 section .text global _print_binary_content _print_binary_content: pop rbp xor ecx, ecx ;zeroing rcx xor ebx, ebx ;zeroing rbx pop bx ;the word to print the binary content of ;sys_brk for current location mov rax, SYS_BRK_NUM mov rdi, 0 syscall ;end sys_brk mov r12, rax ;save the current brake location ;sys_brk for memory allocation 16 bytes lea rdi, [rax + BITS_IN_WORD] mov rax, SYS_BRK_NUM syscall ;end sys_brk xor ecx, ecx mov cl, byte BITS_IN_WORD - 1; used as a counter in the loop below loop: mov dx, bx and dx, FIRST_BIT_BIT_MASK add dx, ASCII_NUMBER_OFFSET mov [r12 + rcx], dl shr bx, 0x01 dec cl cmp cl, 0 jge loop mov rsi, r12 mov rax, SYS_WRITE_NUM mov rdi, STD_OUT_FD mov rdx, BITS_IN_WORD syscall push rbp ; pushing return address back ret
If I compile link and run this program it works. But the question is about performance and maybe conventions of writing assembly programs. In the file
printer.asm I cleaned
ecx twice which looks kind of not optimal. Maybe some registers were used not by their purpose (I used intel-manual).
Can you please help me to improve this very simple program?
brk)? And then not free it when you're done? I'm wondering if you had a specific reason for doing that instead of using stack memory for your small fixed-size buffer like in this integer->decimal string function. Also, "binary" is ambiguous in this context. I thought from the title you were going to call
write(1, &word, 2), but you're actually converting the word to a base-2 string. \$\endgroup\$
push word [word_to_print]- I would expect this one even to fail to compile, but it works! In 64b common OSes there're often very stringent requirements for the
rspmodifications, like keeping it 16B aligned before calling other functions (if you want to respect the ABI calling convention, as in this case you are calling your own custom function, which is not obeying the ABI, you can misalign the stack without running into some crash). But it's not even clear why you put the word argument into the stack, while using custom calling convention, why don't you use registers instead? \$\endgroup\$
pushof return address by using
espfor addressing also arguments in memory), but first check 64b linux ABI examples, which is passing arguments in registers. = much easier to learn and understand and faster performance-wise, overall the 64b linux ABI is superior to 32b ABI (but has lot more requirements for the
rspvalue itself! That's tricky for people moving 32->64). Then check the 32b stack examples, to build your skills. \$\endgroup\$