Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undefined reference to `WinMain' when compiling Nasm program on windows (MinGW)

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?

like image 790
Iter Ator Avatar asked Oct 24 '18 10:10

Iter Ator


1 Answers

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.

like image 123
Margaret Bloom Avatar answered Oct 04 '22 06:10

Margaret Bloom