E’ possibile programmare in C, senza compilare con gcc, sfruttando le dll e nasm; nessun utilizzo di gcc e programmi più snelli.
Di recente, mi sono interessato alle dll di Windows ed ho scoperto che contengono numerose funzioni, scritte in C; in particolare, la dll “msvcrt.dll” ne contiene tantissime ( credo tutte ) della libreria “stdio.h”. Poiché mi piace sperimentare, mi sono chiesto come utilizzare queste funzioni. Dopo varie ricerche su internet e qualche esperimento, ecco cosa ho “scoperto”.
Ottenere un eseguibile exe, che chiama funzioni C senza compilare con gcc.
Per prima cosa ho scritto un semplice programma in C di esempio e l’ho salvato nel file “prg.c”.
Ecco il sorgente:
#include <stdio.h>
int numero;
int main() {
scanf("%d",&numero);
if (numero < 256) { printf("%c\n",numero);};
return 0;
}
IL programma prende un input numerico da tastiera e stampa il carattere col codice ASCII corrispondente. Se, per esempio, si introduce 65, verrà stampato il carattere “A”, perché il codice ASCII 65 corrisponde al carattere “A” maiuscola.
Utilizzando gcc ( unica occasione ), con il comando PROMPT> gcc -S prg.c -masm=intel ho ottenuto un sorgente assembly contenuto nel file “prg.s”. Questo sorgente assembly, non è adatto per l’assemblatore NASM, quindi ho rinominato il file cambiandone l’estensione in .asm e dopo alcune elaborazioni ho ottenuto il file “prg-nasm.asm”, costruito con la sintassi per l’assemblatore NASM.
Infine ho costruito l’eseguibile exe, perfettamente funzionante, con i due seguenti comandi, in sequenza:
PROMPT> nasm -f win64 prg-nasm.asm -o prg-nasm.o
PROMPT> ld msvcrt.dll prg-nasm.o -o prg-nasm-dll.exe
L’eseguibile ottenuto, perfettamente funzionante, occupa meno di 5KB, mentre l’eseguibile che si ottiene compilando con gcc, occupa circa 53KB.
La dll “msvcrt.dll” è presente nel sistema operativo Windows; basta cercarla e copiarla nella propria cartella di lavoro locale. Io ho trovato più di un file con questo nome e ho preso il primo che mi è capitato.
I sorgenti
Per concludere riporto i vari sorgenti, di cui parlo nell’articolo.
File “prg.s”:
.file "prg.c"
.intel_syntax noprefix
.text
.comm numero, 4, 2
.def __main; .scl 2; .type 32; .endef
.section .rdata,"dr"
.LC0:
.ascii "%d\0"
.LC1:
.ascii "%c\12\0"
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
sub rsp, 32
.seh_stackalloc 32
.seh_endprologue
call __main
lea rax, numero[rip]
mov rdx, rax
lea rcx, .LC0[rip]
call scanf
lea rax, numero[rip]
mov eax, DWORD PTR [rax]
cmp eax, 255
jg .L2
lea rax, numero[rip]
mov eax, DWORD PTR [rax]
mov edx, eax
lea rcx, .LC1[rip]
call printf
.L2:
mov eax, 0
add rsp, 32
pop rbp
ret
.seh_endproc
.ident "GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0"
.def scanf; .scl 2; .type 32; .endef
.def printf; .scl 2; .type 32; .endef
File “prg-nasm.asm”:
bits 64
section .text
global main
extern scanf
extern printf
main:
push rbp
mov rbp, rsp
sub rsp, 32
lea rax, numero
mov rdx, rax
lea rcx, LC0
call scanf
lea rax, numero
mov eax, [rax]
cmp eax, 255
jg L2
lea rax, numero
mov eax, [rax]
mov edx, eax
lea rcx, LC1
call printf
L2:
mov eax, 0
add rsp, 32
pop rbp
ret
section .data
LC0: db "%d",0
LC1: db "%c",12,0
section .bss
numero: resb 2
File “prg.c” (già riportato in precedenza):
#include <stdio.h>
int numero;
int main() {
scanf("%d",&numero);
if (numero < 256) { printf("%c\n",numero);};
return 0;
}
Spero che l’articolo vi piaccia e possa esservi utile.
Alla prossima.
Andrea Paolini ( Amministratore del Blog )