2012-05-05 5 views
19

Linux üzerinde salt statik bağlantılı PIE çalıştırılabilirlik kavramını deniyorum ama GNU binutils linker'ın -pie kullanıldığında bile -static verildiğinde bile çıktı ikiliine bir PT_INTERP üstbilgisi ekleme konusunda ısrar ediyor . Bu davranışı engellemek için herhangi bir yolu var mı? Diğer bir deyişle, GNU ld'yi özellikle çıkış dosyasına belirli üstbilgileri yazmamak için bir yol var mı? Belki bir linker betiği ile?GNU ld'nin -dynamic-linker'ı (PT_INTERP) tamamen çıkarması için bir seçenek var mı?

(Lütfen işe yaramayacağına dair iddialarla yanıt vermeyin; programın hala yeniden konumlandırma işlemine gereksinim duyduğunun farkındayım - sadece -Bsymbolic kullanımım nedeniyle yük adrese bağlı değişiklikler Bu işlemin yapılması için standart Scrt1.o yerine başlangıç ​​kodu. Ancak, dinamik bağlantı oluşturucu olmadan çalıştırılmaya başlayamıyorum ve zaten PT_INTERP üstbilgisini hexedit olmadan ikili olarak çalıştıramazsınız.)

+0

ben bu düz varsa bakayım. Kendi giriş noktanızı belirliyorsunuz, bu da bazı özel yer değiştirme işlemlerini ele alıyor ve çekirdeğin standart çeviricide yüklenmesini istemiyor musunuz? Eğer bir başlatmaya ihtiyaç duyduğunuz kütüphanelere karşı .init ile bağlantı kuruyorsanız? Benim deneyimlerimde, eğer yürütücülerinizle bir şeyler yapmak istiyorsanız, ancak LDFLAGS'in bazı permütasyonları ile onu üretmenin bir yolu yoktur, o zaman bu iyi bir fikir değildir. –

+1

Bunu bir uygulamanın yapım sistemine koymaya çalışıyorsam,% 100 size katılıyorum. Bu gibi linker seçenekleri ile aptal bir uygulama için korkunç bir hack, ve bu zaten işe yaramaz çünkü çünkü tüm .a 'kütüphaneleri PIC olarak inşa edilmesini gerektirir. Bununla birlikte, üzerinde çalıştıklarım, setuid ikili dosyaları için dinamik bağlayıcıya sahip olmanın kabul edilemez bir risk olduğu güvenlik odaklı dağıtımlarda kullanılmak üzere tasarlanmış yeni bir takım zinciri seçeneğidir. 'Ld' düzeyinde, yalnızca gcc dosyası ve 'crt' düzeyinde herhangi bir değişiklik gerekmiyorsa dağıtmak çok daha kolaydır. –

+0

görünüşe göre ld için bir yama yazmanız ve sonra neden sandığa uygulanması gerektiği konusunda tartışmanız gerekecek. Ayrıca, bu çok ilginç bir iş gibi geliyor. –

cevap

10

ben bir çözüm buldum düşünüyorum. Davranışı düzeltmek için birkaç ek linker seçeneğine ihtiyacınız var, ancak özel bir linker komutuna gerek duyulmuyor. Başka bir deyişle, -shared linker betiği, temel olarak statik pasta ikililerini bağlamak için doğrudur.

Bununla çalışmayı başarabilirsem, yanıtı kullandığım tam komut satırı ile güncelleştiririm.

Güncelleme: Çalışıyor! İşte komut satırı var:

gcc -shared -static-libgcc -Wl,-static -Wl,-Bsymbolic \ 
    -nostartfiles -fPIE Zcrt1.s Zcrt2.c /usr/lib/crti.o hello.c /usr/lib/crtn.o 
Zcrt1.s normal çalışmasını yapmadan önce Zcrt2.c bir işlevini çağırır Scrt1.s değiştirilmiş bir versiyonudur ve Zcrt2.c kod aux vektörü sadece işler

DYNAMIC bölümünü bulmak için argv ve ortam dizilerini geçtikten sonra, relokasyon tabloları üzerinde döngü yapar ve tüm göreceli tipteki yer değiştirmeleri uygular (yalnızca var olması gerekenler). Şimdi tüm bu (küçük bir çalışma ile) bir komut dosyası veya gcc specfile içine sarılmış olabilir

...

+0

Neat trick. Esas olarak, çıkarmaktan çok ihtiyacın olanı inşa ediyorsun. +1 ve iyi şanslar. –

4

Genişleyen Daha önce yaptığım bu not, bu kutunun içine sığmıyor (ve bu sadece bir fikir veya tartışma, lütfen kabul veya ödüllendirmekle yükümlü hissetmeyin), belki de bunu yapmanın en kolay ve en temiz yolu,'yi çıkarmak için post-build adımı Oluşan ikilidenbaşlık?

