C에서 _start () 사용은 무엇입니까?


125

동료로부터 main()함수 를 작성하지 않고도 C 프로그램을 작성하고 실행할 수 있다는 것을 배웠습니다 . 다음과 같이 할 수 있습니다.

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; 
}

다음 명령으로 컴파일하십시오.

gcc -o my_main my_main.c nostartfiles

다음 명령으로 실행하십시오.

./my_main

언제 이런 일을해야할까요? 이것이 유용한 실제 시나리오가 있습니까?



7
프로그램 시작 방법의 내부 작동 방식을 보여주는 고전 기사 : Linux 용 Really Teensy ELF Executables 만들기에 대한 Whirlwind Tutorial . 이것은의 일부 세부 사항 _start()및 외부의 기타 사항에 대해 논의하는 좋은 읽기 입니다 main().

1
C 언어 자체는에 대해 _start또는 다른 진입 점에 대해 아무 말도하지 않습니다 main(단, 진입 점의 이름이 독립 (임베디드) 구현에 대해 구현 정의 된 경우 제외).
Keith Thompson

답변:


107

기호 _start는 프로그램 의 진입 점 입니다. 즉, 해당 심볼의 주소는 프로그램 시작시 점프 한 주소입니다. 일반적으로 이름의 함수 는 C 런타임 환경의 시작 코드를 포함하는 _start라는 파일에서 제공됩니다 crt0.o. 몇 가지 항목을 설정하고 인수 배열을 채우고 argv거기에 몇 개의 인수가 있는지 계산 한 다음 main. main반환 후 exit호출됩니다.

프로그램이 C 런타임 환경을 사용하지 않으려면 _start. 예를 들어 Go 프로그래밍 언어의 참조 구현은 스택에 마법이 필요한 비표준 스레딩 모델이 필요하기 때문에 그렇게합니다. _start아주 작은 프로그램이나 틀에 얽매이지 않는 일을하는 프로그램을 작성하고 싶을 때 자신 만의 것을 제공하는 것도 유용합니다 .


2
또 다른 예는 자체 _start가 정의 된 Linux의 동적 링커 / 로더입니다.
PP

2
@BlueMoon 그러나 그것은 _start객체 파일 crt0.o에서도 나옵니다 .
fuz

2
@ThomasMatthews 표준은 지정하지 않습니다 _start. 사실, main호출 되기 전에 어떤 일이 발생하는지 지정하지 않고 호출 될 때 충족되어야하는 조건 만 지정합니다 main. _start이전으로 거슬러 올라가는 진입 점에 대한 관습입니다 .
fuz

1
"Go 프로그래밍 언어의 참조 구현은 비표준 스레딩 모델이 필요하기 때문에 그렇게합니다."crt0.o는 C에 따라 다릅니다 (crt-> C 런타임). 다른 언어에 사용될 것으로 기대할 이유가 없습니다. 그리고 이동의 스레딩 모델 표준을 준수 완전히
스티브 콕스

8
@SteveCox 이러한 방식으로 언어를 구현하는 것이 더 쉽기 때문에 많은 프로그래밍 언어가 C 런타임 위에 빌드됩니다. Go는 일반 스레딩 모델을 사용하지 않습니다. 작은 힙 할당 스택과 자체 스케줄러를 사용합니다. 이것은 확실히 표준 스레딩 모델이 아닙니다.
fuz

45

main프로그래머의 관점에서는 프로그램의 진입 점 이지만 _startOS 관점의 일반적인 진입 점입니다 (프로그램이 OS에서 시작된 후 실행되는 첫 번째 명령).

일반적인 C 및 특히 C ++ 프로그램에서는 실행이 main에 들어가기 전에 많은 작업이 수행되었습니다. 특히 전역 변수 초기화와 같은 것입니다. 여기에 당신이 사이에 일어나는 모든 일의 좋은 설명 찾을 수 있습니다 _start()그리고 main()또한 주가 (아래 설명을 참조) 다시 종료 된 후입니다.
이를 위해 필요한 코드는 일반적으로 시작 파일에서 컴파일러 작성자가 제공하지만 플래그를 사용 –nostartfiles하면 기본적으로 컴파일러에 다음 과 같이 알 수 있습니다. "표준 시작 파일을 제공하는 데 신경 쓰지 마십시오. 스타트".

