Программа, скомпилированная с GCC 4.5, дает сбой, в то время как GCC 4.4 работает нормально - PullRequest
3 голосов
/ 09 июня 2011

Недавно я попытался скомпилировать и установить ns-2 , сетевой симулятор на основе C ++ и Tcl.

Используя небольшую модификацию исходного кода (не волнуйтесь, это не приведет к сбою), я могу заставить его скомпилироваться с использованием последней версии gcc 4.5.

Но когда я запускаю бинарный файл, он выдает следующую ошибку.

$bin/ns
*** buffer overflow detected ***: bin/ns terminated

Тот же код , если скомпилирован с более ранней версией gcc , работает нормально . Поэтому я считаю, что это связано с некоторыми улучшенными функциями в gcc 4.5 .

Как мне подойти к этой проблеме? Конечно, компиляция с gcc 4.4 возможна, но я хотел бы знать, что пошло не так:)

Обновление:

Вот полная трассировка стека и обратная трассировка с помощью gdb:

$ bin/ns
*** buffer overflow detected ***: bin/ns terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x37)[0x7f01824ac1d7]
/lib/x86_64-linux-gnu/libc.so.6(+0xfd0f0)[0x7f01824ab0f0]
bin/ns[0x8d5b5a]
bin/ns[0x8d56de]
bin/ns[0x841077]
bin/ns[0x842b19]
bin/ns(Tcl_EvalEx+0x16)[0x843256]
bin/ns(Tcl_Eval+0x1d)[0x84327d]
bin/ns(Tcl_GlobalEval+0x2b)[0x84391b]
bin/ns(_ZN3Tcl4evalEPc+0x27)[0x83352b]
bin/ns(_ZN3Tcl5evalcEPKc+0xdd)[0x8334e9]
bin/ns(_ZN11EmbeddedTcl4loadEv+0x24)[0x834712]
bin/ns(Tcl_AppInit+0xb2)[0x8331a5]
bin/ns(Tcl_Main+0x1d0)[0x8ad6a0]
bin/ns(nslibmain+0x25)[0x8330c5]
bin/ns(main+0x20)[0x833254]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xff)[0x7f01823cceff]
bin/ns[0x5bc1a9]

Использование GDB и с включенными символами:

(gdb) bt
#0  0x00007ffff6970d05 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff6974ab6 in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x00007ffff69a9d7b in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#3  0x00007ffff6a3b1d7 in __fortify_fail () from /lib/x86_64-linux-gnu/libc.so.6
#4  0x00007ffff6a3a0f0 in __chk_fail () from /lib/x86_64-linux-gnu/libc.so.6
#5  0x00000000008d5b5a in strcpy (interp=0xd2dda0, optionIndex=<value optimized out>, objc=<value optimized out>, objv=0x7fffffffdad0)
    at /usr/include/bits/string3.h:105
#6  TraceVariableObjCmd (interp=0xd2dda0, optionIndex=<value optimized out>, objc=<value optimized out>, objv=0x7fffffffdad0)
    at /media/Linux/ns-allinone-2.35-RC7/tcl8.5.8/unix/../generic/tclTrace.c:912
#7  0x00000000008d56de in Tcl_TraceObjCmd (dummy=<value optimized out>, interp=0xd2dda0, objc=<value optimized out>, objv=0xd2ec00)
    at /media/Linux/ns-allinone-2.35-RC7/tcl8.5.8/unix/../generic/tclTrace.c:293
#8  0x0000000000841077 in TclEvalObjvInternal (interp=0xd2dda0, objc=5, objv=0xd2ec00,
    command=0x7ffff7f680fe "trace variable defaultRNG w { abort \"cannot update defaultRNG once assigned\"; }\n\n\nClass RandomVariable/TraceDriven -superclass RandomVariable\n\nRandomVariable/TraceDriven instproc init {} {\n$self instv"..., length=80, flags=0)
    at /media/Linux/ns-allinone-2.35-RC7/tcl8.5.8/unix/../generic/tclBasic.c:3689
#9  0x0000000000842b19 in TclEvalEx (interp=0xd2dda0,
    script=0x7ffff7f52010 "\n\n\n\n\n\nproc warn {msg} {\nglobal warned_\nif {![info exists warned_($msg)]} {\nputs stderr \"warning: $msg\"\nset warned_($msg) 1\n}\n}\n\nif {[info commands debug] == \"\"} {\nproc debug args {\nwarn {Script debugg"..., numBytes=422209, flags=<value optimized out>, line=4141,
    clNextOuter=<value optimized out>,
    outerScript=0x7ffff7f52010 "\n\n\n\n\n\nproc warn {msg} {\nglobal warned_\nif {![info exists warned_($msg)]} {\nputs stderr \"warning: $msg\"\nset warned_($msg) 1\n}\n}\n\nif {[info commands debug] == \"\"} {\nproc debug args {\nwarn {Script debugg"...)
    at /media/Linux/ns-allinone-2.35-RC7/tcl8.5.8/unix/../generic/tclBasic.c:4386
