5

I'm looking for the first free and open source assembler and linker for i386 targeting and running on Win32. My requirements are:

  • It has to be able to build a Win32 PE .exe from assembly source, the minimum is a hello-world Win32 console program with calls to GetStdHandle, WriteFile and ExitProcess.

  • It has to be self-contained: all program, data and library files for building a Win32 PE .exe programs must be included. Depending on kernel32.dll, kernel32.lib, kernel32.def etc. is not OK if not included. The user must be able to declare which DLL contains which functions.

  • It has to be free and open source software as defined by OSI.

  • It has to have an official binary release precompiled for Win32. When comparing assemblers, I'll be considering the date of the earliest binary release.

I've found WASM + WLINK in OpenWatcom 1.0 (binary release on 2003-01-28) and fasm 1.41 (binary release on 2002-11-14). Is there anything earlier?

MASM and TASM are not open source. NASM and other assemblers in this question don't contain a linker targeting Win32.

3
  • 1
    Why all these very specific requirements? I understand you are working on a project but it is a bit unclear what your ultimate goal with all this is? Commented Apr 16 at 9:37
  • 1
    @ThorbjørnRavnAndersen: In my project I'm writing a command-line program in i386 assembly language. I'm considering porting the assembly source code to multiple assemblers and operating systems. Out of curiosity, I'd like to make the source code work with old versions of the assemblers. I'm interested in both free and proprietary assemblers (and I already have a long list). If the assembler is free software, I can bundle it with my program source code for easy reproducibility.
    – pts
    Commented Apr 16 at 12:04
  • 1
    Sounds fun. As the world is moving towards the ARM platform, eventually making your i386 assembly obsolete, why not just use a high level language with modern compilers that can target your desired platforms? Commented Apr 16 at 23:58

4 Answers 4

4

NASM didn’t have its own linker, but on Windows at the turn of the century it was commonly used with ALink, Anthony’s Linker, first released in 1998. The binary in the currently-available archive (1.6) is dated June 1, 1999.

With the supplied import library, win32.lib (dated October 1, 1998), the following program produces the same result as your example programs:

    ;; nasm -fobj hello.asm
    ;; alink -oPE -subsys con hello win32.lib

    [extern GetStdHandle]
    [extern WriteFile]
    [extern ExitProcess]

    [segment code public use32 class='CODE']

    ..start:
    enter 0,0
    push -11
    call GetStdHandle
    push 0
    push 0
    push msg_end-msg
    push msg
    push eax
    call WriteFile
    push 0
    call ExitProcess

msg:    db 'Hello, World!', 13, 10
msg_end:

ALink was initially freeware with source provided, disallowing any modification; but it has been relicensed under the GPL version 3 or later.

To update DLL imports, or add new ones, you can use the associated library manager, ALib. For example,

alib .../kernel32.dll kernel32.lib

will produce a kernel32.lib import library with all of kernel32.dll’s symbols; this can then be used instead of win32.lib when building hello.exe.

(Note that the provided alib.exe is an RSXNT binary, so you’ll also need rsxnt.dll.)

3
  • Thanks for the detailed answer! Unfortunately, this method doesn't let the user declare their own DLLs and imported functions, i.e. to extend or replace the supplied win32.lib. The process with which win32.lib was generated is not open source.
    – pts
    Commented Apr 16 at 13:17
  • 2
    Yes, it can, see the updated answer. Commented Apr 16 at 13:45
  • Thank you for the update! I've added another answer inspired by yours, but it doesn't need or use any of win32.lib, alib.exe, rsxnt.dll, kernel32.dll, kernel32.def, kernel32.lib.
    – pts
    Commented Apr 18 at 3:44
2

fasm, an assembler with a linker built in, has been able to to create Win32 PE console programs since version 1.04 (2000-08-10), it can be built (by itself) for Win32 since version 1.37 (2002-06-12), and it had its first binary release for Win32 (fasmw.exe) in version 1.41 (2002-11-14).

fasm source code of a hello-world Win32 PE program:

format PE console  ; hello.fasm
section '.text' code readable executable

