---
title: 'Bağlayıcı Seçenekleri'
source: 'https://academia.sh/tr/kurslar/go-uretim/baglayici-secenekleri'
course: 'Üretim Go: Test, Araçlar ve Başarım'
language: tr
updated: '2026-09-13T05:08:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Kaynakta hiç yazılmayan bir dizgi değerinin, aynı kaynaktan üç ayrı çağrıyla üç farklı biçimde ikiliye gömüldüğünü ölçer'
    - 'Bağlayıcının gömülen değeri hiç doğrulamadığını, biçimsiz bir girdiyi de sessizce kabul ettiğini gösterir'
    - 'Sembol tablosunu kaldıran bir bağlayıcı seçeneğinin programın çıktısını değiştirmediğini koşturarak ayırt eder'
---

# Bağlayıcı Seçenekleri

Kaynakta hiçbir yerde yazılı olmayan bir dizgi değerinin, üç ayrı derleme çağrısında üç ayrı biçimde ikiliye gömüldüğü ölçülür; kaynak metin üçünde de birebir aynı kalır. Sınırlayıcı ölçüm, gömülen değerin bağlayıcı tarafından hiç doğrulanmadığını — biçimsiz bir dizginin de sessizce kabul edildiğini — ve ayrı bir bağlayıcı seçeneğinin sembol tablosunu silerken programın çıktısını değiştirmediğini koşturarak gösteriyor.

Önceki derste seçilen hedef ikiliye hiçbir iz bırakmadan, yalnızca derleme sürecinin kendi
araçlarıyla belirleniyordu. Bu ders aynı örüntüyü, bu kez bir dizin ya da bir mimari yerine
tek bir dizgi değeri için sürdürüyor: kaynakta hiçbir yerde yazılı olmayan bir değer, yine
derleme anında, yine kaynağın dışından ikiliye giriyor. Bu kez soruyu soran taraf derleyici
değil **bağlayıcı** — kaynağın derlendiği son adımda çalışan, ayrı derlenmiş parçaları tek bir
ikilide birleştiren araç.

## Kaynakta Olmayan Bir Değer

Bir paket düzeyi değişken, kaynakta bir varsayılan değerle bildirilebilir ve bu değer
**bağlayıcı seçeneği (linker flag)** ile derleme anında değiştirilebilir. Aşağıdaki ölçüm
tek bir kaynağı — bir tek `surum` değişkeni ve onu yazdıran bir `main` işlevi — üç ayrı
derleme çağrısıyla çalıştırıyor. Kaynak metin üçünde de birebir aynı; değişen tek şey,
komuta verilen bir bayrak.

Bayrağın kendisi bir eşitlik ifadesi taşıyor: değişkenin **tam paket yolu**, bir nokta, adı,
sonra istenen değer. Bağlayıcı, ayrı ayrı derlenmiş paket parçalarını tek bir ikilide
birleştirirken, her paketin dışa açtığı adları zaten bir tabloda tutuyor — bu tablo normalde
paketler arasındaki çağrıları birbirine bağlamak için var. `-X` seçeneği bu aynı tabloyu bir
başka amaçla kullanıyor: adı tabloda bulup, kaynaktaki ilk değerin yerine verilen dizgiyi
yerleştiriyor. Bu yüzden hedef adın **tam** ve **doğru** yazılması gerekiyor — paket yolu
yanlışsa ya da adın kendisi yanlış yazılmışsa, bağlayıcı genellikle hiçbir eşleşme bulamıyor
ve bayrağı sessizce yok sayıyor; kaynaktaki varsayılan değer olduğu gibi kalıyor.

