Bu bölümde bir programın, kaynak koddan başlayıp ilk instruction’ı çalıştırılana kadar başından geçenleri konuşuyoruz.

Transkript

00:40 - Programlama dili nedir

  • Syntax + semantic tanimlar = dil
  • Bunun yaninda da bir standart kutuphane
  • C ve C++ komite ile tasarlanan diller, bir standart var
  • Rust ve Python bu sekilde degil, compiler/interpreter ne yapiyorsa dil odur
  • Commitee driven design sikintilari: yavas gelistirme, aralikli release’ler
  • Standartsiz diller cok daha hizli ilerleyebiliyor
  • Birden cok implementation gereken isler var: havacilik vs, bunlar icin standart onemli
  • C++ implementation’lari: gcc, clang, msvc vs tamamen birbirinden bagimsiz
  • EDG sirketlere C++ front-end’i satan bir sirket, intel (icc), nvidia (nvcc) vs kullaniyor
  • Compiler yapisi: front end’ler bir programlama dilindeki kaynak kodu alip derleyicinin icindeki IR’a (intermediate representation) donusturur
  • Backend IR’i alip bir makina diline cevirir
  • Backend’leri dogurdan mimariyi tasarlayan sirket (ornegin ARM) saglayabilir veya topluluk gelistirebilir
  • ARM nedir? Bir standart degil, her formal dokuman bir standart degil
  • ARM, bir ISA (Instruction Set Architecture) tasarlayip, bunu diger sirketlere lisansliyor
  • Instruction’larin yaninda ekstra ozellikleri de tasarlarlar (multicore synchronization, bus vs)
  • Biri ARM’dan lisans aldigi zaman, islemciyi kendileri dizayn edebilirler (ornegin Apple M1, A serisi vs)
  • ISA’de su instructionlar olmali ve su encoding’e sahip olmali gibi detaylar var, lisanslayan taraf bunlara uymak zorunda
  • ARM ayni zamanda implementation da saglayabilir (ornegin Cortex A serisi, veya Neoverse serisi)
  • Apple kadar buyuk olmayan veya kaynak ayirmak istemeyen sirketler bunlari aliyor
  • Yanina IO islerini saglayacak bloklari lisanslayan taraf dizayn ediyor, ve bunlar teoride islemciden bagimsiz olabilir
  • Ornegin ayni seri port blogunu hem ARM hem Risc-V islemciye koyabiliriz

09:50 Toolchain nedir

  • Tek bir kaynak koddan executable cikarmak mumkun olsa da pratik degil: gelistirmek zor, derleme zamanlari uzar vs
  • Birden cok dosyayi ayri ayri derleyeyim ve birlestireyim
  • Ornegin paralel olarak veya farkli zamanlarda (kisacasi kutuphane)
  • Bunlari birlestiren programa linker denir
  • Compiler 1 kaynak dosyasini alip 1 tane object file cikartir
  • Compiler’lar genellikle derleyicinin kendisi degil, butun toolchain’in driver’i
  • gcc dedigimiz zaman, aslinda preprocessor + compiler + linker calisiyor
  • Tek bir dosyan olsa bile bu gecerli
  • Cunku bir programin giris noktasi gercekte main degil, main’e gelmeden biseyler calisiyor
  • Hosted C++ icin bir programin girisi int main() ve varyantlari
  • Freestanding icin herhangi bir kisit yok, girisi kendimiz yazdigimiz icin main diyebiliriz ama istedigimiz ismi kullanabiliriz. kaynak

15:00 Linker detaylari

  • Linker birbirini kullanan dosyalardaki sembolleri birbirine baglayan program
  • Compiler tanimini gormedigi bir fonksyon gordugu zaman normal bir call instruction’u generate edip, uydurma bir adres koyar
  • Bu uydurma adresin duzeltilmesi icin de object file icine bir relocation koyuyor
  • Relocation: farkli bir yerden gelecek adresin object file’da duzeltilmesi
  • Bir linker temel olarak relocation cozer
  • Bir programda cozulmemis relocation kalmazsa, elimizde executable bir program olmus olur
  • Bir fonksyonu cagirmak icin derleyicinin fonksyonun tanimini gormesine gerek yok, declaration’dan argumanlari nasil gececegini anlayabilir (hangi register’dan ne gidecek vs gibi)
  • Farkli fonksyonlar birbirine isimleri ustunden baglaniyor
  • Bu C icin tamam, ama C++’da overloading var
  • Cozum: name mangling
  • sort(vector<int>) dedigimizde compiler bunu sort_vector_int gibi bir isme ceviriyor
  • C icin ayni isimli farkli tanima sahip 2 fonksyon varsa sikinti yaratir
  • Cozum olarak genellikle birden cok tanimi olan fonksyonlari linker reddeder
  • inline bu kontrolu kapatiyor, linker istedigi tanimi secmekte ozgur
  • Dolayisiyla ayni isimli ve farkli tanimli 2 inline fonksyon varsa, basiniz belaya gelistirebilir
  • Buna ODR violation denir, ve undefined behaviour’a sonuc verir

20:30 Inline nedir

  • Bir fonksyona inline yazmasak da compiler o fonksyonu inline edebilir
  • Veya inline yazsak da inline etmeyebilir
  • Compiler’in bir programciya gore cok daha fazla bilgisi var
  • Compiler her call site icin bir inline skoru hesapliyor
  • Fonksyon inline ise, bu skor biraz yukseliyor
  • Skor belli bir limitin altinda kalirsa inlinelanmaz
  • Dynamic linking icin sikinti cikarir, bu durumlar icin bu fonksyonu kesinlikle inline’lama diyebiliyoruz

