That'll help. I think the main issue is not "cleaning up the stack" after the call to printf.
I don't know how far you've gotten in asm, Serge... The "call" instruction stores its return address on the stack. and the "ret" instruction expects to find it there. "_main" is called, so the stack looks like:
main's caller
Then we "push msg".
main's caller
msg
Then "call main" puts our local return address - the byte right after the call, push 0 now, but that's wrong - and the "ret" in printf takes it off. We never really "see" it. But the "msg" is still on the stack. The Windows API uses the "stdcall" calling convention, in which the called function *does* remove the parameters from the stack (the function ends in "ret 4" or "ret 8" or... "ret N" - number of bytes, not number of parameters). But we need to do it upon return - "add esp, 4". (more for more parameters) We could also pop a "junk" register "pop ecx" is common. Now the stack just has:
main's caller
on it, ready for the "ret". But you do a "push 0". That would be correct if you were ending with "call _exit" (which should also work - declare it "extern") - "_exit" expects an exitcode on the stack. But the usual convention is that the return value goes in eax. So "mov eax, 0" there, instead. (xor eax, eax is shorter... and more "asmish") You might want to try "mov eax, 42" or so. In Linux, we can see the exitcode of the last program with "echo $?" - dunno if it works on MacOS...
Getting back to your string... it should be zero-terminated. There's probably a zero there by chance, so it might work anyway, but isn't "right". Also, "\n" isn't going to work between regular quote marks, but it will if you use back-apostrophes - '`' (right under the tilde on my keyboard). A trick Nasm recently learned... the classic way was msg db "hello world", 10, 0.
A pitfall that awaits you... the calling convention requires that certain registers be preserved across called functions - that includes main. ebx, esi, edi, and ebp must be preserved. You don't change 'em, but if you do, in future code, put 'em back. Note that this means that calling C functions *will* trash ecx and edx behind your back!
Lessee, printf promotes all floats to double precision (8 bytes) . Functions that return float usually do so on the top of the FPU stack.
... I may know more... I forget...
Good start getting it to compile and link without complaint!
Best,
Frank