```go
// yardimcilar.go — bu dersin paylastigi yardimcilar: gecici paket kurma ve arac cagirma
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// goster bir bilgiyi, kimin soyledigini ve kanitini tek satirda yazdirir.
func goster(bilgi, soyleyen, kanit string) {
	fmt.Printf("%-38s %-7s %s\n", bilgi, soyleyen, kanit)
}

// paket, verilen dosyalari yeni bir gecici modulun icine yazar; yol hicbir yerde basilmaz.
func paket(dosyalar map[string]string) string {
	dizin, err := os.MkdirTemp("", "baglayici")
	if err != nil {
		return ""
	}
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module baglayicideneme\n\ngo 1.24\n"), 0o644)
	for ad, icerik := range dosyalar {
		os.WriteFile(filepath.Join(dizin, ad), []byte(icerik), 0o644)
	}
	return dizin
}

// aracCalistir dilin kendi komutunu kosturur, ciktiyi ve basari durumunu doner.
func aracCalistir(dizin string, arg ...string) (string, bool) {
	cmd := exec.Command("go", arg...)
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	cikti, err := cmd.CombinedOutput()
	return string(cikti), err == nil
}

// calistir, kaynagi verilen baglayici degeriyle calistirir ve yazdirdigi tek satiri doner.
// uygula false ise hicbir -X bayragi verilmez; true ise deger bos dizgi bile olsa uygulanir.
func calistir(dizin string, uygula bool, deger string) string {
	arg := []string{"run", "."}
	if uygula {
		arg = []string{"run", "-ldflags=-X main.surum=" + deger, "."}
	}
	cikti, _ := aracCalistir(dizin, arg...)
	return strings.TrimRight(cikti, "\n")
}

// sembolVarMi, verilen ek baglayici bayragiyla derlenen ikilide bir sembol adinin
// bulunup bulunmadigini soyler; ikilinin kendisi ve yolu hicbir yerde basilmaz.
func sembolVarMi(dizin, ekBayrak, sembolAdi string) bool {
	ikiliYolu := filepath.Join(dizin, "ikili")
	arg := []string{"build", "-o", ikiliYolu}
	if ekBayrak != "" {
		arg = append(arg, "-ldflags="+ekBayrak)
	}
	arg = append(arg, ".")
	if _, tamam := aracCalistir(dizin, arg...); !tamam {
		return false
	}
	cikti, _ := aracCalistir(dizin, "tool", "nm", ikiliYolu)
	return strings.Contains(cikti, sembolAdi)
}
```

```go
// olcum1.go — kaynakta yazili olmayan bir deger, ayni kaynaktan uc kez farkli okunuyor
package main

import "fmt"

const kaynakMetni = "package main\n\nimport \"fmt\"\n\nvar surum = \"gelistirme\"\n\nfunc main() {\n\tfmt.Println(surum)\n}\n"

func main() {
	dizin := paket(map[string]string{"prog.go": kaynakMetni})
	defer func() { _ = dizin }()

	fmt.Println("-- ayni kaynak, uc farkli baglayici girdisi --")
	goster("hicbir deger verilmeden", "arac", calistir(dizin, false, ""))
	goster("birinci deger verildiginde", "arac", calistir(dizin, true, "surum-a"))
	goster("ikinci deger verildiginde", "arac", calistir(dizin, true, "surum-b"))
}
```

```
-- ayni kaynak, uc farkli baglayici girdisi --
hicbir deger verilmeden                arac    gelistirme
birinci deger verildiginde             arac    surum-a
ikinci deger verildiginde              arac    surum-b
```

**DR9.** `kaynakMetni` sabiti üç çalıştırmanın üçünde de karakter karakter aynı; değişen tek
şey `calistir` işlevine verilen bayrak. Kaynakta yazılı olan tek değer `"gelistirme"` — bu,
hiçbir bayrak verilmediğinde görünen değer, yani **kaynak** sınıfına giren tek satır.
Öteki iki satır kaynakta hiç yok; bağlayıcı, derleme sırasında paket düzeyi bir dizgi
değişkeninin ilk değerini, kaynağı hiç değiştirmeden, dışarıdan verilen bir dizgiyle
değiştiriyor. Bu üç satırın hepsi **araç** sınıfına yazılıyor, çünkü değer her çağrıda aynı
bayrakla aynı sonucu veriyor — koşumdan koşuma değişen bir şey yok, değişen yalnızca hangi
bayrağın verildiği.

