Доброго времени суток. Требуется чистить отдаваемую системе память. Перегружаю глобальный оператор delete.
void __cdecl operator delete(void* ptr)
{
SecureZeroMemory(ptr,_msize(ptr));
...
}
После чистки памяти хочется вызвать обычный глобальный delete (тот что был до перегрузки).
::operator delete вызывает мой перегруженный вариант. Можно ли как то вызвать неперегруженный
вариант delete?
Здравствуйте, Melfis, Вы писали:
M>После чистки памяти хочется вызвать обычный глобальный delete (тот что был до перегрузки). M>::operator delete вызывает мой перегруженный вариант. Можно ли как то вызвать неперегруженный M>вариант delete?
можно завернуть старый delete в длл, а в перегруженном delete вызывать тот, который внутри длл находится
Здравствуйте, Melfis, Вы писали:
M>Доброго времени суток. Требуется чистить отдаваемую системе память. Перегружаю глобальный оператор delete.
Не "перегружаю", а "переписываю".
Идея на 3-. По сути, нарушается ODR, и это может аукнуться там, где компилятор проинлайнил, а также где линкер подставил исходный оператор (если рантайм в .dll)
M>После чистки памяти хочется вызвать обычный глобальный delete (тот что был до перегрузки).
Можно пойти другим путём: заодно переписать ::operator new(size_t)
И внутри этой пары new/delete вызывать собственную согласованную пару функций — например, malloc/free.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Melfis, Вы писали:
M>>Доброго времени суток. Требуется чистить отдаваемую системе память. Перегружаю глобальный оператор delete.
К>Не "перегружаю", а "переписываю".
можно сказать "заменяю"
3.7.3 Dynamic storage duration
<skip>
2. The library provides default definitions for the global allocation and deallocation functions. Some global
allocation and deallocation functions are replaceable (18.4.1). A C + + program shall provide at most one
definition of a replaceable allocation or deallocation function. Any such function definition replaces the
default version provided in the library (17.4.3.4).
К>Идея на 3-. По сути, нарушается ODR, и это может аукнуться там, где компилятор проинлайнил, а также где линкер подставил исходный оператор (если рантайм в .dll)
В чем плохость идеи? Я так понял, что ТС хочет занулять освобожденную память. Как иначе это сделать?
ODR не нарушается и переопределение этой функции разрешено стандартом.
Здравствуйте, Кодт, Вы писали:
К>Остаётся вопрос, как эта штука уживается с *crt*.dll ?
а в чем сложность?
линкер в процессе линковки смотрит:
если юзер написал свою функцию, то родной delete не линкуется
иначе линкуется
как это связано с *crt*.dll? она лишь может потянуться, если залинковали дефолтную реализацию delete
Здравствуйте, uzhas, Вы писали:
U>как это связано с *crt*.dll? она лишь может потянуться, если залинковали дефолтную реализацию delete
А в недрах *crt*.dll этот new/delete нигде не используется?
К примеру, я встречал CRT (правда, не для виндов, а для VxWorks) где были явные инстансы basic_string<char> и <wchar_t> aka string и wstring со всеми их методами. (Чтобы в каждом своём модуле не повторяться, а использовать уже готовое).
Вроде бы стандарт не запрещает делать такую оптимизированную реализацию?
Но это значит, что либо там собственный аллокатор, либо возникает обратная зависимость — CRT от пользовательского модуля.
Отсюда вывод: либо в VxWorks кривой несовместимый со стандартом CRT (охотно в это верю!), либо таки существует ограничение: в недрах crt.dll нет никаких вызовов new/delete, всё исключительно в инлайнах, пускай компилятор разбирается сам.
И второй вывод, практический: если в программе два и более модуля, то, как бы они ни использовали общий рантайм, но общий изменённый new/delete использовать у них не получится.
Максимум, что можно — это сделать идентичные определения (например, в общем хедере), устойчивые к существованию дубликатов функций (т.е. без статических переменных и т.п.)
Здравствуйте, Кодт, Вы писали:
К>К примеру, я встречал CRT (правда, не для виндов, а для VxWorks) где были явные инстансы basic_string<char> и <wchar_t> aka string и wstring со всеми их методами. (Чтобы в каждом своём модуле не повторяться, а использовать уже готовое). К>Вроде бы стандарт не запрещает делать такую оптимизированную реализацию? К>Но это значит, что либо там собственный аллокатор, либо возникает обратная зависимость — CRT от пользовательского модуля.
теперь понял о какой проблеме вы говорите
мне казалось, что в студии тоже basic_string реализован в длл (можно посмотреть на экспорты), но не знаю как включить такие строки
в дебугере проверил — в длл не уходит
ну а технически можно ведь через аллокатор вызвать юзерский delete
в первом модуле вызываем basic_string<Allocator>::erase(..); внутри crt.dll могут вызывать Allocator::deallocate, который вернется в первый модуль и вызовет там delete
кстати, задумался, а обязательно ли basic_string должен использовать оператор new?
нашел ответ
20.4.1 The default allocator
namespace std { template <class T> class allocator;
<skip>
}
M>Доброго времени суток. Требуется чистить отдаваемую системе память. Перегружаю глобальный оператор delete. M>void __cdecl operator delete(void* ptr) M>{ M> SecureZeroMemory(ptr,_msize(ptr)); M> ... M>}
M>После чистки памяти хочется вызвать обычный глобальный delete (тот что был до перегрузки). M>::operator delete вызывает мой перегруженный вариант. Можно ли как то вызвать неперегруженный M>вариант delete?
Простого решения не нашлось. Может имеется какой то другой способ вклиниться в процесс освобождения памяти под release сборкой, кроме перегрузки delete?
Здравствуйте, Melfis, Вы писали:
M>Простого решения не нашлось. Может имеется какой то другой способ вклиниться в процесс освобождения памяти под release сборкой, кроме перегрузки delete?
напишите свой аллокатор и суйте во все stl-контейнеры =) но длл попроще сделать=) у нас в дебуге есть подобная длл, помогает нам отлавливать утечки (при выгрузке печатаются все фрагменты памяти со стеком аллокации, которые не были удалены)
Здравствуйте, uzhas, Вы писали:
U>напишите свой аллокатор и суйте во все stl-контейнеры =) но длл попроще сделать=) у нас в дебуге есть подобная длл, помогает нам отлавливать утечки (при выгрузке печатаются все фрагменты памяти со стеком аллокации, которые не были удалены)
Под дебагом есть allocation hook'и, мне нужно под релизом. Аллокатор не пойдет, т.к. мне нужно очищать ВСЮ освобождаемую память.
Здравствуйте, Кодт, Вы писали:
U>>ODR не нарушается и переопределение этой функции разрешено стандартом. К>Как говорится, век живи, век учись... К>Остаётся вопрос, как эта штука уживается с *crt*.dll ?
А какие проблемы? *crt*.dll использует свою реализацию, ваше приложение/dll свою, имхо линкер должен это поддерживать.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали:
V>А какие проблемы? *crt*.dll использует свою реализацию, ваше приложение/dll свою, имхо линкер должен это поддерживать.
Дело в том, что — либо crt.dll не зависит от .exe, и использует свой комплект new/delete изолированно (интересно, где? в шаблонных аллокаторах — не имеет права, это точно создаст нарушение ODR), либо зависит — и линкер здесь бессилен, да и загрузчик тоже.
Единственный способ, как эту обратную зависимость можно воплотить — это глобальные переменные-указатели на функции. *crt*.dll их настраивает на дефолнтую реализацию, а затем .exe на свою собственную.
Здравствуйте, Кодт, Вы писали:
V>>А какие проблемы? *crt*.dll использует свою реализацию, ваше приложение/dll свою, имхо линкер должен это поддерживать. К>Дело в том, что — либо crt.dll не зависит от .exe
Я не совсем понимаю почему crt.dll должен зависить от кого-то, вообще это кто-то должен зависеть от него.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
U>теперь понял о какой проблеме вы говорите U>мне казалось, что в студии тоже basic_string реализован в длл (можно посмотреть на экспорты), но не знаю как включить такие строки U>в дебугере проверил — в длл не уходит U>ну а технически можно ведь через аллокатор вызвать юзерский delete
Через стайтлесс — нельзя.
U>в первом модуле вызываем basic_string<Allocator>::erase(..); внутри crt.dll могут вызывать Allocator::deallocate, который вернется в первый модуль и вызовет там delete
Никуда он не вернется.
Ситуация с динамическими либами. Модуль А с переопределенным оператором. Модуль Б без.
Б получает из А ссылку на vector и вызывает insert. В результате используется оператор и аллокатор, определенные в модуле Б.
А поскольку удалять объект будет модуль А, то будет бабах.
В общем, надо повторять мантру — объекты нельзя передавать через границы (динамических) модулей, если они для этого не предназначены специально (например, имеют гарантии abi). Есть adobe::abi stl, вот там с некоторыми ограничениями так можно.
Здравствуйте, Vain, Вы писали:
V>Я не совсем понимаю почему crt.dll должен зависить от кого-то, вообще это кто-то должен зависеть от него.
Потому что по стандарту.
Если пользователь может переопределить стандартную функцию, — то это пользовательское определение должно распространиться на всю программу.
Здравствуйте, Кодт, Вы писали:
V>>Я не совсем понимаю почему crt.dll должен зависить от кого-то, вообще это кто-то должен зависеть от него. К>Потому что по стандарту. К>Если пользователь может переопределить стандартную функцию, — то это пользовательское определение должно распространиться на всю программу.
Оно и распространяется, только причём здесь зависимость crt от приложения?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали:
К>>Если пользователь может переопределить стандартную функцию, — то это пользовательское определение должно распространиться на всю программу. V>Оно и распространяется, только причём здесь зависимость crt от приложения?
Ещё раз, внимательно.
1. Пусть CRT содержит, например, скомпилированные методы специализации basic_string<char> и, соответственно, allocator<char>.
2. Пусть эти методы используют buf_ = new char[N]; / delete[] buf_;
3. Пусть пользователь переписал ::operator new[] и ::operator delete[]
Вопрос: как CRT узнает о том, что эти операторы переписаны, если оно уже скомпилировано и слинковано в crt.dll ?
Эрго,
— либо оно будет использовать свои, непереписанные, версии этих операторов (нарушаем ODR, э?)
— либо есть какой-то механизм обратного связывания, позволяющий экзешнику подсунуть переписанную версию внутрь dll
Перекуём баги на фичи!
Re[10]: Вызов глобального delete из перегруженного
Здравствуйте, Кодт, Вы писали:
К>>>Если пользователь может переопределить стандартную функцию, — то это пользовательское определение должно распространиться на всю программу. V>>Оно и распространяется, только причём здесь зависимость crt от приложения? К>Ещё раз, внимательно. К>1. Пусть CRT содержит, например, скомпилированные методы специализации basic_string<char> и, соответственно, allocator<char>. К>2. Пусть эти методы используют buf_ = new char[N]; / delete[] buf_; К>3. Пусть пользователь переписал ::operator new[] и ::operator delete[] К>Вопрос: как CRT узнает о том, что эти операторы переписаны, если оно уже скомпилировано и слинковано в crt.dll ?
Я и спрашиваю зачем CRT это узнавать, раз аллокатор уже скомпанован?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[10]: Вызов глобального delete из перегруженного
Кодт:
К>Ещё раз, внимательно.
К>1. Пусть CRT содержит, например, скомпилированные методы специализации basic_string<char> и, соответственно, allocator<char>. К>2. Пусть эти методы используют buf_ = new char[N]; / delete[] buf_; К>3. Пусть пользователь переписал ::operator new[] и ::operator delete[]
К>Вопрос: как CRT узнает о том, что эти операторы переписаны, если оно уже скомпилировано и слинковано в crt.dll ? К>Эрго, К>- либо оно будет использовать свои, непереписанные, версии этих операторов (нарушаем ODR, э?)
А при чём здесь ODR? ODR применимо только к программе, написанной пользователем, а CRT, являющаяся частью реализации, сюда никаким боком не относится. Если в _пользовательском_ коде будет вызываться неправильный operator new, operator new[] и т.д. (при использовании того же basic_string<char>, например), то нарушение требований стандарта может быть, но лишь по части observable behavior.