DOMANDA Cosa contiene effettivamente un registro contenente un indirizzo di memoria?

Hero467

Utente Attivo
689
404
OS
I use ARCH btw
È appurato che indirizzi di memoria e, soprattutto, registri, in asm possono contenere vari bytes (e anche qui non mi è chiara una cosa, ma aprirò un altro topic per questo). Quindi se in un programma in C dichiaro e assegno una variabile int con valore 5, disassemblando l’eseguibile compilato da questo codice troverò un registro contenente un indirizzo di memoria che contiene 0x5.
Ultimamente mi sono messo a studiare i puntatori, e per curiosità sono andato ad esaminare l’asm di un codice simile a questo:
C:
#include <stdio.h>
#include <string.h>

int main() {
    char str_a[20];
    char *pointer;
    char *pointer2;
    
    strcpy(str_a, "Hello, World!\n");
    pointer = str_a;
    printf("%s", pointer);
    
    pointer2 = pointer + 2;
    strcpy(pointer2, "y you guys!\n");
    printf("%s", pointer2);
    
    printf("%s", pointer);
}

Andando ad analizzare pointer con gdb e usando la funzione address-of viene ovviamente fuori l’indirizzo di memoria “puntato” dal puntatore, ma effettivamente come memorizza quell’indirizzo? Il processore non può memorizzare altri che bit e bytes teoricamente
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,223
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Finalmente qualche quesito su questi argomenti... 😁

È appurato che indirizzi di memoria e, soprattutto, registri, in asm possono contenere vari bytes (e anche qui non mi è chiara una cosa, ma aprirò un altro topic per questo). Quindi se in un programma in C dichiaro e assegno una variabile int con valore 5, disassemblando l’eseguibile compilato da questo codice troverò un registro contenente un indirizzo di memoria che contiene 0x5.

No, non è così semplice la questione.
Io assumo come architettura x86 e x86-64. I registri sono pochi, specie quelli generali, che possono essere usati per vari compiti. Quindi non è detto che un valore verrà posizionato - dal compilatore - in un registro.

L'utilizzo di un registro dipende ad esempio dal tipo di operazione che stai facendo: per esempio, il classico "contatore di un ciclo" (la 'i' in un ciclo come for(int i=0; i<n; i++) {}) spesse volte viene inserito/spostato nel registro ECX (o RCX); questo registro è utilizzato come contatore, quando è disponibile (la C sta proprio per Counter).

Quando dichiari delle variabili all'interno di una funzione, queste molto probabilmente verranno memorizzate sullo stack (nello stack frame della funzione).
Ti faccio un esempio più complicato di prima (e non ottimizzato):

C:
int sum_multiples(int n, int max) {
    int sum = 0;
    
    for(int i = 0; i<max; i++) {
        if(0 == i%n) {
            sum += i;
        }
    }
    return sum;
}

Ho compilato da Godbolt per comodità: https://godbolt.org/z/qPsxdPoWs
Il codice asm prodotto è il seguente:

Codice:
sum_multiples(int, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-20], edi
        mov     DWORD PTR [rbp-24], esi
        mov     DWORD PTR [rbp-4], 0
        mov     DWORD PTR [rbp-8], 0
        jmp     .L2
.L4:
        mov     eax, DWORD PTR [rbp-8]
        cdq
        idiv    DWORD PTR [rbp-20]
        mov     eax, edx
        test    eax, eax
        jne     .L3
        mov     eax, DWORD PTR [rbp-8]
        add     DWORD PTR [rbp-4], eax
.L3:
        add     DWORD PTR [rbp-8], 1
.L2:
        mov     eax, DWORD PTR [rbp-8]
        cmp     eax, DWORD PTR [rbp-24]
        jl      .L4
        mov     eax, DWORD PTR [rbp-4]
        pop     rbp
        ret

Qui per esempio noterai che la variabile sum è in realtà [rbp-4] e la variabile i è [rbp-8].
Per queste due variabili è stato scelto di usare lo stack, invece che un registro. Se noti però viene poi spostato in un registro quando è necessario effettuare qualche operazione (i registri sono molto efficienti, ma sono pochi, come dicevo sopra).

Se compili (sempre dal sito che ho linkato sopra) passando come parametro flag -O2, allora il codice che vedrai verrà ottimizzato:

Codice:
sum_multiples(int, int):
        test    esi, esi
        jle     .L5
        xor     ecx, ecx
        xor     r8d, r8d
.L4:
        mov     eax, ecx
        cdq
        idiv    edi
        lea     eax, [r8+rcx]
        test    edx, edx
        cmove   r8d, eax
        add     ecx, 1
        cmp     esi, ecx
        jne     .L4
        mov     eax, r8d
        ret
.L5:
        xor     r8d, r8d
        mov     eax, r8d
        ret

Qui le due variabili di prima sono ECX (variabile i) e R8D (la somma).
Insomma, è per mostrarti che in questo caso, ottimizzandolo, sono stati scelti i registri (e anche gli argomenti della funzione, vengono passati nei registri).

Andando ad analizzare pointer con gdb e usando la funzione address-of viene ovviamente fuori l’indirizzo di memoria “puntato” dal puntatore, ma effettivamente come memorizza quell’indirizzo? Il processore non può memorizzare altri che bit e bytes teoricamente

Se ho ben capito, ti stai chiedendo come viene memorizzato un indirizzo rispetto ad un semplice valore numerico, come ad esempio 0x123. Questo viene memorizzato sempre nell'istruzione stessa.

Dovresti riportare il codice assembly che vedi sotto tag CODE, così possiamo capire qual è il tuo dubbio in merito.
 
  • Mi piace
Reazioni: ilfe98 e Mursey

Hero467

Utente Attivo
689
404
OS
I use ARCH btw
Ho capito la metà di quello che hai scritto effettivamente.

Finalmente qualche quesito su questi argomenti... 😁
Mi aspettavo una tua risposta infatti

Finalmente qualche quesito su questi argomenti... 😁



No, non è così semplice la questione.
Io assumo come architettura x86 e x86-64. I registri sono pochi, specie quelli generali, che possono essere usati per vari compiti. Quindi non è detto che un valore verrà posizionato - dal compilatore - in un registro.
Errore mio. Volevo dire un'altra cosa. So che i registri servono da posti in cui il processore mette gli indirizzi di memoria che gli serviranno per le operazioni da fare in quel momento (spiegata in modo molto semplice, e neanche tanto giusto probabilmente)

Se ho ben capito, ti stai chiedendo come viene memorizzato un indirizzo rispetto ad un semplice valore numerico, come ad esempio 0x123. Questo viene memorizzato sempre nell'istruzione stessa.
Più o meno si. La domanda era: come interpreta la cpu gli indirizzi di memoria se capisce solo 1 e 0? Li memorizza sotto forma di bytes?