Bu düzeneğin çalışabilmesi için iki koşul birden gerekiyor. Değişken paket düzeyinde
olmalı — bir işlevin içindeki yerel bir değişken bağlayıcının erişemeyeceği bir yerde duruyor.
Ve değişkenin tipi dizgi olmalı; bağlayıcı yalnızca dizgi değerlerini bu şekilde
değiştirebiliyor, bir tam sayıyı ya da bir yapının alanını aynı yoldan değiştiremiyor. Bu iki
kısıt, düzeneği kasıtlı olarak dar tutuyor: karmaşık bir veri yapısını değil, tek bir metin
parçasını derleme anında yerleştirmek için var.

Bu darlık bir eksiklik değil, bir tasarım kararı. Bağlayıcı, kaynağı hiç ayrıştırmadan,
yalnızca derlenmiş nesne dosyalarının içindeki ikili temsille çalışıyor — bir dizgi
değişkeninin ikili temsili, uzunluğu ve baytları önceden bilinen bir bellek bloğu, bu yüzden
bağlayıcının yapması gereken tek şey o bloğun içeriğini değiştirmek. Bir tam sayı ya da bir
yapı için aynı işlem çok daha kırılgan olurdu: değerin tipini ve büyüklüğünü bilmeden rastgele
bir baytı yerine koymak, ikilinin geri kalanını bozma riski taşırdı. Dizgi tipi bu riski
taşımıyor, çünkü Go'nun dizgi temsili zaten bir uzunluk alanıyla birlikte tutuluyor ve
bağlayıcı yeni değerin uzunluğunu da bu alana yazabiliyor.

## Sınırlayıcı Ölçüm: Gömülen Değer Doğrulanmıyor

Yukarıdaki üç değer de biçimli, okunaklı dizgilerdi. Bağlayıcının kendisi bu biçime hiç
bakmıyor — verilen her şeyi olduğu gibi yerleştiriyor. Aşağıdaki ölçüm bunu iki uçla
sınıyor: bir yandan hiçbir tanınabilir kalıba uymayan bir dizgi, öte yandan tümüyle boş bir
dizgi.

```go
// olcum2.go — sinirlayici olcum: gomulen deger dogrulanmiyor, sembol tablosu ayri bir sey
package main

import "fmt"

const kaynakMetni = "package main\n\nimport \"fmt\"\n\nvar surum = \"gelistirme\"\n\nfunc main() {\n\tfmt.Println(surum)\n}\n"

func main() {
	dizin := paket(map[string]string{"prog.go": kaynakMetni})
	defer func() { _ = dizin }()

	fmt.Println("-- sinirlayici olcum: gomulen deger dogrulanmiyor --")
	goster("bicimsiz bir deger verildiginde", "arac", calistir(dizin, true, "###GECERSIZ-SURUM-999"))
	goster("bos dizgi verildiginde", "arac", "["+calistir(dizin, true, "")+"]")

	fmt.Println()
	fmt.Println("-- ikinci baglayici secenegi: sembol tablosunu cikarmak --")
	goster("normal derlemede sembol var mi", "arac", fmt.Sprint(sembolVarMi(dizin, "", "main.surum")))
	goster("-s ile derlemede sembol var mi", "arac", fmt.Sprint(sembolVarMi(dizin, "-s -w", "main.surum")))
	goster("-s ile davranis degisiyor mu", "arac", "cikti="+calistir(dizin, false, ""))
}
```

```
-- sinirlayici olcum: gomulen deger dogrulanmiyor --
bicimsiz bir deger verildiginde        arac    ###GECERSIZ-SURUM-999
bos dizgi verildiginde                 arac    []

-- ikinci baglayici secenegi: sembol tablosunu cikarmak --
normal derlemede sembol var mi         arac    true
-s ile derlemede sembol var mi         arac    false
-s ile davranis degisiyor mu           arac    cikti=gelistirme
```

