Il programma C esce immediatamente dopo l'esecuzione, ma ha un ciclo while (1)
Sto cercando di adattare un programma minesweeper in c sotto un limite di 3 KB in modo che possa adattarsi a un codice QR, e funzionava bene fino a quando non ho aggiunto l'opzione -nostdlib
e l'ho compilato, il programma è uscito immediatamente, nonostante avesse un po 'di tempo (1 ) loop in main senza possibilità di uscire.
Le librerie che ho aggiunto in linker sono stati kernel32
, gcc
e msvcrt
(nel caso in cui si vuole ri-produrlo). E anche se tecnicamente restituiva 0x0, il che significa che era "riuscito", ovviamente non lo era; o non l'avrebbe fatto. Il mio codice può essere visto qui:
#include <windows.h>
#define WIDTH 100
#define HEIGHT 100
#define BOMBS 801
int xorshf96(int x, int y, int z)
{
unsigned long t;
x ^= x << 16;
x ^= x >> 5;
x ^= x << 1;
t = x;
x = y;
y = z;
z = t ^ x ^ y;
return z;
}
void ExpandGrid(int fullGrid[WIDTH][HEIGHT], int knownGrid[WIDTH][HEIGHT], int blankPos[2])
{
int neighbors[8][2] = {{0,1}, {1,0}, {1,1},
{0,-1}, {-1,0},
{-1,-1},{-1,1},{1,-1}};
int curTile[2];
knownGrid[blankPos[0]][blankPos[1]] = 1;
if(fullGrid[blankPos[0]][blankPos[1]] != 0) return;
for(int blck = 0; blck < 8; ++blck)
{
curTile[0] = abs(blankPos[0]+neighbors[blck][0]);
curTile[1] = abs(blankPos[1]+neighbors[blck][1]);
if(curTile[0] > WIDTH-1 || curTile[1] > HEIGHT-1) continue;
if(fullGrid[curTile[0]][curTile[1]] == 0 && knownGrid[curTile[0]][curTile[1]] == 0)
{
knownGrid[curTile[0]][curTile[1]] = 1;
ExpandGrid(fullGrid, knownGrid, curTile);
}
else if(fullGrid[curTile[0]][curTile[1]] > 0) knownGrid[curTile[0]][curTile[1]] = 1;
}
}
int main(void)
{
COORD characterBufferSize = { WIDTH, HEIGHT };
COORD characterPosition = { 0, 0 };
SMALL_RECT consoleWriteArea = { 0, 0, WIDTH - 1, HEIGHT - 1 };
CHAR_INFO consoleBuffer[WIDTH][HEIGHT];
HANDLE wHnd = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE rHnd = GetStdHandle(STD_INPUT_HANDLE);
int num1, num2, num3 = 0;
SYSTEMTIME systime;
DWORD numEventsRead = 0;
DWORD numEvents = 0;
INPUT_RECORD *eventBuffer;
int wait = 0;
SetConsoleTitle("Minesweeper!");
int startGrid[WIDTH][HEIGHT] = { 0 };
int knownGrid[WIDTH][HEIGHT] = { 0 };
int arrowPos[2] = {0, 0};
int bomb[2] = {0, 0};
for (int i = 0; i < BOMBS; i++)
{
while (startGrid[bomb[0]][bomb[1]] < -1 || bomb[0] <= 0 || bomb[1] <= 0 || bomb[0] >= WIDTH-1 || bomb[1] >= HEIGHT-1)
{
GetLocalTime(&systime);
num1, num2, num3 = (int) systime.wMilliseconds;
bomb[1] = (xorshf96(num3, num2, num1) % (HEIGHT-1)) + 1;
GetSystemTime(&systime);
num1, num2, num3 = (int) systime.wMilliseconds;
bomb[0] = (xorshf96(num1, num2, num3) % (WIDTH-1)) + 1;
}
startGrid[bomb[0]][bomb[1]] = -9;
startGrid[bomb[0] + 1][bomb[1] + 1]++;
startGrid[bomb[0] + 1][bomb[1]]++;
startGrid[bomb[0]][bomb[1] + 1]++;
startGrid[bomb[0] - 1][bomb[1] + 1]++;
startGrid[bomb[0]][bomb[1] - 1]++;
startGrid[bomb[0] + 1][bomb[1] - 1]++;
startGrid[bomb[0] - 1][bomb[1] - 1]++;
startGrid[bomb[0] - 1][bomb[1]]++;
}
while(1)
{
if (arrowPos[0] > WIDTH-1) arrowPos[0] = WIDTH-1;
if (arrowPos[0] < 0) arrowPos[0] = 0;
if (arrowPos[1] > HEIGHT-1) arrowPos[1] = HEIGHT-1;
if (arrowPos[1] < 0) arrowPos[1] = 0;
for (int x = 0; x < WIDTH; ++x)
{
for (int y = 0; y < HEIGHT; ++y)
{
if (knownGrid[x][y] == 1)
{
if (startGrid[x][y] > 0)
{
consoleBuffer[x][y].Char.AsciiChar = '0' + startGrid[x][y];
consoleBuffer[x][y].Attributes = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
}
else
{
consoleBuffer[x][y].Char.AsciiChar = 'o';
consoleBuffer[x][y].Attributes = (startGrid[x][y] < 0 ? FOREGROUND_RED : FOREGROUND_BLUE) | FOREGROUND_INTENSITY;
}
}
else
{
consoleBuffer[x][y].Char.AsciiChar = 00;
consoleBuffer[x][y].Attributes = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
}
if(arrowPos[0] == x && arrowPos[1] == y)
{
consoleBuffer[x][y].Attributes = BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN;
}
}
}
WriteConsoleOutputA(wHnd, *consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
numEvents = 0;
numEventsRead = 0;
GetNumberOfConsoleInputEvents(rHnd, &numEvents);
if (numEvents)
{
eventBuffer = malloc(sizeof(INPUT_RECORD) * numEvents);
ReadConsoleInput(rHnd, eventBuffer, numEvents, &numEventsRead);
}
if(numEventsRead && wait <= 0)
{
wait = 50;
switch (eventBuffer[0].Event.KeyEvent.wVirtualKeyCode)
{
case VK_UP:
arrowPos[0]--;
break;
case VK_DOWN:
arrowPos[0]++;
break;
case VK_LEFT:
arrowPos[1]--;
break;
case VK_RIGHT:
arrowPos[1]++;
break;
case VK_RETURN:
ExpandGrid(startGrid, knownGrid, arrowPos);
break;
}
}
wait--;
}
}
Risposte
L'uso di -nostdlib
ha due effetti:
- impedisce che il materiale della libreria standard venga automaticamente collegato; e
- interrompe il
crt0
collegamento del codice di avvio del runtime C.
Quest'ultimo è importante, normalmente ci sono molte cose che accadono prima che main()
venga chiamato e, se non si collega, crt0
il vero punto di ingresso _start
(non main
) non esiste.
Quindi la soluzione è fornire il proprio _start
ma senza tutta la confusa configurazione dell'ambiente C necessaria per i veri programmi C. È semplice come fornire un mystart.S
come segue:
.globl _start
_start:
call main # call main without any setup.
movl %eax, %ebx # copy return value to pass back.
movl $1, %eax # function code for exit. int $0x80 # invoke function.
Combinalo con un programma C molto semplice come segue:
int main(void) {
return 42;
}
Quindi puoi compilarlo e testarlo, come da trascrizione seguente:
pax> gcc -nostdlib -o nolibc nolibc.c mystart.S
pax> ./nolibc ; echo $?
42
Questo risponde alla tua domanda specifica : "Perché non funziona?". Tuttavia, ho comunque finito con una dimensione eseguibile piuttosto grossa, anche con alcune ottimizzazioni extra raccolte da altrove:
gcc -nostdlib -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -o nolibc nolibc.c mystart.S
strip -s -R .comment -R .gnu.version --strip-unneeded nolibc
Quindi, oltre a ciò, probabilmente dovrai giocherellare con lo script del linker per caricare solo ciò che è assolutamente necessario per l'esecuzione del tuo eseguibile.
Non ne parlerò qui poiché è un argomento piuttosto ampio e non pertinente alla tua domanda specifica. Suggerirei di porre (dopo aver cercato ovviamente) una domanda diversa su come farlo.
E tieni presente che int $80
è un pensiero Linux, dalla memoria. Se vuoi fare qualcosa di simile per Windows, dovresti esaminare come funziona il crt0
codice su quella piattaforma. È probabile che ExitProcess()
venga utilizzato, quindi avresti bisogno di qualcosa del tipo (non testato, quindi dovrai eseguire il debug da solo):
.globl _start
.extern ExitProcess
_start:
call main # call main without any setup.
push %eax # push main return code for delivery to OS.
# win64 equiv is, I think: mov %eax, %ecx
call ExitProcess # And off we go.
e poi scopri come collegarti ai file dll / lib di Windows.