GCC ve ld ile kullanılmayan C / C ++ sembolleri nasıl kaldırılır?


111

Yürütülebilir dosyamın boyutunu ciddi şekilde optimize etmem gerekiyor ( ARMgeliştirme) ve mevcut yapı şemamda ( gcc+ ld) kullanılmayan sembollerin sıyrılmadığını fark ettim .

arm-strip --strip-unneededSonuçta ortaya çıkan çalıştırılabilir dosyaların / kitaplıkların kullanımı, çalıştırılabilir dosyanın çıktı boyutunu değiştirmez (neden olduğuna dair hiçbir fikrim yok, belki de yapamaz) .

Kullanılmayan sembollerin ortaya çıkan dosyadan çıkarılması için bina boru hattımı değiştirmenin yolu (varsa) ne olabilir?


Hatta bu düşünmek olmaz ama benim şimdiki gömülü ortam çok "güçlü" ve hatta tasarruf değil 500Küzerinden 2Mçok güzel bir yükleme performans artışı sonuçları.

Güncelleme:

Maalesef şimdiki gccsürüm I kullanımı yoktur -dead-stripseçeneği ve -ffunction-sections... + --gc-sectionsiçin ldçıkan çıkış için herhangi önemli bir fark vermez.

Bunun bir sorun haline gelmesine bile şok oldum, çünkü bunun gcc + ldkullanılmayan sembolleri otomatik olarak kaldırması gerektiğinden emindim (neden onları tutmak zorunda kalıyorlar ki?).


Sembollerin kullanılmadığını nasıl anlarsınız?
zvrba

Hiçbir yerde referans verilmemiştir => son uygulamada kullanılmamaktadır. Birleştirme / bağlama sırasında çağrı grafiği oluşturmanın çok zor olmayacağını varsayıyorum.
Yippie-Ki-Yay

1
Ölü sembolleri kaldırarak .o dosyasının boyutunu küçültmeye mi çalışıyorsunuz yoksa yürütülebilir belleğe yüklendikten sonra gerçek kod ayak izinin boyutunu küçültmeye mi çalışıyorsunuz? "Gömülü" demeniz, ikincisini ima ediyor; sorduğunuz soru birincisine odaklanmış görünüyor.
Ira Baxter

@Ira Çıktı yürütülebilir boyutunu azaltmaya çalışıyorum, çünkü (örnek olarak)boost kitaplıkları kullanan bazı mevcut uygulamaları taşımaya çalışırsam , ortaya çıkan .exedosya birçok kullanılmayan nesne dosyası içerir ve mevcut gömülü çalışma zamanımın spesifikasyonları nedeniyle , bir 10mbuygulamayı başlatmak , örneğin bir 500kuygulamayı başlatmaktan çok daha uzun sürer .
Yippie-Ki-Yay

8
@Yippie: Yükleme süresini en aza indirmek için koddan kurtulmak istiyorsunuz; kurtulmak istediğiniz kod kullanılmayan yöntemler / vb. kütüphanelerden. Evet, bunu yapmak için bir arama grafiği oluşturmanız gerekir. O kadar kolay değil; küresel bir arama grafiği olmalı, muhafazakar olmalı (kullanılabilecek bir şeyi kaldıramaz) ve doğru olmalıdır (böylece ideal bir arama grafiğine yakın olursunuz, böylece neyin olmadığını gerçekten bilirsiniz Kullanılmış). Büyük sorun, küresel, doğru bir arama grafiği yapmaktır. Bırakın bağlayıcıları, bunu yapan pek çok derleyici bilmiyorum.
Ira Baxter

Yanıtlar:


131

GCC için bu iki aşamada gerçekleştirilir:

Önce verileri derleyin, ancak derleyiciye kodu çeviri birimi içinde ayrı bölümlere ayırmasını söyleyin. Bu, aşağıdaki iki derleyici bayrağı kullanılarak işlevler, sınıflar ve harici değişkenler için yapılacaktır:

-fdata-sections -ffunction-sections

Bağlayıcı optimizasyon işaretini kullanarak çeviri birimlerini birbirine bağlayın (bu, bağlayıcının referans verilmeyen bölümleri atmasına neden olur):

-Wl,--gc-sections

