Reputation: 24602
With online help, I was able to write nasm code in Mac OS X resulting in an executable that prints its own filename, argv[0]
in equivalent C code. When I use the same code in Windows, I want it to print the programs name:
C:\> nasm -f win32 -o scriptname.obj scriptname.asm
C:\> golink /fo scriptname.exe scriptname.obj /console kernel32.dll Msvcrt.dll
GoLink.Exe Version 0.27.0.0 - Copyright Jeremy Gordon 2002/12 - [email protected]
Output file: scriptname.exe
Format: win32 size: 2,048 bytes
C:\> scriptname.exe
Program: scriptname.exe
But what it actually prints is emptiness:
C:\> scriptname.exe
Program:
Specs:
Upvotes: 0
Views: 2199
Reputation: 1452
Well, yes and no. In Linux, at the _start:
label, argc
is at [esp]
and argv[0]
is at [esp + 4]
. If your code works, this must also be true of Mac OSX. By doing -e main
on the ld command line, essentially main
is lying about its name. It isn't really a "C style main". This label is jumped to, not called. If main
(or _main, for 'doze and Mac OSX) is called by "C startup code" (crt2.o), then there's a return address on the stack, so argc
is at [esp + 4]
and argv[0]
is at [esp + 8]
. Also, as Tim tells you at news:comp.lang.asm.x86 argv
is a **
- a "pointer to pointer" - so you also need the mov ebx, [ebx]
(a "de-reference"). I'm pretty sure in Windows, our code is called regardless of what we name the entrypoint. Can you get it to work that way?
EDIT: Well this has pretty much been beaten to death, and "solved"(?), but I got bored, too. This works in Linux, and "might" be portable.
; prints its own name (possibly portable?) ; nasm -f elf32 myprog.asm ; nasm -f macho myprog.asm --prefix _ ; nasm -f win32 myprog.asm --prefix _ ; gcc -o myprog myprog.o(bj) (-m32 for 64-bit systems) global main extern printf section .data prog db `Program: %s \n`, 0 section .text main: mov eax, [esp + 8] mov eax, [eax] push eax push prog call printf add esp, 4 * 2 ret ;----------------------
Upvotes: 0
Reputation: 5884
You call GetStdHandle and save the returned value to ecx, ecx is a volatile register, the value will not be saved across calls unless you push/pop it. Your first call to WriteConsoleA uses it and clobbers it so the next call, ecx is not what you expect.
* EDIT * I was bored so here is working code:
[bits 32]
section .data
program db "Program: ", 0
programlen equ $-program
nl db "", 13, 10, 0
nllen equ $-nl
section .bss
buf resd 1
argc resd 1
argv resb 255
section .text
global Start
extern GetStdHandle
extern __getmainargs
extern WriteConsoleA
extern ExitProcess
strlen: ; eax: a string ending in 0
push eax ; cache eax
.strloop:
mov bl, byte [eax]
cmp bl, 0
je .strret ; return len if bl == 0
inc eax ; else eax++
jmp .strloop
.strret:
pop ebx ; ebx = cached eax
sub eax, ebx ; eax -= ebx
ret ; eax = len
Start:
push 0
push buf
push argv
push argc
call __getmainargs
add esp, 16 ; clear stack (4 * 4 arguments)
push -11 ; get stdout
call GetStdHandle
mov esi, eax
add esp, 4 ; clear stack (4 * 1 argument)
push 0 ; null
push buf ; [chars written]
push programlen
push program
push esi ; stdout
call WriteConsoleA
add esp, 20 ; clear stack (4 * 5 arguments)
mov edx, [argv]
mov eax, [edx] ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
call strlen
push 0 ; null
push buf ; [chars written]
push eax ; len argv[0]
push dword [edx] ;<<<<<<<<<<<<<<<<<<<<<<<<<<<< ; argv[0]
push esi ; stdout
call WriteConsoleA
add esp, 20 ; clear stack (4 * 5 arguments)
push 0 ; null
push buf ; [chars written]
push nllen
push nl
push esi ; stdout
call WriteConsoleA
add esp, 20 ; clear stack (4 * 5 arguments)
push 0
call ExitProcess
D:\NASM Projects\ReadArgs>ReadArgs.exe
Program: ReadArgs.exe
D:\NASM Projects\ReadArgs>
Upvotes: 2
Reputation: 4686
The argc
and argv
arguments are for C based programs only. Assembly based programs are must use __getmainargs
or __wgetmainargs
functions from the C library to generate those variables like they are internally used by C based programs. See below MSDN article for details:
http://msdn.microsoft.com/en-us/library/ff770599.aspx
Upvotes: 2