Sto ri-imparando l’assemblatore che ho usato su macchine MS-DOS molto vecchie !!!
Questa è la mia comprensione di come dovrebbe essere quella funzione. Compilare ma si blocca con un SIGSEGV quando si tenta di inserire 0xffffffff
in ecx
.
Il codice viene eseguito in una VM con Debian 9. a 32 bit. Qualsiasi aiuto sarebbe apprezzato.
int getStringLength(const char *pStr){ int len = 0; char *Ptr = pStr; __asm__ ( "movl %1, %%edi\n\t" "xor %%al, %%al\n\t" "movl 0xffffffff, %%ecx\n\t" "repne scasb\n\t" "subl %%ecx,%%eax\n\t" "movl %%eax,%0" :"=r" (len) /*Output*/ :"r"(len) /*Input*/ :"%eax" /*Clobbered register*/ ); return len; }
Il problema con l’utilizzo di asm in linea di GCC per imparare l’assembly è che passi la metà del tuo tempo a imparare come funziona l’assembly inline di gcc invece di imparare effettivamente l’assembly. Ad esempio, ecco come potrei scrivere questo stesso codice:
#include int getStringLength(const char *pStr){ int len; __asm__ ( "repne scasb\n\t" "not %%ecx\n\t" "dec %%ecx" :"=c" (len), "+D"(pStr) /*Outputs*/ :"c"(-1), "a"(0) /*Inputs*/ /* tell the compiler we read the memory pointed to by pStr, with a dummy input so we don't need a "memory" clobber */ , "m" (*(const struct {char a; char x[];} *) pStr) ); return len; }
Vedi l’output asm del compilatore sul explorer del compilatore Godbolt . L’input fittizio della memoria è la parte delicata: vedi la discussione nei commenti e sulla mailing list gcc per il modo migliore di farlo, che è ancora sicuro.
Confrontando questo con il tuo esempio
len
, dal momento che asm lo dichiara come output (= c). pStr
poiché è una variabile locale. Per specifica, siamo già autorizzati a cambiarlo (anche se dato che è const
non dovremmo modificare i dati a cui punta). Ptr
in eax
, solo per avere il tuo asm spostarlo in edi
. Ho appena messo il valore in edi
in primo luogo. Nota che poiché il valore di edi
sta cambiando, non possiamo semplicemente dichiararlo come un ‘input’ (per specifica, in linea asm non deve cambiare il valore degli input). Cambiarlo in un output di lettura / scrittura risolve questo problema. eax
, dal momento che puoi avere i vincoli per farlo. Come vantaggio collaterale, gcc “saprà” che ha 0 nel registro eax
e (nei build ottimizzati) può riutilizzarlo (si pensi: controllando la lunghezza di 2 stringhe). ecx
. Come accennato, non è consentito modificare il valore degli input. Ma dal momento che definisco ecx
come output, gcc sa già che lo sto cambiando. Tutto ciò rende il codice (leggermente) più breve e più efficiente.
Ma questo è ridicolo. Come diamine (posso dire ‘diamine’ su SO?) Dovresti sapere tutto questo?
Se l’objective è imparare asm, usare asm in linea non è il tuo approccio migliore (in effetti direi che l’asm in linea è una ctriggers idea nella maggior parte dei casi). Ti consiglio di dichiarare getStringLength come extern e di scriverlo completamente in asm, quindi collegarlo con il tuo codice C.
In questo modo imparerai come passare parametri, restituire valori, conservare i registri (insieme a imparare quali registri devono essere preservati e quali puoi tranquillamente usare come scratch), impilare i frame, come colbind asm con C, ecc. Ecc. Ecc. Tutto che è più utile sapere di questo gobbledygook per in linea asm.
Finalmente ha funzionato:
int getStringLength(const char *pStr){ int len = 0; const char *Ptr = pStr; __asm__ ( "mov %%eax, %%edi\n\t" "xor %%eax, %%eax\n\t" "mov $0xffffffff, %%ecx\n\t" "repne scasb\n\t" "sub %%ecx,%%eax\n\t" "sub $2, %%eax\n\t" :"=a" (len) /*Output*/ :"a"(Ptr) /*Input*/ :"%ecx", "%edi" /*Clobbered register (ecx, edi)*/ ); return len; }