**DR10.** Birinci satır, bir sürüm numarası gibi görünmeyen bir dizgiyi hiçbir uyarı, hiçbir
red olmadan kabul etti; ikinci satır tümüyle boş bir dizgiyi de aynı şekilde kabul etti —
köşeli parantezlerin arası boş çünkü değişkenin kendisi boş bir dizgiye eşitlendi. Bağlayıcı
burada bir tür denetimi ya da bir biçim denetimi yapmıyor; yaptığı iş, verilen bayttan
diziyi kaynak koddaki ilk değerin yerine koymaktan ibaret. Bu, önceki dersteki "derlenmek
çalışmak değildir" sınırının bir başka görünümü: bağlayıcı geçtiyse, gömülen değerin
**anlamlı** olduğu hiç garanti edilmiyor — yalnızca doğru yere, doğru tipte yerleştirildiği
garanti ediliyor.

Bu boşluğu kapatmanın tek yolu, gömülen değeri kaynağın dışında, ayrı bir adımda denetlemek —
bağlayıcının kendisi bu işi hiçbir zaman üstlenmiyor. Bir dağıtım sürecinde bu genellikle
gömülecek dizginin, çağıran tarafından önceden bilinen bir kalıba uyup uymadığının, bağlayıcı
çağrılmadan **önce** sınanması biçiminde çözülüyor — tıpkı bu dersin ölçtüğü gibi, sınama
bağlayıcının kendisinden değil, onu çağıran sürecin kendi disiplininden geliyor. Bu ders o
disiplini uygulamadı, kasıtlı olarak: amaç bağlayıcının kendi davranışını, çevresindeki hiçbir
denetim katmanı olmadan çıplak haliyle görmekti.

**DR11.** İkinci ölçüm ayrı bir bağlayıcı seçeneğine bakıyor: sembol tablosunu ve hata ayıklama
bilgisini kaldıran bayrak. Normal derlemede `main.surum` adı ikilinin sembol tablosunda
bulunuyor — bu ad, bir hata ayıklayıcının ya da bir çözümleme aracının değişkeni tanımasını
sağlıyor. Aynı bayrak eklendiğinde sembol tablosundan bu ad siliniyor, ama programın kendi
çıktısı — son satırdaki `calistir` çağrısı — hiç değişmiyor. Bu, iki farklı bağlayıcı
seçeneğinin iki farklı katmanda çalıştığını gösteriyor: biri **programın verisini**
değiştiriyor (`-X`), öteki yalnızca **programın hakkındaki bilgiyi** siliyor (`-s`) — ikisi
birlikte kullanılabiliyor ve biri ötekinin sonucunu hiç etkilemiyor.

Bu ayrım rastgele değil: `-s` bayrağının kaldırdığı sembol tablosu ve hata ayıklama bilgisi,
programın kendi mantığının **hiçbir parçası değil** — bir hata ayıklayıcının değişken adlarını,
işlev adlarını ve satır numaralarını okuyabilmesi için ikiliye eklenen, ama çalışma zamanında
programın kendisi tarafından hiç kullanılmayan ek veriler. `-X` ise tam tersi bir yere,
programın kendi çalışma zamanı verisinin içine yazıyor. İki seçenek aynı komutta birlikte
verildiğinde bile aralarında bir sıralama sorunu çıkmıyor, çünkü sembol tablosu **derleme
sürecinin sonunda**, bütün değerler zaten yerlerine oturmuşken siliniyor — silinen şey adların
kendisi, adların işaret ettiği veriler değil.

## Kaynakta Bir İz Bırakmadan

İki ölçümün ortak noktası, bağlayıcının çalıştığı katmanın kaynağın kendisinden ayrı olması.
Kaynak dosyası hem `surum-a` hem `surum-b` hem `###GECERSIZ-SURUM-999` çalıştırmalarında
birebir aynı kaldı; hiçbiri diskte, kaynağın hiçbir versiyonunda görünmüyor. Bu, önceki
dersin sonunda işaret edilen paralelliği tamamlıyor: hedef seçimi ikiliye nasıl hiçbir iz
bırakmadan giriyorsa, gömülen bir dizgi de kaynağa hiçbir iz bırakmadan giriyor. İkisi de
derleme sürecinin kendi araçlarıyla, derleme **girdisinin** bir parçası olarak
uygulanıyor — kaynağın **çıktısının** değil.