#10 0x0000000000843256 in Tcl_EvalEx (interp=<value optimized out>, script=<value optimized out>, numBytes=<value optimized out>,
    flags=<value optimized out>) at /media/Linux/ns-allinone-2.35-RC7/tcl8.5.8/unix/../generic/tclBasic.c:4043
#11 0x000000000084327d in Tcl_Eval (interp=0xd2dda0, script=<value optimized out>)
    at /media/Linux/ns-allinone-2.35-RC7/tcl8.5.8/unix/../generic/tclBasic.c:4955
#12 0x000000000084391b in Tcl_GlobalEval (interp=0xd2dda0, command=<value optimized out>)
    at /media/Linux/ns-allinone-2.35-RC7/tcl8.5.8/unix/../generic/tclBasic.c:6005
#13 0x000000000083352b in Tcl::eval(char*) ()
#14 0x00000000008334e9 in Tcl::evalc(char const*) ()
#15 0x0000000000834712 in EmbeddedTcl::load() ()
#16 0x00000000008331a5 in Tcl_AppInit ()
#17 0x00000000008ad6a0 in Tcl_Main (argc=<value optimized out>, argv=0x7fffffffe1d0, appInitProc=0x8330f3 <Tcl_AppInit>)
    at /media/Linux/ns-allinone-2.35-RC7/tcl8.5.8/unix/../generic/tclMain.c:418
#18 0x00000000008330c5 in nslibmain ()
#19 0x0000000000833254 in main ()    

Ответы [ 3 ]

6 голосов
/ 09 июня 2011

Знаменитые последние слова: «Не волнуйся - мои перемены ничего не сломали». Как мы можем быть уверены в этом?

Однако есть небольшая вероятность, что вы правы, если код работал под 4.4 и вылетал под 4.5.

GCC принял некоторые агрессивные оптимизации, связанные с кодом, который пытается обнаружить целочисленное переполнение и удаляет его. В этом случае вам нужно будет найти этот код в ns-2 и попытаться исправить его - либо разработчиками ns-2, либо самостоятельно.

Возможно, вам следует попытаться запустить программу под отладчиком, чтобы вы могли получить контроль в точке, где обнаружено переполнение буфера, и посмотреть, где находится код. Если вы отключили дампы ядра (с ulimit -c 0 или эквивалентным), рассмотрите возможность их включения и посмотрите, получите ли вы дамп ядра, когда он завершится. Это должно дать вам отправную точку.


Дальнейшие мысли:

  • Когда вы компилировали код, насколько строгими были флаги предупреждения? Можете ли вы перекомпилировать с включенными дополнительными предупреждениями?

    Одна методика, которая часто работает (с программами, настроенными на AutoTools), если вы не можете найти другого способа получить специальные опции для компилятора C или C ++:

    ./configure --prefix=/opt/ns CC="gcc -Wall -Wextra" CXX="g++ -Wall -Wextra"
    

    (я также использую эту технику для определения 32-битных и 64-битных сборок, добавляя -m32 или -m64.)

    Предупреждение: если код не был создан для чистой компиляции с этими параметрами, может быть травмирующей первую компиляцию с использованием этих параметров. Тем не менее, есть также неплохая вероятность того, что среди всех предупреждений есть источник вашей проблемы. Однако, это также бесспорно, что, вероятно, будет 50 предупреждений, не связанные с ним к любому 1, который (или хуже), и фиксируя все предупреждения, таким образом, пятнистый до сих пор не может вылечить проблему. Если код компилируется со строгими предупреждениями в любом случае, тогда вы столкнетесь с включением гораздо большего количества экзотических предупреждений. Но если вы можете заставить компилятор помочь диагностировать проблему, которую он вызывает, вам, безусловно, следует это сделать - это гораздо проще, чем найти проблему без посторонней помощи.

  • Также убедитесь, что вы создаете отлаживаемую программу, даже если оптимизация включена.

  • Кроме того, рассмотрите возможность компиляции с отключенной оптимизацией и посмотрите, по-прежнему ли происходит сбой программы. Если программа не дает сбоя без оптимизации и с оптимизацией, у вас есть некоторая полезная информация. Это не поможет найти причину, но вы знаете, что это (вероятно) связано с оптимизатором. Или это может быть просто из-за того, что ошибка перемещается, если она не оптимизирована и не приводит к фатальной ошибке.


