Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

how to end 32bit winapi app

291 views
Skip to first unread message

firr

unread,
Sep 29, 2015, 4:11:45 AM9/29/15
to
I crafted such small win32 bit app (and i am happy with it)

extern _ExitProcess@4
extern _MessageBoxA@16

global _start

section .text

_start:

push 0x0 ; MB_OK
push message ; title
push message ; Messgage
push 0 ; Reserved=0
call _MessageBoxA@16
;We should exit the process otherwise it may cause "access violation"
push 0 ;load 0 to stack
call _ExitProcess@4 ;Call ExitProcess

section .data

message db "Hello world",0


;c:\nasm-2.11.08\nasm -fwin32 helloworld.asm
;ld -s -o helloworld.exe helloworld.obj -lkernel32 -luser32

though i wonder if i could end this prog in other normal way (like by ret) not by calling ExitProcess
Is there a reliable way?

Bernhard Schornak

unread,
Sep 29, 2015, 1:41:45 PM9/29/15
to
firr wrote:


> though i wonder if i could end this prog in other normal way (like by ret) not by calling ExitProcess
> Is there a reliable way?


Set EAX/RAX to the desired return code, clean up the stack
and return:

incl %eax # RC = 1
movq 0xD8(%rsp), %rbp
movq 0xE0(%rsp), %r15
movq 0xE8(%rsp), %rdi
movq 0xF0(%rsp), %rbx
addq $0xF8, %rsp
ret


Works in all (> 10) of my programs...


Greetings from Augsburg

Bernhard Schornak

firr

unread,
Sep 29, 2015, 2:24:00 PM9/29/15
to
this is not 32 bit.. clean up the stack what you mean?

_main:

ret

wonder wit what calling convention this main point may be called... that would be probably cdecl (as more universall) more than stdcall.. thus there no need to clen the stack would be present.. but i dont know it is somewhere documented? Is stack above
the given point meaningfull and has some meaningfull data?

Bernhard Schornak

unread,
Sep 30, 2015, 1:05:48 PM9/30/15
to
My programs all begin with


.p2align 5,,31
.globl _WinMain
.def _WinMain; .scl 2; .type 32; .endef
_WinMain:subq $0xF8, %rsp
movq %rbp, 0xD8(%rsp)
movq %r15, 0xE0(%rsp)
movq %rdi, 0xE8(%rsp)
movq %rbx, 0xF0(%rsp)
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
switch on 3D look
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
movq %rcx, %rbp # RSI = Hinstance
movl %r9d, %ebx # RBX = window state
call _IComCt
...

and end with

...
incl %eax # RC = 1
2:movq 0xD8(%rsp), %rbp
movq 0xE0(%rsp), %r15
movq 0xE8(%rsp), %rdi
movq 0xF0(%rsp), %rbx
addq $0xF8, %rsp
ret


where "2:" is an entry point for failed API calls (e.g. if my
private window class could not be registered, window creation
fails, etc.).

The source of a working WinMain (DatTools):

http://tinyurl.com/oyosa2g

To convert the code: Use the 32 bit portion of the registers,
subtract four rather than eight byte for each stack entry and
use the 32 bit version of MinGW to compile.

The latest (=2009) 32 bit version of my old OS/2 stuff can be
downloaded here:

http://tinyurl.com/p7cffbk

Keep in mind: PUSH is just an (extremely restricted!) form of
MOV... ;)

Bernhard Schornak

unread,
Sep 30, 2015, 1:20:52 PM9/30/15
to
Bernhard Schornak wrote:


> The latest (=2009) 32 bit version of my old OS/2 stuff can be
> downloaded here:
>
> http://tinyurl.com/p7cffbk


Oops... This link works:

http://tinyurl.com/phopes4

firr

