How to make a simple Hello World using C conventions?

Asked

Viewed 319 times

2

Knowing a little of the conventions in C and the generalization of code that it can provide us, I tried to implement my "Hello World":

global main
extern printf

section .data
helloworld db 'Hello World',0

section .text
main:
    push helloworld
    call printf
    ret

Soon followed by nasm -fwin64 test.asm and gcc test.obj. The compilation is perfect. I don’t know much about Assembly (just a bit of higher-level languages), so I don’t see what’s wrong. What happens to my program? Why does this happen? How to fix this?

PS: Machine running Windows 8, 64-bit Intel i3 CPU, and Linker Mingw64. By simple, I mean "no Winapi conventions".

  • In this link there is a example for Windows 64 bit environment.

1 answer

4

Every function must have its own stack frame, this is part of the function convention. So, how main is a function, you must start the stack frame with push ebp and mov ebp, esp and end with leave. The reason you have to do this with main it’s because she nay is the main function of your executable. Actually there will be another, provided by the compiler, which will call main. In Windows would be WinMain while on Linux is the _start. So the code needs to be like this:

global main
extern printf

section .data
helloworld db 'Hello World',0 

section .text
main:
    push    ebp
    mov     ebp, esp
    push    helloworld
    call    printf
    leave              ; Equivalente a "pop ebp"
    mov     eax, 0     ; Retorne 0
    ret

But you are also interested in using code in x64. This way it won’t work. The convention for passing arguments in numerical x86 (or pointers as is the case) is to add them to the stack. But in the case of x64 the convention changes to use the following registers for the first 6 parameters: rdi, rsi, rdx, rcx, r8 and r9. From the seventh onwards, you will need to stack as usual. So the code looks like this:

global main
extern printf

section .data
helloworld db 'Hello World',0 

section .text
main:
    push    rbp
    mov     rbp, rsp
    mov     rdi, helloworld
    call    printf
    leave
    mov     rax, 0     ; Retorne 0
    ret

It is worth noting, however, that different systems or different architectures can change these conventions. You will always need to choose exactly what your target system is and search its conventions.

  • @OP, you better review the examples, declare Hello World in the helloworld variable, globalize the main (Undefined Reference to Winmain) and declare the printf as external, otherwise it does not associate the functions with the binary file.

  • @Lucashenrique had considered as snipets and not complete code compilables. Anyway fixed.

  • @OP I’m sorry, I didn’t know that I considered it so. Anyway, I forgot to say in the other comment that he is giving crash. nasm -fwin64 test.asm -o test.o gcc test.o a = "The program has stopped working", etc etc etc etc. I wonder what happens?

  • With the second code? I will check as soon as I get home with a real compiler.

  • what was its result?

  • @OP I’ve been looking on the internet and saw that there are only 4 arguments: rcx, Rdx, R8 and R9. The rest is pushto the stack. This is correct?

  • @Lucashenrique In fact the convention differs and is highly system dependent. In Windows are used rcx, Rdx, R8 and R9. But on all other Unix systems, 6 registers are used. Anyway, the best way is to define a system and a target architecture, and then search the convention of this pair. Here a good table: List of x86 Calling Conventions, note the last lines.

  • @OP as is possible Rdi = helloworld, sends that Rdi only has 64 bits?

  • @Lucashenrique You pass to the function a pointer to that memory, not the data itself. Then you get rdi = &helloworld.

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.