Ubuntu 12.04에서 GCC로 컴파일할 수 없습니다

Ubuntu 12.04에서 GCC로 컴파일할 수 없습니다

GCC와 VC9가 모두 설치된 Ubuntu 및 Windows 컴퓨터에서 아래 C 프로그램을 컴파일하고 실행하려고 합니다. 그러나 아래 문제에 직면하고 있습니다.

우분투 머신에서:

GCC는 잘 컴파일되지만 실행하면 다음 프롬프트가 표시됩니다.

Segmentation Fault (Core Dump).

Windows 시스템의 경우:

VC9는 잘 컴파일되고 실행됩니다. GCC는 잘 컴파일되지만 프로그램이 실행되면 프로세스가 종료됩니다.

여기에는 전문가의 도움이 필요합니다. 내 코드는 다음과 같습니다.

#include <string.h>
#include <stdio.h>

int calc_slope(int input1,int input2)
{
    int sum=0;
    int start=input1;
    int end=input2;
    int curr=start;

    //some validation:
    if (input1>input2)
        return -1;


    while(curr<=end)
    {
        if (curr>100)
        {
            char *s="";
            int length;
            int left;
            int right;
            int cent;

            sprintf(s,"%d",curr);
            length=strlen(s);
            s++;
            do
            {
                //printf("curr=%d char=%c pointer=%d length=%d \n",curr,*s,s,length);
                left = *(s-1) - '0';
                cent = *s - '0';
                right = *(s+1) - '0';
                //printf("curr=%d l=%d c=%d r=%d\n",curr,left,cent,right);
                if ( (cent>left && cent>right) || (cent<left && cent<right) )
                {
                    sum+=1; //we have either a maxima or a minima.
                }

                s++;
            } while (*(s+1)!='\0');
        }
        curr++;
    }

    return sum;
}

int main()
{
    printf("%d",calc_slope(1,150));
    return 0;
}

업데이트:

신용은 다음과 같습니다엘리아오류를 추적하는 데 도움을 주었을 뿐만 아니라 gcc 컴파일된 프로그램을 디버깅하는 데 매우 유용한 gdb역추적 도구( )를 소개했습니다. bt수정된 버전은 다음과 같습니다. 시행착오를 거쳐 작업했습니다.

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int calc_slope(int input1,int input2)
{
    int sum=0;
    int start=input1;
    int end=input2;
    int curr=start;

    //some validation:
    if (input1>input2)
        return -1;


    while(curr<=end)
    {
        if (curr>100)
        {
            int size=10;
            char *s=(char*)malloc((size+1) * sizeof(char));
            int left;
            int right;
            int cent;

            sprintf(s,"%d",curr);
            s++;
            do
            {
                left = *(s-1) - '0';
                cent = *s - '0';
                right = *(s+1) - '0';
                if ( (cent>left && cent>right) || (cent<left && cent<right) )
                {
                    sum+=1; //we have either a maxima or a minima.
                }

                s++;
            } while (*(s+1)!='\0');
        }
        curr++;
    }

    return sum;
}

int main()
{
    printf("%d",calc_slope(1,150));
    return 0;
}

답변1

세그멘테이션 오류프로그램이 할당된 영역 외부의 메모리에 액세스하려고 할 때 발생합니다.

이 경우 숙련된 C 프로그래머라면 호출된 줄에서 문제가 발생하고 있음을 알 수 있습니다 sprintf. 그러나 분할 오류가 발생하는 위치를 알 수 없거나 코드를 읽어서 확인하고 싶지 않은 경우노력하다이를 알아내기 위해 디버그 기호( 를 사용하면 플래그가 이를 수행함)를 사용하여 프로그램을 빌드한 다음 디버거를 통해 실행할 수 gcc있습니다 -g.

귀하의 소스 코드를 복사하여 이름이 slope.c. 그런 다음 다음과 같이 만들었습니다.

gcc -Wall -g -o slope slope.c