unread,
Sep 30, 2015, 2:36:07 PM9/30/15
to
does it all mean i just can ret or no?
I dont know where to find the info
if the entry point here is like winmain
the winmain takes some agruments wonder if here the args are passed on stack and if this is stdecl or cdcl (winmain is stdcall
this would means that i should empty the stack before ret

where is the info; more complicated the PE has a field to set if this app is guisibsystem or console subsystem, wonder if the eventuall passed args depends on this

Bernhard Schornak

unread,
Sep 30, 2015, 3:36:21 PM9/30/15
to
firr wrote:


> does it all mean i just can ret or no?


Yes, a simple RET will do.


> I dont know where to find the info


Google for

"Microsoft® Win32® Programmer's Reference"

or

"WIN32 Help"

Nowadays, MS provides an online help, but you have to know which
API function you are searching for.


> if the entry point here is like winmain
> the winmain takes some agruments wonder if here the args are passed on stack
> and if this is stdecl or cdcl (winmain is stdcall
> this would means that i should empty the stack before ret


Windows follows stdcall - ECX and EDX are volatile (not restored
on exit), EAX is used to pass return codes / results. The called
function has to clean up the stack before returning. That means:
It has to set ESP to the address where the return address can be
found. All registers except EAX, ECX, EDX have to be restored if
they were altered.


> where is the info; more complicated the PE has a field to set if this app is
> guisibsystem or console subsystem, wonder if the eventuall passed args
> depends on this


Taken from Microsoft® Win32® Programmer's Reference:


>>> The WinMain function is called by the system as the initial entry point for
>>> a Win32-based application.
>>>
>>> int WINAPI WinMain(
>>>
>>> HINSTANCE hInstance, // handle to current instance
>>> HINSTANCE hPrevInstance, // handle to previous instance
>>> LPSTR lpCmdLine, // pointer to command line
>>> int nCmdShow // show state of window
>>> );
>>>
>>>
>>> Parameters
>>>
>>> hInstance
>>>
>>> Identifies the current instance of the application.
>>>
>>> hPrevInstance
>>>
>>> Identifies the previous instance of the application. For a Win32-based
>>> application, this parameter is always NULL. If you need to detect
>>> whether another instance already exists, create a named mutex using
>>> the CreateMutex function. If the GetLastError function returns
>>> ERROR_ALREADY_EXISTS, another instance of your application exists
>>> (it created the mutex).
>>>
>>> lpCmdLine
>>>
>>> Points to a null-terminated string specifying the command line for the
>>> application.
>>>
>>> nCmdShow
>>>
>>> Specifies how the window is to be shown. This parameter can be one of
>>> the following values:
>>>
>>> Value Meaning
>>>
>>> SW_HIDE Hides the window and activates another window.
>>> SW_MINIMIZE Minimizes the specified window and activates the
>>> top-level window in the system's list.
>>> SW_RESTORE Activates and displays a window. If the window is
>>> minimized or maximized, Windows restores it to its
>>> original size and position (same as SW_SHOWNORMAL).
>>> SW_SHOW Activates a window and displays it in its current
>>> size and position.
>>> SW_SHOWMAXIMIZED Activates a window and displays it as a maximized
>>> window.
>>> SW_SHOWMINIMIZED Activates a window and displays it as an icon.
>>> SW_SHOWMINNOACTIVE Displays a window as an icon. The active window
>>> remains active.
>>> SW_SHOWNA Displays a window in its current state. The active
>>> window remains active.
>>> SW_SHOWNOACTIVATE Displays a window in its most recent size and
>>> position. The active window remains active.
>>> SW_SHOWNORMAL Activates and displays a window. If the window is
>>> minimized or maximized, Windows restores it to its
>>> original size and position (same as SW_RESTORE).
>>>
>>>
>>> Return Values
>>>
>>> If the function succeeds, terminating when it receives a WM_QUIT message, it
>>> should return the exit value contained in that message's wParam parameter. If
>>> the function terminates before entering the message loop, it should return 0.
>>>
>>> Remarks
>>>
>>> WinMain initializes an application, displays its main window, and then enters
>>> a message retrieval-and-dispatch loop that is the top-level control structure
>>> for the remainder of the application's execution. The message loop terminates
>>> when a WM_QUIT message is received. At that point, WinMain exits the application,
>>> returning the value passed in the WM_QUIT message's wParam parameter. If WM_QUIT
>>> was received as a result of calling PostQuitMessage, the value of wParam is the
>>> value of the PostQuitMessage function's nExitCode parameter. For more information,
>>> see Creating a Message Loop.
>>>
>>> See Also
>>>
>>> CreateMutex, DispatchMessage, GetMessage, PostQuitMessage, TranslateMessage


You can ignore all input parameters except hInstance - it is re-
quired for some functions you might want to call later on...

firr

unread,
Sep 30, 2015, 4:51:36 PM9/30/15
to
so in one place you write ret will sufice then later you write that this is stdcall and got 4 input params

here on this page
http://ccm.net/faq/1559-compiling-an-assembly-program-with-nasm

->>

Under the windows the convention is not the same. The main function is not available under Windows and must be replaced by WinMain.

If your entry point is "_start" or "main", it should be changed to "_WinMain @ 16" and change the "ret" at the end of the procedure to "ret 16":
section .text
global _WinMain@16

_WinMain@16:
mov eax, 0
ret 16

<--

so... ?
As im not sure i dont know who to belive..
I am not quite sure if this entry poinyt in
"PE compiled assembly " is this winmain routine or this is some another (as some compilers are said to begin with maincrtstartup or something that later prepare and calls inner routine called winmain (im not sure), so is this entry point maincrtstartup or winmain or yet
smthng other?

Bernhard Schornak

unread,
Oct 1, 2015, 12:56:29 PM10/1/15
to
firr wrote:


> so in one place you write ret will sufice then later you write that this is stdcall and got 4 input params


Yes, I did. I also said you can silently ignore all input parameters
except hInstance which might be required later on.

This is assembler, so you can ignore input parameters not handled by
your program. Most things are handled by Windows automatically, e.g.
minimised / maximised windows are treated by the OS itself. When the
user terminates your program, you only have to care about the proper
content of ESP/RSP - cleaning up parameters is a task of the caller,
not of the callee. If I remember corrrectly, only Pascal changes the
rules for cleaning up, so the callee becomes responsible.


> here on this page
> http://ccm.net/faq/1559-compiling-an-assembly-program-with-nasm
>
> ->>
>
> Under the windows the convention is not the same. The main function is not available under Windows and must be replaced by WinMain.
>
> If your entry point is "_start" or "main", it should be changed to "_WinMain @ 16" and change the "ret" at the end of the procedure to "ret 16":
> section .text
> global _WinMain@16
>
> _WinMain@16:
> mov eax, 0
> ret 16
>
> <--
>
> so... ?


As I told before: I write code for the AS included in MinGW. If your
code is written for another assembler+linker+compiler package, it is
up to you to learn how to feed additional information to that combi-
nation.


> As im not sure i dont know who to belive..
> I am not quite sure if this entry poinyt in
> "PE compiled assembly " is this winmain routine or this is some another (as some compilers are said to begin with maincrtstartup or something that later prepare and calls inner routine called winmain (im not sure), so is this entry point maincrtstartup or winmain or yet
> smthng other?


Assembler is not a religion, so there is nothing to believe - what I
posted is code used in more than ten working 64 bit Windows programs
and about 20 working 32 bit OS/2 programs. You just have to *launch*
them to see that this code works as I described it.

It is your task to get in touch with your tools (to learn how to use
them...), so do not blame people who try to help you for not knowing
your working environment. What I can do is providing code to solve a
problem. I surely will not read manuals for Nasm or other assemblers
to do your homework.

(When I started to write assembler code, I had nothing more than the
instruction manuals provided by iNTEL and the A86 assembler manual -
when I encountered a problem, I tried until the code worked properly
and then went on to do the next step...)

Bartc

unread,
Oct 1, 2015, 1:59:23 PM10/1/15
to
On 30/09/2015 21:47, firr wrote:

> Under the windows the convention is not the same. The main function is not available under Windows and must be replaced by WinMain.
>
> If your entry point is "_start" or "main", it should be changed to "_WinMain @ 16" and change the "ret" at the end of the procedure to "ret 16":
> section .text
> global _WinMain@16
>
> _WinMain@16:
> mov eax, 0
> ret 16
>
> <--
>
> so... ?
> As im not sure i dont know who to belive..
> I am not quite sure if this entry poinyt in
> "PE compiled assembly " is this winmain routine or this is some another (as some compilers are said to begin with maincrtstartup or something that later prepare and calls inner routine called winmain (im not sure), so is this entry point maincrtstartup or winmain or yet
> smthng other?

From what I can figure out, WinMain is not part of Windows, and needs
to be provided by an extra library, I think normally supplied by the
high level language. (I don't know what you have to do to make WinMain
available using a program which is 100% assembly, other than write an
entry-point function yourself - not called WinMain - which obtains the
expected arguments and then calls your 'WinMain' entry.)

Since I switched from using gcc as a linker, which takes care of all
these details and provides the libraries, and used only LD, I now need
to work at a lower level. This is useful as as you get to know what
normally does what.

So, directly using LD as a linker:

* You don't automatically get 'main' as an entry point; you have to
specify it.

* You can't use main(nargs, args) in a program (either C or ASM), you
just get a basic function call to the label 'main'. (I think that
main(nargs, args) is made possible using a special library function).

* And you can't use Winmain(hinst, hprevinst, cmdline, cmdshow). Sure,
you can add a label WinMain and specify that as the entry point, but the
'system', whatever that means, won't know to push hinst, hprevinst etc
before calling it. (This is in Win64, so it doesn't need all the @N
stuff on the name.)

Working in a bare environment like this, I /think/ that you have to:

* Provide an entry function, eg. 'main' or 'start' that takes no
arguments. Specify that as the entry point.

* Then call any OS or MSVCRT library functions to obtain the input
parameters needed. I use GetCommandLIneA() to obtain the command line
parameters (but in a form that needs to be chopped up into separate
tokens to give you what you get with main(nargs, args).)

* And I /think/ you can just do: mov rax,0 and ret to exit (mov eax,0 on
x86-32). You might be able to use ExitProcess (I haven't tried it). Or
exit() from the C runtime; these are handy to exit from anywhere in your
program, without having to get all the way to the end of your
entry-point function.

This is a minimal working Win64 program (Nasm syntax):

segment .text
global main
main:
mov rax,0
ret

After assembling to program.obj (nasm -fwin64 program.asm), it can be
linked using LD with:

ld -e main -oprogram.exe program.obj

With Golink, it's:

golink /entry main program.obj


--
Bartc

Bernhard Schornak

unread,
Oct 1, 2015, 4:15:36 PM10/1/15
to
Bartc wrote:


> From what I can figure out, WinMain is not part of Windows, and needs to be provided by an extra
> library, I think normally supplied by the high level language.


Wrong. The cited text was taken from Win32 Help, Microsoft's *official*
manual for the entire Win32 API. WinMain() is the main function for all
Windows programs. Might be different if you step back and write command
line tools rather than GUI programs...

Bartc

unread,
Oct 1, 2015, 5:16:34 PM10/1/15
to
What it says in your link isn't quite right. It says that main is not
available, and that WinMain should be called instead. In fact either can
be used.

Also, you're implying that WinMain is part of Windows. It's not; you
have to write it yourself as part of your application, in the same way
that C programs need a main() function to be created.

But what I was talking about wasn't exactly WinMain itself (and if your
read the MS docs, 'WinMain' and its parameters are a convention). I
meant what happens in between the OS being told to execute your program,
and control arriving at the function that you've declared as the entry
point, whether it be WinMain or something else.

What is it that assembles those four arguments and pushes them (32 bits)
or loads them to registers (64 bits)? It is that bit of code that I
don't believe is provided by Windows, but by some magic somewhere that
is usually glossed over by compilers and linkers.

(In fact, if I compile a program containing only an empty WinMain
function with just return 0, no main, and dissemble the result, I can
see a block of instructions labelled main, that appear to first call
__main, then sets up the rcx, rdx, r8 and r9 registers, and which then
calls WinMain. Where did that come from?

It appears to be part of the executable, and must have been arranged by
the C compiler. That doesn't seem to happen with bare ASM programs.

Also in the executable were references to WinMainCRTStartup and
mainCRTStartup; I don't know what they do. Plus a pile of other stuff.

When, however, I look inside the executable of my minimal Windows
program containing just those two instructions, then the disassembly
contains exactly those two instructions! Nothing else.)

--
Bartc

firr

unread,
Oct 1, 2015, 8:02:18 PM10/1/15
to
fellow on stack sait that this PE entry point (call it for example _start not to confuse with main or winmain) has no args passed, each process in windows has its own process structure (called PEB or something),
fellow says that windows shell puts the ewentual arguments somewhere there - he is probably right.. i will ned to read more on this in spare time

Bartc

unread,
Oct 2, 2015, 6:04:26 AM10/2/15
to
I've no idea where those arguments reside exactly. Ie. 'hInstance',
'CmdLine', and 'CmdShow', the parameters of WinMain.

I don't think 'hPrevInstance' is that useful. The PEB block doesn't look
that useful either, full of complicated stuff that I can't see the need for.

But, in a bare ASM program that doesn't have access to these special
compiler-supplied startup routines, I think some of those WinMain
parameters can be obtained with GetCommandLineA(), GetModuleHandle(),
and GetStartupInfoA().

(Looking at how gcc does this stuff, it appears to load these values
from low offsets within the exe image in memory. It does that after
calling __main. Whether __main sets up that info, or that's where
Windows keeps them anyway, I don't know.)


--
Bartc



Bernhard Schornak

unread,
Oct 2, 2015, 1:50:28 PM10/2/15
to
Bartc wrote:


> On 01/10/2015 21:06, Bernhard Schornak wrote:
>> Bartc wrote:
>>
>>> From what I can figure out, WinMain is not part of Windows, and needs
>>> to be provided by an extra
>>> library, I think normally supplied by the high level language.
>>
>>
>> Wrong. The cited text was taken from Win32 Help, Microsoft's *official*
>> manual for the entire Win32 API. WinMain() is the main function for all
>> Windows programs. Might be different if you step back and write command
>> line tools rather than GUI programs...
>
> What it says in your link isn't quite right. It says that main is not available, and that WinMain
> should be called instead. In fact either can be used.
>
> Also, you're implying that WinMain is part of Windows. It's not; you have to write it yourself as
> part of your application, in the same way that C programs need a main() function to be created.


Now I understand what you mean, and, of course, WinMain() is
not provided by microsoft - it is the *entrypoint* into your
program and you have to write it yourself. Nevertheless, you
have to name it "WinMain" and you have to take care of a few
other conventions.


> But what I was talking about wasn't exactly WinMain itself (and if your read the MS docs, 'WinMain'
> and its parameters are a convention). I meant what happens in between the OS being told to execute
> your program, and control arriving at the function that you've declared as the entry point, whether
> it be WinMain or something else.
>
> What is it that assembles those four arguments and pushes them (32 bits) or loads them to registers
> (64 bits)? It is that bit of code that I don't believe is provided by Windows, but by some magic
> somewhere that is usually glossed over by compilers and linkers.
>
> (In fact, if I compile a program containing only an empty WinMain function with just return 0, no
> main, and dissemble the result, I can see a block of instructions labelled main, that appear to
> first call __main, then sets up the rcx, rdx, r8 and r9 registers, and which then calls WinMain.
> Where did that come from?


That's automatically done by the task scheduler whenever you
start a new process. It looks for free memory, assigns it to
the process, loads the code and data sections defined in the
program header. Finally, the passed start parameters are set
and the WinMain (provided by the program!) is started as new
process.

The __main is inserted by the C compiler, because it assumes
that your code uses C functions like printf() or invokes C's
built-in error handling facilities if something went wrong.


> It appears to be part of the executable, and must have been arranged by the C compiler.


Yes.


> That doesn't seem to happen with bare ASM programs.


Maybe. I prefer AS over most assemblers because I don't have
to learn a lot of magic spells to tinker with code...


> Also in the executable were references to WinMainCRTStartup and mainCRTStartup; I don't know what
> they do. Plus a pile of other stuff.


As their names suggest, they are functions to initialise the
C runtime stuff (e.g. load the required DLLs, initialise the
C error handler, et cetera).


> When, however, I look inside the executable of my minimal Windows program containing just those two
> instructions, then the disassembly contains exactly those two instructions! Nothing else.)


If you use MinGW, much more happens under the hood. WinDebug
shows tons of code which will be executed before the program
code you fed MinGW with. All of those extra instructions are
initialisation code for the C runtime and never will be used
if you wrote your program in pure assembler. It is something
you have to live with if you prefer the most comfortable AS.
If you are Arabic and ready to learn thousand and one spells
to invoke a few magic trinkets, you might consider to switch
to Fasm, Masm or Nasm, but you have to read stuff in reverse
direction... ;)

Bernhard Schornak

unread,
Oct 2, 2015, 2:50:46 PM10/2/15
to
Bartc wrote:


> On 02/10/2015 00:58, firr wrote:
>> W dniu czwartek, 1 października 2015 23:16:34 UTC+2 użytkownik Bartc napisał:
>
>> fellow on stack sait that this PE entry point (call it for example _start not to confuse with main
>> or winmain) has no args passed, each process in windows has its own process structure (called PEB
>> or something),
>> fellow says that windows shell puts the ewentual arguments somewhere there - he is probably
>> right.. i will ned to read more on this in spare time
>
> I've no idea where those arguments reside exactly. Ie. 'hInstance', 'CmdLine', and 'CmdShow', the
> parameters of WinMain.
>
> I don't think 'hPrevInstance' is that useful. The PEB block doesn't look that useful either, full of
> complicated stuff that I can't see the need for.


hPrevInstance is an Win16 artifact (and always is NULL),
the PEB block is an *internal* structure of the OS which
might be changed by MS whenever they want to. Might be a
good idea to leave it alone, even if it holds some stuff
which might be usable, see

https://support.microsoft.com/en-us/kb/106385


> But, in a bare ASM program that doesn't have access to these special compiler-supplied startup
> routines, I think some of those WinMain parameters can be obtained with GetCommandLineA(),
> GetModuleHandle(), and GetStartupInfoA().


In 32 bit Windows, they are stored on stack @ -0x08[ESP]
through -0x14[ESP], in 64 bit programs they're passed in
RCX, RDX, R8 and R9. (This should be valid for all kinds
of assemblers, except they alter registers between start
and calling WinMain).


> (Looking at how gcc does this stuff, it appears to load these values from low offsets within the exe
> image in memory. It does that after calling __main. Whether __main sets up that info, or that's
> where Windows keeps them anyway, I don't know.)


__main just initialises the C runtime and nothing else.

firr

unread,
Oct 2, 2015, 3:21:03 PM10/2/15
to
i get good quick info on my question and this bring ahe acknowledged of the suspected things with some slight surprise too

http://blogs.msdn.com/b/oldnewthing/archive/2011/05/25/10168020.aspx
>>
WinMain is the conventional name for the user-provided entry point in a Win32 program. Just like in 16-bit Windows, where the complicated entry point requirements were converted by language-provided startup code into a call to the the user's WinMain function, the language startup code for 32-bit programs also does the work of converting the raw entry point into something that calls WinMain (or wWinMain or main or _wmain).

The raw entry point for 32-bit Windows applications has a much simpler interface than the crazy 16-bit entry point:

DWORD CALLBACK RawEntryPoint(void);
The operating system calls the function with no parameters, and the return value (if the function ever returns) is passed to the ExitThread function. In other words, the operating system calls your entry point like this:

...
ExitThread(RawEntryPoint());
/*NOTREACHED*/
Where do the parameters to WinMain come from, if they aren't passed to the raw entry point?

The language startup code gets them by asking the operating system. The instance handle for the executable comes from GetModuleHandle(NULL), the command line comes from GetCommandLine, and the nCmdShow comes from GetStartupInfo. (As we saw before, the hPrevInstance is always NULL.)

If you want to be hard-core, you can program to the raw entry point. Mind you, other parts of your program may rely upon the work that the language startup code did before calling your WinMain. For example, the C++ language startup code will run global constructors before calling into WinMain, and both C and C++ will initialze the so-called security cookie used as part of stack buffer overrun detection. Bypass the language startup code at your peril.

Bonus chatter: Notice that if you choose to return from your entry point function, the operating system passes the return value to ExitThread and not ExitProcess. For this reason, you typically don't want to return from your raw entry point but instead want to call ExitProcess directly. Otherwise, if there are background threads hanging around, they will prevent your process from exiting.
<<

the surprise is such exit thread code, instead of exit process, i dont know the rationale behind it

firr

unread,
Oct 2, 2015, 3:21:09 PM10/2/15
to
to be honest tyour answers bring more confusion than clear answers.. you probably have good attitude but i think you should improve on it

Bartc

unread,
Oct 2, 2015, 3:21:16 PM10/2/15
to
On 02/10/2015 19:45, Bernhard Schornak wrote:
> Bartc wrote:

>> But, in a bare ASM program that doesn't have access to these special
>> compiler-supplied startup
>> routines, I think some of those WinMain parameters can be obtained
>> with GetCommandLineA(),
>> GetModuleHandle(), and GetStartupInfoA().
>
>
> In 32 bit Windows, they are stored on stack @ -0x08[ESP]
> through -0x14[ESP], in 64 bit programs they're passed in
> RCX, RDX, R8 and R9. (This should be valid for all kinds
> of assemblers, except they alter registers between start
> and calling WinMain).

I didn't find that to be that case. At least, the results are not what I
would expected (like hinst not being the same as cmdline!). For example,
I get this output:

hinst=fffd9000 hprev=401000 cmdline=fffd9000 cmdshow=401000

after running this 64-bit code (main is the primary entry point, but
it's the same with WinMain, or anything):

global main
extern printf
main:
push r9
sub rsp,32
mov r9,r8
mov r8,rdx
mov rdx,rcx
mov rcx,KK201
call printf
add rsp,40
mov rax,0
ret

KK201: db 'hinst=%x hprev=%x cmdline=%x cmdshow=%x',13,10,0

I can't see how it could work as you say anyway: WinMain is optional, so
how does the process loader know that I have a start function that
correspond to WinMain, and not one like C's main, or C's main(nargs,
args), or a different one entirely.

--
Bartc

Bartc

unread,
Oct 2, 2015, 4:06:31 PM10/2/15
to
On 02/10/2015 20:09, firr wrote:
> W dniu piątek, 2 października 2015 12:04:26 UTC+2 użytkownik Bartc napisał:

>> But, in a bare ASM program that doesn't have access to these special
>> compiler-supplied startup routines, I think some of those WinMain
>> parameters can be obtained with GetCommandLineA(), GetModuleHandle(),
>> and GetStartupInfoA().

> i get good quick info on my question and this bring ahe acknowledged of the suspected things with some slight surprise too
>
> http://blogs.msdn.com/b/oldnewthing/archive/2011/05/25/10168020.aspx
>>>
> WinMain is the conventional name for the user-provided entry point in a Win32 program. Just like in 16-bit Windows, where the complicated entry point requirements were converted by language-provided startup code into a call to the the user's WinMain function, the language startup code for 32-bit programs also does the work of converting the raw entry point into something that calls WinMain (or wWinMain or main or _wmain).

That's a good article. It asks the right sorts of questions and gives
straight answers. Most other sources of this information seem to want to
retain some mystery about how these things work.

As it happens, I figured out how much of this stuff is done, but it's
nice to have it confirmed.

--
Bartc

firr

unread,
Oct 2, 2015, 5:06:47 PM10/2/15
to
ye, i agree

as to articles there s a plenty on various levels, such low level stuff is the best

Bernhard Schornak

unread,
Oct 2, 2015, 5:51:57 PM10/2/15
to
Bartc wrote:


> On 02/10/2015 19:45, Bernhard Schornak wrote:
>> Bartc wrote:
>
>>> But, in a bare ASM program that doesn't have access to these special
>>> compiler-supplied startup
>>> routines, I think some of those WinMain parameters can be obtained
>>> with GetCommandLineA(),
>>> GetModuleHandle(), and GetStartupInfoA().
>>
>>
>> In 32 bit Windows, they are stored on stack @ -0x08[ESP]
>> through -0x14[ESP], in 64 bit programs they're passed in
>> RCX, RDX, R8 and R9. (This should be valid for all kinds
>> of assemblers, except they alter registers between start
>> and calling WinMain).
>
> I didn't find that to be that case. At least, the results are not what I would expected (like hinst
> not being the same as cmdline!). For example, I get this output:
>
> hinst=fffd9000 hprev=401000 cmdline=fffd9000 cmdshow=401000


00401000 is an address in the code section, FFFD9000 seems to
be a bunch of flags (looks like window creation flags).

BTW: My code stores hInstance as first entry in the program's
working data area provided by my DBE. It's always 00400000 in
all programs. Which is the start address assigned to each GUI
process (might be different for command line apps).


> after running this 64-bit code (main is the primary entry point, but it's the same with WinMain, or
> anything):
>
> global main
> extern printf
> main:
> push r9
> sub rsp,32
> mov r9,r8
> mov r8,rdx
> mov rdx,rcx
> mov rcx,KK201
> call printf
> add rsp,40
> mov rax,0
> ret


main() and WinMain() are two different things. Looking at the
emitted parameters, it seems something was running before the
listed code, otherwise

RCX = 0000000000400000 (hInstance)
RDX = 0000000000000000 (hPrevInstance)
R8 = 0000000000401000 (command line address)
R9 = 00000000000000** (show state)

If this is the entire program, 00401000 might be your command
line address (in the .data section). Windows uses a page size
of 4096 byte...


> KK201: db 'hinst=%x hprev=%x cmdline=%x cmdshow=%x',13,10,0
>
> I can't see how it could work as you say anyway: WinMain is optional, so how does the process loader
> know that I have a start function that correspond to WinMain, and not one like C's main, or C's
> main(nargs, args), or a different one entirely.


All programs *have to* begin at a specified offset within the
code segment. The assembler/compiler/linker knows it and will
write the first byte of the first instruction exactly to that
offset. As a matter of fact, it does not matter if the main()
has a name or not as long as you write code with a hex editor
like Wolfgang Kern. As long as you use any program to compile
your code, you *have to* use the name defined by that program
for the main function used as entrypoint of your program. For
most compilers, it is main() for command line programs or the
discussed WinMain() for GUI programs.

If you encountered problems with those few lines of code, you
should check the assembler, compiler, linker (and programmer)
if they do the proper things. One link in that chain seems to
be weak. ;)

As posted before:

.p2align 5,,31
.globl _WinMain
.def _WinMain; .scl 2; .type 32; .endef
_WinMain:subq $0xF8, %rsp
movq %rbp, 0xD8(%rsp)
movq %r15, 0xE0(%rsp)
movq %rdi, 0xE8(%rsp)
movq %rbx, 0xF0(%rsp)
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
switch on 3D look
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
movq %rcx, %rbp # RSI = Hinstance
movl %r9d, %ebx # RBX = window state
call _IComCt
...


The entire code can be found here: http://tinyurl.com/oyosa2g

If you scroll down you can see that both, RBP as well as RBX,
are used later on. And both hold proper data. Otherwise, this
program did fail to create its main window...

Bartc

unread,
Oct 2, 2015, 6:37:10 PM10/2/15
to
On 02/10/2015 22:42, Bernhard Schornak wrote:
> Bartc wrote:

>> after running this 64-bit code (main is the primary entry point, but
>> it's the same with WinMain, or
>> anything):
>>
>> global main
>> extern printf
>> main:
>> push r9
>> sub rsp,32
>> mov r9,r8
>> mov r8,rdx
>> mov rdx,rcx
>> mov rcx,KK201
>> call printf
>> add rsp,40
>> mov rax,0
>> ret
>
>
> main() and WinMain() are two different things.

No, they're not! The names are arbitrary. I happened to used 'main'
because that's the default used by my IDE to link, otherwise there's
some fiddly typing to do.

I could have used 'xyzzy' and I would have got the same results.

Or are you saying that if I used 'WinMain', then this, somehow,
magically makes the linker insert extra set-up code into my program?

> All programs *have to* begin at a specified offset within the
> code segment. The assembler/compiler/linker knows it and will
> write the first byte of the first instruction exactly to that
> offset. As a matter of fact, it does not matter if the main()
> has a name or not as long as you write code with a hex editor
> like Wolfgang Kern. As long as you use any program to compile
> your code, you *have to* use the name defined by that program
> for the main function used as entrypoint of your program. For
> most compilers, it is main() for command line programs or the
> discussed WinMain() for GUI programs.

You keep mentioning compilers, perhaps that is causing confusion. There
is no compiler involved here, just bare assembly code. (I mentioned gcc
as an example of how it worked when a HLL was involved.)

> .p2align 5,,31
> .globl _WinMain
> .def _WinMain; .scl 2; .type 32; .endef
> _WinMain:subq $0xF8, %rsp
> movq %rbp, 0xD8(%rsp)
> movq %r15, 0xE0(%rsp)
> movq %rdi, 0xE8(%rsp)
> movq %rbx, 0xF0(%rsp)

What's the entry point (ie. the 'raw' entry point given to the linker)
of this program? I suspect it is not WinMain. (The link that firr posted
at blogs.msdn.com suggested that none of this setup has been done at the
raw entry point.)

> movq %rcx, %rbp #
> RSI = Hinstance

(This is also confusing; should rbp be rsi or vice versa?)

--
Bartc

Bernhard Schornak

unread,
Oct 3, 2015, 9:55:03 AM10/3/15
to
Bartc wrote:


> On 02/10/2015 22:42, Bernhard Schornak wrote:
>> Bartc wrote:
>
>>> after running this 64-bit code (main is the primary entry point, but
>>> it's the same with WinMain, or
>>> anything):
>>>
>>> global main
>>> extern printf
>>> main:
>>> push r9
>>> sub rsp,32
>>> mov r9,r8
>>> mov r8,rdx
>>> mov rdx,rcx
>>> mov rcx,KK201
>>> call printf
>>> add rsp,40
>>> mov rax,0
>>> ret
>>
>>
>> main() and WinMain() are two different things.
>
> No, they're not! The names are arbitrary. I happened to used 'main' because that's the default used
> by my IDE to link, otherwise there's some fiddly typing to do.
>
> I could have used 'xyzzy' and I would have got the same results.


It seems we are comparing apples and eggs - it was nice if you
tell us which tools you are using to create executables...


> Or are you saying that if I used 'WinMain', then this, somehow, magically makes the linker insert
> extra set-up code into my program?


Linkers only read object files (or archives) and store them in
a form the OS can handle (e.g. PE file format for Windows). It
requires no magic, but a lot of definitions and conventions to
make a linker emit proper executables.


>> All programs *have to* begin at a specified offset within the
>> code segment. The assembler/compiler/linker knows it and will
>> write the first byte of the first instruction exactly to that
>> offset. As a matter of fact, it does not matter if the main()
>> has a name or not as long as you write code with a hex editor
>> like Wolfgang Kern. As long as you use any program to compile
>> your code, you *have to* use the name defined by that program
>> for the main function used as entrypoint of your program. For
>> most compilers, it is main() for command line programs or the
>> discussed WinMain() for GUI programs.
>
> You keep mentioning compilers, perhaps that is causing confusion. There is no compiler involved
> here, just bare assembly code. (I mentioned gcc as an example of how it worked when a HLL was
> involved.)


So you type your code into a hex editor and start that created
output file. In this case, you should know everything about PE
headers and how to tell your OS where it will find which parts
of your code (.text, .data, etc.). If so, I wonder why you ask
questions rather than to explain how these things work... :)


>> .p2align 5,,31
>> .globl _WinMain
>> .def _WinMain; .scl 2; .type 32; .endef
>> _WinMain:subq $0xF8, %rsp
>> movq %rbp, 0xD8(%rsp)
>> movq %r15, 0xE0(%rsp)
>> movq %rdi, 0xE8(%rsp)
>> movq %rbx, 0xF0(%rsp)
>
> What's the entry point (ie. the 'raw' entry point given to the linker) of this program? I suspect it
> is not WinMain. (The link that firr posted at blogs.msdn.com suggested that none of this setup has
> been done at the raw entry point.)
>
>> movq %rcx, %rbp #
>> RSI = Hinstance
>
> (This is also confusing; should rbp be rsi or vice versa?)


Why? As it is a 32 bit address, any of the old registers: EAX,
EBX, ECX, EDX, EBP, EDI, ESI will do. As long as you don't use
RSP as multipurpose register without saving its content first,
everything is fine.

Bartc

unread,
Oct 3, 2015, 11:10:12 AM10/3/15
to
On 03/10/2015 14:54, Bernhard Schornak wrote:
> Bartc wrote:

>> I could have used 'xyzzy' and I would have got the same results.
>
>
> It seems we are comparing apples and eggs - it was nice if you
> tell us which tools you are using to create executables...

I thought I'd mentioned them enough times**! For assembling prog.asm I
use nasm.exe 2.x:

nasm -fwin64 prog.asm

For linking I use ld.exe from mingw:

ld -m64 -e main -oprog.exe prog.obj

For this example, I get the same results from using golink.exe.

As far as I can see, there is nothing linked in that I don't know about.
(For the purpose of using printf for my test, I added in msvcrt.dll as
further input to the linker, but my code *only* calls printf().)

There seem to be two opinions in this thread:

(1) That there is some code inserted before my declared entry point that
handily gives me the documented parameters when using WinMain, or it
might be that the process loader of Windows sets them up before passing
control to my entry point. Or:

(2) Nothing of the sort happens. Whatever code is encountered at that
entry point is responsible for setting these things up. Since I'm
writing that code, then it's up to me.

Before I started messing about with this, I had no idea how it worked.
But my experiments, supported by firr's link, suggest that (2) is the case.

> So you type your code into a hex editor and start that created
> output file. In this case, you should know everything about PE
> headers and how to tell your OS where it will find which parts
> of your code (.text, .data, etc.). If so, I wonder why you ask
> questions rather than to explain how these things work... :)

I know little about PE formats (I've written to executable formats in
the past but am too old now for that routine stuff now). But I shouldn't
need to know.

All I'm inquiring about is how 'main(nargs,args)' and 'WinMain(a,b,c,d)'
actually work.

BTW I don't believe you've mentioned what tools you use to assemble, and
link, or otherwise create the executable, what other library functions
are added in, and what raw entry point is being used.

If (2) above is what actually happens, then probably there is some
hidden startup routine in your code that is confusing matters.

>>> movq %rcx, %rbp #
>>> RSI = Hinstance
>>
>> (This is also confusing; should rbp be rsi or vice versa?)
>
> Why? As it is a 32 bit address, any of the old registers: EAX,
> EBX, ECX, EDX, EBP, EDI, ESI will do. As long as you don't use
> RSP as multipurpose register without saving its content first,
> everything is fine.

It's confusing because the instruction copies rcx to rbp, yet the
comment talks about rsi!

(If this is a true WinMain function, with the correct startup code
behind it, then at this point I believe that rcx contains the hInstance
parameter. I suspect the comment is wrong.)

(** This will probably add to the confusion, but I use my own HLL
compiler. One of its targets is x64 source code in Nasm syntax. It uses
LD as the linker. Although the language can make use of imported C
runtime functions from MSVCRT.DLL, it doesn't link in hidden startup
routines.

I found that I couldn't just have a function called main, with
parameters (nargs, args), or one called Winmain() with hInstance etc.
They didn't work. I only got a basic entry point function with no
parameters. This is when I started doing experiments, and got involved
in the thread.

And, no, I don't wish to explicitly add in those mysterious startup
functions. I don't want any dependencies on parts of other languages and
especially not gcc.)

--
Bartc


firr

unread,
Oct 3, 2015, 1:10:21 PM10/3/15
to
too old for what and exactly why? (you have some thoughts that being old is especially
bad for learning PE? (it may be tru somewhat)

besides understanding PE and disassembly listings is imo essential (i may write a tutorial on this as this is not sooo hard imo)

the most essential thing is imo that (IINW) code section is raw copied under 0x0040 1000
then fired (used dlls are copied like 0x6000 0000 - 0x7fff ffff), stack 0x0023 0000 down below to 0x0003 0000 (probably im not quite sure), the space like 0x0050 0000 to 0x6000 0000 is for arrays

olly debug is of great usage here

Bartc

unread,
Oct 3, 2015, 4:10:34 PM10/3/15
to
On 03/10/2015 18:08, firr wrote:
> W dniu sobota, 3 października 2015 17:10:12 UTC+2 użytkownik Bartc napisał:

>> I know little about PE formats (I've written to executable formats in
>> the past but am too old now for that routine stuff now). But I shouldn't
>> need to know.
>>
>
> too old for what and exactly why? (you have some thoughts that being old is especially
> bad for learning PE? (it may be tru somewhat)

It's more a question of having less time to spend on something that is
not so interesting, and IMO not so useful to know about.

While I used to do *all* my own software (including compilers,
assemblers and linkers/loaders, because I had to), not I'm not that
interested in assemblers and linkers, which would be the only reason to
learn about object files and executable formats.

--
Bartc

Philip Lantz

unread,
Oct 3, 2015, 5:10:39 PM10/3/15
to
It sounds to me that you understand what is going on better than those
whom you are asking questions, so it's no wonder you're getting
confusing answers.

I think the following description will confirm what you already have
figured out:

For most people, who use WinMain or main as their entry point, the
linker "automatically"* links in some runtime initialization code that
sets up the appropriate arguments and calls the user-defined entry
point. The true entry point (the parameter to -e) is set to this startup
code. The user-defined function *does* need to be named WinMain or main,
because the startup code calls it by that name. (See below for which
name.)

* Of course it's not really automatic; there are some parameters passed
by the compiler invocation or IDE that control it. When you use ld
directly, you don't get this.

I don't know what the parameters to the true entry point code are, and
as far as I can tell, no one else here does, either, because they keep
confusing the user-defined (WinMain or main) entry point with the true
entry point. I'm sure it is documented, though, and you should be able
to google it without too much trouble (as long as you ignore everything
that insists that WinMain is actually the entry point). It's possible
that there are *no* parameters, and you have to actually execute system
calls to query for the instance handle and the command line (but I doubt
it).

Regarding whether the user-defined entry point is main or WinMain, this
is also controlled by the compiler invocation or IDE, based on whether
the user has specified that he is building a console application or a
windowed application. It's possible to open windows from a console
application and it's possible to open a console from a windowed
application, but Windows has tried to make it easier for people who are
doing the common thing.

In addition to changing the startup code, the selection of console vs.
windowed application is indicated in the PE header, so the OS knows
which it is when it loads the application; there are some differences in
the way the process is initialized, but I don't remember what they are.

Philip

Bartc

unread,
Oct 3, 2015, 6:56:04 PM10/3/15
to
On 03/10/2015 22:08, Philip Lantz wrote:

> For most people, who use WinMain or main as their entry point, the
> linker "automatically"* links in some runtime initialization code that
> sets up the appropriate arguments and calls the user-defined entry
> point. The true entry point (the parameter to -e) is set to this startup
> code. The user-defined function *does* need to be named WinMain or main,
> because the startup code calls it by that name. (See below for which
> name.)
>
> * Of course it's not really automatic; there are some parameters passed
> by the compiler invocation or IDE that control it. When you use ld
> directly, you don't get this.

While you're here, perhaps you might know something about a strange
problem I've just encountered with LD. If I create a minimal program
like this but calling ExitProcess:

global main
extern ExitProcess
main:
mov rax,0
call ExitProcess
; ret

and I try and link the 64-bit obj file from this using:

ld -e main -otest.exe test.obj ...kernel32.dll

I get:

ertr000004.o:(.rdata+0x0): undefined reference to
`_pei386_runtime_relocator'

I don't give it an object file called ertr00004.o, and there's no
dependency on _pei386... in kernel32.dll. It's OK with golink.exe or I
get rid of ExitProcess (or ExitThread which is the same).

It's just very odd!

--
Bartc

Philip Lantz

unread,
Oct 3, 2015, 10:56:36 PM10/3/15
to
Bartc wrote:

> While you're here, perhaps you might know something about a strange
> problem I've just encountered with LD. If I create a minimal program
> like this but calling ExitProcess:
>
> global main
> extern ExitProcess
> main:
> mov rax,0
> call ExitProcess
> ; ret
>
> and I try and link the 64-bit obj file from this using:
>
> ld -e main -otest.exe test.obj ...kernel32.dll
>
> I get:
>
> ertr000004.o:(.rdata+0x0): undefined reference to
> `_pei386_runtime_relocator'
>
> I don't give it an object file called ertr00004.o, and there's no
> dependency on _pei386... in kernel32.dll. It's OK with golink.exe or I
> get rid of ExitProcess (or ExitThread which is the same).
>
> It's just very odd!

I agree, it is odd. Sorry, I haven't a clue.

firr

unread,
Oct 4, 2015, 4:12:13 AM10/4/15
to
W dniu sobota, 3 października 2015 22:10:34 UTC+2 użytkownik Bartc napisał:
> On 03/10/2015 18:08, firr wrote:
> > W dniu sobota, 3 października 2015 17:10:12 UTC+2 użytkownik Bartc napisał:
>
> >> I know little about PE formats (I've written to executable formats in
> >> the past but am too old now for that routine stuff now). But I shouldn't
> >> need to know.
> >>
> >
> > too old for what and exactly why? (you have some thoughts that being old is especially
> > bad for learning PE? (it may be tru somewhat)
>
> It's more a question of having less time to spend on something that is
> not so interesting, and IMO not so useful to know about.
>

ye PE format has large 'boring barrier' (now its my 3rd attempt to it, spaned over a year or so) but is extremally important to know
about.. (when i will learn it yet a bit more i could write some tutorial post on it, then it will be easy to get)

Bernhard Schornak

unread,
Oct 4, 2015, 4:57:23 AM10/4/15
to
Philip Lantz wrote:


> I think the following description will confirm what you already have
> figured out:
>
> For most people, who use WinMain or main as their entry point, the
> linker "automatically"* links in some runtime initialization code that
> sets up the appropriate arguments and calls the user-defined entry
> point. The true entry point (the parameter to -e) is set to this startup
> code. The user-defined function *does* need to be named WinMain or main,
> because the startup code calls it by that name. (See below for which
> name.)
>
> * Of course it's not really automatic; there are some parameters passed
> by the compiler invocation or IDE that control it. When you use ld
> directly, you don't get this.
>
> I don't know what the parameters to the true entry point code are, and
> as far as I can tell, no one else here does, either, because they keep
> confusing the user-defined (WinMain or main) entry point with the true
> entry point. I'm sure it is documented, though, and you should be able
> to google it without too much trouble (as long as you ignore everything
> that insists that WinMain is actually the entry point). It's possible
> that there are *no* parameters, and you have to actually execute system
> calls to query for the instance handle and the command line (but I doubt
> it).


Sorry to disappoint you, but that's exactly what I wrote - or,
at least, what I had in mind while writing... ;)

https://msdn.microsoft.com/en-us/library/windows/desktop/ff381406%28v=vs.85%29.aspx
https://msdn.microsoft.com/en-us/library/f9t8842e.aspx
https://msdn.microsoft.com/en-us/library/bb918180.aspx
http://tinyurl.com/yan297f


> Regarding whether the user-defined entry point is main or WinMain, this
> is also controlled by the compiler invocation or IDE, based on whether
> the user has specified that he is building a console application or a
> windowed application. It's possible to open windows from a console
> application and it's possible to open a console from a windowed
> application, but Windows has tried to make it easier for people who are
> doing the common thing.


It is quite hard to get real information about this stuff, but
one article said that C-style main() is a stub hiding the call
to WinMain(). Input parameters probably are passed by the OS -
why should all programs waste time to retrieve parameters when
they are at hand while the program is launched?


> In addition to changing the startup code, the selection of console vs.
> windowed application is indicated in the PE header, so the OS knows
> which it is when it loads the application; there are some differences in
> the way the process is initialized, but I don't remember what they are.


A console application has no message queue and no mouse input.
Even if you can select some text with the mouse and there is a
scroll bar, those "features" are provided by the OS.

Bernhard Schornak

unread,
Oct 4, 2015, 4:57:23 AM10/4/15
to
Bartc wrote:


> On 03/10/2015 14:54, Bernhard Schornak wrote:
>> Bartc wrote:
>
>>> I could have used 'xyzzy' and I would have got the same results.
>>
>>
>> It seems we are comparing apples and eggs - it was nice if you
>> tell us which tools you are using to create executables...
>
> I thought I'd mentioned them enough times**! For assembling prog.asm I use nasm.exe 2.x:
>
> nasm -fwin64 prog.asm
>
> For linking I use ld.exe from mingw:
>
> ld -m64 -e main -oprog.exe prog.obj


Okay. I feed my assemler files to "windres.exe" and GCC:

windres DT5.rc DT5.o 2>zz.err

gcc -mwindows -m64 -s bas.S dt5.S cvt.S fld.S fmk.S fvw.S sca.S sef.S snb.S sol.S spn.S std.S
..\..\..\lib\libs\fh.a ..\..\..\lib\libs\mhm.a ..\..\..\lib\libs\olh.a ..\..\..\lib\libs\ssm.a
..\..\..\lib\libs\sys.a C:\MinGW64\x86_64-w64-mingw32\lib\libcomctl32.a DT5.o 2>>zz.err

"2>zz.err" creates a file "zz.err" with error messages, while
"2>>zz.err" appends error messages to the newly created file.
I use a lot of libraries (all created from pure assembler *.S
files), so the line is quite long...


> For this example, I get the same results from using golink.exe.
>
> As far as I can see, there is nothing linked in that I don't know about. (For the purpose of using
> printf for my test, I added in msvcrt.dll as further input to the linker, but my code *only* calls
> printf().)


It does not matter how much functions of a DLL you call - the
OS has to load the entire DLL, anyway. If you took the printf
from GCC's libraries (*.a), only that printf() code was added
to your executable.


> There seem to be two opinions in this thread:
>
> (1) That there is some code inserted before my declared entry point that handily gives me the
> documented parameters when using WinMain, or it might be that the process loader of Windows sets
> them up before passing control to my entry point. Or:
>
> (2) Nothing of the sort happens. Whatever code is encountered at that entry point is responsible for
> setting these things up. Since I'm writing that code, then it's up to me.
>
> Before I started messing about with this, I had no idea how it worked. But my experiments, supported
> by firr's link, suggest that (2) is the case.


Where should user land code get the input parameters from? If
they were not passed by the OS while it starts a new process,
it was almost impossible to retrieve them.


>> So you type your code into a hex editor and start that created
>> output file. In this case, you should know everything about PE
>> headers and how to tell your OS where it will find which parts
>> of your code (.text, .data, etc.). If so, I wonder why you ask
>> questions rather than to explain how these things work... :)
>
> I know little about PE formats (I've written to executable formats in the past but am too old now
> for that routine stuff now). But I shouldn't need to know.


My assumption is void - if I got it right meanwhile, you feed
your code to Nasm...

( I'm only 59 - still enough free neurons to store data. ;) )


> All I'm inquiring about is how 'main(nargs,args)' and 'WinMain(a,b,c,d)' actually work.


Okay. I played around with archive and executable evaluation,
but it became quite boring after a while.


> BTW I don't believe you've mentioned what tools you use to assemble, and link, or otherwise create
> the executable, what other library functions are added in, and what raw entry point is being used.


I did: AS as assembler and GCC as linker - for pure assembler
code, no extra compiler is required, and GCC just feeds files
to ld (after appending its C runtime initialisation...).


> If (2) above is what actually happens, then probably there is some hidden startup routine in your
> code that is confusing matters.


GCC adds everything required for C/C++ programs - the reason,
why executables created via GCC never have sizes below 20 KB.

(I do not call C functions, because I prefer my own libraries
over HLL bloat...)


>>>> movq %rcx, %rbp #
>>>> RSI = Hinstance
>>>
>>> (This is also confusing; should rbp be rsi or vice versa?)
>>
>> Why? As it is a 32 bit address, any of the old registers: EAX,
>> EBX, ECX, EDX, EBP, EDI, ESI will do. As long as you don't use
>> RSP as multipurpose register without saving its content first,
>> everything is fine.
>
> It's confusing because the instruction copies rcx to rbp, yet the comment talks about rsi!


Ouch. Using RSI for some special purposes is a "fall-back" to
my old OS/2 stuff. Meanwhile, I use R8...R15 for 64 bit data,
while RAX...RSI are "reserved" for 32 bit stuff - no prefixes
required as long as the upper 32 bit are not used.


> (If this is a true WinMain function, with the correct startup code behind it, then at this point I
> believe that rcx contains the hInstance parameter. I suspect the comment is wrong.)


Right - the comment is wrong! ;)

In the latest version (2015-01-04), the comment says:

"# RBP = Hinstance"

I should update the "trunk", but I *hate* to play around with
that "versions" stuff. As old school programmer, I prefer the
straight path (aka ZIP archives).


> (** This will probably add to the confusion, but I use my own HLL compiler. One of its targets is
> x64 source code in Nasm syntax. It uses LD as the linker. Although the language can make use of
> imported C runtime functions from MSVCRT.DLL, it doesn't link in hidden startup routines.
>
> I found that I couldn't just have a function called main, with parameters (nargs, args), or one
> called Winmain() with hInstance etc. They didn't work. I only got a basic entry point function with
> no parameters. This is when I started doing experiments, and got involved in the thread.
>
> And, no, I don't wish to explicitly add in those mysterious startup functions. I don't want any
> dependencies on parts of other languages and especially not gcc.)


I do not think it is a "mystery", if we (including me) do not
understand how the one or other thing works "under the hood".
No one is omniscient, and I rely on knowledge, not on beliefs
of any kind. ;)

firr

unread,
Oct 4, 2015, 5:57:31 AM10/4/15
to
i think if talking about this mingw generated process startup code, the source may be here (im not sure but it was not so much easy to find at least it:

http://sourceforge.net/p/mingw/mingw-org-wsl/ci/21762bb4a1bd0c88c38eead03f59e8d994349e83/tree/src/libcrt/crt/

you could fint c main there and WinMian call there, also some routines then agree with that names i see when disasembly mingw generated empty winmain c++ app, but still
i cannot find the raw entry point and
spy what is called in which order

can you find this ? (maybe i will ask on c++ group or stack)

Bartc

unread,
Oct 4, 2015, 6:12:34 AM10/4/15
to
On 04/10/2015 09:55, Bernhard Schornak wrote:
> Bartc wrote:

>> As far as I can see, there is nothing linked in that I don't know
>> about. (For the purpose of using
>> printf for my test, I added in msvcrt.dll as further input to the
>> linker, but my code *only* calls
>> printf().)
>
>
> It does not matter how much functions of a DLL you call - the
> OS has to load the entire DLL, anyway. If you took the printf
> from GCC's libraries (*.a), only that printf() code was added
> to your executable.

(I give an actual .dll file to the linker. (.a files would create a
dependency on C and gcc that I want to avoid.)

Probably MSVCRT.DLL is already resident - I assume the same copy of the
code is shared between processes. But I don't explicitly call anything
else. (It might be that MSVCRT.DLL contains a DllMain entry point that
is called to initialise the library, but I don't think that's anything
to do with my code.

In fact I sometimes load MSVCRT.DLL dynamically, in the middle of an
application, and it presumably calls the same DllMain set-up code.)

>> (2) Nothing of the sort happens. Whatever code is encountered at that
>> entry point is responsible for
>> setting these things up. Since I'm writing that code, then it's up to me.
>>
>> Before I started messing about with this, I had no idea how it worked.
>> But my experiments, supported
>> by firr's link, suggest that (2) is the case.
>
>
> Where should user land code get the input parameters from? If
> they were not passed by the OS while it starts a new process,
> it was almost impossible to retrieve them.

Not so impossible: I simply call GetCommandLineA() to get the command
line. I haven't needed them yet but GetModuleHandle() gets hInstance,
and GetStartupInfo() is called for CmdShow.

>> BTW I don't believe you've mentioned what tools you use to assemble,
>> and link, or otherwise create
>> the executable

>> If (2) above is what actually happens, then probably there is some
>> hidden startup routine in your
>> code that is confusing matters.
>
> GCC adds everything required for C/C++ programs - the reason,
> why executables created via GCC never have sizes below 20 KB.

OK, so the chances are that gcc is adding in extra stuff that is called
before WinMain or main.

>>>>> movq %rcx,
>>>>> %rbp #
>>>>> RSI = Hinstance

>> I suspect the comment is wrong.)
>
> Right - the comment is wrong! ;)
>
> In the latest version (2015-01-04), the comment says:
>
> "# RBP = Hinstance"

Maybe it's better to avoid naming explicit registers within comments!
Then they need less maintenance.

>> And, no, I don't wish to explicitly add in those mysterious startup
>> functions. I don't want any
>> dependencies on parts of other languages and especially not gcc.)
>
>
> I do not think it is a "mystery", if we (including me) do not
> understand how the one or other thing works "under the hood".
> No one is omniscient, and I rely on knowledge, not on beliefs
> of any kind. ;)

