Nice programing

main을 배열로 선언하는 이유는 무엇입니까?

nicepro 2020. 12. 30. 20:23
반응형

main을 배열로 선언하는 이유는 무엇입니까?


나는 거대한 배열로 선언 된 컴파일러 폭탄으로 의도 된 CodeGolf에서 코드 조각을 보았다 main. 다음 (폭탄이 아닌) 버전을 시도했습니다.

int main[1] = { 0 };

Clang에서 잘 컴파일되고 GCC에서 경고 만 표시됩니다.

경고 : 'main'은 일반적으로 함수입니다. [-Wmain]

결과 바이너리는 물론 쓰레기입니다.

그러나 왜 그것이 전혀 컴파일되지 않습니까? C 사양에서도 허용됩니까? 관련성이 있다고 생각하는 섹션은 다음과 같습니다.

5.1.2.2.1 프로그램 시작

프로그램 시작시 호출되는 함수의 이름은 main입니다. 구현은이 함수에 대한 프로토 타입을 선언하지 않습니다. 반환 유형이 int이고 매개 변수없이 [...] 또는 두 매개 변수 [...] 또는 다른 구현 정의 방식으로 정의되어야합니다.

"다른 구현 정의 방식"에 전역 배열이 포함됩니까? (스펙이 여전히 기능을 참조하는 것 같습니다 .)

그렇지 않은 경우 컴파일러 확장입니까? 아니면 다른 목적으로 사용되는 툴체인의 기능이 프론트 엔드를 통해 사용 가능하도록 결정 했습니까?


C는 main기능이 필요하지 않은 "호스팅되지 않은"또는 독립 환경을 허용하기 때문 입니다. 이것은 이름 main이 다른 용도로 사용 가능 하다는 것을 의미 합니다. 이것이 언어가 그러한 선언을 허용하는 이유입니다. 대부분의 컴파일러는 두 가지를 모두 지원하도록 설계되었으므로 (대부분 링크가 수행되는 방식이 다름) 호스트 환경에서 불법 인 구문을 허용하지 않습니다.

표준에서 참조하는 섹션은 호스팅 된 환경을 나타내며 독립형에 해당하는 항목은 다음과 같습니다.

독립 환경 (운영 체제의 이점없이 C 프로그램 실행이 발생할 수 있음)에서는 프로그램 시작시 호출되는 함수의 이름과 유형이 구현에 따라 정의됩니다. 4 절에서 요구하는 최소 세트를 제외하고 독립 프로그램에 사용할 수있는 모든 라이브러리 기능은 구현에서 정의됩니다.

그런 다음 평상시처럼 연결하면 링커가 일반적으로 기호의 특성에 대한 지식이 거의 없기 때문에 문제가 발생합니다 (어떤 유형이 있는지 또는 함수 나 변수 인 경우에도). 이 경우 링커는 main라는 변수에 대한 호출을 기꺼이 해결합니다 main. 기호를 찾을 수 없으면 링크 오류가 발생합니다.

평소와 같이 연결하는 경우 기본적으로 호스팅 된 작업에서 컴파일러를 사용하려고 시도한 다음 main부록 J.2에 따라 정의되지 않은 동작을 의미하는 것으로 정의하지 않습니다 .

다음 상황에서는 동작이 정의되지 않습니다.

  • ...
  • 호스팅 된 환경의 프로그램이 지정된 형식 (5.1.2.2.1) 중 하나를 사용하여 main이라는 함수를 정의하지 않습니다.

독립 가능성의 목적은 (예를 들어) 표준 라이브러리 또는 CRT 초기화가 제공되지 않는 환경에서 C를 사용할 수 있도록하는 것입니다. 즉, 이전에 실행 된 코드 main가 호출 (즉, C 런타임을 초기화하는 CRT 초기화)이 제공되지 않을 수 있으며 사용자가 직접 제공해야합니다 (사용 main하거나 사용하지 않기로 결정할 수 있음).


메인 배열에서 프로그램을 만드는 방법에 관심이 있다면 https://jroweboy.github.io/c/asm/2015/01/26/when-is-main-not-a-function.html . 예제 소스에는 main기계 명령어로 채워진 char (및 나중에 int) 배열 이 있습니다.

주요 단계와 문제점은 다음과 같습니다.

  • gdb 메모리 덤프에서 주 함수의 기계 명령어를 가져 와서 배열에 복사합니다.
  • 데이터를 main[]const로 선언 하여 실행 파일에 태그 지정 (데이터는 쓰기 가능하거나 실행 가능함)
  • 마지막 세부 사항 : 실제 문자열 데이터의 주소를 변경합니다.

결과 C 코드는

const int main[] = {
    -443987883, 440, 113408, -1922629632,
    4149, 899584, 84869120, 15544,
    266023168, 1818576901, 1461743468, 1684828783,
    -1017312735
};

그러나 64 비트 PC에서 실행 가능한 프로그램이 생성됩니다.

$ gcc -Wall final_array.c -o sixth
final_array.c:1:11: warning: ‘main’ is usually a function [-Wmain]
 const int main[] = {
           ^
$ ./sixth 
Hello World!

문제는 main예약 된 식별자가 아니라는 것입니다. C 표준은 호스팅 된 시스템에는 일반적으로 main이라는 함수가 있다고 만 말합니다. 그러나 표준의 어떤 것도 다른 사악한 목적을 위해 동일한 식별자를 남용하는 것을 방지하지 않습니다.

GCC는 "메인은 일반적으로 함수입니다"라는 잘난 경고를 제공하여, main관련없는 다른 목적으로 식별자 사용 하는 것은 좋은 생각이 아님을 암시합니다 .


어리석은 예 :

#include <stdio.h>

int main (void)
{
  int main = 5;
  main:

  printf("%d\n", main);
  main--;

  if(main)
  {
    goto main;
  }
  else
  {
    int main (void);
    main();
  }
}

이 프로그램은 스택 오버플로가 발생하고 충돌 할 때까지 5,4,3,2,1 숫자를 반복적으로 인쇄합니다 (집에서 시도하지 마십시오). 불행히도 위의 프로그램은 엄격하게 준수하는 C 프로그램이며 컴파일러는 작성하는 것을 막을 수 없습니다.


main 컴파일 후-다른 많은 (전역 함수, 전역 변수 등)처럼 개체 파일의 또 다른 기호입니다.

The linker will link the symbol main regardless of its type. Indeed, the linker cannot see the type of the symbol at all (he can see, that it isn't in the .text-section however, but he doesn't care ;))

Using gcc, the standard entry point is _start, which in turn calls main() after preparing the runtime environment. So it will jump to the address of the integer array, which usually will result in a bad instruction, segfault or some other bad behaviour.

This all of course has nothing to do with the C-standard.


It only compiles because you don't use the proper options (and works because linkers sometimes only care for the names of symbols, not their type).

$ gcc -std=c89 -pedantic -Wall x.c
x.c:1:5: warning: ISO C forbids zero-size array ‘main’ [-Wpedantic]
 int main[0];
     ^
x.c:1:5: warning: ‘main’ is usually a function [-Wmain]

const int main[1] = { 0xc3c3c3c3 };

This compiles and executes on x86_64... does nothing just return :D

ReferenceURL : https://stackoverflow.com/questions/34764796/why-does-declaring-main-as-an-array-compile

반응형