Dovresti riportare il codice assembly che vedi sotto tag CODE, così possiamo capire qual è il tuo dubbio in merito.
Codice:
   0x0000000000001169 <+0>:        endbr64
   0x000000000000116d <+4>:        push   rbp
   0x000000000000116e <+5>:        mov    rbp,rsp
   0x0000000000001171 <+8>:        sub    rsp,0x40
   0x0000000000001175 <+12>:    mov    DWORD PTR [rbp-0x34],edi
   0x0000000000001178 <+15>:    mov    QWORD PTR [rbp-0x40],rsi
   0x000000000000117c <+19>:    mov    rax,QWORD PTR fs:0x28
   0x0000000000001185 <+28>:    mov    QWORD PTR [rbp-0x8],rax
   0x0000000000001189 <+32>:    xor    eax,eax
   0x000000000000118b <+34>:    lea    rax,[rbp-0x20]
   0x000000000000118f <+38>:    movabs rcx,0x57202c6f6c6c6548
   0x0000000000001199 <+48>:    mov    QWORD PTR [rax],rcx
   0x000000000000119c <+51>:    mov    DWORD PTR [rax+0x8],0x646c726f
   0x00000000000011a3 <+58>:    mov    WORD PTR [rax+0xc],0xa21
   0x00000000000011a9 <+64>:    mov    BYTE PTR [rax+0xe],0x0
   0x00000000000011ad <+68>:    lea    rax,[rbp-0x20]
   0x00000000000011b1 <+72>:    mov    QWORD PTR [rbp-0x30],rax
   0x00000000000011b5 <+76>:    mov    rax,QWORD PTR [rbp-0x30]
   0x00000000000011b9 <+80>:    mov    rdi,rax
   0x00000000000011bc <+83>:    mov    eax,0x0
   0x00000000000011c1 <+88>:    call   0x1070 <printf@plt>
   0x00000000000011c6 <+93>:    mov    rax,QWORD PTR [rbp-0x30]
   0x00000000000011ca <+97>:    add    rax,0x2
   0x00000000000011ce <+101>:    mov    QWORD PTR [rbp-0x28],rax
   0x00000000000011d2 <+105>:    mov    rax,QWORD PTR [rbp-0x28]
   0x00000000000011d6 <+109>:    mov    rdi,rax
   0x00000000000011d9 <+112>:    mov    eax,0x0
   0x00000000000011de <+117>:    call   0x1070 <printf@plt>
   0x00000000000011e3 <+122>:    mov    rax,QWORD PTR [rbp-0x28]
   0x00000000000011e7 <+126>:    movabs rdx,0x756720756f792079
   0x00000000000011f1 <+136>:    mov    QWORD PTR [rax],rdx
   0x00000000000011f4 <+139>:    mov    DWORD PTR [rax+0x8],0xa217379
   0x00000000000011fb <+146>:    mov    BYTE PTR [rax+0xc],0x0
   0x00000000000011ff <+150>:    mov    rax,QWORD PTR [rbp-0x30]
   0x0000000000001203 <+154>:    mov    rdi,rax
   0x0000000000001206 <+157>:    mov    eax,0x0
   0x000000000000120b <+162>:    call   0x1070 <printf@plt>
   0x0000000000001210 <+167>:    mov    eax,0x0
   0x0000000000001215 <+172>:    mov    rdx,QWORD PTR [rbp-0x8]
   0x0000000000001219 <+176>:    sub    rdx,QWORD PTR fs:0x28
   0x0000000000001222 <+185>:    je     0x1229 <main+192>
   0x0000000000001224 <+187>:    call   0x1060 <__stack_chk_fail@plt>
   0x0000000000001229 <+192>:    leave
   0x000000000000122a <+193>:    ret
Questo è il codice, disassemblato con gdb.

Però, se io andassi a impostare un breakpoint sul primo printf, ed esaminassi pointer con x/4xb pointer verrebbero fuori i primi quattro caratteri della stringa "Hello world" in formato esadecimale. Se invece facessi x &pointer mi verrebbe fuori l'indirizzo di memoria corrispondente a dove sono memorizzati quei bytes. Quindi il processore come interpreta quell'indirizzo? come memorizza un indirizzo in unaltro indirizzo, se questo puo contenere solo bytes?
 
Ultima modifica:

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,223
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Ho capito la metà di quello che hai scritto effettivamente.

Cercherò di spiegarmi meglio allora.

Errore mio. Volevo dire un'altra cosa. So che i registri servono da posti in cui il processore mette gli indirizzi di memoria che gli serviranno per le operazioni da fare in quel momento (spiegata in modo molto semplice, e neanche tanto giusto probabilmente)

Non è proprio corretto in effetti, ma per comprendere meglio alcune cose dovresti studiare un pò di assembly x86.

Per farti un esempio semplice, vedi un registro come una locazione di memoria qualsiasi, solo che è molto più veloce, e la sua memoria è limitata (lasciando perdere i set di istruzioni come SSE/AVX, che sono ben più ampi, ma la sostanza è la medesima).

In assembly esiste l'operatore di deriferimento come esiste in C, solo che invece di essere l'asterisco, si utilizzano le parentesi quadrate.

Codice:
mov  esi, 0x00400000

con questo codice stai spostando il numero 0x00400000 nel registro ESI. Se ora fai questo:

Codice:
mov  eax, [esi]

stai accedendo alla locazione di memoria 0x00400000, e stai prendendo il valore che è contenuto in quella posizione.

Questo per darti una base minima quando leggi del codice in assembly.

Più o meno si. La domanda era: come interpreta la cpu gli indirizzi di memoria se capisce solo 1 e 0? Li memorizza sotto forma di bytes?

L'indirizzo a cui la CPU accede è codificato nell'istruzione stessa. Ci sono però casi nei quali l'indirizzo non è conosciuto in fase di compilazione.

Ho compilato anche io, così riesco a mostrare del codice, sperando di essere comprensibile:

Codice:
00007FF63E491591   | 48:8D45 D0            | lea rax,qword ptr ss:[rbp-30]                          |
00007FF63E491595   | 48:BA 48656C6C6F2C205 | mov rdx,57202C6F6C6C6548                               |
00007FF63E49159F   | 48:8910               | mov qword ptr ds:[rax],rdx                             |