STD_OUTPUT_HANDLE equ -11
EXIT_SUCCESS equ 0
NULL equ 0
_mainCRTStartup:
start:  ; Used by FASM.
        push STD_OUTPUT_HANDLE
        call [__imp__GetStdHandle@4]  ; Returns stdout handle in EAX.
        push NULL
        push NULL
        push msg_end-msg
        push msg
        push eax  ; stdout handle.
        call [__imp__WriteFile@20]
        push EXIT_SUCCESS
        call [__imp__ExitProcess@4]

msg     db 'Hello, World!', 13, 10
msg_end:

stack 10000h
section '.idata' import data readable writeable
  dd 0, 0, 0, rva kernel_name, rva kernel_table
  dd 0, 0, 0, 0, 0
  kernel_table:
    __imp__GetStdHandle@4 dd rva __name__GetStdHandle  ; HANDLE __stdcall GetStdHandle(DWORD nStdHandle);  /* KERNEL32.DLL */
    __imp__WriteFile@20   dd rva __name__WriteFile     ; BOOL __stdcall WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);  /* KERNEL32.DLL */
    __imp__ExitProcess@4  dd rva __name__ExitProcess   ; void __stdcall __noreturn ExitProcess(UINT uExitCode);  /* KERNEL32.DLL */
    dd 0
  kernel_name db 'KERNEL32.DLL', 0
  __name__GetStdHandle db 0, 0, 'GetStdHandle', 0
  __name__WriteFile    db 0, 0, 'WriteFile', 0
  __name__ExitProcess  db 0, 0, 'ExitProcess', 0
section '.reloc' fixups data readable discardable

Compile it like this to hello.exe:

fasm hello.fasm hello.exe
2

OpenWatcom 1.0 was released on 2003-01-28, and its cm_core_nt.zip download contains the WASM assembler and the WLINK linker precompiled for Win32. (Its cm_core_doswin.zip download contains the same tools precompiled for 32-bit DOS.) Its source code is available in the open_watcom_1.0.0-src.zip download.

Here is the assembly source code of a hello-world program (hello.asm):

.386p  ; hello.asm
.model flat
.code
public _mainCRTStartup
extrn _GetStdHandle@4:BYTE  ; HANDLE __stdcall GetStdHandle(DWORD nStdHandle);  /* KERNEL32.DLL */
extrn _WriteFile@20:BYTE  ; BOOL __stdcall WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);  /* KERNEL32.DLL */
extrn _ExitProcess@4:BYTE  ; void __stdcall __noreturn ExitProcess(UINT uExitCode);  /* KERNEL32.DLL */
STD_OUTPUT_HANDLE equ -11
EXIT_SUCCESS equ 0
NULL equ 0
_mainCRTStartup:
        push STD_OUTPUT_HANDLE
        call near ptr _GetStdHandle@4  ; Returns stdout handle in EAX.
        push NULL
        push NULL
        push msg_end-msg
        push offset msg
        push eax  ; stdout handle.
        call near ptr _WriteFile@20
        push EXIT_SUCCESS
        call near ptr _ExitProcess@4
msg     db 'Hello, World!', 13, 10
msg_end:
end _mainCRTStartup

Here is the linker config file win32con.lnk with DLL and function declarations:

# win32con.lnk for WLINK.
format windows nt
runtime console
option quiet
import '_GetStdHandle@4' 'kernel32.dll'.GetStdHandle
import '_WriteFile@20'   'kernel32.dll'.WriteFile
import '_ExitProcess@4'  'kernel32.dll'.ExitProcess

Here is how to compile and link it to Win32 hello.exe:

wasm -q hello.asm
wlink @win32con.lnk f hello.obj
1

It's possible to write Win32 programs with NASM 0.95 (1997-07-16) or later and ALINK 1.5 (alink.exe on 1998-11-16), without using win32.lib, kernel32.dll or alib.exe. (This answer expands on the answer by Stephen Kitt. That answer uses win32.lib, and some of the other files as well.)

Instead of using win32.lib, we will create the import library file imports.obj, using NASM. Here is the source file imports.nasm:

; imports.nasm
; Compile with: nasm -f bin -o imports.obj imports.nasm
THEADR equ 0x80
COMENT equ 0x88
MODEND equ 0x8a
IMPORT_COMENT equ 0x1a080

FAKE_CHECKSUM equ 0x00

%macro lenstr 1
  db %%data_end-%%data
  %%data:
  db %1
  %%data_end:
