Шаг в стандартный вызов библиотеки с Godbolt - PullRequest
7 голосов
/ 21 мая 2019

Я хочу знать, как различные компиляторы реализуют std::random_device, поэтому я вставил его в godbolt .

К сожалению, единственное, что он говорит, это

std::random_device::operator()():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    std::random_device::_M_getval()
        leave
        ret

, что не очень полезно. Как я могу войти в вызов _M_getval() и проверить сборку там?

1 Ответ

7 голосов
/ 21 мая 2019

Вы не можете "войти в" функции; Godbolt не отладчик, это дизассемблер. Ваша программа не запускается , она просто компилируется. (И если вы не выберете «двоичный» вариант вывода, он компилируется только в asm, а не в машинный код, и фактически не связывается.)

Но независимо от терминологии, нет, вам не удастся заставить Godbolt показать вам разборку для какой-либо версии библиотеки, которую он установил.

Одноступенчатая программа на рабочем столе. (Скомпилируйте с gcc -O3 -fno-plt, чтобы избежать необходимости выполнять ленивую динамическую компоновку PLT.)

(Я так и сделал, и libstdc ++ 6.2.1 в Arch Linux запускает cpuid в конструкторе для std::random_device. Если rdrand доступен, он использует его при вызовах _M_getval(). Это можно понять из простой разборки было бы сложно, есть несколько уровней вызовов функций и ветвления, и без символов было бы трудно понять, что к чему. Мой Skylake имеет rdseed доступный, но он не использовал его. Да, как вы прокомментировал, что это был бы лучший выбор.)


Разные компиляторы могут генерировать разные версии библиотечных функций из одного и того же источника, что является основным моментом существования проводника компилятора. И нет, у него нет отдельной версии libstdc ++, скомпилированной каждым компилятором в выпадающем списке.

Не будет никакой гарантии, что код библиотеки, который вы видели, будет соответствовать тому, что находится на вашем рабочем столе, или чему-либо еще.

На самом деле на нем установлены библиотеки Linux x86-64, хотя, теоретически, Godbolt мог бы дать вам возможность находить и разбирать определенные библиотечные функции, но эта функциональность в настоящее время не существует. И будет работать только для целей, где доступна опция «двоичный»; Я думаю, что для большинства целей кросс-компиляции у него есть только заголовки, а не библиотеки. Или, может быть, есть какая-то другая причина, по которой он не будет связывать и разбирать для ISA, отличных от x86.


Использование -static и бинарный режим показывает вещи, но не то, что мы хотим.

Я попытался скомпилировать с -static -fno-plt -fno-exceptions -fno-rtti -nostartfiles -O3 -march=skylake (поэтому rdrand и rdseed будут доступны в случае, если они будут встроены; они не будут). -fno-plt избыточен с -static, но полезно без , чтобы удалить этот беспорядок.

-static приводит к тому, что код библиотеки фактически оказывается в связанном бинарном файле, который разбирает Godbolt . Но вывод ограничен 500 строками, и определение std::random_device::_M_getval() оказывается не близко к началу файла.

-nostartfiles позволяет избежать загромождения двоичного файла с помощью _start и т. Д. Из файлов запуска CRT. Я думаю, что Godbolt уже отфильтровывает их из разборки, потому что вы не видите их в обычном двоичном выводе (без -static). Вы не собираетесь запускать программу, поэтому не имеет значения, что компоновщик не смог найти символ _start и просто по умолчанию установил точку входа ELF в начале раздела .text.

Несмотря на компиляцию с -fno-exceptions -fno-rtti (поэтому не включен обработчик развёртывания для вашей функции), функции libstdc ++ были скомпилированы с включенной обработкой исключений. Таким образом, связывание их приводит к загрузке кода исключения. Статический исполняемый файл начинается с определений таких функций, как std::__throw_bad_exception(): и std::__throw_bad_alloc():

Кстати, без -fno-exceptions, есть также определение get_random_seed() [clone .cold]:, которое я считаю обработчиком раскрутки. Это не определение вашей действительной функции. В начале статического двоичного файла находится operator new(unsigned long) [clone .cold]:, который, как мне кажется, снова является кодом обработчика исключений libstdc ++.

Я думаю, к сожалению, сначала были связаны разделы .text.cold или .init, поэтому ни одна из интересных функций не будет видна в первых 500 строках.


Даже если это сработало, это только разборка в двоичном режиме, а не компилятор asm

Даже с символами отладки мы не знали бы, к какому элементу структуры обращались, только смещения чисел из регистров, потому что objdump не заполняет их.

И с большим количеством ветвлений трудно следовать за сложными логическими возможностями. Один шаг во время выполнения автоматически следует фактическому пути выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...