Başlıkları el ile düzenlemekten ve potansiyel olarak her şeyi değiştirmekten daha kolay olanı 'u PT_NULL ile değiştirmektir. Dosyanın mevcut araçlarla (bir çeşit okunabilir hex bul ve değiştir) ya da bunu yapmak için küçük bir program yazmanız gerektiğinde bir yolunu bulup bulamayacağınızı bilmiyorum. Biliyorum ki, libbfd (GNU İkili Dosya Tanımlayıcı kütüphanesi), tüm işin çok daha kolay olmasını sağlayacağından, ikinci durumda arkadaşınız olabilir.

Sadece bunun neden bir ld seçeneği aracılığıyla gerçekleştirildiğinin önemli olduğunu anlamıyorum. Varsa, neden tercih edileceğini görebiliyorum; ama bazı (göze batan ışık) Googling'in böyle bir özellik olmadığını göstermesi, bunu ayrı ayrı yapmak ve sonradan yapmak için daha az güçlük çekebilir. (Belki ld bayrağı ekleyerek PT_NULL ile PT_INTERP değiştirilmesini komut dosyası, ancak akıntıya karşı farklı bir konudur çekmeye devs ikna daha kolaydır.) Bu bir şey ise


Görünüşe (ve beni düzeltin lütfen '' Zaten görüldüğü gibi '' ld 'un with the PHDRS command linkerdeki ELF başlıklarından herhangi birine göre ve :none ile herhangi bir segmente özel bir başlık tipinin dahil edilmemesi gerektiğini belirtebilirsiniz. Ben sözdizimi emin değilim, ama böyle bir şey olmazdı sanırım:

--library-path=searchdir 

ekle yol searchdir: ld docs Eğer --library-path ile bağlayıcı komut dosyası geçersiz kılabilir itibaren

PHDRS 
{ 
    headers PT_PHDR PHDRS ; 
    interp PT_INTERP ; 
    text PT_LOAD FILEHDR PHDRS ; 
    data PT_LOAD ; 
    dynamic PT_DYNAMIC ; 
} 

SECTIONS 
{ 
    . = SIZEOF_HEADERS; 
    .interp : { } :none 
    ... 
} 

ld, arşiv kitaplıklarını ve ld denetim komut dosyalarını arayacak olan yolların listesine. Bu seçeneği herhangi bir kez kullanabilirsiniz. Dizinler, komut satırında belirtildikleri sırayla aranır. komut satırında belirtilen dizinler, varsayılan dizinlerden önce aranır. Tüm seçeneklerinin tümü, seçeneklerinin göründüğü sıraya bakmaksızın tüm -l seçenekleri için geçerlidir.Aranan varsayılan yollar ( `-L 'ile belirtilmemiş), hangi öykünme modunun kullandığını ve bazı durumlarda da nasıl yapılandırıldığına bağlı olarak bağlıdır. Çevre Değişkenler bölümüne bakınız. Yollar, SEARCH_DIR komutuyla bir link komut dosyasında da belirtilebilir. Bu şekilde belirtilen dizinler, linker betiğinin komut satırında göründüğü noktasında aranır. Ayrıca

, the section on Implicit Linker Scripts den:

Eğer bağlayıcı bir nesne dosyası veya bir arşiv dosyası olarak tanıyamaz bir bağlayıcı girdi dosyasını belirtirseniz, bir dosyayı okumak için çalışacağız linker komut dosyası. Dosya bir bağlayıcı komut dosyası olarak ayrıştırılamıyorsa, bağlayıcı bir hata bildirir. örtülü olarak tanımlanan bağlayıcı komut aksine, kullanıcı tanımlı bağlayıcı komut değerleri ima etmek gibi görünüyor

, varsayılan komut değerlerin yerine geçer.

+1

Bu, deney yapmak için çok kolaydır ve aslında deney yapmak için yaptığım şeydir. Ancak, hedef, CFLAGS ve LDFLAGS'de bazı ekstra seçenekler bırakabileceğiniz ve statik pasta oluşturmak için herhangi bir program alabileceğiniz bir takım zinciri oluşturmak olduğunda hiç yardımcı olmaz. –

+0

"PT_INTERP" işlevini atlamak için bir destek sağlamak için ld akış yönündeki bir yamayı engelleme (zaten var olmadığı varsayılırsa), genel bir araç zinciri oluşturabilecek herhangi bir alternatif olduğunu düşünmüyorum. –

+0

Bazıları buldum. Herhangi bir ELF başlığı için varsayılan davranışı geçersiz kılma ve cevabımı güncellemeyle ilgili bilgi - bu sizin için geçerli midir? –

12

Belki de naif oluyorum, ancak ... varsayılan bağlayıcı komut dosyasını aramak, düzenlemek ve .interp bölümünün bağlantılarını kaldırmak için yeterli olmaz mı? Örneğin, makinemde komut dosyaları /usr/lib/ldscripts'da ve SECTIONS bölümündeki interp : { *(.interp) } satırındadır.

Aşağıdaki komutu çalıştırarak kullanılan varsayılan komut dumpp edebilirsiniz.

$ ld --verbose ${YOUR_LD_FLAGS} | \ 
    gawk 'BEGIN { s = 0 } { if ($0 ~ /^=/) s = !s; else if (s == 1) print; }' 

Sen interp satırı silin (veya sadece grep -v kullanabilir ve program bağlamak için bu komut dosyasını kullanmak için hafifçe gawk komut dosyasını

+0

Şimdiye kadar bu muhtemelen en iyi yaklaşımdır. Ne yazık ki, her sistem için linker betiklerinin yeni sürümlerini oluşturmayı gerektiriyor ve sadece mevcut bir çalışma linker betiğine geri yüklenememektedir. Ama eğer konseptin iyi işlediğini ispatlayabilirsem, belki de üst üste binutularda alabilirim. –

+0

@R .. - 'strace' kullanma hilem işe yaramaz çünkü betik aslında 'ld' içine derlenmiş ve diskten okunmamıştır. Ama sen 'ld --verbose' ve çıktıda bir miktar sihir kullanarak elde edebilirsin (güncellenmiş cevaba bakın. – rodrigo

+0

Bu koddan, ld sarıcısını, bazılarının [gcc] için yaptığı gibi yapabilirsin (http://users.sdsc.edu/~kst/gcc-wrapper/) – alexander

2

bi'şey değil GNU ld konusunda uzman, ama documentation aşağıdaki bilgileri bulduk:

Giriş bölümlerini silmek için özel sekme `/ DISCARD/'kullanılabilir. `/ DISCARD/' adlı bir çıkış bölümüne atanan tüm bölümler son bağlantıya dahil değildir.

Umarım bu size yardımcı olacaktır.

Update:

(Bu interp bölüm başlığı PT_INTERP birlikte damlatılır için çalışmaz çözeltisi, birinci versiyonu.)

main.c:

int main(int argc, char **argv)                                
{                                        
    return 0;                                     
} 

main.x:

SECTIONS {                                      
    /DISCARD/ : { *(.interp) }                                 
} 

inşa komut:

-Wl, -T, main.x:

$ gcc -nostdlib -pie -static main.c 
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000000218 
$ readelf -S a.out | grep .interp 
    [ 1] .interp   PROGBITS  00000134 000134 000013 00 A 0 0 1 

GÜNCELLEME 2: (. bağlayıcı komut dosyası interp)

bu çözüm fikri orijinal bölümü 'interp' .interp1 yeniden adlandırıldı olmasıdır. Başka bir deyişle, bölümün tüm içeriği .interp1 bölümüne yerleştirilir. Bu nedenle, INTERP bölümünü (şimdi boş) varsayılan linker betiği ayarlarını kaybetme korkusu olmadan güvenli bir şekilde kaldırabiliyoruz ve dolayısıyla INTERP_PT başlığı da kaldırılacak. amacıyla

SECTIONS { 
    .interp1 : { *(.interp); } : NONE 
    /DISCARD/ : { *(.interp) } 
} 

bölümü (.interp1) dosyası içinde mevcut interp ama çıkarılır INTERP_PT başlığının muhtevası, bir readelf + grep'in bir arada kullanmak olduğunu gösterir. basitçe pasta ikilileri yapmak -shared yerine -pie kullanarak:

$ gcc -nostdlib -pie -Wl,-T,main.x main.c 
$ readelf -l a.out | grep interp 
    00  .note.gnu.build-id .text .interp1 .dynstr .hash .gnu.hash .dynamic .got.plt 
$ readelf -S a.out | grep interp 
    [ 3] .interp1   PROGBITS  0000002e 00102e 000013 00 A 0 0 1 
+0

Bunu ".interp" ile kullanmayı denedim, ancak varsayılan dosya tanıtıcısı ve kaynak dosyaları tarafından oluşturulmadığından işe yaramadı. Nasıl yazılması gerektiğine dair bir fikriniz var mı? –

+0

Cevabını örnekle güncelledim – alexander

+0

Eh '' değil '' '' '' 'ı bakmanız gerekir (program başlıkları). 'INTERP' başlığını engeller, ancak aynı zamanda tek bir sahte 'LOAD' başlığının doğru olanlar yerine oluşturulmasına neden olduğu görülmektedir (tüm program okuma-yazma ile yüklenir), bu nedenle sadece sistemdeki dd komut dosyasını atlıyor gibi görünür. .. –

İlgili konular