( -Wall선택 사항입니다. 더 많은 상황에 대해 경고를 생성하도록 하기 위한 것입니다. 이는 무엇이 잘못되었는지 파악하는 데도 도움이 될 수 있습니다.)

gdb그런 다음 먼저 프로그램을 gdb ./slope시작하기 위해 실행하여 디버거에서 프로그램을 실행한 gdb다음 디버거에서 run디버거에 다음 명령을 제공합니다.

ek@Kip:~/source$ gdb ./slope
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/ek/source/slope...done.
(gdb) run
Starting program: /home/ek/source/slope 
warning: Cannot call inferior functions, you have broken Linux kernel i386 NX (non-executable pages) support!

Program received signal SIGSEGV, Segmentation fault.
0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6

you have broken Linux kernel i386 NX(내 ... 메시지 에 대해 걱정하지 마십시오 . 이 메시지가 이 프로그램을 디버깅하는 데 효과적으로 사용되는 것을 support방해하지 않습니다 .)gdb

그 정보는 매우 비밀스럽습니다. libc용 디버그 기호가 설치되어 있지 않으면 기호 함수 이름 대신 16진수 주소가 있는 훨씬 더 비밀스러운 메시지를 받게 됩니다 _IO_default_xsputn. 다행히도 그것은 중요하지 않습니다. 왜냐하면 우리가 정말로 알고 싶은 것은프로그램의 어디에문제가 발생하고 있습니다.

SIGSEGV따라서 해결책은 신호가 최종적으로 트리거 된 시스템 라이브러리에서 특정 함수 호출로 이어지는 어떤 함수 호출이 발생했는지 과거를 되돌아보는 것입니다 .

gdb(그리고 모든 디버거에는) 이 기능이 내장되어 있습니다.스택 추적또는역추적. 디버거 명령을 사용하여 bt역추적을 생성합니다 gdb.

(gdb) bt
#0  0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
#1  0x00178e04 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#2  0x0019b234 in vsprintf () from /lib/i386-linux-gnu/libc.so.6
#3  0x0017ff7b in sprintf () from /lib/i386-linux-gnu/libc.so.6
#4  0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
#5  0x08048578 in main () at slope.c:52
(gdb)

함수가 의도한 함수를 main호출한 다음 (이 시스템에서) 몇 가지 다른 관련 라이브러리 함수에 대한 호출로 구현되는 를 호출하는 것을 볼 수 있습니다 .calc_slopecalc_slopesprintf

일반적으로 관심 있는 것은 함수 호출입니다.귀하의 프로그램에서함수를 호출하는 것프로그램 외부. 사용 중인 라이브러리/라이브러리 자체(이 경우 libc라이브러리 파일에서 제공하는 표준 C 라이브러리 libc.so.6)에 버그가 없는 한 충돌을 일으키는 버그는 프로그램에 있으며자주마지막 통화 시간이나 그 근처에 있을 거야귀하의 프로그램에서.

이 경우는 다음과 같습니다.

#4  0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26

이것이 프로그램이 호출하는 곳입니다 sprintf. sprintf이것이 다음 단계이기 때문에 우리는 이것을 알고 있습니다 . 하지만 그렇게 말하지 않아도 당신은 이것을 알고 있습니다.그게 26번째 줄에서 일어나는 일이야, 그리고 내용은 다음과 같습니다.

... at slope.c:26

프로그램의 26행에는 다음이 포함됩니다.

            sprintf(s,"%d",curr);

(적어도 현재 행에 대해서는 행 번호를 자동으로 표시하는 텍스트 편집기를 항상 사용해야 합니다. 이는 컴파일 시간 오류와 디버거를 사용하는 동안 밝혀진 런타임 문제를 모두 해석하는 데 매우 유용합니다.)

에서 논의한 바와 같이Dennis Kaarsemaker의 답변s1바이트 배열입니다. (0이 아닙니다. 할당한 값 ""은 1바이트 길이입니다. 즉, { '\0' }와 같은 방식으로 "Hello, world!\n"와 같습니다 { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }.)

