Como escrever "hello world" em montador debaixo das janelas?
Como Escrever e compilar hello world sem a ajuda de funções C no Windows?
8 answers
printf
, implementando int main(){ return printf(message); }
; ----------------------------------------------------------------------------
; helloworld.asm
;
; This is a Win32 console program that writes "Hello, World" on one line and
; then exits. It needs to be linked with a C library.
; ----------------------------------------------------------------------------
global _main
extern _printf
section .text
_main:
push message
call _printf
add esp, 4
ret
message:
db 'Hello, World', 10, 0
Então corre
nasm -fwin32 helloworld.asm
gcc helloworld.obj
a
Há também O Guia ignorante de Newbies para Hello World em Nasm sem o uso de uma biblioteca C. Então o código ficaria assim.
16-código de bits com chamadas do sistema MS-DOS: funciona em em emuladores DOS ou em janelas de 32 bits com suporte a NTVDM. Não pode ser executado " diretamente "(transparente) sob qualquer Windows de 64 bits, porque um o kernel x86-64 não pode usar o modo vm86.
org 100h
mov dx,msg
mov ah,9
int 21h
mov ah,4Ch
int 21h
msg db 'Hello, World!',0Dh,0Ah,'$'
Construa isto num executável .com
para que seja carregado em cs:100h
com todos os registos de segmentos iguais um ao outro (modelo de memória minúsculo).
Este exemplo mostra como ir diretamente para a API do Windows e não ligar na Biblioteca Padrão do C.
global _main
extern _GetStdHandle@4
extern _WriteFile@20
extern _ExitProcess@4
section .text
_main:
; DWORD bytes;
mov ebp, esp
sub esp, 4
; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE)
push -11
call _GetStdHandle@4
mov ebx, eax
; WriteFile( hstdOut, message, length(message), &bytes, 0);
push 0
lea eax, [ebp-4]
push eax
push (message_end - message)
push message
push ebx
call _WriteFile@20
; ExitProcess(0)
push 0
call _ExitProcess@4
; never here
hlt
message:
db 'Hello, World', 10
message_end:
Para compilar, vai precisar do NASM e do LINK.EXE (from Visual studio Standard Edition)
nasm -fwin32 hello.asm link /subsystem:console /nodefaultlib /entry:main hello.obj
Estes são exemplos Win32 e Win64 usando chamadas da API do Windows. Eles são para MASM em vez de NASM, mas dê uma olhada neles. Você pode encontrar mais detalhes em este artigo.
;---ASM Hello World Win32 MessageBox
.386
.model flat, stdcall
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.data
title db 'Win32', 0
msg db 'Hello World', 0
.code
Main:
push 0 ; uType = MB_OK
push offset title ; LPCSTR lpCaption
push offset msg ; LPCSTR lpText
push 0 ; hWnd = HWND_DESKTOP
call MessageBoxA
push eax ; uExitCode = MessageBox(...)
call ExitProcess
End Main
;---ASM Hello World Win64 MessageBox
extrn MessageBoxA: PROC
extrn ExitProcess: PROC
.data
title db 'Win64', 0
msg db 'Hello World!', 0
.code
main proc
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, msg ; LPCSTR lpText
lea r8, title ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call MessageBoxA
add rsp, 28h
mov ecx, eax ; uExitCode = MessageBox(...)
call ExitProcess
main endp
End
Para montar e ligar estes usando MASM, use isto para um executável de 32 bits:
ml.exe [filename] /link /subsystem:windows
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:Main
Ou isto para um executável de 64 bits:
ml64.exe [filename] /link /subsystem:windows
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:main
Assembler Plano não precisa de um linker extra. Isso torna a programação do assembler bastante fácil. Ele também está disponível para Linux.
Isto é hello.asm
dos exemplos Fasm:
include 'win32ax.inc'
.code
start:
invoke MessageBox,HWND_DESKTOP,"Hi! I'm the example program!",invoke GetCommandLine,MB_OK
invoke ExitProcess,0
.end start
Fasm cria um executável:
>fasm hello.asm flat assembler version 1.70.03 (1048575 kilobytes memory) 4 passes, 1536 bytes.E este é o programa da IDA.: Podes ver as três chamadas.:
GetCommandLine
, MessageBox
e ExitProcess
global WinMain
extern ExitProcess ; external functions in system libraries
extern MessageBoxA
section .data
title: db 'Win64', 0
msg: db 'Hello world!', 0
section .text
WinMain:
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx,[msg] ; LPCSTR lpText
lea r8,[title] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call MessageBoxA
add rsp, 28h
mov ecx,eax
call ExitProcess
hlt ; never here
Se este código for salvo em, por exemplo, " test64.asm", então para compilar:
nasm -f win64 test64.asm
Produz " test64.obj" Então para ligar a partir da linha de comandos:
path_to_link\link.exe test64.obj /subsystem:windows /entry:WinMain /libpath:path_to_libs /nodefaultlib kernel32.lib user32.lib /largeaddressaware:no
Where path_to_link could be C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin ou onde quer que esteja a sua ligação.programa exe na sua máquina, o path_ to_libs pode ser C:\Program ficheiros (x86)\Windows Kits\8.1\Lib\winv6.3\um\x64 ou onde quer que estejam as suas bibliotecas (neste caso, ambos os kernel32.lib e user32.lib estão no mesmo lugar, caso contrário, use uma opção para cada caminho que você precisa) e /largeaddressaware:não opção é necessária para evitar a vinculador se queixam de endereços (por user32.lib neste caso). Além disso, como é feito aqui, se linker do Visual é invocado a partir do comando prompt, é necessário configurar o ambiente anteriormente (execute uma vez vcvarsall.morcego e / ou ver MS C++ 2010 e mspdb100.dll ).
A menos que chames alguma função isto não é nada trivial. (E, a sério, não há nenhuma diferença real na complexidade entre chamar printf e chamar uma função API win32.)
Mesmo DOS int 21h é realmente apenas uma chamada de função, mesmo que seja uma API diferente.
Se quiser fazê-lo sem ajuda, precisa de falar directamente com o seu hardware de vídeo, provavelmente escrevendo bitmaps das letras de "Hello world" num framebuffer. Mesmo assim, o cartão de vídeo está fazendo o trabalho de traduzir esses valores de memória em sinais VGA / DVI.
Note que, na verdade, nenhuma destas coisas até ao hardware é mais interessante na ASM do que em C. UM programa "hello world" resume-se a uma chamada de função. Uma coisa boa sobre a ASM é que você pode usar qualquer ABI que você quer razoavelmente fácil; você só precisa saber o que essa ABI é.
nasm -fwin32 helloworld.asm
link.exe helloworld.obj libcmt.lib
Espero que isto ajude alguém.
Os melhores exemplos são aqueles com fasm, porque fasm não usa um linker, que esconde a complexidade da programação do windows por outra camada opaca de complexidade. Se você está satisfeito com um programa que escreve em uma janela de gui, então há um exemplo para isso no diretório de exemplo de fasm.
Se você quer um programa de console, que permite redirecionamento do padrão dentro e fora padrão que também é possível. Há um programa de exemplo (helas altamente não-trivial) disponível que não usa um gui, e trabalha estritamente com o console, que é o próprio fasm. Isto pode ser reduzido ao essencial. (Eu escrevi um compilador forth que é outro exemplo não-gui, mas também não-trivial).
Tal programa tem o seguinte comando para gerar um cabeçalho executável adequado, normalmente feito por um linker.
FORMAT PE CONSOLE
Uma secção chamada'.o idata ' contém uma tabela que ajuda o windows durante o arranque para alguns nomes de funções para os endereços de execução. Também contém uma referência ao KERNEL.DLL que é o Sistema Operacional Windows.
section '.idata' import data readable writeable
dd 0,0,0,rva kernel_name,rva kernel_table
dd 0,0,0,0,0
kernel_table:
_ExitProcess@4 DD rva _ExitProcess
CreateFile DD rva _CreateFileA
...
...
_GetStdHandle@4 DD rva _GetStdHandle
DD 0
O formato da tabela é imposto pelo windows e contém Nomes que são pesquisados em arquivos do sistema, quando o programa é iniciado. FASM esconde alguns dos complexidade por trás da palavra-chave rva. Então _ExitProcess@4 é uma etiqueta fasm e _exitProcess é uma cadeia que é observada pelo Windows.
O seu programa está na secção'.texto". Se declarar que essa secção pode ser escrita e executável, é a única secção tens de acrescentar.
section '.text' code executable readable writable
Pode ligar para todas as instalações que declarou .secção idata. Para um programa de consola, você precisa de _GetStdHandle para encontrar o ficheiro filedescriptors para o standard in and standardout (usando nomes simbólicos como o STD_ INPUT_ handle que o fasm encontra no ficheiro include win32a.inc).
Uma vez que você tem os descritores de arquivo você pode fazer WriteFile e ReadFile.
Todas as funções são descritas na documentação kernel32. Você provavelmente está ciente disso ou você não tentaria montagem programacao.
Em resumo: existe uma tabela com nomes asci que se juntam ao Sistema Operacional windows. Durante a inicialização isto é transformado em uma tabela de endereços ligáveis, que você usa em seu programa.