Yani, test.cpp adında, içinde iki işlev tanımlanmış bir dosyanız varsa, ancak bunlardan biri kullanılmamışsa, kullanılmayan dosyayı aşağıdaki komutla gcc (g ++) ile atlayabilirsiniz:

gcc -Os -fdata-sections -ffunction-sections test.cpp -o test -Wl,--gc-sections

(-Os'un, GCC'ye boyut için optimize etmesini söyleyen ek bir derleyici bayrağı olduğunu unutmayın)


3
Lütfen bunun yürütülebilir dosyayı GCC'nin seçenek açıklamalarına göre yavaşlatacağını unutmayın (test ettim).
metamorfoz

1
Bununla birlikte mingw, statik olarak libstdc ++ ve libgcc'yi bayrakla statik olarak bağlarken çalışmaz -static. Bağlayıcı seçeneği -strip-allbiraz yardımcı olur, ancak yine de oluşturulan yürütülebilir dosya (veya dll), Visual Studio'nun oluşturacağından yaklaşık 4 kat daha büyüktür. Mesele şu ki, nasıl libstdc++derlendiğine dair hiçbir kontrolüm yok . ldTek bir seçenek olmalı .
Fabio

34

Eğer bu parçacığı inanılması gereken sen sağlamanız gerekmektedir -ffunction-sectionsve-fdata-sections kendi bölümünde her fonksiyon ve veri nesnesini koyacağız gcc, için. Daha sonra --gc-sectionskullanılmayan bölümleri kaldırmak için GNU ld'ye ve verirsiniz .


6
@MSalters: Varsayılan değil, çünkü C ve C ++ standartlarını ihlal ediyor. Birdenbire küresel başlatma gerçekleşmez, bu da bazı programcıları çok şaşırttı.
Ben Voigt

1
@MSalters: Yalnızca, varsayılan davranışı yapmayı önerdiğiniz standart olmayan davranış kırma seçeneklerini geçerseniz.
Ben Voigt

1
@MSalters: Statik başlatıcıları çalıştıran bir yama oluşturabilirseniz, ancak ve ancak programın doğru çalışması için yan etkiler gerekliyse, bu harika olur. Maalesef, bunu mükemmel bir şekilde yapmanın çoğu zaman durma problemini çözmeyi gerektirdiğini düşünüyorum, bu yüzden muhtemelen zaman zaman fazladan bazı semboller eklerken hata yapmanız gerekecek. Temelde Ira'nın soruya yaptığı yorumda söylediği şey bu. (BTW: "programın doğru çalışması için gerekli değil", standartlarda kullanılan terimin "kullanılmamış" ın farklı bir tanımıdır)
Ben Voigt

2
@BenVoigt C'de, genel başlatmanın yan etkileri olamaz (başlatıcılar sabit ifadeler olmalıdır)
MM

2
@Matt: Ama bu C ++ için doğru değil ... ve aynı bağlayıcıyı paylaşıyorlar.
Ben Voigt

25

Gcc & ld sürümünüz için dokümanlarınızı kontrol etmek isteyeceksiniz:

Ancak benim için (OS X gcc 4.0.1) Bunları ld için buluyorum

-dead_strip

Giriş noktası veya dışa aktarılan semboller tarafından erişilemeyen işlevleri ve verileri kaldırın.

-dead_strip_dylibs

Giriş noktası veya dışa aktarılan semboller tarafından ulaşılamayan dylib'leri kaldırın. Yani, bağlantı sırasında hiçbir sembol sağlamayan dylib'ler için yükleme komutu komutlarının oluşturulmasını bastırır. Bu seçenek, dylib'in önemli bir başlatıcıya sahip olması gibi bazı dolaylı nedenlerden dolayı çalışma zamanında gerekli olan bir dylib'e bağlanırken kullanılmamalıdır.

Ve bu yararlı seçenek

-why_live symbol_name

Symbol_name için bir başvuru zinciri günlüğe kaydeder. Yalnızca ile uygulanabilir -dead_strip. Ölü şeridin kaldırılması gerektiğini düşündüğünüz bir şeyin neden kaldırılmaması konusunda hata ayıklamaya yardımcı olabilir.