questa parte più o meno è simile alla tua.
RAX è praticamente il tuo array di caratteri, la destinazione. In RAX qui viene salvato l'indirizzo del buffer, grazie all'istruzione LEA (viene eseguita praticamente quella RBP-30 e il risultato è l'indirizzo che viene salvato in RAX).

I numeri che vedi successivamente sono una parte della tua stringa: il compilatore per ottimizzare ha scelto di comporre la stringa usando direttamente i bytes: infatti se li guardi al contrario sono già i caratteri della tua stringa:

Codice:
0x48 = H
0x65 = e
0x6C = l
0x6C = l
...

visto che RAX contiene un indirizzo, viene usato l'operatore di deriferimento (le parentesi quadre, in C useresti *), per accedere a quell'indirizzo. A questo indirizzo viene salvato un numero, che è appunto la stringa di cui sopra.
Proseguendo si vede:

Codice:
00007FF63E4915A2   | 48:B9 576F726C64210A0 | mov rcx,A21646C726F57                                  |
00007FF63E4915AC   | 48:8948 07            | mov qword ptr ds:[rax+7],rcx                           |

viene fatta la medeisma cosa con RCX che conterrà i numeri che rappresentano "World!"; viene inserito alla posizione RAX+7 in quanto nella parte precedente, da RAX+0 a RAX+6 c'è "Hello ".

In questo caso è possibile fare questa cosa perchè la stringa è conosciuta a runtime. E' stato fatto in questo modo in quanto non l'hai memorizzata in una variabile, altrimenti avresti visto direttamente un indirizzo di memoria, guardando da debugger.

Quindi il processore come interpreta quell'indirizzo? come memorizza un indirizzo in unaltro indirizzo, se questo puo contenere solo bytes?

Non è il processore a "memorizzare un indirizzo", è stabilito nella codifica dell'istruzione.
In sostanza il compilatore in questo caso per effettuare la copia che fai con strcpy fa questo:

(usando il tuo codice)
Codice:
0x000000000000118f <+38>:    movabs rcx,0x57202c6f6c6c6548
   0x0000000000001199 <+48>:    mov    QWORD PTR [rax],rcx
   0x000000000000119c <+51>:    mov    DWORD PTR [rax+0x8],0x646c726f
   0x00000000000011a3 <+58>:    mov    WORD PTR [rax+0xc],0xa21
   0x00000000000011a9 <+64>:    mov    BYTE PTR [rax+0xe],0x0

in pratica il compilatore non richiama la funzione strcpy, ma la mette "inline" nel codice. Quindi non sta memorizzando l'indirizzo; sta proprio memorizzando i caratteri che compongono la stringa (ma sottoforma di numeri).

Probabilmente queste cose è più comodo vederle da GUI, magari con Radare o Ghidra se sei su Linux; se sei su Windows hai l'imbarazzo della scelta x64dbg e Olly sono solo due tra queste.

Se hai domande specifiche fai pure; ti consiglierei di guardarti un pò assembly anche (per l'altro argomento, se la domanda non è "in topic", aprine pure un altro 😉).
 

Andretti60

Utente Èlite
6,440
5,091
Una cella di memoria non è altro che un insieme di “bit” (un bit può memorizzare solo zero e uno, ossia due valori), ci sono tanti bit quanto la cpu ne può elaborare, i calcolatori più recenti sono a 64 bit.
Quattro bit corrispondono a un byte, quindi in un calcolatore moderno ogni memoria può memorizzare 8 bytes.

Come questi uno e zeri vengono interpretati dal calcolatore (più precisamente dalla CPU, ossia il processore) dipende da “cosa” ci si aspetta ci sia in quella cella di memoria.
Può essere l’indirizzo di un’altra memoria (nel quale caso i 64 bit vengono letti come un numero binario a 64 bit), può essere un valore numerico (non necessariamente a 64 bit, ne può usare di meno) può essere la rappresentazione numerica di un simbolo letterale. E ovviamente può essere una istruzione che la CPU deve elaborare, nel quale caso alcuni bit della cella rappresentano il codice numerico della istruzione, gli altri dipendono dal tipo di istruzione.
 

Hero467

Utente Attivo
689
404
OS
I use ARCH btw
Non è proprio corretto in effetti, ma per comprendere meglio alcune cose dovresti studiare un pò di assembly x86.
Nel libro che sto leggendo (che qualcosina ha introdotto, ma giusto il necessario a comprendere programmi molto basici in C come questo) è previsto un capitolo sull'assembly x86, ma in 32 bit, visto che il libro è un po' datato (The art of exploitation 2nd edition, 2008)

Per farti un esempio semplice, vedi un registro come una locazione di memoria qualsiasi, solo che è molto più veloce, e la sua memoria è limitata (lasciando perdere i set di istruzioni come SSE/AVX, che sono ben più ampi, ma la sostanza è la medesima).

In assembly esiste l'operatore di deriferimento come esiste in C, solo che invece di essere l'asterisco, si utilizzano le parentesi quadrate.

Codice:
mov  esi, 0x00400000

con questo codice stai spostando il numero 0x00400000 nel registro ESI. Se ora fai questo:

Codice:
mov  eax, [esi]

stai accedendo alla locazione di memoria 0x00400000, e stai prendendo il valore che è contenuto in quella posizione.

Questo per darti una base minima quando leggi del codice in assembly.



L'indirizzo a cui la CPU accede è codificato nell'istruzione stessa. Ci sono però casi nei quali l'indirizzo non è conosciuto in fase di compilazione.

Ho compilato anche io, così riesco a mostrare del codice, sperando di essere comprensibile:

Codice:
00007FF63E491591   | 48:8D45 D0            | lea rax,qword ptr ss:[rbp-30]                          |
00007FF63E491595   | 48:BA 48656C6C6F2C205 | mov rdx,57202C6F6C6C6548                               |
00007FF63E49159F   | 48:8910               | mov qword ptr ds:[rax],rdx                             |

questa parte più o meno è simile alla tua.
RAX è praticamente il tuo array di caratteri, la destinazione. In RAX qui viene salvato l'indirizzo del buffer, grazie all'istruzione LEA (viene eseguita praticamente quella RBP-30 e il risultato è l'indirizzo che viene salvato in RAX).

I numeri che vedi successivamente sono una parte della tua stringa: il compilatore per ottimizzare ha scelto di comporre la stringa usando direttamente i bytes: infatti se li guardi al contrario sono già i caratteri della tua stringa:

Codice:
0x48 = H
0x65 = e
0x6C = l
0x6C = l
...

visto che RAX contiene un indirizzo, viene usato l'operatore di deriferimento (le parentesi quadre, in C useresti *), per accedere a quell'indirizzo. A questo indirizzo viene salvato un numero, che è appunto la stringa di cui sopra.
Proseguendo si vede:

Codice:
00007FF63E4915A2   | 48:B9 576F726C64210A0 | mov rcx,A21646C726F57                                  |
00007FF63E4915AC   | 48:8948 07            | mov qword ptr ds:[rax+7],rcx                           |

viene fatta la medeisma cosa con RCX che conterrà i numeri che rappresentano "World!"; viene inserito alla posizione RAX+7 in quanto nella parte precedente, da RAX+0 a RAX+6 c'è "Hello ".

In questo caso è possibile fare questa cosa perchè la stringa è conosciuta a runtime. E' stato fatto in questo modo in quanto non l'hai memorizzata in una variabile, altrimenti avresti visto direttamente un indirizzo di memoria, guardando da debugger.



Non è il processore a "memorizzare un indirizzo", è stabilito nella codifica dell'istruzione.
In sostanza il compilatore in questo caso per effettuare la copia che fai con strcpy fa questo:

(usando il tuo codice)
Codice:
0x000000000000118f <+38>:    movabs rcx,0x57202c6f6c6c6548
   0x0000000000001199 <+48>:    mov    QWORD PTR [rax],rcx
   0x000000000000119c <+51>:    mov    DWORD PTR [rax+0x8],0x646c726f
   0x00000000000011a3 <+58>:    mov    WORD PTR [rax+0xc],0xa21
   0x00000000000011a9 <+64>:    mov    BYTE PTR [rax+0xe],0x0

in pratica il compilatore non richiama la funzione strcpy, ma la mette "inline" nel codice. Quindi non sta memorizzando l'indirizzo; sta proprio memorizzando i caratteri che compongono la stringa (ma sottoforma di numeri).
È molto da digerire, ma me lo studierò meglio. Grazie della risposta dettagliata

Probabilmente queste cose è più comodo vederle da GUI, magari con Radare o Ghidra se sei su Linux; se sei su Windows hai l'imbarazzo della scelta x64dbg e Olly sono solo due tra queste.
Gli darò sicuramente un'occhiata, mi semplificherebbe la vita

Se hai domande specifiche fai pure; ti consiglierei di guardarti un pò assembly anche (per l'altro argomento, se la domanda non è "in topic", aprine pure un altro 😉).
Probabilmente aprirò un altro topic, ma era una semplice curiosità su quanto spazio può contenere un indirizzo di memoria
Post unito automaticamente:

Una cella di memoria non è altro che un insieme di “bit” (un bit può memorizzare solo zero e uno, ossia due valori), ci sono tanti bit quanto la cpu ne può elaborare, i calcolatori più recenti sono a 64 bit.
Quattro bit corrispondono a un byte, quindi in un calcolatore moderno ogni memoria può memorizzare 8 bytes.

Come questi uno e zeri vengono interpretati dal calcolatore (più precisamente dalla CPU, ossia il processore) dipende da “cosa” ci si aspetta ci sia in quella cella di memoria.
Può essere l’indirizzo di un’altra memoria (nel quale caso i 64 bit vengono letti come un numero binario a 64 bit), può essere un valore numerico (non necessariamente a 64 bit, ne può usare di meno) può essere la rappresentazione numerica di un simbolo letterale. E ovviamente può essere una istruzione che la CPU deve elaborare, nel quale caso alcuni bit della cella rappresentano il codice numerico della istruzione, gli altri dipendono dal tipo di istruzione.
Quindi essenzialmente il processore memorizza un indirizzo di memoria con un codice numerico?
 
Ultima modifica:

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,223
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Nel libro che sto leggendo (che qualcosina ha introdotto, ma giusto il necessario a comprendere programmi molto basici in C come questo) è previsto un capitolo sull'assembly x86, ma in 32 bit, visto che il libro è un po' datato (The art of exploitation 2nd edition, 2008)
Va bene anche quello.
La versione x86 è 32bit, l'altro è x86-64.

Probabilmente aprirò un altro topic, ma era una semplice curiosità su quanto spazio può contenere un indirizzo di memoria

Se è solo questo allora la risposta è breve.
L'indirizzo di memoria è solo un indirizzo che fa riferimento a una cella di memoria. L'unità più piccola a cui puoi accedere è 1byte.

Nelle istruzioni sopra se noti ci sono tipi di dato come QWORD, ovvero 64bit. Questo dipende dall'architettura.
In quel caso avviene un accesso alla memoria e viene letta una QWORD.
 

BAT

Moderatore
Staff Forum
Utente Èlite
22,923
11,563
CPU
1-Neurone
Dissipatore
Ventaglio
RAM
Scarsa
Net
Segnali di fumo
OS
Windows 10000 BUG
Quindi essenzialmente il processore memorizza un indirizzo di memoria con un codice numerico?
un indirizzo di memoria è un numero per sua natura: le celle di RAM sono numerate a partire da 0 in formato binario
 

bigendian

Utente Attivo
742
425
OS
Linux
Errore mio. Volevo dire un'altra cosa. So che i registri servono da posti in cui il processore mette gli indirizzi di memoria che gli serviranno per le operazioni da fare in quel momento (spiegata in modo molto semplice, e neanche tanto giusto probabilmente)
Sono un po' arrugginito su assembly x86, comunque integro le risposte sopra,

1) di un programma C, a seconda delle ottimizzazioni scelte, possono avvenire cose molto diverse nel disassemblato,
2) come hai detto correttamente all'inizio, ci si puo riferire alla variabile, nel tuo caso nello stack, con un puntatore. Nello stack, per x86_64, significa generalmente in cache della cpu,
3) Se su quella variabile si devono eseguire dei calcoli, la cpu puo portarne il valore in un registro, ed in genere a questo servono i registri, eseguire calcoli, ma non necessariamente, perche' alcune istruzioni consentono di operare calcoli su valori in memoria. Ci sono modi in C per specificare che una variabile stia in un registro, vedi "register", si usa per scopi ben specifici, per eseguire calcoli in maniera performante o perche' serve accedere ad un registro alla fine di un operazione.
4) in x86_64 il bus connesso alla memoria e' 64 bit, se accedi a un byte che non e' in cache, comunque il tempo e' lo stesso che accedere a 64bit.

