Некоторые функции-члены kext должны быть переопределены, чтобы избежать неразрешенных символов - PullRequest
0 голосов
/ 18 сентября 2018

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 виртуальных функций-членов, поэтому, если бы возникли какие-либо проблемы со связыванием, я бы предположил, что это будет "все или ничего".При добавлении простой реализации к этим функциям (с использованием пространства имен суперкласса) ошибки компоновки исчезают.

Возникают два вопроса

  1. КакМогут ли сосуществовать несколько реализаций одних и тех же функций?Они оба живут в адресном пространстве ядра.
  2. Что делает эти особые функции такими особенными, в то время как остальные 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 выбрала эти функции, чтобы они не были внешними?они подразумевают, что они должны быть реализованы другими - и другими, почему такая же область, как у суперкласса?Я прыгнул в источник, чтобы найти ответ на это, но не нашел.Это то, что больше всего беспокоит меня - это не следует моей логике.Я понимаю, что полный исчерпывающий ответ, вероятно, слишком сложен, поэтому, по крайней мере, помогите мне понять, на более высоком уровне, что здесь происходит, в чем логика того, чтобы подкласс не получал реализацию этих конкретных функций таким странным образом(почему не чистый абстракция)?

Большое спасибо за чтение этого!

Ответы [ 2 ]

0 голосов
/ 22 сентября 2018

Непосредственным объяснением действительно является то, что символы не экспортируются кекстом IO80211.Однако вероятная причина этого заключается в том, что функции реализованы встроенными, например, так:

class IO80211Controller : public IOEthernetController
{
    //...

    virtual IOReturn enablePacketTimestamping()
    {
        return kIOReturnUnsupported;
    }
    //...
};

Например, если я создаю этот код:

#include <cstdio>

class MyClass
{
public:
        virtual void InlineVirtual() { printf("MyClass::InlineVirtual\n"); }
        virtual void RegularVirtual();
};

void MyClass::RegularVirtual()
{
        printf("MyClass::RegularVirtual\n");
}

int main()
{
        MyClass a;
        a.InlineVirtual();
        a.RegularVirtual();
}

с помощью команды

clang++ -std=gnu++14 inline-virtual.cpp -o inline-virtual

, а затем проверьте символы, используя nm:

$ nm ./inline-virtual
0000000100000f10 t __ZN7MyClass13InlineVirtualEv
0000000100000e90 T __ZN7MyClass14RegularVirtualEv
0000000100000ef0 t __ZN7MyClassC1Ev
0000000100000f40 t __ZN7MyClassC2Ev
0000000100001038 S __ZTI7MyClass
0000000100000faf S __ZTS7MyClass
0000000100001018 S __ZTV7MyClass
                 U __ZTVN10__cxxabiv117__class_type_infoE
0000000100000000 T __mh_execute_header
0000000100000ec0 T _main
                 U _printf
                 U dyld_stub_binder

Вы можете видеть, что MyClass::InlineVirtual имеет скрытую видимость (t), а MyClass::RegularVirtual экспортируется (T).Реализация для функции, объявленной как inline (либо явно с ключевым словом, либо неявно путем помещения ее в определение class), должна быть предусмотрена во всех вызывающих ее модулях компиляции, поэтому имеет смысл, чтобы они не имели внешнихсвязь.

0 голосов
/ 21 сентября 2018

Вы столкнулись с очень простым явлением: неэкспортированные символы.

$ nm /System/Library/Extensions/IO80211Family.kext/Contents/MacOS/IO80211Family | fgrep __ZN17IO80211Controller | egrep '\w{16} t'
00000000000560c6 t __ZN17IO80211Controller13enableFeatureE18IO80211FeatureCodePv
00000000000560f6 t __ZN17IO80211Controller15flowIdSupportedEv
0000000000055fd4 t __ZN17IO80211Controller16apple80211_ioctlEP16IO80211InterfaceP7__ifnetmPv
0000000000055f74 t __ZN17IO80211Controller21monitorModeSetEnabledEP16IO80211Interfacebj
0000000000056154 t __ZN17IO80211Controller24enablePacketTimestampingEv
0000000000056008 t __ZN17IO80211Controller24hardwareOutputQueueDepthEP16IO80211Interface
0000000000056160 t __ZN17IO80211Controller25disablePacketTimestampingEv
0000000000056010 t __ZN17IO80211Controller27performCountryCodeOperationEP16IO80211Interface20IO80211CountryCodeOp
00000000000560ee t __ZN17IO80211Controller27requiresExplicitMBufReleaseEv
0000000000055ffc t __ZN17IO80211Controller7stopDMAEv
0000000000057452 t __ZN17IO80211Controller9MetaClassD0Ev
0000000000057448 t __ZN17IO80211Controller9MetaClassD1Ev

За исключением двух деструкторов MetaClass, единственное отличие в вашем списке ошибок компоновки - monitorModeSetEnabled (есть ли вероятность, что вы это переопределите?).

Теперь в моей системе есть ровно один класс, расширяющий IO80211Controller, то есть AirPort_BrcmNIC, реализованный com.apple.driver.AirPort.BrcmNIC. Итак, давайте посмотрим, как это происходит:

$ nm /System/Library/Extensions/AirPortBrcmNIC-MFG.kext/Contents/MacOS/AirPortBrcmNIC-MFG | egrep '13enableFeatureE18IO80211FeatureCodePv|15flowIdSupportedEv|16apple80211_ioctlEP16IO80211InterfaceP7__ifnetmPv|21monitorModeSetEnabledEP16IO80211Interfacebj|24enablePacketTimestampingEv|24hardwareOutputQueueDepthEP16IO80211Interface|25disablePacketTimestampingEv|27performCountryCodeOperationEP16IO80211Interface20IO80211CountryCodeOp|27requiresExplicitMBufReleaseEv|7stopDMAEv'
0000000000046150 t __ZN17IO80211Controller15flowIdSupportedEv
0000000000046120 t __ZN17IO80211Controller16apple80211_ioctlEP16IO80211InterfaceP7__ifnetmPv
0000000000046160 t __ZN17IO80211Controller24enablePacketTimestampingEv
0000000000046170 t __ZN17IO80211Controller25disablePacketTimestampingEv
0000000000046140 t __ZN17IO80211Controller27requiresExplicitMBufReleaseEv
000000000003e880 T __ZN19AirPort_BrcmNIC_MFG13enableFeatureE18IO80211FeatureCodePv
0000000000025b10 T __ZN19AirPort_BrcmNIC_MFG21monitorModeSetEnabledEP16IO80211Interfacebj
0000000000025d20 T __ZN19AirPort_BrcmNIC_MFG24hardwareOutputQueueDepthEP16IO80211Interface
0000000000038cf0 T __ZN19AirPort_BrcmNIC_MFG27performCountryCodeOperationEP16IO80211Interface20IO80211CountryCodeOp
000000000003e7d0 T __ZN19AirPort_BrcmNIC_MFG7stopDMAEv

Итак, одна группа методов, которые они переопределили, а остальные ... они повторно реализованы локально. Запустив дизассемблер, мы видим, что это на самом деле просто заглушки:

;-- IO80211Controller::apple80211_ioctl(IO80211Interface*,__ifnet*,unsignedlong,void*):
;-- method.IO80211Controller.apple80211_ioctl_IO80211Interface____ifnet__unsignedlong_void:
0x00046120      55             push rbp
0x00046121      4889e5         mov rbp, rsp
0x00046124      4d89c1         mov r9, r8
0x00046127      4989c8         mov r8, rcx
0x0004612a      4889d1         mov rcx, rdx
0x0004612d      488b17         mov rdx, qword [rdi]
0x00046130      488b82900c00.  mov rax, qword [rdx + 0xc90]
0x00046137      31d2           xor edx, edx
0x00046139      5d             pop rbp
0x0004613a      ffe0           jmp rax
0x0004613c      0f1f4000       nop dword [rax]
;-- IO80211Controller::requiresExplicitMBufRelease():
;-- method.IO80211Controller.requiresExplicitMBufRelease:
0x00046140      55             push rbp
0x00046141      4889e5         mov rbp, rsp
0x00046144      31c0           xor eax, eax
0x00046146      5d             pop rbp
0x00046147      c3             ret
0x00046148      0f1f84000000.  nop dword [rax + rax]
;-- IO80211Controller::flowIdSupported():
;-- method.IO80211Controller.flowIdSupported:
0x00046150      55             push rbp
0x00046151      4889e5         mov rbp, rsp
0x00046154      31c0           xor eax, eax
0x00046156      5d             pop rbp
0x00046157      c3             ret
0x00046158      0f1f84000000.  nop dword [rax + rax]
;-- IO80211Controller::enablePacketTimestamping():
;-- method.IO80211Controller.enablePacketTimestamping:
0x00046160      55             push rbp
0x00046161      4889e5         mov rbp, rsp
0x00046164      b8c70200e0     mov eax, 0xe00002c7
0x00046169      5d             pop rbp
0x0004616a      c3             ret
0x0004616b      0f1f440000     nop dword [rax + rax]
;-- IO80211Controller::disablePacketTimestamping():
;-- method.IO80211Controller.disablePacketTimestamping:
0x00046170      55             push rbp
0x00046171      4889e5         mov rbp, rsp
0x00046174      b8c70200e0     mov eax, 0xe00002c7
0x00046179      5d             pop rbp
0x0004617a      c3             ret
0x0004617b      0f1f440000     nop dword [rax + rax]

Что примерно соответствует этому:

static uint32_t IO80211Controller::apple80211_ioctl(IO80211Interface *intf, __ifnet *net, unsigned long some, void *whatev)
{
    return this->apple80211_ioctl(intf, (IO80211VirtualInterface*)NULL, net, some, whatev);
}

static bool IO80211Controller::requiresExplicitMBufRelease()
{
    return false;
}

static bool IO80211Controller::flowIdSupported()
{
    return false;
}

static IOReturn IO80211Controller::enablePacketTimestamping()
{
    return kIOReturnUnsupported;
}

static IOReturn IO80211Controller::disablePacketTimestamping()
{
    return kIOReturnUnsupported;
}

Я не пытался скомпилировать вышесказанное, но это должно привести вас на правильный путь. :)

...