"argv [0] = name-of-executable"이 허용되는 표준입니까 아니면 일반적인 규칙입니까?


102

main()C 또는 C ++ 응용 프로그램에서 인수를 전달할 때 argv[0]항상 실행 파일의 이름이됩니까? 아니면 이것은 일반적인 관례 일 뿐이며 100 % 진실이라고 보장되지 않습니까?


20
Unix에서는 다음을 고려하십시오 execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);.. 실행 파일의 이름은의 값과 관련이 없습니다 argv[0].
Jonathan Leffler

답변:


119

추측 (교육을받은 추측조차도)은 재미 있지만 확실히하기 위해서는 표준 문서로 가야합니다. 예를 들어 ISO C11은 다음과 같이 말합니다 (내 강조).

의 값 argc이 0보다 크면로 가리키는 문자열 은 프로그램 이름 을 argv[0] 나타냅니다 . argv[0][0]호스트 환경에서 프로그램 이름을 사용할 수없는 경우 널 문자가됩니다.

따라서 해당 이름을 사용할 수있는 경우에만 프로그램 이름 입니다. 그리고 그것은 프로그램 이름을 "나타내고" , 반드시 프로그램 이름 일 필요 없습니다 . 그 앞의 섹션은 다음과 같습니다.

의 값 argc이 0보다 크면 inclusive를 argv[0]통한 배열 구성원 argv[argc-1]은 프로그램 시작 전에 호스트 환경에서 구현 정의 값을 제공하는 문자열에 대한 포인터를 포함해야합니다.

이것은 이전 표준 인 C99에서 변경되지 않았으며, 조차도 표준에 의해 지시되지 않는다는 것을 의미합니다. 이는 전적으로 구현에 달려 있습니다.

호스트 환경이 경우 수단은 프로그램 이름이 비어있을 수 없는 호스트 환경이 경우를 제공하고, 아무것도 하지 "무엇은"어떻게 든 프로그램 이름을 나타내는 제공을 제공합니다. 더 가학적인 순간에 스와힐리어로 번역하고 대체 암호를 통해 실행 한 다음 역 바이트 순서로 저장하는 것을 고려할 것입니다 :-).

그러나 구현 정의 ISO 표준에서 특정 의미를 갖습니다. 구현시 작동 방식을 문서화해야합니다. 따라서 호출 계열 argv[0]과 함께 원하는 것을 넣을 수있는 UNIX조차도이를 exec문서화해야합니다.


3
그것이 표준 일 수 있지만, 유닉스는 단순히 그것을 강제하지 않으며 당신은 그것을 믿을 수 없습니다.
dmckee --- 전 중재자 새끼 고양이

4
이 질문은 UNIX 를 전혀 언급하지 않았습니다 . C 질문은 평범하고 단순했기 때문에 ISO C는 참조 문서입니다. 프로그램 이름은 표준에 정의 된 구현이므로 구현은 실제 이름이 아닌 것을 허용하는 것을 포함하여 원하는 것을 자유롭게 할 수 있습니다. 두 번째 문장에서 명확하게 한 것 같습니다.
paxdiablo

2
팍스, 나는 당신에게 투표하지 않았고,이 대답이 얻을 있는 한 권위가 있기 때문에 그렇게 한 사람들을 승인하지 않았습니다 . 그러나 나는의 가치의 비 신뢰성 argv[0]이 현실 세계에서의 프로그래밍에 적합 하다고 생각합니다 .
dmckee --- 전 중재자 새끼 고양이

4
@caf, 맞습니다. 프로그램의 전체 경로 ( '/ progpath / prog'), 파일 이름 ( 'prog'), 약간 수정 된 이름 ( '-prog'), 설명 적 이름 ( ' prog-progging을위한 프로그램 ') 그리고 아무것도 (' '). 구현은 보유하고있는 것을 정의해야하지만 이것이 표준이 요구하는 전부입니다.
paxdiablo

3
모두 감사합니다! (겉보기에) 간단한 질문에서 훌륭한 토론. Richard의 대답은 * nix 운영 체제에 유효하지만 특정 OS의 동작에 덜 관심이 있고 주로 허용되는 표준의 존재 (또는 부재)에 관심이 있기 때문에 paxdiablo의 대답을 선택했습니다. (호기심이 있다면 : 원래 질문의 맥락에서-저는 운영 체제가 없습니다. 저는 임베디드 장치에로드 된 실행 파일에 대한 원시 argc / argv 버퍼를 빌드하는 코드를 작성하고 있으며 무엇을해야하는지 알아야합니다. argv [0] 사용). 멋진 사람이되어 StackOverflow에 +1하세요!
Mike Willekes

49