Puoi accedere a parti dei registri usando gli appositi nomi

1664867705800.png

Trovato immagine carina anche se non in scala, la parte dei 32bit alti dovrebbe essere piu lunga. Quindi non puoi accedere al singolo byte alto dei 64bit, ne ai 2 alti dei 32bit, etc, devi operare sui 64bit con shift e or.
Post unito automaticamente:

ecco., meglio questa

1664868241300.png
 
Ultima modifica:

Andretti60

Utente Èlite
6,440
5,091
Quindi essenzialmente il processore memorizza un indirizzo di memoria con un codice numerico?
Esatto.
Pensa alla memoria come una cassettiera, un cassetto sopra l’altro, il primo cassetto ha indice zero per cui il numero “zero” è il suo indirizzo.
Un programma “traduce” il nome di una variabile con un indirizzo di memoria, per esempio nel tuo programma puoi usare la variabile “pluto” ma internalmente il codice userà un “cassetto” di memoria e quindi sostituisce il nome “pluto” con l’indirizzo del cassetto corrispondente (che sarà poi il primo disponibile). Quando il programma dice “prendi il valore della memoria con indirizzo 14” la CPU “apre” il cassetto numero 14 e ne prende il valore al suo interno. Proprio la stessa cosa che facciamo noi quando vogliamo un paio di calzini ;) sappiamo già che cassetto aprire.