이것은 때때로 필요하며 종종 임베디드 시스템에서 사용됩니다. 예를 들어 OS가없고 전역 개체를 초기화하기 전에 메모리 시스템의 특정 부분 (예 : 캐시)을 수동으로 활성화해야하는 경우.


전역 변수는 데이터 섹션의 일부이므로 프로그램을로드하는 동안 설정됩니다 (const 인 경우 텍스트 섹션의 일부인 동일한 스토리). _start 함수는 완전히 관련이 없습니다.
Cheiron

@Cheiron : 죄송합니다. 제 emistake C ++에서 전역 변수는 종종 내부에서 실행되는 생성자 _start()(또는 실제로 호출되는 다른 함수)에 의해 초기화되며 많은 Bare-Metal-Programs에서는 모든 전역 데이터를 플래시에서 RAM으로 명시 적으로 복사합니다. 첫째,에서도 발생 _start()하지만이 질문은 C ++ 나 베어 메탈 코드에 관한 것이 아닙니다.
MikeMB 2015

1
자체를 제공하는 프로그램 _start에서 C 라이브러리는 사용자가 직접 수행하기위한 특별한 단계를 수행하지 않는 한 초기화되지 않습니다. 이러한 프로그램에서 비동기 신호 안전이 아닌 함수를 사용하는 것은 안전하지 않을 수 있습니다. (즉, 공식적인 보장도 없다 어떤 라이브러리 기능이 작동합니다,하지만 그들은 고장에 그들의 방법의 외출해야 할 것이다, 그래서 비동기 시그널에 안전 기능은 전혀 전역 데이터를 참조 할 수 없습니다.)
zwol

@zwol은 부분적으로 만 정확합니다. 예를 들어, 이러한 함수는 메모리를 할당 할 수 있습니다. 내부 데이터 구조 malloc가 초기화되지 않은 경우 메모리 할당에 문제가 있습니다.
fuz

1
@FUZxxl 내가 그 비동기 시그널에 안전 기능 통지, 그런 말 데 있다 수정할 수 errno(예를 들면 readwrite비동기 신호 안전하고 설정할 수 있습니다 errno) 및 당 스레드 때 생각할 정확히에 따라 문제가 될 수 errno위치가 할당 .
zwol 2015

2

다음이전에 프로그램을 시작하는 동안 발생하는 일에 대한 좋은 개요입니다 main. 특히__start 입니다 실제 진입 점 OS의 관점에서 프로그램에.

이 주소는 명령어 포인터 가 프로그램에서 카운트를 시작할 .

거기에있는 코드는 일부 하우스 키핑을 수행하기 위해 C 런타임 라이브러리 루틴을 호출 한 다음을 호출 main한 다음 작업을 중단하고 반환 된 exit종료 코드 main로 호출 합니다.


사진은 천 단어의 가치가 있습니다.

C 런타임 시작 다이어그램


추신 :이 답변은 SO가 도움이되는 다른 질문 에서 이식 된 것입니다.


우수한 분석 과 멋진 사진 을 보존하기 위해 교차 게시되었습니다 .
ulidtko

1

언제 이런 일을해야할까요?

프로그램에 대한 자체 시작 코드를 원할 때.

main C 프로그램의 첫 번째 항목이 아닙니다. _start 의 첫 번째 항목이 아니라 커튼 뒤의 첫 번째 항목입니다.

Linux의 예 :

_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

이것이 유용한 실제 시나리오가 있습니까?

당신이 의미한다면, 우리 자신을 구현하십시오 _start:

예, 제가 함께 작업 한 대부분의 상업용 임베디드 소프트웨어에서 _start특정 메모리 및 성능 요구 사항과 관련하여 자체적으로 구현해야합니다 .

의미하는 경우 main함수를 삭제 하고 다른 것으로 변경하십시오.

아니요, 그렇게하면 어떤 이점도 보지 못합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.