Well, that `_pei386_runtime_relocator' error I mentioned elsewhere is a
mystery at the minute. Currently it only comes up with using ExitProcess
(in an attempt to solve yet another mysterious problem where I link,
run, then can't link again because the .exe is inaccessible). If it
comes up with a more essential function, I'll have to switch linkers.

I don't like mysteries...


--
Bartc

Bernhard Schornak

unread,
Oct 4, 2015, 10:42:57 AM10/4/15
to
firr wrote:


> i think if talking about this mingw generated process startup code,
> the source may be here (im not sure but it was not so much easy to
> find at least it:
>
> http://sourceforge.net/p/mingw/mingw-org-wsl/ci/21762bb4a1bd0c88c38eead03f59e8d994349e83/tree/src/libcrt/crt/
>
> you could fint c main there and WinMian call there, also some routines
> then agree with that names i see when disasembly mingw generated empty
> winmain c++ app, but still i cannot find the raw entry point and spy
> what is called in which order
>
> can you find this ? (maybe i will ask on c++ group or stack)


The mechanism we were talking about can be found in "main.c".

I still do not know if the OS passes the required parameters,
but the function defined as main() emits a bunch of API calls
to retrieve them (again?).

Bernhard Schornak

unread,
Oct 4, 2015, 10:42:58 AM10/4/15
to
Bartc wrote:


> On 04/10/2015 09:55, Bernhard Schornak wrote:
>> Bartc wrote:
>
>>> As far as I can see, there is nothing linked in that I don't know
>>> about. (For the purpose of using
>>> printf for my test, I added in msvcrt.dll as further input to the
>>> linker, but my code *only* calls
>>> printf().)
>>
>> It does not matter how much functions of a DLL you call - the
>> OS has to load the entire DLL, anyway. If you took the printf
>> from GCC's libraries (*.a), only that printf() code was added
>> to your executable.
>
> (I give an actual .dll file to the linker. (.a files would create a dependency on C and gcc that I
> want to avoid.)
>
> Probably MSVCRT.DLL is already resident - I assume the same copy of the code is shared between
> processes. But I don't explicitly call anything else. (It might be that MSVCRT.DLL contains a
> DllMain entry point that is called to initialise the library, but I don't think that's anything to
> do with my code.
>
> In fact I sometimes load MSVCRT.DLL dynamically, in the middle of an application, and it presumably
> calls the same DllMain set-up code.)


Right. Nevertheless, the OS either has to load the requested
DLL code or to establish a "connection" to the DLL if it was
previously loaded.


>>> (2) Nothing of the sort happens. Whatever code is encountered at that
>>> entry point is responsible for
>>> setting these things up. Since I'm writing that code, then it's up to me.
>>>
>>> Before I started messing about with this, I had no idea how it worked.
>>> But my experiments, supported
>>> by firr's link, suggest that (2) is the case.
>>
>> Where should user land code get the input parameters from? If
>> they were not passed by the OS while it starts a new process,
>> it was almost impossible to retrieve them.
>
> Not so impossible: I simply call GetCommandLineA() to get the command line. I haven't needed them
> yet but GetModuleHandle() gets hInstance, and GetStartupInfo() is called for CmdShow.


Okay, but it is redundant work, because these data are there
whenever the OS launches a new process - copying its working
parameters isn't the big deal and could save a lot of super-
fluous activities.

Looking at firr's link to GCC's CRT initialisation code, GCC
takes the long way home, and retrieves the parameters as you
described above.

Sometimes, I just wonder how stupid OS / compiler developers
can be...


>>> BTW I don't believe you've mentioned what tools you use to assemble,
>>> and link, or otherwise create
>>> the executable
>
>>> If (2) above is what actually happens, then probably there is some
>>> hidden startup routine in your
>>> code that is confusing matters.
>>
>> GCC adds everything required for C/C++ programs - the reason,
>> why executables created via GCC never have sizes below 20 KB.
>
> OK, so the chances are that gcc is adding in extra stuff that is called before WinMain or main.


HLL compilers do that - might be interesting, how Fasm & Co.
behave while they are compiling and linking.


>>>>>> movq %rcx,
>>>>>> %rbp #
>>>>>> RSI = Hinstance
>
>>> I suspect the comment is wrong.)
>>
>> Right - the comment is wrong! ;)
>>
>> In the latest version (2015-01-04), the comment says:
>>
>> "# RBP = Hinstance"
>
> Maybe it's better to avoid naming explicit registers within comments! Then they need less maintenance.


Actually, I fly through my comments because RBP is faster to
detect than "%rbp". (Besides that, most of my comments start
with a three letter register name...


>>> And, no, I don't wish to explicitly add in those mysterious startup
>>> functions. I don't want any
>>> dependencies on parts of other languages and especially not gcc.)
>>
>> I do not think it is a "mystery", if we (including me) do not
>> understand how the one or other thing works "under the hood".
>> No one is omniscient, and I rely on knowledge, not on beliefs
>> of any kind. ;)
>
> Well, that `_pei386_runtime_relocator' error I mentioned elsewhere is a mystery at the minute.
> Currently it only comes up with using ExitProcess (in an attempt to solve yet another mysterious
> problem where I link, run, then can't link again because the .exe is inaccessible). If it comes up
> with a more essential function, I'll have to switch linkers.


Searching for that hateword ("_pei386_runtime_relocator"), I
found this

http://tinyurl.com/pwwc9ah


> I don't like mysteries...


A synonym for "I don't know!"... ;)
0 new messages