TL; DR
Подкласс переопределяет (переопределяет) виртуальную функцию суперкласса (базовый класс) в области действия суперкласса , потому что динамический загрузчик требует это сделать.Это не имеет никакого смысла для меня.
Пример:
class IO80211Controller : public IOEthernetController
{
virtual IOReturn enablePacketTimestamping(); // Implemented in binary, I can see the disassembly.
};
// .cpp - Redefinition with superclass namespace.
IOReturn IO80211Controller::enablePacketTimestamping()
{
return kIOReturnUnsupported; // This is from the disassembly of IO80211Controller
}
Выше не настоящий заголовок, я надеюсь, что он близок к тому, что должен быть - заголовок недоступен.
// .hpp
class AirPortBrcm4331 : public IO80211Controller
{
// Subclass stuff goes here
};
// .cpp - Redefinition with superclass namespace.
IOReturn IO80211Controller::enablePacketTimestamping()
{
return kIOReturnUnsupported; // This is from the disassembly of AirPortBrcm4331
}
Фон
Я исследую IO80211Family.kext
(для которого нет доступных заголовков) и, в частности, IO80211Controller
класс - я нахожусь в процессе реверсазаголовок, так что можно будет наследовать от этого класса и создавать собственные драйверы 802.11.
Обнаружение проблемы
IO80211Controller
определяет много виртуальных функций-членов, которые мне нужно объявитьв моем обратном заголовочном файле.Я создал файл заголовка со всеми виртуальными функциями (извлеченный из vtable таблицы IO80211Controller) и использовал его для моего подкласса.
При загрузке моего нового kext (с подклассом) возникли ошибки компоновки:
kxld[com.osxkernel.MyWirelessDriver]: The following symbols are unresolved for this kext:
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::enableFeature(IO80211FeatureCode, void*)
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::flowIdSupported()
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::apple80211_ioctl(IO80211Interface*, __ifnet*, unsigned long, void*)
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::enablePacketTimestamping()
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::hardwareOutputQueueDepth(IO80211Interface*)
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::disablePacketTimestamping()
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::performCountryCodeOperation(IO80211Interface*, IO80211CountryCodeOp)
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::requiresExplicitMBufRelease()
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::_RESERVEDIO80211Controllerless7()
kxld[com.osxkernel.MyWirelessDriver]: IO80211Controller::stopDMA()
Link failed (error code 5).
Обратный заголовок суперкласса содержит более 50 виртуальных функций-членов, поэтому, если бы возникли какие-либо проблемы со связыванием, я бы предположил, что это будет "все или ничего".При добавлении простой реализации к этим функциям (с использованием пространства имен суперкласса) ошибки компоновки исчезают.
Возникают два вопроса
- КакМогут ли сосуществовать несколько реализаций одних и тех же функций?Они оба живут в адресном пространстве ядра.
- Что делает эти особые функции такими особенными, в то время как остальные 50 в порядке без странного повторного требования?
Гипотеза
Я не могу ответить на первый вопрос, но я начал исследование о втором.
Я изучил таблицу символов IO80211Family
mach-o и все функции сошибка связывания не содержит бит N_EXT
в их поле типа - это означает, что они не являются внешними символами, в то время как другие функции содержат бит N_EXT
.
Я не был уверенкак это влияет на процедуру загрузки kext, поэтому я погрузился в исходный код XNU и искал код загрузки kext.Здесь есть главный игрок, называемый vtable patching, который может пролить свет на мой первый вопрос.
В любом случае, есть функция предиката под названием kxld_sym_is_unresolved
, которая проверяет, является ли символ неразрешенным.kxld вызывает эту функцию для всех символов, чтобы убедиться, что все они в порядке.
boolean_t
kxld_sym_is_unresolved(const KXLDSym *sym)
{
return ((kxld_sym_is_undefined(sym) && !kxld_sym_is_replaced(sym)) ||
kxld_sym_is_indirect(sym) || kxld_sym_is_common(sym));
}
Результат этой функции в моем случае сводится к возвращаемому значению kxld_sym_is_replaced
, которое просто проверяет, был ли пропатчен символ (vtable patching), я недостаточно хорошо понимаю, что это такоеи как это влияет на меня ...
Главный вопрос
Почему Apple выбрала эти функции, чтобы они не были внешними?они подразумевают, что они должны быть реализованы другими - и другими, почему такая же область, как у суперкласса?Я прыгнул в источник, чтобы найти ответ на это, но не нашел.Это то, что больше всего беспокоит меня - это не следует моей логике.Я понимаю, что полный исчерпывающий ответ, вероятно, слишком сложен, поэтому, по крайней мере, помогите мне понять, на более высоком уровне, что здесь происходит, в чем логика того, чтобы подкласс не получал реализацию этих конкретных функций таким странным образом(почему не чистый абстракция)?
Большое спасибо за чтение этого!