I'm learning how to code in x86-64 assembly, using the GNU assembler (AT&T syntax). My more long term goal was to learn assembly for some microcontroller platforms, like AVR, but x86-64 seemed like a good place to start, since it is easy to setup my environment and there is lots of documentation.
I want to get low-level, and write code that is purely assembly and not just inline code to a C program. I'm not necessarily against calling external functions from a dynamic library, though, if I can do so with the standard ABI.
At the moment I'm just doing some exercises I've made up myself. I might look for some online programming challenges later. The most complicated program I have written so far is call "sine-ascii" and outputs asterisks to the terminal, following a sinusoidal curve.
/* sine-ascii.s gcc -pie -nostdlib sine-ascii.s -o sine-ascii SPDX-FileCopyrightText: 2022 Christopher HowardSPDX-License-Identifier: GPL-3.0-or-later */ .global _start .data /* table generated with guile scheme: (map (lambda (n) (inexact->exact (round (+ 20 (* 20 (sin (* (/ 6.28 24) n))))))) (iota 24 0)) */ margin_table: .byte 20, 25, 30, 34, 37, 39, 40, 39 .byte 37, 34, 30, 25, 20, 15, 10, 6 .byte 3, 1, 0, 1, 3, 6, 10, 15 /* single byte buffer for printing */ char_buffer: .byte 0 .text /* print whatever character is in char_buffer */ print_char: mov $1,%rax /* write syscall */ mov $1,%rdi /* file descriptor */ leaq char_buffer(%rip),%rsi /* buffer to write */ mov $1,%rdx /* number of bytes */ syscall ret /* print N spaces, where N is in rdi */ print_spaces: cmp $0,%rdi je 1f dec %rdi movb $0x20,char_buffer(%rip) push %rdi call print_char pop %rdi jmp print_spaces 1: ret /* print asterisk and CRLF */ print_marker: movb $0x2a,char_buffer(%rip) call print_char movb $0x0d,char_buffer(%rip) call print_char movb $0x0a,char_buffer(%rip) call print_char ret /* loop through margin_table, printing margin_table[n] number of spaces, with an asterisk and CRLF at the end of each line. */ table_loop: push %rbx mov $0,%rbx 1: cmp $24,%rbx je 2f lea margin_table(%rip),%rcx mov $0,%rdi mov (%rbx,%rcx,1),%dil call print_spaces call print_marker inc %rbx jmp 1b 2: pop %rbx ret /* program entry point */ _start: call table_loop mov $0x3c,%rax /* exit syscall */ mov $0,%rdi /* exit code */ syscall
Here is the program output:
$ ./sine-ascii * * * * * * * * * * * * * * * * * * * * * * * *
After first coding this program, I learned about position independent coding, which makes it possible for the operating system to reuse more memory space. So I recoded it to its current form, for compatibility with the -pie option (Position Independent Executable). The basic difference is that in PIC, all the memory references, including function calls, must be made relative to the position of the calling code (or, the code right after it... something like that) rather than using an absolute memory address. This allows the code to execute correctly regardless of what memory address it is loaded into.
One interesting note about PIE, however: without linking with -pie, the program above has no external dependencies (other than the linux kernel system calls), but including -pie adds a dynamic dependency to ld-linux-x86-64.so.2, or the equivalent interpreter on your system.
These are some Web resources I found very helpful for x86-64 assembly programming:
=> https://www.felixcloutier.com/x86/
=> https://www.cs.uaf.edu/2017/fall/cs301/reference/x86_64.html
These articles about relocation and PIC are also quite interesting:
=> https://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries/
=> https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/
=> https://eli.thegreenplace.net/2011/11/11/position-independent-code-pic-in-shared-libraries-on-x64
The folks at the #asm Libera IRC channel (irc.libera.chat) are also very helpful.
text/gemini
This content has been proxied by September (ba2dc).