2 копии DLL в одном процессе
От: TheRawGod  
Дата: 20.12.07 13:08
Оценка: 10 (2)
Коллеги,

сразу хочу отметить, что вопрос сюда пришел постепенно, по пути msdn -> microsoft.public.vc.atl -> wasm -> rsdn. К сожалению, ответ нигде получить не получилось, поэтому дублирую здесь, прошу прощения у тех, если кто его читает не в первый раз.

Ситуация следующая: в процессе работы над проектом мы натолкнулись на то, что при определенных условиях нам удается загрузить одну и ту же dll в один процесс 2 раза. Фактический mapping type в обоих случаях Image, пути загрузки — идентичны. Чтобы показать проблему был сделан простенький пример (solution из 3-х native win32 C++ проектов для Visual Studio 2005).

В нем мы грузим проблемную длл вначале с помощью SxS COM, создавая объект, живущий в ней, а воторой раз — как статически прилинкованную к загружаемой через LoadLibrary длл (т.е. делаем LoadLibrary "промежуточной" длл, в таблице импорта которой находится "проблемная" первоначальная дублирующаяся длл (та, в которой живут созданные раньше через SxS com объекты)). Порядок загрузки изображен тут.

В результате смотрим, например, Process Explorer'ом и видим 2 копии одной и той же длл.

Меня бы может и устроил ответ, что, типа, в одном случае у нас com грузит, а в другом — LoadLibrary, да и то опосредованно через таблицу импорта и все такое (хотя это, имхо, бред, какая разница как оно грузится, если образ один и тот-же, да и поискать в ctnb если, то везде говорят, что "dll can't be loaded twice" с различным вариациями), если бы не еще несколько НО с нашей точки зрения:
1. Если в указанном примере использовать обычный com, а не SxS, то копия длл оказывается одна.
2. Если изменить порядок загрузки и вначале грузить через LoadLibrary, а потом создавать объект через SxS com — то опять-же, копия одна.

И пускай уже, пункт 1 я готов принять как некий by design, но вот пункт 2 ставит большой знак вопроса. Какими бы ни были разными механизмы загрузки, но порядок их использования не должен влиять на конечный результат.

Более того, если поставить бряки в DllMain дублирующейся длл, то студия дуреет: при первоначальной загрузке через SxS все красиво брякается, а вот при повторной непонятной загрузке студийные бряки не срабатывают. Если поусердствовать и вручную всунуть int3, то это приводит студию в шок и она показывает в стеке вызовов полную ерунду + асм код вместо исходников. Т.е. она видит, что длл загружена по одному базовому адресу и никак не хочет принимать тот факт, что та же длл есть и еще по другому адресу в том-же процессе.

В результате мы подозреваем баг в загрузчике WinXP SP2 / Vista (воспроизводится аналогично), неким образом очевидно связанный с Side by Side COM.

Очень буду вам благодарен за опровержение и любые иные аргументированные соображения.
Re: 2 копии DLL в одном процессе
От: Tom Россия http://www.RSDN.ru
Дата: 20.12.07 14:14
Оценка:
TRG>Очень буду вам благодарен за опровержение и любые иные аргументированные соображения.
А ты уверен, что Dll не выгружена COM-ом? Попробуй протрейсить события в dllmain-е.
Народная мудрось
всем все никому ничего(с).
Re[2]: 2 копии DLL в одном процессе
От: TheRawGod  
Дата: 20.12.07 14:37
Оценка:
Здравствуйте, Tom, Вы писали:

TRG>>Очень буду вам благодарен за опровержение и любые иные аргументированные соображения.

Tom>А ты уверен, что Dll не выгружена COM-ом? Попробуй протрейсить события в dllmain-е.

Большое спасибо за Ваш ответ.

Я абсолютно уверен, что не выгружена . Ведь мы держим указатель на объект, который создаем.

В случае описаного сценария (когда длл грузится именно 2 раза) мы имеем такие события (ставим int 3 в DllMain и в конструкторе глобального объекта, объявленного в длл):
--- Создаем SxS com объект ---
1. Конструктор глобального объекта.
2. DllMain: ATTACH

--- Делаем LoadLibrary "промежуточной" dll ---
3. Конструктор глобального объекта, но студия не может с этим разобраться и показывает асм код + безымянный стек trace
4. DllMain: ATTACH, асм код + безымянный стек trace

--- Закрываем программу ---
5. DllMain: DETACH, нормальный, т.е. загруженой com'ом dll'ки.
6. DllMain: DETACH, асм код + безымянный стек trace, т.е. выгружается загруженная ранее "опосредовано" через LoadLibrary dll'ка


Деструктор объекта просто не объявлен, но он тут сути не играет. К моменту закрытия уже и трейсить особо ничего больше не надо (шаги 5,6) — т.к. проблема налицо.
Re[3]: 2 копии DLL в одном процессе
От: Tom Россия http://www.RSDN.ru
Дата: 20.12.07 15:46
Оценка:
TRG>Я абсолютно уверен, что не выгружена . Ведь мы держим указатель на объект, который создаем.
Это ничего не значит.

TRG>В случае описаного сценария (когда длл грузится именно 2 раза) мы имеем такие события (ставим int 3 в DllMain и в конструкторе глобального объекта, объявленного в длл):

Не надо int 3, хватит простого outputDebugString-а.


TRG>--- Создаем SxS com объект ---

TRG>1. Конструктор глобального объекта.
TRG>2. DllMain: ATTACH
Который attach?