에서 *nix와 타입 시스템 exec*()호출, argv[0]에 어떤 발신자 풋 될 것 argv0에 자리 exec*()전화.

쉘은 이것이 프로그램 이름이라는 규칙을 사용하고 대부분의 다른 프로그램은 동일한 규칙을 따르므로 argv[0]일반적으로 프로그램 이름입니다.

그러나 악성 Unix 프로그램은 원하는 것을 호출 exec()하고 만들 argv[0]수 있으므로 C 표준이 말하는 것과 상관없이이 시간을 100 % 믿을 수 없습니다.


4
이것은 위의 paxdiablo보다 더 나은 대답입니다. 표준은 단지 "프로그램 이름"이라고 부르지 만 이것은 내가 아는 한 어디에도 적용되지 않습니다. Unix 커널은 execve ()에 전달 된 문자열을 변경하지 않고 자식 프로세스에 균일하게 전달합니다.
Andy Ross

4
C 표준은 'execve ()'등에 대해 알지 못하기 때문에 말할 수있는 내용이 제한적입니다. POSIX 표준 ( opengroup.org/onlinepubs/9699919799/functions/execve.html )에는 더 많은 말이 있습니다. argv [0]에있는 것은 'execve ()'(또는 관련) 시스템 호출을 실행하는 프로세스의 변덕에 있습니다.
Jonathan Leffler

1
@Andy, 당신은 당신의 의견을 자유롭게 가질 수 있습니다 :-) 그러나 당신은 집행에 대해 틀 렸습니다. 구현이 표준을 따르지 않으면 부적합입니다. 그리고 사실, 이후의 구현에 정의 된 "프로그램 이름이"무엇인지, UNIX와 같은 OS가로 되어 한이 지정으로 이름이 무엇인지에 부합. 여기에는 exec 호출 계열에서 원하는대로 argv [0]을로드하여 프로그램 이름을 노골적으로 위조 할 수있는 것도 포함됩니다.
paxdiablo

이것이 argv [0] ( "프로그램 이름을 나타냄") 및 argv [1..N] ( "프로그램 인수를 나타냄")을 참조 할 때 표준에서 "represents"라는 단어의 아름다움입니다. "unladen swallow"는 유효한 프로그램 이름입니다.
Richard Pennington

9

C ++ 표준, 섹션 3.6.1에 따르면 :

argv [0]은 프로그램을 호출하는 데 사용되는 이름을 나타내는 NTMBS의 초기 문자에 대한 포인터 또는 ""입니다.

따라서 최소한 표준에 의해 보장되지는 않습니다.


5
나는 그것이 null로 끝나는 멀티 바이트 문자열이라고 가정합니까?
paxdiablo

6

ISO-IEC 9899는 다음과 같이 말합니다.

5.1.2.2.1 프로그램 시작

의 값 argc이 0보다 크면로 가리키는 문자열은 프로그램 이름을 argv[0]나타냅니다. argv[0][0]호스트 환경에서 프로그램 이름을 사용할 수없는 경우 널 문자가됩니다. 의 값 argc이 1보다 크면 argv[1]through가 가리키는 문자열 argv[argc-1]프로그램 매개 변수 를 나타냅니다 .

나는 또한 사용했다 :

#if defined(_WIN32)
  static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
  {
    return GetModuleFileNameA(NULL, pathName, (DWORD)pathNameCapacity);
  }
#elif defined(__linux__) /* elif of: #if defined(_WIN32) */
  #include <unistd.h>
  static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
  {
    size_t pathNameSize = readlink("/proc/self/exe", pathName, pathNameCapacity - 1);
    pathName[pathNameSize] = '\0';
    return pathNameSize;
  }
#elif defined(__APPLE__) /* elif of: #elif defined(__linux__) */
  #include <mach-o/dyld.h>
  static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
  {
    uint32_t pathNameSize = 0;

    _NSGetExecutablePath(NULL, &pathNameSize);

    if (pathNameSize > pathNameCapacity)
      pathNameSize = pathNameCapacity;

    if (!_NSGetExecutablePath(pathName, &pathNameSize))
    {
      char real[PATH_MAX];

      if (realpath(pathName, real) != NULL)
      {
        pathNameSize = strlen(real);
        strncpy(pathName, real, pathNameSize);
      }

      return pathNameSize;
    }

    return 0;
  }
#else /* else of: #elif defined(__APPLE__) */
  #error provide your own implementation
#endif /* end of: #if defined(_WIN32) */

그런 다음 문자열을 구문 분석하여 경로에서 실행 파일 이름을 추출해야합니다.


2
/proc/self/path/a.out심볼릭 링크는 솔라리스 10 및 최대에 사용할 수 있습니다.
ephemient

