Translating GAS to NASM, part 4
March 29, 2021
This chapter was a project. Not only was there far more code than any previous chapter, but I also put off learning make
until I was nearly finished, so I spent a whooooole lot of time re-assembling and re-linking. For anyone similarly ignorant of the wonders of makefiles, you can grab mine here (or learn about them here). Store the makefile in the same directory as your ASM files and enter make
in your terminal to handle all your assembling and linking. Run make clean
to get rid of all your object files.
New NASM changes!!!!
- We can now keep all our syscalls in a convenient external file! The only change here is that we use
%include
to use them, not.include
. - Functions and data exported from other modules with the
global
keyword must be imported using theextern
keyword. - To pad a data item, NASM uses
times
instead of.rept
, and instead of counting word lengths to figure out how much padding you need, there’s some useful pointer math you can do using the symbol$
, which stands for “current memory location”. These linesmean “store the word Fredrick, then store 0s from the end of that word up to byte 40 of record1”.db "Fredrick" times record1+40-$ db 0
- This chapter uses
push
with a memory address as its operand. To do this in NASM, the size of the data being pushed has to be defined using one of thetype
keywords:byte
,word
,dword
,qword
,tword
,oword
,yword
, orzword
. These follow the pattern of NASM’s other size keywords. - These
type
keywords are used whenever dereferencing data whose size isn’t apparent. In the code below, you’ll see them whenmov
-ing a constant into memory, and wheninc
-rementing a value stored in memory. - Escape characters like newline and
NUL
only work if your strings are wrapped in backticks (`). You could also use the ASCII values of those characters: 10 for newline, and 0 forNUL
. - In the book, read_record and write_record both preserve
%rbx
since it’s used in the syscalls. x64 syscalls don’t use%rbx
so I didn’t bother.
Exercise 6 - Writing structured data
linux.asm
record_firstname equ 0
record_lastname equ 40
record_address equ 80
record_age equ 320
record_size equ 328
record-defs.asm
;; Common Linux Definitions
sys_read equ 0
sys_write equ 1
sys_open equ 2
sys_close equ 3
sys_exit equ 60
sys_brk equ 12
;; Standard file descriptors
stdin equ 0
stdout equ 1
stderr equ 2
;; Common Status Codes
eof equ 0
write-record.asm
%include "record-defs.asm"
%include "linux.asm"
st_write_buffer equ 16
st_filedes equ 24
section .text
global write_record
write_record:
push rbp
mov rbp, rsp
mov rax, sys_write
mov rdi, [rbp + st_filedes]
mov rsi, [rbp + st_write_buffer]
mov rdx, record_size
syscall
mov rsp, rbp
pop rbp
ret
write-records.asm
%include "record-defs.asm"
%include "linux.asm"
section .data
record1:
db "Fredrick"
times record1+40-$ db 0
db "Bartlett"
times record1+80-$ db 0
db `4242 S Prairie\nTulsa, OK 55555`
times record1+320-$ db 0
dq 45
record2:
db "Marilyn"
times record2+40-$ d1 db 0
db "Taylor"
times record2+80-$ db 0
db `2224 S Johannan St\nChicago, IL 12345`
times record2+320-$ db 0
dq 29
record3:
db "Derrick"
times record3+40-$ db 0
db "McIntire"
times record3+80-$ db 0
db `500 W Oakland\nSan Diego, CA 54321`
times record3+320-$ db 0
dq 36
filename:
db "test.dat", 0
section .text
st_file_descriptor equ -8
extern write_record
global _start
_start:
mov rbp, rsp
sub rsp, 8
mov rax, sys_open
mov rdi, filename
mov rsi, 0o101
mov rdx, 0o666
syscall
mov [rbp + st_file_descriptor], rax
push qword [rbp + st_file_descriptor]
push record1
call write_record
add rsp, 16
push qword [rbp + st_file_descriptor]
push record2
call write_record
add rsp, 16
push qword [rbp + st_file_descriptor]
push record3
call write_record
add rsp, 16
mov rax, sys_close
mov rdi, [rbp + st_file_descriptor]
syscall
mov rax, sys_exit
mov rdi, 0
syscall
Exercise 7 - Reading structured data
count-chars.asm
global count_chars
st_string_start_address equ 8
count_chars:
push rbp
mov rbp, rsp
mov rcx, 0
mov rdx, [rbp + st_string_start_address]
count_loop_begin:
mov al, [rdx]
cmp al, 0
je count_loop_end
inc rcx
inc rdx
jmp count_loop_begin
count_loop_end:
mov rax, rcx
pop rbp
ret
write-newline.asm
%include "linux.asm"
global write_newline
section .data
newline: db `\n`
section .text
st_filedes equ 16
write_newline:
push rbp
mov rbp, rsp
mov rax, sys_write
mov rdi, [rbp + st_filedes]
mov rsi, newline
mov rdx, 2
syscall
mov rsp, rbp
pop rbp
ret
read-record.asm
%include "linux.asm"
%include "record-defs.asm"
st_read_buffer equ 16
st_filedes equ 24
section .text
global read_record
read_record:
push rbp
mov rbp, rsp
mov rax, sys_read
mov rdi, [rbp + st_filedes]
mov rsi, [rbp + st_read_buffer]
mov rdx, record_size
syscall
mov rsp, rbp
pop rbp
ret
read-records.asm
%include "linux.asm"
%include "record-defs.asm"
extern count_chars, write_newline, read_record
section .data
filename:
db "test.dat", 0
section .bss
record_buffer: resb record_size
section .text
global _start
_start:
st_input_descriptor equ -8
st_output_descriptor equ -16
mov rbp, rsp
sub rsp, 16
mov rax, sys_open
mov rdi, filename
mov rsi, 0
mov rdx, 0o666
syscall
mov [rbp + st_input_descriptor], rax
mov qword [rbp + st_output_descriptor], stdout
record_read_loop:
push qword [rbp + st_input_descriptor]
push record_buffer
call read_record
add rsp, 8
cmp rax, record_size
jne finished_reading
push record_buffer + record_firstname
call count_chars
add rsp, 8
mov rdx, rax
mov rax, sys_write
mov rdi, [rbp + st_output_descriptor]
mov rsi, record_buffer + record_firstname
syscall
push qword [rbp + st_output_descriptor]
call write_newline
add rsp, 8
jmp record_read_loop
finished_reading:
mov rax, sys_exit
mov rdi, 0
syscall
Exercise 8 - Modifying structured data
modify-records.asm
%include "linux.asm"
%include "record-defs.asm"
section .data
input_file_name: db "test.dat", 0
output_file_name: db "testout.dat", 0
section .bss
record_buffer: resb record_size
st_input_descriptor equ -8
st_output_descriptor equ -16
section .text
global _start
extern read_record, write_record
_start:
mov rbp, rsp
sub rsp, 16
mov rax, sys_open
mov rdi, input_file_name
mov rsi, 0
mov rdx, 0o666
syscall
mov [rbp + st_input_descriptor], rax
mov rax, sys_open
mov rdi, output_file_name
mov rsi, 0o101
mov rdx, 0o666
syscall
mov [rbp + st_output_descriptor], rax
loop_begin:
push qword [rbp + st_input_descriptor]
push record_buffer
call read_record
add rsp, 16
cmp rax, record_size
jne loop_end
inc qword [record_buffer + record_age]
push qword [rbp + st_output_descriptor]
push record_buffer
call write_record
add rsp, 16
jmp loop_begin
loop_end:
mov rdi, rax
mov rax, sys_exit
syscall