4

I'm just getting started with assembly and I wanted to create a simple program that adds two numbers and prints the result

This is what I have so far:

.globl main
   .type main, @function
main:
   movl $14, %eax
   movl $10, %ebx
   add %eax, %ebx
call printf

From my understanding here is what's happening line by line

Line 1: I'm creating a label main that can be accessed by the linker

Line 2: I'm specifying the type of label main to a function

Line 3: I begin my definition of main

Line 4: I store the numeric value 14 into the general register eax

Line 5: I store the numeric value 10 into the general register ebx

Line 6: I add the values at eax and ebx and store the result in ebx

Line 7: I call the function printf(here's where I get confused)

How do I specify what value at which register gets printed?

Also, how do I complete this program? Currently when run, the program results in a segmentation fault.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Stephen Thomas
  • 79
  • 1
  • 1
  • 5
  • 5
    First of all you need to learn how to *use* [`printf`](http://en.cppreference.com/w/c/io/fprintf), for example by experimenting with some C programs. Then you need to learn how functions are called, what gets passed in registers (little to nothing) and what goes on the stack. I suggest you look at the generated code from some C programs to see how it is done. – Some programmer dude Jul 19 '18 at 23:30
  • 1
    Look at compiler output for a C function that calls printf. http://gcc.godbolt.org/, and [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116). – Peter Cordes Jul 19 '18 at 23:43
  • You can't just send a number to `printf`. Even if the calling convention says that `ebx` is the first function argument, it will try to use that as a pointer to a format string. You must provide a format string, and the value to put in it, according to the calling convention, which may place arguments in registers, or on the stack. – Weather Vane Jul 19 '18 at 23:49
  • 2
    That first line is more like "assigns label `main` global visibility (for linking)", the word "create" is for my taste too strong there, I would use that one for the third line instead, that's where the label `main` points in memory in the machine code, just at the first byte of `movl $14..` instruction opcode (and that's how you create local labels as well, which don't need `.globl` at all). – Ped7g Jul 20 '18 at 08:12

1 Answers1

10
SECTION .data

    extern printf
    global main

fmt:
    db "%d", 10, 0

SECTION .text

main:
    mov     eax, 14
    mov     ebx, 10
    add     eax, ebx

    push    eax
    push    fmt
    call    printf

    mov     eax, 1
    int     0x80

Unfortunately I don't know which compiler/assembler you are using, and I'm not familiar with at&t syntax so I have given you a working example in Intel style x86 for Nasm.

$ nasm -f elf32 test.s -o test.o
$ gcc test.o -m32 -o test
$ ./test
24

In order to use printf you need to actually push the arguments for it onto the stack, I do this here in reverse order (push the last arguments first):

push    eax
push    fmt

EAX contains the result of add eax, ebx and the label 'fmt' is an array of chars: "%d\n\0" (%d format, newline, null terminator).

After calling printf you need to actually exit your program with the exit system call, otherwise (at least for me) the program will segfault AFTER printf even though it worked and you won't see the result.

So these two lines:

mov    eax, 1
int    0x80

are performing the sys_exit system call by placing the ordinal of exit on x86 (1) into EAX, and then invoking interrupt 0x80, this exits the program cleanly.

user6567423
  • 353
  • 4
  • 9
  • 1
    The calling convention requires 16-byte alignment before a `call`. One more dummy `push` would do it, or `sub esp, 4` on function entry. (4 total pushes including the `call` that reached `main`) `printf` happens to work with an under-aligned stack, but for example glibc's `scanf` segfaults with a misaligned stack even when scanning integers (at least the x86-64 version does: [scanf throws Segmentation fault (core dumped) when using nasm + gcc on linux 64bit](https://stackoverflow.com/q/51070716)). I wouldn't be surprised if 32-bit glibc `scanf` is the same if glibc is compiled with `-msse2 – Peter Cordes Jul 20 '18 at 02:05
  • This isn't x86-64, I made that clear, therefore the stack is perfectly aligned with two 4 byte pushes. There is several things missing from this post including fixing the stack after the call to printf (as per calling convention). But I will leave that up to the OP to research and learn about the stack, after-all he only asked for a working example - not an explanation on the stack. – user6567423 Jul 20 '18 at 15:53
  • 1
    No, 32-bit needs *three* 4-byte pushes before you can make an ABI-compliant `call` with 16-byte alignment. I only mentioned x86-64 because that's a case where I know a glibc function actually crashes if you violate the ABI. In 32-bit you may be able to get away with it. `gcc -mpreferred-stack-boundary=4` isn't just a good idea, it's the law (since maybe 2005 or 2010, not sure when it officially changed, but see [Stack alignment on x86](https://stackoverflow.com/posts/comments/32895192) for example). – Peter Cordes Jul 21 '18 at 21:28
  • Holy what lol, okay then, I guess they changed the x86 ABI because of SSE instructions that might need 16 byte alignment? Or rather it says here that it might have something to do with performance implications and/or support for the __m128 type: stackoverflow.com/questions/49391001/… Thanks for pointing this out to me though I never realized this happened. – user6567423 Jul 23 '18 at 18:41
  • I did some research, but couldn't find anything official about when it changed. I added a section at the bottom of [Why does System V / AMD64 ABI mandate a 16 byte stack alignment?](https://stackoverflow.com/q/49391001) (which you linked in a previous version of your last comment, but then broke the link.) – Peter Cordes Jul 23 '18 at 19:00
  • hahaha woops, I made a typo in the comment and it wouldn't let me edit the comment anymore (too old I guess?) so I copy-pasted it to fix the error... I guess I broke the link in the process: https://stackoverflow.com/questions/49391001/why-does-system-v-amd64-abi-mandate-a-16-byte-stack-alignment But yeah it appears like it happened in gcc 4.2 from my research, and it seems to be so that opcodes or features that may require 16 byte alignment will always work and there will never be a need to ensure the stack is aligned. – user6567423 Jul 23 '18 at 20:19
  • 1
    It is possible to exit gracefully from the program by putting the return code for your program in eax, e.g. `mov eax, 0` and then just exitting from main. with `ret`. this is why in c we do `return 0;` to exit from our programs. – Lennon McLean Mar 24 '21 at 11:57