LINUX.ORG.RU

Научите готовить бинарник, чтобы addr2line распознавал адреса функций

 ,


1

3

Всем привет, хочу найти утечку с помощью функций mtrace/muntrace, но не могу понять, почему утилита addr2line не может перевести адрес в код и строку.

Вот демо-пример:

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

int main(int argc, char** argv) {
    mtrace();

    for (int j = 0; j < 2; j++)
        malloc(100);            /* Never freed--a memory leak */

    calloc(16, 16);             /* Never freed--a memory leak */
    exit(EXIT_SUCCESS);
}

Пробовал собирать с разными уровнями включения отладочных символов:

g++ -g test.cpp -o test
g++ -g3 -ggdb test.cpp -o test
g++ -g3 test.cpp -o test
g++ -O0 -g3 test.cpp -o test
g++ -g -rdynamic test.cpp -o test
g++ -g3 -rdynamic test.cpp -o test

Запускаю приложение: MALLOC_TRACE=/tmp/test.log ./test

Получаю вывод с помощью команды mtrace ./test /tmp/test.log:

Memory not freed:
-----------------
           Address     Size     Caller
0x00005586d69a6690     0x64  at 0x5586d6472190
0x00005586d69a6700     0x64  at 0x5586d6472190
0x00005586d69a6770    0x100  at 0x5586d64721a5

Утилита mtrace пытается конвертировать адреса в читабельные строки в коде с помощью addr2line, но ничего не выходит. Что я делаю не так?

Дополнительная информация:

  1. Когда в malloc включена трассировка, адреса вызывающих функцию malloc/realloc/free функций определяются через встроенную в gcc функцию: __builtin_return_address(0)
  2. Далее этот адрес через функцию dladdr преобразуется в пару <бинарник>[адрес]
  3. утилита mtrace использует далее парсит пару <бинарник>[адрес] и отдает addr2line


Последнее исправление: sotlef (всего исправлений: 3)

Ответ на: комментарий от sotlef

А что за компилятор используется?

lve@gw3: ~/tmp/21$ g++ -o x x.c -rdynamic -g3
x.c: In function ‘int main(int, char**)’:
x.c:9:15: warning: ignoring return value of ‘void* malloc(size_t)’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
    9 |         malloc(100);            /* Never freed--a memory leak */
      |         ~~~~~~^~~~~
x.c:11:11: warning: ignoring return value of ‘void* calloc(size_t, size_t)’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
   11 |     calloc(16, 16);             /* Never freed--a memory leak */
      |     ~~~~~~^~~~~~~~
lve@gw3: ~/tmp/21$ MALLOC_TRACE=x.log ./x
lve@gw3: ~/tmp/21$ cat x.log 
= Start
@ ./x:(main+0x27)[0x40116d] + 0x1ab32a0 0x64
@ ./x:(main+0x27)[0x40116d] + 0x1ab3310 0x64
@ ./x:(main+0x40)[0x401186] + 0x1ab3380 0x100
lve@gw3: ~/tmp/21$ mtrace ./x x.log 

Memory not freed:
-----------------
           Address     Size     Caller
0x0000000001ab32a0     0x64  at /opt/home/lve/tmp/21/x.c:8 (discriminator 3)
0x0000000001ab3310     0x64  at /opt/home/lve/tmp/21/x.c:8 (discriminator 3)
0x0000000001ab3380    0x100  at /opt/home/lve/tmp/21/x.c:12
lve@gw3: ~/tmp/21$ gcc -v
Reading specs from /usr/lib64/gcc/x86_64-slackware-linux/11.2.0/specs
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-slackware-linux/11.2.0/lto-wrapper
Target: x86_64-slackware-linux
Configured with: ../configure --prefix=/usr --libdir=/usr/lib64 --mandir=/usr/man --infodir=/usr/info --enable-shared --enable-bootstrap --enable-languages=ada,brig,c,c++,d,fortran,go,lto,objc,obj-c++ --enable-threads=posix --enable-checking=release --enable-objc-gc --with-system-zlib --enable-libstdcxx-dual-abi --with-default-libstdcxx-abi=new --disable-libstdcxx-pch --disable-libunwind-exceptions --enable-__cxa_atexit --disable-libssp --enable-gnu-unique-object --enable-plugin --enable-lto --disable-install-libiberty --disable-werror --with-gnu-ld --with-isl --verbose --with-arch-directory=amd64 --disable-gtktest --enable-clocale=gnu --disable-multilib --target=x86_64-slackware-linux --build=x86_64-slackware-linux --host=x86_64-slackware-linux
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.2.0 (GCC) 
lve@gw3: ~/tmp/21$ mtrace --version
mtrace (GNU libc) 2.33
vel ★★★★★
()
Ответ на: комментарий от sotlef

Почитайте, что такое position independed executable, зачем он нужен.

Код в мануале не работает, потому что в последних версиях GCC его включили по умолчанию. Где-то с пару лет назад.

PPP328 ★★★★★
()
Ответ на: комментарий от PPP328

Код в мануале не работает, потому что в последних версиях GCC его включили по умолчанию. Где-то с пару лет назад.

Вот г…оны. Не знал. А есть где почитать на тему что их сподвигло на столь «гениальное» решение?

bugfixer ★★★★
()
Ответ на: комментарий от PPP328

Почему-то при добавлении -no-pie не смог дождаться окончания теста на реальном приложении

В PIE тест отрабатывает минут за 30 и отдает 11Гб логов от mtrace, а тут 4 часа ждал - получил 50Гб логов и приложение продолжает их генерировать…

sotlef
() автор топика
Ответ на: комментарий от sotlef

Ну у вас же есть логи, смотрите в какой цикл вошло приложение.

-no-pie на поведение может повлиять только в одном случае - у вас где-то кривая работа с указателем на функцию, потому что меняется только это. В теории компилятор, (но это не точно) может применить какие-то оптимизации на уровне -О3, но опять же, вилами по воде. Поведение приложения для пользователя с PIE и без него не должно отличаться в принципе. Смотрите другие флаги, если вы говорите про 11 ГБ логов - то вы уже компилируете не пример из мануала, а боевой проект.

Для начала выкиньте mtrace и запустите процесс под valgrind, собрав его с -Wall, -Wextra, -Werror. Можно еще использовать ASAN.

PPP328 ★★★★★
()
Ответ на: комментарий от PPP328

Секурность же. PIE позволяет включить рандомизацию адресации.

Нужно далеко не всем. И вот тут говорят что в 32 битах это стоит 10-25% перформанса на пустом месте. Как хорошо что Вы мне про это рассказали - у нас как раз upgrade gcc для prod сборок на горизонте. Премного благодарен (кроме шуток).

bugfixer ★★★★
()