C de _start () kullanımı nedir?


126

Meslektaşımdan, bir kişinin bir main()işlev yazmadan bir C programı yazıp çalıştırabileceğini öğrendim . Şu şekilde yapılabilir:

my_main.c

/* Compile this with gcc -nostartfiles */

#include <stdlib.h>

void _start() {
  int ret = my_main();
  exit(ret); 
}

int my_main() {
  puts("This is a program without a main() function!");
  return 0; 
}

Şu komutla derleyin:

gcc -o my_main my_main.c nostartfiles

Şu komutla çalıştırın:

./my_main

Böyle bir şeyi ne zaman yapmak gerekir? Bunun yararlı olacağı herhangi bir gerçek dünya senaryosu var mı?



7
Programların nasıl başladığına dair bazı iç işleyişlerini gösteren klasik makale: Linux için Really Teensy ELF Yürütülebilir Dosyaları Oluşturma Üzerine Bir Kasırga Eğitimi . Bu, bazı ince noktaları _start()ve dışındaki diğer şeyleri tartışan iyi bir okuma main().

1
C dilinin kendisi dışında _startherhangi bir giriş noktası hakkında veya hakkında hiçbir şey söylemez main(giriş noktasının adının bağımsız (gömülü) uygulamalar için uygulama tanımlı olması dışında).
Keith Thompson

Yanıtlar:


108

Sembol _start, programınızın giriş noktasıdır . Yani bu sembolün adresi, program başlangıcında atlanan adrestir. Normalde, isimli işlev , C çalışma zamanı ortamı için başlangıç ​​kodunu içeren _startbir dosya tarafından sağlanır crt0.o. Bazı şeyler kurar, argüman dizisini doldurur, argvkaç tane argüman olduğunu sayar ve sonra çağırır main. Sonra mainiadeler, exitdenir.

Bir program C çalışma zamanı ortamını kullanmak istemiyorsa, için kendi kodunu sağlaması gerekir _start. Örneğin, Go programlama dilinin referans uygulaması bunu yapar çünkü standart olmayan bir iş parçacığı modeline ihtiyaç duyarlar ve yığınla biraz sihir gerektirir. Ayrıca _start, alışılmadık şeyler yapan gerçekten küçük programlar veya programlar yazmak istediğinizde , kendinizinkini sağlamanız da yararlıdır .


2
Başka bir örnek, Linux'un kendi _start tanımlı dinamik bağlayıcı / yükleyicisidir.
PP

2
@BlueMoon Ama bu _startda nesne dosyasından geliyor crt0.o.
Fuz

2
@ThomasMatthews Standart belirtmiyor _start; aslında, mainçağrılmadan önce ne olduğunu belirtmez , sadece çağrıldığında hangi koşulların karşılanması gerektiğini belirtir main. Daha çok giriş noktasının _starteski günlere dayanan bir konvansiyonu .
Fuz

1
"Go programlama dilinin referans uygulaması bunu yapar çünkü standart olmayan bir iş parçacığı modeline ihtiyaç duyar" crt0.o C'ye özgüdür (crt-> C çalışma zamanı). Başka bir dil için kullanılmasını beklemek için hiçbir sebep yok. Ve Go'nun diş çekme modeli tamamen standart uyumlu
Steve Cox

8
@SteveCox Pek çok programlama dili C çalışma zamanının üzerine inşa edilmiştir çünkü dilleri bu şekilde uygulamak daha kolaydır. Go, normal diş çekme modelini kullanmaz. Küçük, yığın olarak ayrılmış yığınlar ve kendi zamanlayıcılarını kullanırlar. Bu kesinlikle standart bir diş açma modeli değildir.
Fuz

45

mainProgramcılar açısından programınızın giriş noktası iken , _startişletim sistemi perspektifinden olağan giriş noktasıdır (programınız işletim sisteminden başlatıldıktan sonra yürütülen ilk talimat)

Tipik bir C ve özellikle C ++ programında, yürütme main'e girmeden önce çok fazla çalışma yapılmıştır. Özellikle küresel değişkenlerin ilklendirilmesi gibi şeyler. İşte size arasına oluyor her şeyin iyi bir açıklama bulabilirsiniz _start()ve main()ayrıca ana (aşağıdaki yorumunu bakınız) tekrar çıktıktan sonra.
Bunun için gerekli kod genellikle derleyici yazarları tarafından bir başlangıç ​​dosyasında sağlanır, ancak –nostartfilesderleyiciye temel olarak şunu söylersiniz: "Bana standart başlangıç ​​dosyasını verme zahmetine girmeyin, bana doğru olanı tam olarak kontrol edin. Başlat".