코드에 대한 찬성 (이상적이거나 정확하다고 말하지 않음. 예를 들어 Windows GetModuleFileNameW에서는 모든 경로를 검색 할 수 있어야하지만 코드의 존재만으로 좋은 지침이 됨).
건배와 hth. - 알프

4

argv[0] !=실행 가능한 이름 을 갖는 응용 프로그램

  • 많은 쉘이를 확인하여 로그인 쉘인지 판별합니다 argv[0][0] == '-'. 로그인 셸은 다른 속성을 가지고 있으며 특히 /etc/profile.

    일반적으로 init 자체이거나 getty선행을 추가합니다 -. /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password 도 참조하십시오. -인-빌드 / 300152 # 300152

  • 다중 호출 바이너리, 아마도 가장 주목할만한 것은 Busybox 입니다. 이 심볼릭 링크 여러 이름은 예 /bin/sh/bin/ls단일 exebutable에 /bin/busybox에서 사용하는 도구를 인식하고,argv[0] .

    이를 통해 여러 도구를 나타내는 하나의 작은 정적으로 연결된 실행 파일을 가질 수 있으며 기본적으로 모든 Linux 환경에서 작동합니다.

참조 : /unix/315812/why-does-argv-include-the-program-name/315817

실행 가능한 POSIX execve예에서 argv[0] !=실행 파일 이름

언급 된 기타 exec 했지만 여기에 실행 가능한 예가 있습니다.

ac

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    char *argv[] = {"yada yada", NULL};
    char *envp[] = {NULL};
    execve("b.out", argv, envp);
}

기원전

#include <stdio.h>

int main(int argc, char **argv) {
    puts(argv[0]);
}

그때:

gcc a.c -o a.out
gcc b.c -o b.out
./a.out

제공 :

yada yada

예, 다음이 argv[0]될 수도 있습니다.

Ubuntu 16.10에서 테스트되었습니다.


3

이 페이지 는 다음을 설명합니다.

argv [0] 요소는 일반적으로 프로그램의 이름을 포함하지만, 이것은 의존해서는 안됩니다. 어쨌든 프로그램이 자신의 이름을 알지 못하는 것은 드문 일입니다!

그러나 다른 페이지는 항상 실행 파일의 이름이라는 사실을 백업하는 것처럼 보입니다. 이것은 다음과 같이 말합니다.

argv [0]이 프로그램 자체의 경로와 이름임을 알 수 있습니다. 이를 통해 프로그램은 자신에 대한 정보를 검색 할 수 있습니다. 또한 프로그램 인수 배열에 하나를 더 추가하므로 명령 줄 인수를 가져올 때 흔히 발생하는 오류는 argv [1]을 원할 때 argv [0]을 가져 오는 것입니다.


11
일부 프로그램은 호출에 사용 된 이름을 모른다는 사실을 이용합니다. BusyBox ( busybox.net/about.html )가 이런 식으로 작동 한다고 생각 합니다. 다양한 명령 줄 유틸리티를 구현하는 실행 파일은 하나뿐입니다. 실행해야 할 명령 줄 도구를 결정하기 위해 일련의 심볼릭 링크와 argv [0]을 사용합니다.
Trent

예, "gunzip"이 "gzip"에 대한 심볼릭 링크라는 것을 알고 잠시 동안 어떻게 작동하는지 궁금합니다.
David Thornley

2
많은 프로그램에서 argv [0]에서 정보를 확인합니다. 예를 들어, 이름의 마지막 구성 요소가 대시 (예 : '/ bin / -sh')로 시작하면 셸은 로그인 셸과 같은 프로필 및 기타 항목을 실행합니다.
Jonathan Leffler

2
@Jon : 로그인 쉘이 시작되었다고 생각 argv[0]="-/bin/sh"했습니까? 어쨌든 내가 사용한 모든 기계의 경우입니다.
ephemient

3

거의 보편적 인 관습인지 표준인지는 잘 모르겠지만 어느 쪽이든 따라야합니다. 나는 그것이 유닉스와 유닉스 계열 시스템 밖에서 악용되는 것을 본 적이 없다. 유닉스 환경에서, 특히 예전에는 프로그램이 호출되는 이름에 따라 상당히 다른 동작을 보일 수 있습니다.

편집 됨 : 다른 게시물에서 누군가가 특정 표준에서 나온 것으로 식별 한 것을 동시에 다른 게시물에서 볼 수 있지만,이 협약이 표준보다 오래 전부터 있다고 확신합니다.


1
나는 사람들이 내 응답을 "표시"할 경우 그들이 싫어하는 부분을 표시하기를 바랍니다.
Joe Mabel

0

Workbench로 Amiga 프로그램을 시작하면 argv [0]이 설정되지 않고 CLI로만 설정됩니다.

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