From b652bc257ae631e77781291bd48111381e41cd79 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Thu, 15 Dec 2022 11:28:54 -0800 Subject: [PATCH] add x86 ISA assembly --- hello-amd64.s | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ hello-x86-32.s | 47 +++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 hello-amd64.s create mode 100644 hello-x86-32.s diff --git a/hello-amd64.s b/hello-amd64.s new file mode 100644 index 0000000..26d3baa --- /dev/null +++ b/hello-amd64.s @@ -0,0 +1,76 @@ +# gold linker has smallest binary size others probably can emit smaller +# binaries with custom linker scripts. Their default ones +# are not optimized for hello world programs +# as --64 hello-amd64.s && ld.gold -s -n -o hello a.out +.data # section declaration +msg: + .string "All your codebase is belong to us\n" # output string + +len = . - msg # length of output string + +.text # section declaration + # we must export the entry point to the ELF linker or + .global _start # loader. They conventionally recognize _start as their + # entry point. Use ld −e foo to override the default. + +# https://stackoverflow.com/questions/3683144/linux-64-command-line-parameters-in-assembly +# https://wiki.cdot.senecacollege.ca/wiki/X86_64_Register_and_Instruction_Quick_Start +square: + pushq %rbp # Save rbp - this must be restored at end of call + movq %rsp, %rbp # Update base pointer from current stack pointer + movq %rdi,-8(%rbp) # Move 1st argument (rdi) into stack memory + # This is not strictly necessary here, but + # is done as a way to demonstrate generic handling + # of arguments + movq -8(%rbp), %rax # Move stack memory into rax for multiplication + imulq -8(%rbp), %rax # Do multiplication + # The above 3 instructions could be done with the + # following 2 instructions instead in such a simple + # case: + #movq %rdi, %rax # Move 1st argument (rdi) to rax for processing + + #imulq %rax, %rax # Do multiplication + popq %rbp # Restore rbp for return (eax/rax has return val) + retq # Return + +_start: + +# write our string to stdout +# https://man7.org/linux/man-pages/man2/syscall.2.html +# https://filippo.io/linux-syscall-table/ +# https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl + movq $1,%rax # system call number (sys_write) + movq $1,%rdi # first argument: file handle (stdout) + movq $msg,%rsi # second argument: pointer to message to write + movq $len,%rdx # third argument: message length + syscall # call kernel + # argc is stored in (%rsp) + # this is an eightbyte according to table 3.9 + # of the System V AMD64 psABI + # https://gitlab.com/x86-psABIs/x86-64-ABI + # + # It is a bit questionable here whether the + # upper 32 bits of rdi are cleared when moving + # into edi, but this instruction is generated + # from compilers, which leads me to "yes". + # Documentation also states this (with some exceptions): + # + # When executing MOV Reg, Sreg, the processor + # copies the content of Sreg to the 16 least + # significant bits of the general-purpose register. + # The upper bits of the destination register + # are zero for most IA-32 processors (Pentium + # Pro processors and later) and all Intel 64 + # processors, with the exception that bits 31:16 + # are undefined for Intel Quark X1000 processors, + # Pentium and earlier processors. + # + # Above language pulled from MOV documentation + # in Vol 2B, Chapter 4: + # https://www.intel.com/content/dam/develop/public/us/en/documents/325462-sdm-vol-1-2abcd-3abcd.pdf + # + movl (%rsp),%edi # See: https://wiki.cdot.senecacollege.ca/wiki/X86_64_Register_and_Instruction_Quick_Start + callq square # Square our argc, result in %eax + movq %rax,%rdi # mov %eax to the first syscall argument (exit code) + movq $60,%rax # system call number (sys_exit) + syscall # call kernel and exit diff --git a/hello-x86-32.s b/hello-x86-32.s new file mode 100644 index 0000000..c47803a --- /dev/null +++ b/hello-x86-32.s @@ -0,0 +1,47 @@ +# as --32 hello-x86-32.s && ld.gold -m32 -s -n -o hello a.out +.data # section declaration +msg: + .string "All your codebase is belong to us\n" # our dear string + +len = . - msg # length of our dear string + +.text # section declaration + # we must export the entry point to the ELF linker or + .global _start # loader. They conventionally recognize _start as their + # entry point. Use ld −e foo to override the default. + +# https://stackoverflow.com/questions/3683144/linux-64-command-line-parameters-in-assembly +# https://wiki.cdot.senecacollege.ca/wiki/X86_64_Register_and_Instruction_Quick_Start +square: + pushl %ebp # Save ebp - this must be restored at end of call + movl %esp, %ebp # Update base pointer from current stack pointer + # 8 bytes above ebp is our first parameter + # (4 bytes above is our return address) + # In this case, _start is not explictly adding + # our parameter to the stack as that is being + # done prior to us being exec'd in the first place + # There is a good diagram of this in the calling + # convention section on https://www.cs.virginia.edu/~evans/cs216/guides/x86.html + movl 8(%ebp), %eax # Move stack memory into eax for multiplication + imull 8(%ebp), %eax # Do multiplication + popl %ebp # Restore rbp for return (eax/rax has return val) + ret # Return + +_start: + +# Linux syscalls for 32 bit: +# https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_32.tbl +# write our string to stdout + movl $4,%eax # system call number (sys_write) + movl $1,%ebx # first argument: file handle (stdout) + movl $msg,%ecx # second argument: pointer to message to write + movl $len,%edx # third argument: message length + int $0x80 # call kernel and exit + # argc is stored in (%esp) + movl (%esp),%edi # See: https://wiki.cdot.senecacollege.ca/wiki/X86_64_Register_and_Instruction_Quick_Start + # The above instruction is part of the calling convention, + # so left here, but it's useless/unnecessary and can be removed + call square # Square our argc, result in %eax + movl %eax,%ebx # mov %eax to the first syscall argument (exit code) + movl $1,%eax # system call number (sys_exit) + int $0x80 # call kernel