2

I am following along with a tutorial online which provides code written in NASM Syntax. The code provided is:

global _start

_start:
    sub esp, 4
    mov [esp], byte 'H'
    mov [esp+1], byte 'e'
    mov [esp+2], byte 'y'
    mov [esp+3], byte '!'
    mov eax, 4    ; sys_write system call
    mov ebx, 1    ; stdout file descriptor
    mov ecx, esp  ; pointer to bytes to write
    mov edx, 4    ; number of bytes to write
    int 0x80      ; perform system call
    mov eax, 1    ; sys_exit system call
    mov ebx, 0    ; exit status is 0
    int 0x80

I have tried to translate this into AT&T syntax. I have written the following:

.global _start

_start:
    sub $4, %esp
    movb $'H', (%esp)
    movb $'e', 1(%esp)
    movb $'y', 2(%esp)
    movb $'!', 3(%esp)
    mov $4, %eax
    mov $1, %ebx
    mov %esp, %ecx
    mov $4, %edx
    int $0x80
    mov $1, %eax
    mov $0, %ebx
    int $0x80

However when I compile and execute this code, I get:

./build.sh: line 35: 1159 Segmentation fault    (core dumped) ./$FILE
139

Is my translation from NASM to AT&T correct? And if so, is there something I am missing in terms of being able to read/write from the memory allocated for the Stack? (Permissions etc.)


Build Info:

Building on Arch Linux for a 64-bit system

build.sh script:

function usage { 
    echo $'\nUsage: ./build.sh -f [filename]\n'
    echo $' -f  The filename of the source file to be compiled without the file extension.\n'
}

if [ $# -lt 1 ]; then
    echo "No input files specified!"
    usage
    exit 1
fi

while [ $# -gt 0 ]; do
    case "$1" in
        -f)
            shift
            FILE=$1
            shift
            ;;
        *)
            echo "Please use -f to specify the input file!"
            usage
            exit 1
            ;;
    esac
done

if [ ! -f $FILE.s ]; then
    echo "$FILE.s doesn't exist!"
    usage
    exit 1
fi

gcc -c $FILE.s -o $FILE.o
ld $FILE.o -o $FILE
./$FILE
echo $?
Matty2532
  • 23
  • 4
  • 2
    How do your assemble and execute your code? Please show your `build.sh` (which should be part of a good [mcve] anyway. – fuz Jul 24 '20 at 16:55
  • Did you build this as 64-bit code, so you're truncating the 64-bit stack pointer in RSP by only using the low half? Porting a tutorial on the fly only works if you already know enough about how to build + run, and any possible deeper issues, to get it right. Just changing syntax should be fine if you know the right build commands, though, unlike trying to port 32 to 64-bit, or using a Linux tutorial on Mac or Windows or something. – Peter Cordes Jul 24 '20 at 17:03
  • Anyway, this is either a duplicate of [Assembling 32-bit binaries on a 64-bit system (GNU toolchain)](https://stackoverflow.com/q/36861903) or it's something else not visible without the build commands, in which case should be closed because of the lack of a [mcve]. (Did the NASM version work for you? If not, perhaps you're using WSL1 on Windows, which doesn't support 32-bit code.) – Peter Cordes Jul 24 '20 at 17:05
  • 2
    I have downvoted your question because it does not contain a [mcve] and thus doesn't admit a precise diagnosis. Will retract my downvote once you provide the requested build script. – fuz Jul 24 '20 at 17:07
  • 1
    @fuz I have provided the requested build.sh script, as well as some more general information about the system that I am building on – Matty2532 Jul 24 '20 at 17:18
  • Ok, then yes it's a duplicate, and JCWasmx86's answer is appropriate. If you want 64-bit code, find a 64-bit tutorial. You have to already know both to port a 32-bit tutorial to 64-bit. But it should work ok to syntax-port a 32-bit tutorial as long as you build 32-bit executables from it. – Peter Cordes Jul 24 '20 at 17:26
  • Yes, this seems to be the case. Glad I could get it resolved. The issue arose because the tutorial uses NASM and so I didn't know about the --32 flag until just now. I have a question though. I have written code before ([link](https://github.com/MattYoung-UoY/x86_tutorials_davy_wybiral/blob/master/tut1/ex2.s)) which was compiled in the way that caused this issue, but it still worked. This issue only arose once I started to use the stack to hold characters for printing. So how did this previous code sucessfully run if I was using a 32-bit interrupt? – Matty2532 Jul 24 '20 at 17:33
  • @Matty2532 Thanks! Please provide such details up front in the future! – fuz Jul 24 '20 at 17:34
  • Or should the above question be posted as a separate question? – Matty2532 Jul 24 '20 at 17:37
  • @Matty2532 I have added another duplicate that addresses this question. TL;DR: the `int $0x80` API is available in 64 bit processes, but it obviously only takes 32 bit addresses. Addresses that don't fit (e.g. addresses on the stack) are truncated, causing the issues you observe. WSL goes a bit further and doesn't support `int $0x80` at all. – fuz Jul 24 '20 at 17:52

1 Answers1

4

You compiled it as 64 bit code, although int 0x80 is a 32 bit interrupt. What I used to compile:

as --32 inAssembly.s &&ld -o executable -m elf_i386 a.out.

as --32 inAssembly.s makes the gnu assembler assume a 32 bit architecture, emitting a 32-bit objectfile.

ld -o executable -m elf_i386 a.out invokes the linker, setting the target to elf_i386 and puts the linked file into executable.

JCWasmx86
  • 3,473
  • 2
  • 11
  • 29
  • Alternatively, `gcc -m32 -nostdlib -nostartfiles program.s` is pretty simple, too. – fuz Jul 24 '20 at 17:35
  • I only need -m32 for it to work. Is there any particular reason you're using -nostdlib? Seems a bit strange as I am only compiling assembly and not c code. – Matty2532 Jul 24 '20 at 17:50
  • 1
    @Matty2532 `-nostartfiles` leaves out the C runtime initialisation that provides `_start` and calls `main`, `-nostdlib` does not link in the libc (C library). As you don't use any libc symbols, this doesn't make a difference, but it could if you were to use any symbols with the same name as libc symbols. – fuz Jul 24 '20 at 17:53