As far as i know, on windows it’s visual studio, in there you have registers, debugging and other stuff. How about linux?
I tried setting up with this: https://medium.com/@muhammadmeeran2003/how-to-set-up-assembly-language-on-visual-studio-code-2021-587a7b01c9a1 and this: https://github.com/newtonsart/vscode-assembly
That github repo is pretty good, but still no console output and can’t see x64 registers while debugging.
Any other good alternatives?
What I use on Linux:
- Vim
- GNU Make
- NASM (nasm.us)
NASM uses Intel assembly syntax. If you want to learn and use AT&T syntax you can use GNU Assembler (
as
) provided by thebintutils
package instead.How I use it:
Create a project
mkdir hello_world cd hello_world touch Makefile hello_world.asm
Write a Makefile
Note the indents below are supposed to be TAB characters, not spaces.
Makefile
all: hello_world hello_world.o: hello_world.asm nasm -o $@ -f elf32 -g $< hello_world: hello_world.o ld -m elf_i386 -g -o $@ $< .PHONY: clean clean: rm -f hello_world *.o
Write a program
hello_world.asm
; Assemble as a 32-bit program bits 32 ; Constants SYS_EXIT equ 1 ; Kernel system call: exit() SYS_WRITE equ 4 ; Kernel system call: write() FD_STDOUT equ 1 ; System file descriptor to write to EXIT_SUCCESS equ 0 ; Variable storage section .data msg: db "hello world from ", 0 msg_len: equ $-msg linefeed: db 0xa ; '\n' linefeed_len: equ $-linefeed ; Program storage section .text global _start _start: ; Set up stack frame push ebp mov ebp, esp ; Set base pointer to argv[0] add ebp, 8 ; Write "hello world from " message to stdout mov eax, SYS_WRITE mov ebx, FD_STDOUT mov ecx, msg mov edx, msg_len int 80h ; Get length of argv[0] push dword [ebp] call strlen mov edx, eax ; Write the program execution path to stdout mov eax, SYS_WRITE mov ebx, FD_STDOUT mov ecx, [ebp] ; edx length already set int 80h ; Write new line character mov eax, SYS_WRITE mov ebx, FD_STDOUT mov ecx, linefeed mov edx, linefeed_len int 80h ; End of stack frame pop ebp ; End program mov eax, SYS_EXIT mov ebx, EXIT_SUCCESS int 80h strlen: ; Set up stack frame push ebp mov ebp, esp ; Set base pointer to the first argument on the stack ; strlen(buffer); ; ^ add ebp, 8 ; Save registers we plan to write to push ecx push esi ; Clear string direction flag ; (i.e. lodsb will *increment* esi) cld ; Zero counter xor ecx, ecx ; Load address of buffer into the "source index" register mov esi, [ebp] .loop: ; Read byte from esi ; Store byte in eax lodsb ; Loop until string NUL terminator cmp eax, 0 je .return ; else: Increment counter and continue inc ecx jmp .loop .return: ; Return string length in eax mov eax, ecx ; Restore written registers pop esi pop ecx ; End stack frame pop ebp ; Pop stack argument ; 32-bit word is 4 bytes. We had one argument. ret 4 * 1
Compile and run
$ make nasm -o hello_world.o -f elf32 -g hello_world.asm ld -m elf_i386 -g -o hello_world hello_world.o
$ ./hello_world hello world from ./hello_world
Adding a debug target to the makefile
Want to fire up your debugger immediately and break on the main entrypoint? No problem.
Makefile
gdb: hello_world gdb -tui -ex 'b _start' -ex 'run' --args $<
Now you can clean the project, rebuild, and start a debugging session with one command…
$ make clean gdb rm -f hello_world *.o nasm -o hello_world.o -f elf32 -g hello_world.asm ld -m elf_i386 -g -o hello_world hello_world.o gdb -tui -ex 'b _start' -ex 'run' --args hello_world # You're debugging the program in GDB now. Poof.