22:30 Dynamic linking nedir

  • Calismasi icin OS gerekmeyebilir, sadece bir dynamic linker’a ihtiyac var
  • Freestanding bir embedded sistemde de gerceklestirilebilir
  • Kisaca yaptigi sey, ozel bir relocation koyup, bu relocation’in calisma zamaninda gelecegini soyleyebiliyor
  • Hangi dinamik kutuphanelere ihtiyac duydugumuz executable icinde tutuluyor
  • Dynamic linker programi calistirmadan once butun kutuphaneleri acip relocation’lari cozmeye calisiyor
  • Static linking’in aksine, dosyayi degistirmiyoruz, sadece memory’deki adresleri guncelliyoruz

24:40 Assembler nedir

  • Derleyiciler assembly mi cikarir?
  • Genellikle compiler’lar dogrudan binary dosya cikarir, cunku assembler da zaten compiler’in parcasi ve araya bir assembly adimi koymak gereksiz overhead

25:50 Binary nedir

  • Compiler’larin cikardigi dosya nedir? Dumduz binary mi?
  • Linux ortaminda ve siklikla embedded icin derleyiciler ELF dosyasi cikarir
  • Duz binary’ler executable kod tasimali ve dolayisiyla relocation’lar vs tasinamaz
  • Her OS boyle degil, macOS icin mach-o, windows icin portable executable cikariliyor
  • ELF dosyasini bir container gibi dusunebiliriz
  • Icinde section’lardan olusan segmentler var
  • Segmentlerin yuklenmesi gereken adresleri var
  • Sabit adreslerden dolayi virtual memory’ye ihtiyac duyuyoruz
  • Address space layout randomization (ASLR)
  • Adresler hep ayni olursa, bir exploit hep calisabilir cunku hersey hep ayni
  • Return oriented programming: fonksyonlarin donus adresini degistirerek istenmeyen seyler calistirmak
  • ROP yapmamizin sebebi, disardan calistirilabilir kod yuklemek artik pek mumkun degil
  • Loader programi istedigi yere degil rastgele bir adrese koyuyor
  • Rastgele koymak problemler cikarabilir: relocation’lar vs bosa gitcek
  • Temel cozum: position independent code (PIC) koymak
  • PIC kodlarda dogrudan adresler koymak yerine, oldugumuz instruction’dan offset koyulur
  • Rastgele yerlere koyarsak da, offsetler ayni kalacagi icin direk calisacak
  • Embedded dunyada ise, segmentler tam olarak istenen yere gider
  • Linker’a hangi segmentin nereye gidecegini linker scriplerle soyluyoruz
  • Kendimiz istedigimiz segmentleri tanimlayabiliriz, ama hic bir tool o segmenti anlamayacak
  • .mytext‘e gidebilir butun kod, ama loader yukleyemez
  • Debugging bilgisi ELF dosyalarinda ozel bir segment icinde duruyor
  • Programi her calistirdigimizda debug bilgisi RAM’e yuklenmiyor
  • Belirli convention’lar var

33:40 Debugging bilgisi

  • En basit olarak, su adresler arasinda su fonksyon tanimi var diyebilir
  • Daha basiti nm ile fonksyonlarin baslangic adreslerini alip, binary search ile stack trace cikarabiliriz
  • Gunumuzde bunlar iyice karisti tabi, bir fonksyon parcalanabilir vs
  • Machine function splitting: her fonksyonu hot-cold iki parcaya ayirip, fonksyonlarin hot kisimlarini bir araya koymak
  • Cold yerlerde bol bol cache miss alsak da cok problem degil

36:00 Embedded calistirma

  • Execute in place (XIP): programlarin .text‘ini RAM’e yuklememize gerek yok, direk flash’tan calisabilir
  • Flash ve RAM birbirinden tamamen ayri
  • Calisirken instruction’lar RAM’i asla gormuyor
  • Flash yavas oldugu icin arada bir cache oluyor, ornegin STM’lerde ART accelerator var
  • Olmayabilir de ama anlamsiz yavas olacaktir
  • Islemcilerin durmasina stall denir
  • Segment’ler Flash’ta tutulup, RAM’den adreslenebilir, ornegin initialized data
  • Bu tarz durumlarda, datayi flash’tan alip RAM’e yuklemek bizim isimiz oluyor

40:00 Loading nedir

  • Embedded veya bir PC ustunde loading cok da farkli degil aslinda
  • PC’de de .text aslinda RAM’e kopyalanmiyor
  • Ihtiyac duydukca diskten RAM’e getiriliyor
  • Bu durumda RAM, text icin bir onbellek gibi kullaniliyor
  • Linux yuklemeyi runtime’da yapiyor
  • Embedded tarafta onden yapiliyor
  • Aslinda tamamen ayni isi yapiyoruz
  • Datayi kopyaladiktan sonra, main programina zipliyoruz
  • Bu noktada toolchain’in isi bitti, programimiz calisiyor
  • maine girmeden once global constructor’lar calistiriliyor
  • Dolayisiyla, main bir programin tam olarak girisi olamaz
  • Isletim sistemi, baska bir yere zipliyor (_start olur genelde)
  • Libc tarafindan saglanabilir