Ayrıca gcc / g ++ adamında, belirli türdeki ölü kodların ortadan kaldırılmasının yalnızca derleme sırasında optimizasyon etkinleştirildiğinde gerçekleştirildiğine dair bir not vardır.

Bu seçenekler / koşullar derleyiciniz için geçerli olmasa da, belgelerinizde benzer bir şey aramanızı öneririm.


Bu hiçbir şey yapmıyor gibi görünüyor mingw.
Fabio

-dead_stripbir gccseçenek değil .
ar2015

21

Programlama alışkanlıkları da yardımcı olabilir; örneğin static, belirli bir dosyanın dışından erişilemeyen işlevlere ekleme ; semboller için daha kısa isimler kullanın (biraz yardımcı olabilir, muhtemelen çok fazla değil); const char x[]mümkün olan yerlerde kullanın ; ... bu makale , dinamik paylaşılan nesnelerden bahsediyor olsa da, takip edilirse, son ikili çıktı boyutunuzu küçültmenize yardımcı olabilecek öneriler içerebilir (hedefiniz ELF ise).


4
Semboller için daha kısa isimler seçmek nasıl yardımcı olur?
fuz

1
semboller sıyrılmamışsa, ça va sans korkunç - ama şimdi söylenmesi gerekiyor gibi görünüyor.
ShinTakezou