Информация трассировки расширенного стека любопытна:

#5  0x00000000008d5b5a in strcpy (interp=0xd2dda0, optionIndex=<value optimized out>,
                                  objc=<value optimized out>, objv=0x7fffffffdad0)
    at /usr/include/bits/string3.h:105
#6  TraceVariableObjCmd (interp=0xd2dda0, optionIndex=<value optimized out>,
                         objc=<value optimized out>, objv=0x7fffffffdad0)
    at /media/Linux/ns-allinone-2.35-RC7/tcl8.5.8/unix/../generic/tclTrace.c:912

Это не обычные аргументы strcpy(). Обычно у вас есть только два аргумента. Я не могу сразу думать об обстоятельствах, когда было бы целесообразно скопировать строку поверх указателя на основную структуру управления интерпретатора Tcl. Итак, чтобы продвинуться дальше, я бы очень внимательно посмотрел на строки 900-920 или около того в tclTrace.c и, в частности, на строку 912. Это может быть просто артефактом того, как оптимизатор манипулирует объектным кодом, или это может быть подлинной проблемой.

Я нашел источник tcl8.5.8 и строка 912 файла tclTrace.c - это strcpy() в этом коде:

    if ((enum traceOptions) optionIndex == TRACE_ADD) {
        CombinedTraceVarInfo *ctvarPtr;

        ctvarPtr = (CombinedTraceVarInfo *) ckalloc((unsigned)
                (sizeof(CombinedTraceVarInfo) + length + 1
                - sizeof(ctvarPtr->traceCmdInfo.command)));
        ctvarPtr->traceCmdInfo.flags = flags;
        if (objv[0] == NULL) {
            ctvarPtr->traceCmdInfo.flags |= TCL_TRACE_OLD_STYLE;
        }
        ctvarPtr->traceCmdInfo.length = length;
        flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
        strcpy(ctvarPtr->traceCmdInfo.command, command);       // Line 912
        ctvarPtr->traceInfo.traceProc = TraceVarProc;
        ctvarPtr->traceInfo.clientData = (ClientData)
                &ctvarPtr->traceCmdInfo;
        ctvarPtr->traceInfo.flags = flags;
        name = Tcl_GetString(objv[3]);
        if (TraceVarEx(interp,name,NULL,(VarTrace*)ctvarPtr) != TCL_OK) {
            ckfree((char *) ctvarPtr);
            return TCL_ERROR;
        }
    } else {

Итак, вывод из GDB и трассировка стека выглядит несколько вводящим в заблуждение; две переменные передаются в strcpy(), и одна из них локально размещается в куче.

Я бы подумал о компиляции tcl в автономном режиме из источника, встроенного в ns-2, и посмотрю, можете ли вы исправить ошибку (извините, ужасный каламбур) самостоятельно. Этот код связан с отслеживанием переменной tcl - trace add varname ... AFAICT.

Предполагая, что это пройдет, я бы подумал о том, чтобы овладеть GCC 4.6 и посмотреть, возникает ли такая же проблема при компиляции ns-2 с этим вместо GCC 4.5.


Valgrind

Поскольку вы работаете в Linux, вы сможете использовать Valgrind.Отлично подходит для выявления проблем со злоупотреблением памятьюДля максимальной пользы используйте отладочную сборку ns-2.

2 голосов
/ 09 июня 2011

«Обнаружено переполнение буфера»: вы выполняете запись в зону, которая не была выделена. gcc 4.4, очевидно, сгенерировал код, который не вызывал проблему (или имел проблему, которая не выявила себя как сбой, но так же, как неправильные результаты, которые теперь не обнаруживаются как таковые), gcc 4.5 генерирует код, который обнаруживает проблему и предупреждает вас о ней , Единственное решение - найти источник проблемы и исправить код.

1 голос
/ 10 июня 2011

Это могут быть разные вещи. Это может быть ошибка GCC. Это может быть ошибка Tcl (я надеюсь, что это не так, выступая в качестве одного из разработчиков Tcl, но я не буду исключать это, поскольку Tcl довольно часто предполагает, что в структурах нет защитного кода; Tcl определенно код C89). Это может быть ошибка в ns2. Насколько я знаю, это может быть даже ошибка в другом месте (поскольку ns2 построен на Tcl, он может загружать библиотеки внешнего кода; там вполне возможно иметь проблему).

Увы, мы не можем сказать по опубликованной информации, какая из этих возможностей это есть. Знаете ли вы, в какой библиотеке был колл-стэк при сбое программы? Хотя это и не является гарантией того, что это фактическое место возникновения проблемы, это, по крайней мере, место для начала поиска ошибок…

...