ottenere la lunghezza della stringa in GNU Assembler in linea

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

  1. Non inizializzo len , dal momento che asm lo dichiara come output (= c).
  2. Non è necessario copiare 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).
  3. Non c’è motivo di dire inline asm per mettere 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.
  4. Non c’è bisogno di avere l’asma zero 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).
  5. Posso usare i vincoli per inizializzare anche 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.
  6. Poiché i contenuti di ecx, eax ed edi sono tutti esplicitamente specificati, non è più necessario clobberare nulla.

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; }