%endm

%macro import 3
  db COMENT  ; OMF record type.
  dw %%record_end-%%record
  %%record:
  dd IMPORT_COMENT
  lenstr %1
  lenstr %2
  lenstr %3
  db FAKE_CHECKSUM
  %%record_end:
%endm

%macro simple 2+
  db %1  ; OMF record type.
  dw %%record_end-%%record
  %%record:
  %2
  db FAKE_CHECKSUM
  %%record_end:
%endm

simple THEADR, lenstr 'imports'
import '__imp_GetStdHandle', 'kernel32.dll', 'GetStdHandle'
import '__imp_WriteFile',    'kernel32.dll', 'WriteFile'
import '__imp_ExitProcess',  'kernel32.dll', 'ExitProcess'
simple MODEND, db 0

The imports.nasm file above instructs NASM to manually build an OMF .obj file with DLL import COMENT records understood by ALINK. (Please note that these records have the same format which WLINK also recognizes, but WLINK interprets them differently, and WLINK generates a hello.exe which crashes from the hello.asm above.)

The corresponding hello.nasm file:

; hello.nasm
; Compile with: nasm -f obj -o hello.obj hello.nasm
[extern __imp_GetStdHandle]
[extern __imp_WriteFile]
[extern __imp_ExitProcess]

[segment code public use32 class='CODE']

..start:
push -11
call [__imp_GetStdHandle]
push 0
push 0
push msg_end-msg
push msg
push eax
call [__imp_WriteFile]
push 0
call [__imp_ExitProcess]

msg: db 'Hello, World!', 13, 10
msg_end:

Compilation of Win32 hello.exe from these sources:

nasm -f bin -o imports.obj imports.nasm
nasm -f obj -o hello.obj hello.nasm
alink -oPE -subsys con hello imports.obj

Alternatively, since NASM 0.97 (1997-12-06), it's possible to use the import directive, without having to create imports.nasm:

; hello.nasm
; Compile with: nasm -f obj -o hello.obj hello.nasm
[import __imp_GetStdHandle kernel32.dll GetStdHandle]
[import __imp_WriteFile    kernel32.dll WriteFile]
[import __imp_ExitProcess  kernel32.dll ExitProcess]

[extern __imp_GetStdHandle]
[extern __imp_WriteFile]
[extern __imp_ExitProcess]

[segment code public use32 class=CODE]

..start:
push dword -11
call [__imp_GetStdHandle]
push byte 0
push byte 0
push dword msg_end-msg
push dword msg
push eax
call [__imp_WriteFile]
push byte 0
call [__imp_ExitProcess]

msg: db 'Hello, World!', 13, 10
msg_end:

Compilation of Win32 hello.exe from these sources:

nasm -f obj -o hello.obj hello.nasm
alink -oPE -subsys con hello

Alternatively, it's possible to create hello.obj with WASM 10.5 (1995-07-11) or later (e.g. the first open source WASM: WASM in OpenWatcom 1.0, 2003-01-28) or MASM or TASM, from hello.asm:

; hello.asm
; Compile with: wasm -q -c -fo=hello.obj hello.asm
.386p
.model flat
.code
public _mainCRTStartup
extrn __imp_GetStdHandle:DWORD  ; HANDLE __stdcall GetStdHandle(DWORD nStdHandle);  /* KERNEL32.DLL */
extrn __imp_WriteFile:DWORD  ; BOOL __stdcall WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);  /* KERNEL32.DLL */
extrn __imp_ExitProcess:DWORD  ; void __stdcall __noreturn ExitProcess(UINT uExitCode);  /* KERNEL32.DLL */
STD_OUTPUT_HANDLE equ -11
EXIT_SUCCESS equ 0
NULL equ 0
_mainCRTStartup:
        push STD_OUTPUT_HANDLE
        call dword ptr __imp_GetStdHandle  ; Returns stdout handle in EAX.
        push NULL
        push NULL
        push msg_end-msg
        push offset msg
        push eax  ; stdout handle.
        call dword ptr __imp_WriteFile
        push EXIT_SUCCESS
        call dword ptr __imp_ExitProcess
msg     db 'Hello, World!', 13, 10
msg_end:
end _mainCRTStartup

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .