c:/djgpp/tmp/cbaaaaa(.text+0xac):fake: undefined reference to 'xxx'Cosa vuol dire?
xxx
.
Succede, normalmente, se:
%
davanti al nome di qualche registro
(ve ne accorgete perché xxx
è proprio il nome del registro in questione).
L'assemblatore ha interpretato il nome del registro come una etichetta di cui, poi,
il collegatore non ha trovato la definizione;
.global
e che le due etichette
coincidano; controllate infine le istruzioni call
.
xxx.s:Assembler messages: xxx.s:0: Warning: end of file not at the end of a line; new line insertedCosa vuol dire?
ret
: se è così,
il vostro programma, invece di tornare al chiamante, ``tira dritto'' e, generalmente,
l'effetto è quello descritto nella domanda successiva.
Exiting due to signal SIGSEGV Stack fault at eip xxxxxxxxe poi il contenuto in esadecimale di tutti i registri. Come posso individuare l'errore?
test
);
movl
al posto di una leal
o viceversa;
%edi
e %esi
vengono modificati da movsl
;
movl %ebp,%esp
al posto
di movl %esp, %ebp
.
%eip
al momento dell'errore (quello mostrato nell'output).
Usate poi il seguente comando:
objdump --disassemble a.exe > dis.txt(al posto di
a.exe
scrivete il nome dell'eseguibile che vi da errore).
In questo modo otteniamo il file dis.txt
che contiene il disassemblato
dell'eseguibile con, nella prima colonna, l'indirizzo di ogni istruzione.
Qui potete cercare l'istruzione che si trova all'indirizzo che era in %eip
quando si è verificato l'errore.
Purtroppo, anche se l'accesso scorretto è stato causato al 99.9999% da un
errore nel vostro programma (e non in Windows, o in DJGPP, o ...), l'effetto dell'errore potrebbe non essersi
manifestato subito, ma potrebbe aver causato un errore molto dopo, magari
durante l'invocazione di una funzione di libreria. In questo caso,
l'istruzione che corrisponde ad %eip
potrebbe non essere una tra quelle che
avete scritto voi, oppure non essere proprio all'interno dell'eseguibile. In
questo caso la faccenda è molto più complicata e, senza un debugger, non
resta che ispezionare meglio ciò che avete scritto.
A
,
gcc
passa per riferimento anche i parametri di tipo A
dichiarati per valore?A
e le seguenti funzioni
void f(A) {} // oggetto di classe A passato per valore A g() { A a; return a; } //oggetto di classe A restituitoe supponiamo di dover tradurre l'espressione:
f(g());la chiamata a
g
deve essere eseguita prima di quella a f
, e
bisogna passare a g
l'indirizzo a cui scrivere l'oggetto di classe A
da lei restituito. Sarebbe complicato impostare le cose in modo
che g
lo vada a scrivere direttamente nella posizione dello stack
in cui poi lo vuole f
(bisognerebbe cominciare a preparare il frame di f
prima di quello di g
, che però va chiamata per prima), quindi si preferisce
allocare un oggetto temporaneo (una variabile locale,
non dichiarata esplicitamente dal programmatore) e passare l'indirizzo di
tale variabile a g
. Terminata la chiamata a g
, bisogna costruire il frame
di attivazione di f
.
Ora: se non è stato ridefinito il costruttore di copia, basta copiare bit a
bit la variabile temporanea nel posto giusto del frame di f
. Ma se è stato
ridefinito il costruttore di copia? evidentemente il programmatore ha
ritenuto che la copia bit a bit, per questa classe, non ha senso, quindi non
possiamo prenderci la libertà di farla, in generale. Ma nemmeno possiamo
chiamare il costruttore di copia, perchè la necessità di questa copia è
nata da una nostra esigenza implementativa, e non era nella semantica del
linguaggio. Da qui il comportamento di gcc
: se il costruttore di copia
è stato ridefinito, la copia non viene fatta,
e l'oggetto viene passato a f
per riferimento.