How to set my custom entry point?

I have previously written in C two simple Win32 games: Minesweeper and Gomoku. In these two games, not a single bit of the C library is used, only Win32 API functions are used. Therefore, I have set the entry point of these two programs as WinMainCRTStartup. This way I have avoided all the boilerplate codes of IO, heap initialization, etc.

Now I want to port these two programs to Rust. I searched the Rust documentation, but I found they only mentioned how to do when you don't want libstd, they still assume you would need libcore or libc. My situation is I don't want any of them. One example they give has code like

#![feature(lang_items, core_intrinsics)]
#![feature(start)]
#![no_std]
use core::intrinsics;

// Pull in the system libc library for what crt0.o likely requires.
extern crate libc;

// Entry point for this program.
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    0
}

What I want is something like

fn start() {
           // all the implementation details
}

Where the start function is my entry point. It has no parameters and no return value, just like WinMainCRTStartup. Can I achieve this in Rust?

I have thought of a solution myself. I'll report my progress soon.

I think I have found the solution. Here is how:

  1. The main point is that you should not let cargo create a binary project, instead you must create a library crate. And the entry function is in that library crate. Let the name of that crate be "start".

cargo new start

  1. Now the project has been created. The lib.rs file is supposed to contain the start function, which in turn calls Win32 functions. Today I have not yet begun to write such calls, so I have left the body of start function empty. The content of lib.rs is as follows:

#![no_std]
#[no_mangle]
pub extern "C" fn start() {
}

  1. Now let cargo buid the target:

cargo build --release

  1. Now in the target\release directory, you should find the "libstart.rlib" file. This time we will let Microsoft linker to build the exe. Before that, make sure that the path of link.exe is included in the PATH environment string. In the target\release directory, type the following command:

link /NOLOGO /NXCOMPAT /LARGEADDRESSAWARE /SAFESEH /RELEASE /ENTRY:start /SUBSYSTEM:WINDOWS libstart.rlib /OPT:REF,ICF /LIBPATH:.\deps /OUT:start.exe

Some parameters in the above command line are still a mystery to me, as I just copied them from the cargo generated command line. To see how I obtained that command line, see my post Some findings of the rust compilation process - #3 by Michael-F-Bryan

And now the start.exe should have just been generated, except it does nothing at the moment.

Shouldn't the start() function have a signature of fn(isize, *const *const u8) -> isize? I would have thought you'd encounter segfaults and other stack issues when exiting your custom start() function because the OS passes you two parameters but the compiler doesn't take this into account when cleaning up the stack frame.

It's interesting that you can create a GUI program without needing the standard library. How did you go not using things like Rust's Vec or even filesystem APIs like std::fs::File? From memory, windows GUIs use a lot of heap allocation and reference counting (COM objects are essentially reference-counted trait objects), so I'm surprised you were able to write the original C programs without using anything from libc.

1 Like

Win32 API already provided many file and graphic operations.
My program is an educational program for teaching Win32 programming. It is very simple, and only uses stack memory, no heap memory, so malloc is not needed, and not any of C library is needed.

I just commited this my gomoku program to github. Welcome to take a look!

https://github.com/cnruster/gomoku

By the way, at least in Windows, the main or WinMain function is not the absolute entry point. The real entry is mainCRTStartup for console program, and WinMainCRTStartup for GUI program. It is the mainCRTStartup function that parses the command line, and obtain argc, argv, and then call main, which is user provided. mainCRTStartup and other initialization routines are provided in the C standard library.

Since my program does not use the C library and provides my own WinMainCRTStartup, I have modified my project settings so that the default libraries are ignored to avoid symbol conflict.

1 Like

According to Raymond Chen, Windows passes nothing into RawEntryPoint:

The operating system calls the function with no parameters,
and the return value (if the function ever returns) is passed
to the ExitThread function.
WinMain is just the conventional name for the Win32 process entry point - The Old New Thing