Questo tanto per darti una idea generale. Poi in realtà un computer ha più di una memoria, quindi più di una cassettiera, e a volte tale cassettiera è divisa in sezioni. A volte una variabile non viene nemmeno messa in memoria, bensì viene utilizzato uno dei “registri di memoria” (che sono memorie specialistiche interne alla cpu), a seconda del tipo di utilizzazione usata quando il programma viene compilato.

Una cosa da tenere a mente che gli indirizzi di memoria in un programma sono “virtuali”. Sono numeri, ma sono simbolici. Quando fai partire un programma, tale programma viene letto da disco e messo in memoria, e tale locazione di memoria non è mai la stessa, dipende da cosa altro è caricato in memoria in quel momento. Tra la CPU e la memoria vi è un ulteriore chip chiamato MMU (memory management unit) responsabile di tradurre gli indirizzi “logici” scritti nel programma in indirizzi “fisici” nella memoria stessa, e tutto questo avviene mentre il programma gira (ossia in “real time”, dinamicamente). Puoi ignorare pure questo passaggio, ma tienilo in mente.

In soldoni, una locazione di memoria è caratterizzata dal suo indirizzo (che è un numero) e dal suo valore (che è ancora un numero, che può essere qualsiasi cosa, può essere il valore di una variabile come può essere l’indirizzo di un’altra memoria. Quello che aiuta (per fortuna) è che nei linguaggi moderni tutto ciò (ossia le gestione della memoria) è completamente trasparente al programmatore. Almeno nella maggior parte dei casi, escludendo ossia programmazioni molto specifiche (per esempio nel caso di programmazione embedded)
 
  • Mi piace
Reazioni: bigendian

bigendian

Utente Attivo
742
425
OS
Linux
ra la CPU e la memoria vi è un ulteriore chip chiamato MMU (memory management unit) responsabile di tradurre gli indirizzi “logici” scritti nel programma in indirizzi “fisici” nella memoria stessa, e tutto questo avviene mentre il programma gira (ossia in “real time”, dinamicamente). Puoi ignorare pure questo passaggio, ma tienilo in mente.
si, anche se oggidi' non c'e' nessun chip intermedio, nelle moderne cpu MMU e' un unita' hw built-in nella cpu, che effettua la traduzione per il range di memoria fisica configurato/desiderato. Anche perche' per recenti x86_64 il bus dati e' direttamente connesso alle ddr.

Credo fosse esterno fino ai tempi di ddr2.

In molte altre architetture, mmu e' quasi sempre un modulo hw dentro alla cpu.
La cosa cmq a livello programmazione ti e' trasparente, della mmu se ne occupa l'os, ma giusto per sapere.
 
  • Mi piace
Reazioni: Andretti60

Ci sono discussioni simili a riguardo, dai un'occhiata!

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!

Discussioni Simili