Menu

Daxil olun Qeydiyyat

Assembler x86 Linux (64 bit)

Stek

Stek

Hər bir yeni proqram işə salındıqda, əməliyyatlar sistemi bu proqram üçün yaddaşda müəyyən yer ayırır. Bu yer yalnız həmin proqrama məxsus olub, onun yaddaş sahəsi adlanır. Proqramın yaddaş sahəsi, başqa sözlə, icra olunan proqramın yaddaşdakı surəti aşağıdakı struktura malikdir:

Proqramın icraolunma vəziyyətindəki strukturu:

 


 

Burada .text və .data hissələri proqramın uyğun olaraq instruksiyalar və məlumatlar hissələridir (ikili formada). Bu hissələrin ölçüsü proqramın icrası boyu dəyişmir. Heap və stek sahələrinə proqramın icrası boyu müxtəlif məlumatlar yerləşdirilir. Heap sahəsi dinamik dəyişənlər, stek isə funksiyalara ötürülən parametrləri yerləşdirmək və digər məqsədlər üçün istifadə olunur. Şəkildən göründüyü kimi, stek yuxarıdan aşağı, heap isə aşağıdan yuxarıya doğru artır. Əgər bu yaddaş sahələrinə davamlı məlumat yerləşdirsək, onlar bir-birlərinin sərhədlərinə toxunar və nəticədə yaddaş xətası baş verər.

Əgər şəklə diqqət yetirsək görərik ki, stek sahəsi başlanğıc sərhədi yaddaş fəzasının ən yuxarı hissəsində yerləşir. Stekə məlumatlar yerləşdirdikcə, stekin sərhədi yaddaş fəzası boyunca "aşağıya" doğru hərəkət edir. Stek sərhədinin hal-hazırdakı ünvanı %esp registri vasitəsilə təyin edilir.

 


 

Stekə məlumat yerləşdirmək üçün push instruksiyasından istifadə olunur. push instruksiyasının sintaksisi aşağıdakı kimidir:

push melumat

Bu zaman məlumat stekə yerləşdirilmiş olur.

Stekə məlumat yerləşdirdikdə və ya götürdükdə %esp-in də qiyməti müvafiq olaraq dəyişir və beləliklə də, onun həmişə stekin üst hissəsinə istinad etməsi şərti təmin olunmuş olur. Bu işlər prosessor tərəfindən avtomatik yerinə yetirilir.

Misal üçün, tutaq ki, baxdığımız anda stekin vəziyyəti aşağıdakı kimidir:

 


 

Stekə 3 qiymətini yerləşdirmək üçün aşağıdakı instruksiyanı daxil edirik:

pushl $3

(Qeyd: 32 bitlik maşınlarda pushl, 64 bitliklərdə isə pushq instruksiyasından istifadə olunur.)

Bu zaman stek göstəricisinin (%esp) qiyməti 4 bayt azalar və stekin vəziyyəti aşağıdakı kimi olar:

 


 

Şəkildən göründüyü kimi, stek göstəricisinin baxdığımız anda qiyməti 0xfffff1a4 idi. Stekə 3 qiymətini yerləşdirdikdən sonra, stek yaddaşının həcmi artdı və stek göstəricisinin yeni qiyməti oxfffff1a0 oldu, əvvəlki qiymətindən 4 bayt az. Bunu göstərməkdə məqsəd stekə məlumat yerləşdirən zaman onun sahəsinin artmasının, stek göstəricisinin isə azalmasının izahıdır. Bu hissədə anlaşılmazlıq ola bilər, amma stekə məlumat yerləşdirən zaman onun göstəricisinin qiymətinin azalması, başqa sözlə, stekin aşağıya doğru artması ideyası sistem arxitektorlarına məxsusdur.

Daha bir neçə məlumatı stekə əlavə edək:


pushl $55
pushl $300
pushl $23

Stekin cari vəziyyəti aşağıdakı kimi olar:

 


 

Ünvanlar 16-lıq say sistemində göstərilir. Qeyd edim ki, nümunələr 32 bitlik maşın üçün verilir. Bu zaman stekə məlumat yerləşdirdikdə və ya onda olan məlumatı götürdükdə stekgöstəricisinin qiyməti uyğun olaraq, 4 bayt azalır (artır), 64 bitlik maşınlarda isə 8 bayt.

Stekdən məlumat götürmək üçün isə, pop instruksiyasından istifadə edirlər. pop instruksiyasının sintaksisi aşağıdakı kimidir:

pop arq

Bu zaman pop instruksiyası stekin ən aşağı hissəsində, yəni stek göstəricisinin istinad etdiyi yerdə olan 4 bayt (8) məlumatı götürüb arq-a yerləşdirir. Stek göstəricisini 4 bayt yuxarı sürüşdürür.

