Newsgroup: it.comp.lang.delphi
Titolo: [ASM] Impossibile scrivere in memoria sulle stringhe

[postato anche su libero.delphi di 
news.marcocantu.it
]
Ciao a tutti. 
Sto facendo delle prove con l'assembler. La procedura è la seguente:
//***************************************************************
procedure Fx23;
  procedure _Mask(var text; const start, len: Byte; mask: Char);
  asm
    PUSH  EDI               { salvo il registro di destinazione sullo 
                              stack }
    MOV   EDI,EAX           { carico in EDI il puntatore a text }
    ADD   EDI,BYTE PTR ECX  { sposto in avanti il puntatore su EDI, di un
                              numero di posti pari a start }
    MOV   AL,[EBP+$8]       { recupero il parametro mask e lo metto in 
                              EAX, come richiesto da STOS }
    MOV   CL,DL             { carico in ECX il parametro len - ECX è il 
                              contatore delle iterazioni di STOS }
    REP   STOSB             { riempio la destinazione puntata da EDI col 
                              valore in AL (è una STOSB, quindi 
                              trasferisce byte) e ripeto per ECX volte }
    POP   EDI               { ripristino EDI }
  end;
var
  P: array[0..50] of Char;       { zero-based null-terminated array di 
                                   Char }
begin
  P:= 'INIZIO_abcdefghi_FINE'#0; { lo #0 finale sembra inutile, forse 
                                   perchè P viene già inizializzato con 
                                   tutti #0 ?}
  _Mask(P, 11, 6, '@');
  ShowMessage(P);                { qui c'è una conversione tra PChar e 
                                   long string (AnsiString) }
end;
//*******************************************************************
Finche uso gli array di Char zero-based null-terminated, tutto bene, ma 
quando tento di usare le stringhe, mi viene sollevata un'eccezione mentre 
tento di scriverci sopra.
In particolare, sostituendo queste due righe:
  MOV   EDI,EAX           { carico in EDI il puntatore a text }
  var
    P: array[0..50] of Char; 
con queste:
  MOV   EDI,[EAX]         { modifica necessaria perchè la stringa
                            è un puntatore, quindi va dereferenziato }
  var 
    P: string;
e lanciando, mi viene sollevata una bella 'Access violation at address [la 
riga dove c'è scritto "REP STOSB"] in module [...]. Write of address 
[esattamente l'indirizzo di memoria pari al parametro start, cioè dove 
dovrebbe iniziare a scrivere sulla stringa] ...'
Questo è un'ostacolo insormontabile, vero? 
Può essere dovuto al meccanismo di gestione della memoria, che impedisce 
la scrittura su "aree protette"?
-- 
Mauro Falzari 
   [ 
http://digilander.libero.it/mauro.falzari
 ]
 
 "Se guardi nell'abisso, anche l'abisso guarda in te." 
   (Friedrich Nietzsche)

Questo è un'ostacolo insormontabile, vero? 
Io non mi azzarderei ad usare l'assembly direttamente sulle "long"
string, come ti ho già consigliato sui ng di Cantù, a meno di
conoscere in che modo implementare, via asm, i meccanismi interni di
gestione da parte di Delphi (come il copy-on-write, tanto per fare un
esempio).

 string, come ti ho già consigliato sui ng di Cantù
e infatti non si trova uno straccio di esempio in Rete (uno che sia uno!!)
pazienza :-)


 la scrittura su "aree protette"?
certo. Inizia ad abbandonare queste tecniche aggressive di gestione della
memoria: pensa che con Palladium non potrai neanche scrivere nelle aree di
memoria interne della tua applicazione, a meno che tu non abbia la chiave di
crittografia giusta ;)
Ciao, Sandro

e infatti non si trova uno straccio di esempio in Rete (uno che sia uno!!)
Onestamente, non capisco perché non si sia adottato lo stesso
meccanismo del C... curiosità: come mai ti interessa gestire le
stringhe via asm ?

 stringhe via asm ?
per pura curiosità! 
mentre studiavo l'inline assembler di Delphi e i modi in cui si può 
utilizzare il codice assembler all'interno di Delphi (e i modi in cui si può 
trarne beneficio), mi sono chiesto se si poteva intervenire direttamente 
sulle AnsiString in memoria, come si può fare per esempio con un PChar o un 
Integer e mi sono dato da fare per trovare delle risposte.
Mi sarebbe bastato iniziare con qualcosa di semplice, del tipo:
function X: string;
asm
end;
ma evidentemente non è così semplice come sembrava in un primo momento.
Vuol dire che, per quanto riguarda le AnsiString, restano le operazioni in 
"sola lettura"!


 la chiave di crittografia giusta ;)