Bu bazen gereklidir ve genellikle gömülü sistemlerde kullanılır. Örneğin, bir işletim sisteminiz yoksa ve global nesnelerinizin başlatılmasından önce bellek sisteminizin belirli kısımlarını (örneğin önbellekler) manuel olarak etkinleştirmeniz gerekiyorsa.


Genel değişkenler veri bölümünün bir parçasıdır ve bu nedenle programın yüklenmesi sırasında kurulurlar (sabit iseler metin bölümünün bir parçasıdırlar, aynı hikaye). _Start işlevi bununla tamamen ilgisizdir.
Cheiron

@Cheiron: Üzgünüm, benim emistake c ++ 'da, global değişkenler genellikle içinde çalıştırılan _start()(veya aslında onun tarafından adlandırılan başka bir işlev) bir kurucu tarafından başlatılır ve birçok Bare-Metal-Programında, tüm global verileri flash'tan RAM'e açıkça kopyalarsınız ilki, içinde de olur _start(), ancak bu soru ne c ++ ne de çıplak metal kod ile ilgiliydi.
MikeMB

1
Kendine ait olan bir programda, _startbunu kendiniz yapmak için özel adımlar atmadığınız sürece C kitaplığının başlatılmayacağını unutmayın - böyle bir programdan eşzamansız sinyal güvenli olmayan herhangi bir işlevi kullanmak pek de güvenli olmayabilir. ( Herhangi bir kütüphane işlevinin çalışacağına dair resmi bir garanti yoktur , ancak eşzamansız sinyal güvenli işlevler hiçbir küresel veriye
başvuramaz

@zwol bu yalnızca kısmen doğru. Örneğin, böyle bir işlev bellek ayırabilir. Dahili veri yapıları mallocbaşlatılmadığında bellek tahsisi sorunludur .
Fuz

1
@FUZxxl O zaman uyumsuz-sinyal güvenli fonksiyonları fark, söyledikten olan değiştirmesine izin errno(örn readve writezaman uyumsuz-sinyal-güvenli olan ve ayarlayabilirsiniz errno) ve başına iplik zaman o makul tam bağlı bir sorun olabilir errnokonum tahsis edilir .
zwol

2

Daha önce program başlangıcında ne olduğuna dair iyi bir genel bakış main. Özellikle, gösteriyor ki __startbir gerçek giriş noktası OS açısından programınıza.

Programınızda komut işaretçisinin saymaya başlayacağı ilk adrestir .

Buradaki kod, sadece biraz temizlik yapmak için bazı C çalışma zamanı kitaplık yordamlarını çağırır, sonra sizin arar mainve sonra exither şeyi aşağı indirir ve maindöndürülen çıkış koduyla çağırır .


Bir resim bin kelime değerinde bir olup:

C çalışma zamanı başlangıç ​​diyagramı


Not: Bu yanıt, SO'nun bunun kopyası olarak yardımcı bir şekilde kapattığı başka bir sorudan nakledilmiştir .


Mükemmel analizi ve güzel resmi korumak için çapraz yayınlanmıştır .
ulidtko

1

Böyle bir şeyi ne zaman yapmak gerekir?

Programınız için kendi başlangıç ​​kodunuzu istediğinizde.

mainbir C programı _startiçin ilk giriş değil, perdenin arkasındaki ilk giriştir.

Linux'ta örnek:

_start: # _start is the entry point known to the linker
    xor %ebp, %ebp            # effectively RBP := 0, mark the end of stack frames
    mov (%rsp), %edi          # get argc from the stack (implicitly zero-extended to 64-bit)
    lea 8(%rsp), %rsi         # take the address of argv from the stack
    lea 16(%rsp,%rdi,8), %rdx # take the address of envp from the stack
    xor %eax, %eax            # per ABI and compatibility with icc
    call main                 # %edi, %rsi, %rdx are the three args (of which first two are C standard) to main

    mov %eax, %edi    # transfer the return of main to the first argument of _exit
    xor %eax, %eax    # per ABI and compatibility with icc
    call _exit        # terminate the program

Bunun yararlı olacağı herhangi bir gerçek dünya senaryosu var mı?

Yani, kendimizinkini uygulayın _start:

Evet, birlikte çalıştığım ticari gömülü yazılımların çoğunda, _startözel bellek ve performans gereksinimlerimizle ilgili olarak kendi yazılımımızı uygulamamız gerekiyor .

Yani, mainişlevi bırakın ve başka bir şeyle değiştirin:

Hayır, bunu yapmanın bir faydası görmüyorum.

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.