Bunun pratik sonucu, aynı kaynak ağacından üretilen her ikilinin, kendine özgü bir "kimlik"
taşıyabilmesi: bir sürüm dizgisi, bir yapı zaman damgası, bir dağıtım kimliği gibi değerler
kaynağı hiç değiştirmeden, yalnızca derleme çağrısı değiştirilerek her ikiliye ayrı ayrı
verilebiliyor. Bedeli de aynı yerde duruyor: bu değerlerin doğruluğu kaynak denetiminin dışında
kalıyor, bir bakan-denetleyen kişi ya da süreç olmadan yanlış bir değer sessizce ikiliye
girebiliyor. Bir sonraki derste kaynakta **gerçekten yazılan** bir sürüm bilgisiyle
karşılaştırıldığında bu fark daha keskin görünüyor: orada sürüm numarası kaynağın kendisinde
durur ve bir araç yalnızca onu **denetler**, burada sürüm benzeri değer kaynakta hiç durmaz ve
hiçbir araç onu denetlemez.

Bu iki uç arasındaki seçim genellikle şu soruya bağlı: değer, kaynağın **kimliğinin** bir
parçası mı, yoksa kaynağın **hangi anda, hangi ortamda** derlendiğinin bir parçası mı? Bir
kütüphanenin taşıdığı bir sürüm numarası birincisine örnek — o değer kaynakla birlikte
sürüyor, kaynak değişmeden değişmiyor, bu yüzden kaynakta yazılı olması daha tutarlı bir
kayıt bırakıyor. Bir yapı kimliği ya da bir derleme ortamının bir bilgisi ikincisine örnek —
bu değer kaynaktan değil, o **anki** derleme çağrısından geliyor ve her derlemede farklı
olması beklenebiliyor; kaynağa yazmak bu durumda kaynağı, aslında ona ait olmayan bir bilgiyle
kirletmek anlamına geliyor. Bağlayıcı seçeneği tam bu ikinci durumu, kaynağı hiç dokunmadan
karşılıyor.

## Özet

- Paket düzeyinde bildirilen bir dizgi değişkeni, kaynak hiç değişmeden, bağlayıcı seçeneğiyle
  derleme anında farklı bir değerle doldurulabiliyor; bu değer kaynakta hiçbir zaman yazılı
  değil.
- Aynı kaynak üç ayrı çağrıyla üç ayrı çıktı verdi; fark yalnızca verilen bayraktaydı, kaynak
  dosyanın hiçbir sürümünde değil.
- Sınırlayıcı ölçüm: bağlayıcı gömülen değeri hiç doğrulamıyor — biçimsiz bir dizgi de, boş
  bir dizgi de aynı şekilde, hiçbir uyarı üretmeden kabul ediliyor.
- Sembol tablosunu kaldıran ayrı bir bağlayıcı seçeneği, programın kendi çıktısını
  değiştirmiyor; iki seçenek birbirinden bağımsız iki katmanda çalışıyor.
- Hedef seçimi (önceki ders) ve gömülen değer (bu ders) aynı örüntüyü paylaşıyor: ikisi de
  derleme girdisinin bir parçası, kaynağın kendisinin bir parçası değil.

## Sonraki Adım

Bu derste gömülen değer kaynakta hiç yazılı değildi ve hiçbir araç onu denetlemedi. Sıradaki
ders tam tersi bir durumu ele alıyor: bir sürüm numarası kaynağın kendisinde, açıkça
bildiriliyor — ama bu kez bir araç, o sürümün taşıdığı bir sözü, yani eski koda karşı hâlâ
uyumlu olup olmadığını denetliyor. Soru artık "değer nereden geliyor" değil, "kaynakta yazılan
bir sayı neyi garanti ediyor".