Misal üçün, yuxarıdakı nümunədə stekə ən son yerləşdirdiyimiz qiyməti %eax registrinə köçürmək istəsək, onda aşağıdakı kimi yazmalıyıq:

popl %eax

Nəticədə %eax registrinə 23 qiyməti yazılar, stekin vəziyyəti isə aşağıdakı kimi olar:

 


 

İndi isə, proqram nümunələri ilə tanış olaq. Əvvəlki mövzuda biz hər hansı dəyişənin qiymətini ekranda çap edən proqram tərtib etdik. Hər hansı məlumatın ekranda çap olunması istifadəçi üçün çox mühümdür. Lakin eyni dərəcədə istifadəçinin də proqrama hər hansı məlumat daxil edə bilməsinə ehtiyac var.

Gəlin, indi bu məsələnin assembler dilində necə realizə olunması isə məşğul olaq. Əvvəlcə yenə ilk olaraq sadə hal, hər hansı sətrin istifadəçi tərəfindən daxil olunması ilə məşğul olacağıq. Daha sonra isə, ədədlərin hansı yolla daxil olunmasını öyrənəcəyik.

Elə proqram tərtib edək ki, istifadəçidən hər hansı sətir qəbul etsin və ekranda həmin sətirdə olan 'a' simvollarının sayını çap etsin. Əgər istifadəçinin daxil etdiyi sətirdə 'a' simvolu yoxdursa, onda bu barədə məlumat çap eləsin.


#prog14.s

.data

setir1:
.ascii "100 simvoldan çox olmayan hər hansı sətir daxil edin\n"

#sətirdə olan simvolların sayı
say1:
.long 53

setir2:
.ascii "a simvollarının sayı -> "

#sətirdə olan simvolların sayı
say2:
.long 24

yeni_setir:
.ascii "\n"

#nəticəni sətrə çevirmək üçün məlumatlar
dey:
.long 0

onluq_simvollar:
.ascii "0123456789"

say:
.long 0

bufer:
.rept 100
.ascii "\0"
.endr
  
   .text 
 
   .globl _start
   .type _start, @function

_start:
#sətri çap edək
movl $4, %eax
movl $1, %ebx
movl $setir1, %ecx
movl say1, %edx
int $0x80

#istifadəçidən hər hansı sətir qəbul edirik (max 20 simvol)
movl $3, %eax
movl $0, %ebx
movl $bufer, %ecx
movl $100, %edx
int $0x80

#dövrə hazırlıq
movl $0, %edi
movl $0, %esi
#%ebx və %eax-də olan məlumatları silirik
#bunun üçün movl $0, %ebx də yaza bilərdik
#amma xorl daha sürətlə işləyir

xorl %ebx, %ebx
xorl %eax, %eax
movb $'a', %bl
   
dovr:
movb bufer(,%edi,1), %al
incl %edi
cmpl $100, %edi
je dovr1_son
cmpb %al, %bl
jne dovr
incl %esi
jmp dovr

dovr1_son:   
movl %esi, dey
#setir2 cap_et

movl $4, %eax
movl $1, %ebx
movl $setir2, %ecx
movl say2, %edx
int $0x80

#nəticəni ekranda çap etmək üçün sətrə çevirək
dovr1:
movl $10, %edi
movl $0, %edx
movl dey, %eax
div %edi

pushq %rdx
movl %eax, dey

incl say

movl $0, %esi
cmpl dey, %esi
je dovr_son

jmp dovr1

dovr_son:
movl $0, %esi

cap_et: 
cmpl %esi, say
je  yeni_str_cap

popq %rdi

movl $4, %eax
movl $1, %ebx
movl $onluq_simvollar, %ecx
addl %edi, %ecx
movl $1, %edx
int $0x80

incl %esi
jmp cap_et

yeni_str_cap:
#yeni sətir çap et
movl $4, %eax
movl $1, %ebx
movl $yeni_setir, %ecx
movl $1, %edx
int $0x80
son:
movl $1, %eax
int $0x80

Nəticəni yoxlayaq:


[user@unix progs_as]$ 
[user@unix progs_as]$ as prog14.s -o prog14.o
[user@unix progs_as]$ ld prog14.o -o prog14
[user@unix progs_as]$ ./prog14
100 simvoldan çox olmayan hər hansı sətir daxil edin
kszdfjhlaskja aaa kdfjnka ads
a simvollarının sayı -> 7
[user@unix progs_as]$
[user@unix progs_as]$ ./prog14
100 simvoldan çox olmayan hər hansı sətir daxil edin
aaa
a simvollarının sayı -> 3
[user@unix progs_as]$

Qeyd etdiyimiz kimi, stekdən funksiyalara çağırışların realizə olunmasında geniş istifadə olunur. Növbəti mövzuda biz bu məsələ ilə məşğul olacağıq.


Bizi dəstəkləyənlər