@fuz Makale, dinamik paylaşılan nesnelerden (örn. .soLinux'ta) bahsediyor , bu nedenle sembol adlarının, Python'un ctypesFFI modülü gibi API'lerin çalışma zamanında sembolleri ada göre aramak için kullanabilmesi için saklanması gerekir .
ssokolow

18

Cevap şudur -flto. Bunu hem derlemenize hem de bağlantı adımlarınıza iletmelisiniz, aksi takdirde hiçbir şey yapmaz.

Aslında çok iyi çalışıyor - yazdığım bir mikrodenetleyici programının boyutunu önceki boyutunun% 50'sinden daha azına düşürdü!

Ne yazık ki biraz hatalı görünüyordu - doğru inşa edilmeyen şeylerin örnekleri vardı. Kullandığım derleme sisteminden kaynaklanıyor olabilir (QBS; çok yeni), ancak her durumda, mümkünse yalnızca son yapınız için etkinleştirmenizi ve bu derlemeyi iyice test etmenizi öneririm.


1
"-Wl, - gc-section" MinGW-W64 üzerinde çalışmıyor, "-flto" benim için çalışıyor. Teşekkürler
rhbc73

Çıktı derlemesi çok garip -fltoçünkü sahne arkasında ne yaptığını anlamıyorum.
ar2015

Onunla -fltoher dosyayı derleme için derlemediğine inanıyorum , onları LLVM IR'ye derliyor ve sonra son bağlantı hepsini tek bir derleme birimindeymiş gibi derliyor. Bu, kullanılmayan işlevleri ve satır içi staticolmayanları ve muhtemelen diğer şeyleri de ortadan kaldırabileceği anlamına gelir . Bkz llvm.org/docs/LinkTimeOptimization.html
Timmmm

13

Tamamen sembollerle ilgili olmasa da, boyut için gidiyorsanız - her zaman -Osve -sbayrakları ile derleyin . -Osortaya çıkan kodu minimum yürütülebilir boyut için optimize eder ve -ssimge tablosunu ve yeniden konumlandırma bilgilerini yürütülebilir dosyadan kaldırır.

Bazen - küçük boyut isteniyorsa - farklı optimizasyon bayraklarıyla oynamanın önemi olabilir veya olmayabilir. Örneğin geçiş yapmak -ffast-mathve / veya -fomit-frame-pointerbazen düzinelerce bayt tasarrufu sağlayabilir.


Çoğu optimizasyon ayarlaması, dil standardına uyduğunuz sürece yine de doğru kodu verecektir, ancak -ffast-mathtamamen standartlara uygun C ++ kodunda hasara yol açtım, bu yüzden asla tavsiye etmem.
Raptor007

11

Bana öyle geliyor ki Nemo'nun verdiği cevap doğru. Bu talimatlar işe yaramazsa, sorun kullandığınız gcc / ld sürümüyle ilgili olabilir, burada ayrıntılı olarak açıklanan talimatları kullanarak örnek bir program derledim.

#include <stdio.h>
void deadcode() { printf("This is d dead codez\n"); }
int main(void) { printf("This is main\n"); return 0 ; }

Ardından, aşamalı olarak daha agresif ölü kod kaldırma anahtarları kullanarak kodu derledim:

gcc -Os test.c -o test.elf
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections -Wl,--strip-all

Bu derleme ve bağlantı parametreleri, sırasıyla 8457, 8164 ve 6160 bayt boyutunda çalıştırılabilir dosyalar üretti ve en önemli katkı 'hepsini soyma' bildiriminden geldi. Platformunuzda benzer indirimler üretemiyorsanız, o zaman gcc sürümünüz bu işlevi desteklemiyor olabilir. Linux Mint 2.6.38-8-generic x86_64 üzerinde gcc (4.5.2-8ubuntu4), ld (2.21.0.20110327) kullanıyorum


8

strip --strip-unneededyalnızca çalıştırılabilir dosyanızın sembol tablosunda çalışır. Aslında herhangi bir çalıştırılabilir kodu kaldırmaz.

Standart kitaplıklar, tüm işlevlerini kullanılarak birleştirilen ayrı nesne dosyalarına bölerek aradığınız sonucu elde eder ar. Daha sonra ortaya çıkan arşivi bir kitaplık olarak bağlarsanız (yani,-l your_library , ld ), o zaman ld yalnızca nesne dosyalarını ve dolayısıyla gerçekten kullanılan sembolleri içerecektir.

Bu benzer kullanım sorusuna verilen yanıtlardan bazılarını da bulabilirsiniz .


2
Kitaplıktaki ayrı nesne dosyaları yalnızca statik bir bağlantı yapılırken ilgilidir. Paylaşılan kitaplıklarla, tüm kitaplık yüklenir, ancak tabii ki yürütülebilir dosyaya dahil edilmez.
Jonathan Leffler

4

Bu yeni bir özellik olduğundan mevcut durumunuza yardımcı olup olmayacağını bilmiyorum, ancak sembollerin görünürlüğünü küresel bir şekilde belirtebilirsiniz. Geçen-fvisibility=hidden -fvisibility-inlines-hiddenDerlemede , bağlayıcının daha sonra gereksiz sembollerden kurtulmasına yardımcı olabilir. Bir yürütülebilir dosya üretiyorsanız (paylaşılan bir kitaplığın aksine) yapacak başka bir şey yok.

Daha fazla bilgi (ve örneğin kütüphaneler için ayrıntılı bir yaklaşım) GCC wiki'de mevcuttur .


4

GCC 4.2.1 kılavuzundan bölüm -fwhole-program:

Mevcut derleme biriminin derlenen tüm programı temsil ettiğini varsayalım. mainÖzniteliğin dışında kalan ve öznitelikle birleştirilen tüm genel işlevler ve değişkenler externally_visiblestatik işlevler haline gelir ve bir etki içinde işlemler arası iyileştiriciler tarafından daha agresif bir şekilde optimize edilir. Bu seçenek, statictek dosyadan oluşan programlar için anahtar sözcüğün doğru kullanımına eşdeğer olsa da , --combinebu bayrak, seçenekle birlikte , daha küçük ölçekli C programlarının çoğunu derlemek için kullanılabilir, çünkü işlevler ve değişkenler, tüm birleşik derleme birimi için yerel hale gelir; tek kaynak dosyanın kendisi.


Evet ama bu muhtemelen herhangi bir artımlı derlemeyle çalışmıyor ve muhtemelen biraz yavaş olacak.
Timmmm

@Timmmm: Düşündüğünden şüpheleniyorum -flto.
Ben Voigt

Evet! Daha sonra buldum (neden cevaplardan hiçbiri değil?). Maalesef biraz hatalı görünüyordu, bu yüzden onu yalnızca son sürüm için tavsiye eder ve daha sonra bu yapıyı çokça test ederdim!
Timmmm

-1

Tüm sembolleri ondan çıkarmak için nesne dosyasında şerit ikili kullanabilirsiniz (örn. Yürütülebilir).

Not: dosyanın kendisini değiştirir ve kopya oluşturmaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.