ПС:
Запустить не удалось, у меня 2008-я студия не стоит, только 2005.
Народная мудрось
всем все никому ничего(с).
Re[4]: 2 копии DLL в одном процессе
От: TheRawGod  
Дата: 20.12.07 16:19
Оценка:
Здравствуйте, Tom, Вы писали:


TRG>>Я абсолютно уверен, что не выгружена . Ведь мы держим указатель на объект, который создаем.

Tom>Это ничего не значит.

TRG>>В случае описаного сценария (когда длл грузится именно 2 раза) мы имеем такие события (ставим int 3 в DllMain и в конструкторе глобального объекта, объявленного в длл):

Tom>Не надо int 3, хватит простого outputDebugString-а.


TRG>>--- Создаем SxS com объект ---

TRG>>1. Конструктор глобального объекта.
TRG>>2. DllMain: ATTACH
Tom>Который attach?

Tom>ПС:

Tom>Запустить не удалось, у меня 2008-я студия не стоит, только 2005.

2008 не нужна, у меня тоже 2005. Собственно, о 2005 и шла речь в оригинальном сообщении, почему вы решили, что нужна 2008?

Аттач — тот, который DLL_PROCESS_ATTACH, оба раза.
Второй раз надо смотреть в дизасме, так как студия не соображает, что есть код, как я и писал. Тем не менее, оба раза DLL_PROCESS_ATTACH.
Re[5]: 2 копии DLL в одном процессе
От: Tom Россия http://www.RSDN.ru
Дата: 26.12.07 02:30
Оценка:
TRG>2008 не нужна, у меня тоже 2005. Собственно, о 2005 и шла речь в оригинальном сообщении, почему вы решили, что нужна 2008?
Потому, что при запуске требует CRT от 2008-й
Народная мудрось
всем все никому ничего(с).
Re[6]: 2 копии DLL в одном процессе
От: TheRawGod  
Дата: 27.12.07 13:30
Оценка:
Здравствуйте, Tom, Вы писали:

TRG>>2008 не нужна, у меня тоже 2005. Собственно, о 2005 и шла речь в оригинальном сообщении, почему вы решили, что нужна 2008?

Tom>Потому, что при запуске требует CRT от 2008-й

Коллега, ошибаетесь. 2008 студии не видел в глаза ни один из моих рабочих PC, 9-й CRT там быть неоткуда. Тем более в зависимостях студийного (2005!) проекта. Требуется CRT версий 8.хххх.
Не знаю, обновляллось ли CRT с выходом SP1 для студии 2005. Подозреваю, что как раз обновлялось, и что у Вас, возможно, это обновление не стоит.

В любом случае, это имеет мало отношения к сути проблемы.
После Нового Года будем контактировать с MS в рамках Gold Partner программы, должны уделить внимание, по идее. Больше ничего не остается.
Оказыается, есть не так много людей, которые используют SxS COM и еще меньше людей, которые при этом вызывают LoadLibrary тех же библиотек
Re: 2 копии DLL в одном процессе
От: Ivan Россия www.rsdn.ru
Дата: 27.12.07 19:20
Оценка: 12 (2)
Здравствуйте, TheRawGod, Вы писали:

TRG>В результате мы подозреваем баг в загрузчике WinXP SP2 / Vista (воспроизводится аналогично), неким образом очевидно связанный с Side by Side COM.

TRG>Очень буду вам благодарен за опровержение и любые иные аргументированные соображения.

посмотрел пример в отладчике — выяснилось следующее:

— нужно ли загружать dll определяет фукнция LdrpCheckForLoadedDll, при повторной загрузке SideBySide.dll она возвращает false и поэтому dll загружается повторно, при этом взводится флаг 0x10000000 в LDR_MODULE.flags
(в WinDbg это можно увидеть с помощью команды !dlls -c <адрес модуля>)
описание LDR_MODULE http://undocumented.ntinternals.net/UserMode/Structures/LDR_MODULE.html

— эта логика как-то связана с тем, что предыдущая загрузка была в некотором activation context'е (т.е. с использованием манифеста), а текущая загрузка происходит через импорт другой dll.
— в последующих загрузках (через таблицу импорта) LdrpCheckForLoadedDll уже будет проверять флаг 0x10000000)

является ли это багом или фичей — непонятно. механизм загрузки модулей через LoadLibrary и таблицы импорта разный. как минимум тем, что в первом случае передаются пути для поиска dll, а во втором — нет.
могу предположить, что манифесты предназначены для динамической загрузки модулей, в случае статической загрузки манифест никак не влияет на поиск подходящего модуля, с этим возможно и связан тот факт, что при статической загрузке ранее загруженного с помощью манифеста модуля будет создана отдельная копия. наличие специального флага 0x10000000 и логики по его проверке подтверждает версию, что все-таки это фича, а не баг
Re: 2 копии DLL в одном процессе
От: dZab Россия  
Дата: 28.12.07 02:27
Оценка:
Подобный эффект должен наблюдаться, если набор прав у загружающих dll моделей, разный. У Рихтера эта ситуация подробно описана. Однако обычной ситуация является, когда dll грузят разные процессы, как могут быть разные права в пределах одного процесса, мне не понятно. К сожалению, не знаю, что скрывается за сочетанием "SxS", может оно и обеспечивает секьюрити?
Еще одна возможность — com-объект создается как внепроцессный.
Re[2]: 2 копии DLL в одном процессе
От: aloch Россия  
Дата: 28.12.07 21:55
Оценка:
Здравствуйте, dZab, Вы писали:
> К сожалению, не знаю, что скрывается за сочетанием "SxS"

SxS — side-by-side


 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.