8

I would like to compile the Hello World NASM example on windows.

I've pasted the code above into a main.asm file, and compiled it into an obj file with this command:

nasm -fwin32 .\main.asm -o main.obj

After that I wanted to compile this obj file to an exe, like this:

g++ .\main.obj -o main.exe -m32

But I get this error:

C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/../lib/gcc/i686-w64-mingw32/8.1.0/../../../../i686-w64-mingw32/lib/../lib/libmingw32.a(lib32_libmingw32_a-crt0_c.o):crt0_c.c:(.text.startup+0x39): undefined reference to `WinMain@16'

What do I miss? How is it possible to fix this error?

Iter Ator
  • 8,226
  • 20
  • 73
  • 164
  • After `section .text` insert two lines `global _main` and `_main:`. That should at least make it link, but it doesn't work for me using `wine`. Maybe it works on windows. [Here is a simpler one that works](https://stackoverflow.com/a/1023600/547981). – Jester Oct 24 '18 at 11:49
  • It compiles this way, but nothing is printed to the console – Iter Ator Oct 24 '18 at 13:32

1 Answers1

8

That Hello World program is trying to create the PE import table manually. In order for that to work, you need to instruct the linker carefully (the PE sections are not tied to PE directories, idata is just a name).
Further assumptions are made in that source (e.g. the base address of the image and the need for the CRT).

Honestly, it's just nonsense. Use the linker properly, like Jester shown.
Being really honest, that whole Wikipedia section is just informational at best.
Long story short: never use Wikipedia as a programming tutorial.

EDIT: The x86-64 Linux example on the Wikipedia page has been updated by Peter Cordes; the others may still be misleading.

A bit of brief theory

You can create a 32-bit Windows console program mainly in two ways:

  1. Use the C run time (CRT)
    This lets you use the common C functions (above all printf).
    There are two ways to use the CRT:

    1. Statically
      The object files resulting from the compilation of the CRT source code are linked with the object file resulting from the compilation/assembling of your source code.
      The CRT code is embedded entirely in your application.
      In this scenario your main function (main/WinMain/DllMain and unicode variants) is being called by the CRT that runs first by a properly set PE entry-point).
      In order to use this method you need the CRT object files, these can be found with Visual Studio or MinGW (to name twos).
      The order of execution is: The Windows loader calls your PE entry-point, this is set to something like _mainCRTStartup that initialize the CRT and the CRT calls your main function.
    2. Dynamically
      The CRT main dll is msvcrt.dll for the version shipped with Windows installation or msvcrtXX0.dll for the version shipped with Visual Studio installation (where XX depends on the VS version).
      The CRT dll has the initialization and tear down code in the DLL entry point so by just putting it in the PE import table the CRT is automagically managed. The order of execution is: The Windows loader loads your PE dependencies, including the CRT DLL (that got initialised as per above) and then call your PE entry-point.
  2. Use only the Windows API
    The Windows API are the OS exposed functions, these are what the CRT implementation ends up calling.

    You can use the Windows API and the CRT (the common scenario is for a graphical application to have the CRT statically linked and use WinMain as the entry-point - where the Windows APIs are intermixed with C utility functions) or the Windows API alone.
    When using them alone you get a smaller, faster and easy to make executable.

To use 1.1 you need the CRT object files and these are usually shipped with a compiler (they once were shipped with the Windows SDK but now that VS is free Microsoft moved them in the VS package - fair but VS is orders of magnitude more heavy than the SDK).
1.2 and 2 don't need these object files.
Note however that compilers/assemblers/linkers compatibility may be a nasty beast, especially the .lib machinery for linking external APIs (basically libs file are a way to make the linker find the functions that will be resolved by the loader at runtime - i.e. those defined in an external DLL).

Hello, world!

Method 2

First, to write Hello, World! using the method 2., see this other answer of mine.
It was written when a linker was available in the Windows SDK, today I use GoLink.
It is a minimalist, very easy to use, linker.
One key point of it is that it doesn't need the .lib files, instead you can pass it the path of the DLLs where the external functions reside.

The NASM command is the same, to link use:

 golink /console /entry main c:\windows\system32\kernel32.dll hello.obj -fo hello.exe

Untested - optionally add /largeaddressaware if you code can handle that

That example is for 64-bit programming, it's more involved than a 32-bit one but may be useful anyway.

Method 1.2

This is what the Wikipedia article is trying to use.
Before analyzing that specific code, let me show how I'd write it:

BITS 32

GLOBAL _main

EXTERN printf
EXTERN exit

SECTION .text

_main:
 push strHelloWorld 
 call printf 
 add esp, 04h
 
 push 0
 call exit 
 

SECTION .data

 strHelloWorld db "Hello, world!", 13, 10, 0

This is pretty straightforward compared to the Wiki's one.
To make an executable:

nasm -fwin32 helloworld.asm -o helloworld.obj
golink /console /entry _main c:\windows\system32\msvcrt.dll helloworld.obj -fo helloworld.exe

The Wikipedia's code is creating an .idata sections that stores the PE Import Address Table.
This is a silly move, the linker is used to generate that table based on the dynamic dependencies of the object files.
To make that program link we need to:

  1. Tell the linker that the base address is 0x400000. This can be done with any linker (for golink use /base 0x400000).
  2. Tell the linker that the entry-point is where the .text section starts. I don't know if link.exe can take .text as a valid symbol name or if allows to specify an entry-point relative to .text but that seems very unlikely. Golink won't allow for that. In short a label is probably missing.
  3. Tell the linker to make the Import directory points to the .idata section. I'm not aware of any linker that would allow for that (though it may exists).

In short, forget about it.

Method 1.1

This is what the link Jester pointed out is using.
The assembly code is the same as for 1.2 but you use MinGW for linking.

Community
  • 1
  • 1
Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124
  • `R_X86_64_64` links into a PIE executable without complaint, because Linux's dynamic linker supports runtime fixups as long as the slot is wide enough for an arbitrary address. The real problem is with YASM, where `mov rsi, Hello` assembles to a `R_X86_64_32` relocation which can only work in a non-PIE executable. (I hadn''t realized NASM and YASM were different on this; fortunately I used AT&T / GAS for my main example in [Difference between movq and movabsq in x86-64](https://stackoverflow.com/q/40315803)) – Peter Cordes Oct 28 '18 at 00:42
  • @PeterCordes Not everywhere. In my CentOS 7 I have to explicitly set the dynamic linker to the 64-bit version (under `/lib64`) or it won't load (It assembles and links fine though). – Margaret Bloom Oct 28 '18 at 09:11
  • But that's not because of the relocation, is it? If I run `ld` directly, on my system it makes a static executable. But if I want to make a dynamic executable running `ld` directly, instead of linking with `gcc -nostdinc` or `gcc -nostdlib`, I do need to override the dynamic linker path last I checked, because the default doesn't work for modern x86-64 multilib systems. Did you add a `-pie` arg to `ld`? Wikipedia's build command is making a static executable with `ld wiki-hello.o -o wiki-hello`, no ld.so involved. `ld -pie` breaks regardless of relocations. – Peter Cordes Oct 28 '18 at 14:18
  • @PeterCordes I've just checked again. I think you are right, it's the `-pie` that breaks the default dyn loader. I must have confused `-pie` with `-shared`. Thank you! Actually I have remarked that happens only for PIE in my notes but somehow skipped it XD – Margaret Bloom Oct 28 '18 at 17:28
  • Yup, `-pie` is normally a good idea, and the gcc default in many distros. But `ld` defaults haven't changed, and the build commands listed in Wikipedia don't make a PIE. (But the 32-bit code seems to assume 32-bit LD). non-PIE is a very good idea for 32-bit code, though, and even in x86-64 non-PIE makes indexing static arrays more efficient. I'd recommend non-PIE for beginners learning asm, so everything Just Works when they do `mov rsi, symbol` in any assembler, even if it uses a 32-bit immediate. Grokking PIC *after* you know the basics is easy. – Peter Cordes Oct 28 '18 at 17:34
  • But the Wikipedia examples don't use `-shared` either in their build commands. `-pie` implies a dynamic executable, just like linking any dynamic libraries. Any 64-bit dynamic executable on a modern distro will break if you don't specify `--dynamic-linker /lib64/ld-linux-x86-64.so.2`, whether it's PIE or not. – Peter Cordes Oct 28 '18 at 17:38
  • The only build command I see that's wrong is `gcc -o name name.o` for making a 32-bit dynamic executable that calls printf. It omits `-m32`, apparently written for a 32-bit distro. And it implies that simply changing the registers to RAX will make it work as 64-bit code, but of course the calling convention changes, too. – Peter Cordes Oct 28 '18 at 17:39
  • there're two different `.lib` `s : static libraries and dll reference files. And say in masm you'll have both – Алексей Неудачин Oct 28 '18 at 18:07
  • 1
    I updated the [Wikipedia x86-64 Linux NASM Hello World example](https://en.wikipedia.org/wiki/X86_assembly_language#%22Hello_world!%22_program_for_64-bit_mode_Linux_in_NASM_style_assembly) to not suck. – Peter Cordes May 15 '20 at 20:47
  • @PeterCordes ahahah nice! :) – Margaret Bloom May 16 '20 at 16:52
  • Thanks for the great answer. Have you considered editing the wikipedia article to include your simplified Windows NASM hello world code? I think it would be an improvement. – RedDragonWebDesign Sep 24 '20 at 19:24
  • @RedDragonWebDesign I've only been a reader of Wikipedia so far :) I don't feel comfortable editing it. – Margaret Bloom Sep 25 '20 at 17:50