~할 수 있었다이것은 여전히 ​​일부 플랫폼에서 작동합니까(그리고 Windows용 VC9로 컴파일할 때 분명히 작동합니다)?

사람들은 종종 메모리를 할당한 다음 그 외부의 메모리에 액세스하려고 하면 오류가 발생한다고 말합니다. 그러나 그것은 사실이 아닙니다. C 및 C++ 기술 표준에 따르면 이것이 실제로 생성하는 것은 다음과 같습니다.정의되지 않은 동작.

즉, 어떤 일이든 일어날 수 있습니다!

그럼에도 불구하고 어떤 것들은 다른 것보다 가능성이 더 높습니다. 일부 구현에서 스택의 작은 배열이 스택의 더 큰 배열처럼 작동하는 것처럼 보이는 이유는 무엇입니까?

이는 플랫폼마다 다를 수 있는 스택 할당이 구현되는 방식에 따라 결정됩니다. 실행 파일은 실제로 한 번에 사용하려는 것보다 더 많은 메모리를 스택에 할당할 수 있습니다. 때로는 이를 통해 아직 저장하지 않은 메모리 위치에 쓸 수 있게 될 수도 있습니다.명시적으로귀하의 코드에 소유권을 주장했습니다. VC9에서 프로그램을 빌드할 때 이런 일이 발생할 가능성이 매우 높습니다.

하지만,VC9에서도 이 동작에 의존해서는 안 됩니다.이는 잠재적으로 다양한 Windows 시스템에 존재할 수 있는 다양한 버전의 라이브러리에 따라 달라질 수 있습니다. 하지만훨씬 더 가능성이 높습니다실제로 사용할 의도로 추가 스택 공간을 할당하는 문제이므로실제로 사용될 수도 있습니다.그런 다음 "정의되지 않은 동작"이라는 완전한 악몽을 경험하게 됩니다. 이 경우 둘 이상의 변수가 동일한 위치에 저장될 수 있으며, 하나에 쓰면 다른 변수를 덮어쓰게 됩니다. 그러나 항상 그런 것은 아닙니다. 왜냐하면 때로는 변수에 쓰기 때문입니다. 레지스터에 캐시되어 실제로 즉시 수행되지 않습니다(또는 변수에 대한 읽기가 캐시될 수 있거나 변수에 할당된 메모리가 변수를 통해 기록되지 않은 것으로 컴파일러에 의해 알려지기 때문에 변수가 이전과 동일하다고 가정할 수 있음). 변수 자체).

그리고 이는 VC9로 구축했을 때 프로그램이 작동한 이유에 대한 또 다른 가능성을 제시합니다. 그럴 수도 있고, 그럴 가능성도 어느 정도 있다.일부 배열 또는 기타 변수1바이트 배열 뒤의 공간을 사용하기 위해 실제로 프로그램에 의해 할당되었습니다(프로그램에서 사용하는 라이브러리에 의해 할당되는 것을 포함할 수 있음) s. 따라서 s1바이트보다 긴 배열로 처리하면 해당 변수/배열의 내용에 액세스하는 효과가 있으며 이는 좋지 않을 수도 있습니다.

결론적으로 이런 실수를 하게 되면운이 좋은"분할 오류" 또는 "일반 보호 오류"와 같은 오류가 발생합니다. 때를~하지 않다프로그램이 너무 늦을 때까지 알 수 없을 수도 있습니다.정의되지 않은 동작.

답변2

안녕하세요 버퍼 오버플로입니다!

char *s="";
sprintf(s,"%d",curr);
length=strlen(s);

스택의 문자열에 대해 1바이트를 할당한 다음 해당 문자열에 1바이트 이상을 씁니다. 그리고 무엇보다도 해당 배열의 끝 이상을 읽습니다. C 매뉴얼, 특히 문자열과 이에 대한 메모리 할당 섹션을 읽어보세요.

관련 정보