Что означают вопросительные знаки '?' в трассировках вызовов паники ядра Linux?

Трассировка вызовов содержит такие записи:

 [<deadbeef>] FunctionName+0xAB/0xCD [module_name]
 [<f00fface>] ? AnotherFunctionName+0x12/0x40 [module_name]
 [<deaffeed>] ClearFunctionName+0x88/0x88 [module_name]

Что означает '?' пометить перед AnotherFunctionName?


person qdot    schedule 28.10.2012    source источник


Ответы (1)


'?' означает, что информация об этой записи стека, вероятно, ненадежна.

Механизм вывода стека (см. реализацию функции dump_trace()) не смог доказать, что найденный адрес является действительным адресом возврата в стеке вызовов.

'?' сам выводится с помощью printk_stack_address().

Запись стека может быть действительной или нет. Иногда его можно просто пропустить. Может быть полезно исследовать дизассемблированный задействованный модуль, чтобы увидеть, какая функция вызывается в ClearFunctionName+0x88 (или, в x86, непосредственно перед этой позицией).

Надежность

В x86 при вызове dump_stack() функция, которая фактически проверяет стек, — это print_context_stack() определено в arch/x86/kernel/dumpstack.c. Взгляните на его код, я попытаюсь объяснить его ниже.

Я предполагаю, что средства раскрутки стека DWARF2 недоступны в вашей системе Linux (скорее всего, их нет, если это не OpenSUSE или SLES). В этом случае print_context_stack(), кажется, делает следующее.

Он начинается с адреса (переменная «стек» в коде), который гарантированно является адресом расположения стека. На самом деле это адрес локальной переменной в dump_stack().

Функция многократно увеличивает этот адрес (while (valid_stack_ptr ...) { ... stack++}) и проверяет, может ли то, на что он указывает, также быть адресом в коде ядра (if (__kernel_text_address(addr)) ...). Таким образом он пытается найти адреса возврата функций, помещаемые в стек при вызове этих функций.

Конечно, не всякое длинное значение без знака, которое выглядит как адрес возврата, на самом деле является адресом возврата. Поэтому функция пытается это проверить. Если в коде ядра используются указатели кадров (для этого используются регистры %ebp/%rbp, если установлен CONFIG_FRAME_POINTER), их можно использовать для обхода кадров стека функций. Адрес возврата для функции находится чуть выше указателя фрейма (т.е. в %ebp/%rbp + sizeof(unsigned long)). print_context_stack проверяет именно это.

Если есть кадр стека, для которого значение 'stack' указывает на адрес возврата, это значение считается надежной записью стека. ops->address будет вызван для него с reliable == 1, в конечном итоге он вызовет printk_stack_address(), и значение будет выведено как надежная запись стека вызовов. В противном случае адрес будет считаться недостоверным. Он будет выводиться в любом случае, но с '?' добавлено.

[NB] Если информация об указателе фрейма недоступна (например, как это было в Debian 6 по умолчанию), по этой причине все записи стека вызовов будут помечены как ненадежные.

Системы с поддержкой раскручивания DWARF2 (и с установленным CONFIG_STACK_UNWIND) — это совсем другая история.

person Eugene    schedule 29.10.2012
comment
Отличный ответ - в нем отсутствует одна вещь, чтобы сделать его полным (и я немного сбит с толку уровнем косвенности в коде арки) - что делает запись ненадежной? - person qdot; 29.10.2012
comment
Я отредактировал свой ответ. Надеюсь, мое объяснение не слишком запутанное. - person Eugene; 29.10.2012
comment
Как добраться :) Ваш ответ на самом деле подтверждает некоторые из моих подозрений о том, как это работает - чтобы дать немного справочной информации, я пытаюсь обновить драйвер, подобный бинарному BLOB-объекту + оболочке, - так что ядро ​​​​на самом деле является моей собственной сборкой. , Причина, по которой я запутался и нуждался в некотором объяснении, заключается в том, что, по-видимому, некоторые функции в большом двоичном объекте хранят указатели функций в локальных переменных, что немного отбрасывает всю систему. Пожалуйста, закончите свой "совсем другой рассказ" - особенно как это работает, когда основное ядро ​​- DWARF2, а какая-то часть модуля - нет. - person qdot; 29.10.2012
comment
К сожалению, я ни в коем случае не являюсь экспертом в DWARF2. Единственная реализация раскручивания DWARF2, которую я видел до сих пор, — это реализация SuSE Linux. Насколько я знаю, основное ядро ​​его еще не поддерживает. Или - неужели? Используете ли вы патч от SuSE или какого-либо другого дистрибутива Linux, чтобы добавить поддержку DWARF2 в ваше пользовательское ядро? Там могут быть разные реализации. - person Eugene; 29.10.2012
comment
Кроме того, в ядрах, которые я видел, раскрутка DWARF2 была включена либо для всех компонентов режима ядра, либо вообще ни для одного. В настоящее время я не могу точно сказать, что произойдет, если ядро ​​и модули используют разные подходы к раскрутке стека. - person Eugene; 29.10.2012
comment
Я согласен, если адрес AnotherFunctionName+0x12 находится в локальной переменной, он может отображаться в трассировке стека. То же самое, если он каким-то образом попал в регистр, который затем был сохранен в стеке. Однако в обоих случаях это не помешало бы ни размотчику на основе указателя кадра, ни размотчику на основе DWARF2 выполнять свою работу. Поскольку эта подозрительная запись окружена действительными, возможно, ее можно просто проигнорировать? - person Eugene; 29.10.2012
comment
Его, конечно, можно игнорировать - я просто пытаюсь понять, что там происходит - только двоичный код полон таблиц поиска и того, что напоминает диспетчеры объектов, похожие на виртуальные функции. - person qdot; 29.10.2012