sai che ti dico: W LINUX!!!



Sei assolutamente certo di questo?
Che io sappia un AnsiString e' un puntatore al primo carattere della stringa
(se presente) o nil se la stringa e' vuota.
In ogni caso, come giustamente ti hanno suggerito, devi fare ben attenzione
ai vari meccanismi di gestione dell'AnsiString. Per risolvere il problema
del meccanismo "copy on write" puoi chiamare la funzione UniqueString.
Ciao,
Stefano

 UniqueString. 
accidenti, sulle prime non ci volevo credere perchè prensavo di averle 
provate tutte e invece funziona!!! 
GRANDE!
La cosa strana è che una CALL UniqueString riesce, mentre per SetLength (come 
per SetString) bisogna ricorrere al trucchetto di chiamare una funzione 
definita in una unit che non sia System.
Questa funzione restituisce effettivamente la stringa '12345678':
function Fx19b: string;
  procedure MySetLength(var S: string; NewLength: Integer);
  begin
    SetLength(S, NewLength)
  end;
asm
  MOV   EAX,Result    //primo parametro per MySetLength
  MOV   EDX,8         //secondo parametro per MySetLength
  CALL  MySetLength
  CALL  UniqueString  //il primo parametro è già in EAX
  MOV   [EAX],'4321'     //scrivo in Result!!
  MOV   [EAX+4],'8765'
end;



Perche' SetLength non e' una funzione come le altre, un po' come Exit, Break
e Continue e la maggior parte delle implementazioni della unit System.
Se vuoi chiamare la SetLength direttamente, chiama la _LStrSetLength,
ricordando che il parametro AnsiString e' passato per indirizzo ed il
secondo parametro int e' la nuova lunghezza della stringa
Ciao,
Stefano

 _LStrSetLength
in effetti ci avevo provato perchč avevo trovato che i sorgenti delle 
procedure di System si trovano in $(DELPHI)\Source\Rtl\Sys\System.pas (i nomi 
delle procedure iniziano con un underscore), solo che non posso chiamare nč 
_LStrSetLength nč per esempio _SetLength, _New, _GetMem, _Dispose, da un 
blocco assembler perchč dovrei includere System.pas in una clausola uses.
Perņ non si puņ includere System.pas perchč:
"Identifier redeclared: 'System'"


"Identifier redeclared: 'System'"
Un workaround sarebbe il rinominare la unit e/o la dichiarazione incriminata
in altro modo, e usare quella. 
P.S.
Non e' la stessa cosa, ma io questo giochetto lo faccio quando voglio
duplicare un form che devo riutilizzare in gran parte: sposto il .pas e il
.dfm in una temp. Carico da delphi il .pas, rinomino il nome del form, salvo e
cosi' ottengo una unit nuova da includere nel progetto.
-- 
Morde. 
(morde at programmer dot net)


Premetto che non ho fatto prove, ma la System e' inclusa di default, quindi
una sua aggiunta nella lista delle uses non sortirebbe effetto alcuno.
Prova ad effettuare la call scrivendo:
System._LStrSetLength
Ciao,
Stefano

 form, salvo e cosi' ottengo una unit nuova da includere nel progetto.
Cosa c'e' che non va nell'InheritFrom (file / New / Other -> |Project1|)
Saluti
Paolo Arosio
--
"La dimostrazione dell'esistenza di specie intelligenti
 nell'universo e' che non ci vengono a trovare"

Cosa c'e' che non va nell'InheritFrom (file / New / Other -> |Project1|)
C'e' con non l'ho mai provato, Paolo!!!!
Ci sei allo smiauz venerdi mattina?


 Ci sei allo smiauz venerdi mattina?
Negativo, e neanche in altri giorni.


 System._LStrSetLength
purtroppo l'avevo già provato, e non funziona (Undeclared identifier: 
'_LStrSetLength').
Per  questo dicevo che non ci volevo credere che CALL UniqueString 
funzionasse (cioè venisse compilato), perchè UniqueString è definita in 
System, proprio come _SetLength, _LStrSetLength & co.
