Необходимо pin_ptr на C ++ типах значений CLR, почему? - PullRequest
2 голосов
/ 09 июля 2009

Поскольку типы значений .NET (управляемые структуры C ++) хранятся в стеке, почему (или, на самом деле, альтернативно) необходимо их pin_ptr для передачи указателя на неуправляемую функцию?

Например. БАЙТ b [100];

Если я передам & b неуправляемой функции без предварительного ее закрепления, может ли стек быть поврежден?

Может ли стек CLR изменяться так же, как и куча GC? Меня убеждают, что в стеке CLR используются необычные оптимизации, такие как использование регистров процессора, что делает его непригодным для использования в качестве буфера для неуправляемых функций. Правила, касающиеся закрепления типов значений в стеке, кажутся неясными.

Я заметил, что при отправке таким образом буферных массивов в функцию NTDLL ядра NtfsControlFile, похоже, происходит некоторое искажение. Закрепление типа значения решает проблему. Но никогда не вызов API.

Не является ли поэтому принципиально небезопасным передача указателей на любые типы значений в стеке любым неуправляемым функциям без предварительного их закрепления?

Ответы [ 4 ]

2 голосов
/ 13 декабря 2009

Вы правы, что BYTE b[100]; создано в собственном стеке и поэтому не подлежит перемещению управляемой кучи и т. Д. Однако я считаю, что ваша проблема - простая ошибка C ++.

Вы говорите,

Если я передам & b неуправляемой функции без предварительного закрепления, пусть стек поврежден?

Не следует использовать оператор адреса (&) для имени массива (b), поскольку само имя массива уже является адресом начала массива. Использование &b не будет работать, и результирующее поведение будет зависеть от множества факторов (например, компилятор и его настройки).

Проще говоря, вы должны просто передавать имя массива (b) вместо использования оператора address-of для имени массива (& b) при вызове функции.

Кстати, я полагаю, что вы путаете проблемы, спрашивая, можно ли передать управляемый тип значения в стеке собственной функции без предварительного ее закрепления, поскольку в качестве примера вы передаете неуправляемую стековую функцию. родной тип массива, который не имеет ничего общего с типами управляемых значений.

1 голос
/ 09 июля 2009

Да, управление памятью может переключать адреса и просто обновляет свои внутренние ссылки на них. Как только вы погружаетесь под управляемый слой, вы должны быть уверены, что указатель, с которым вы работаете, не будет перемещен в другое место. Использование pin_ptr указывает менеджеру памяти оставить этот фрагмент памяти в покое.

0 голосов
/ 13 июля 2009

Насколько я могу судить, все это относится к объектам в куче ГХ.

Дело в том, что я имею в виду именно память STACK.

Я опубликовал пример, который, по-видимому, предполагает, что стековая память повреждена во время передачи вызова стека API в виде буфера: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/3779c1ee-90b8-4a6a-9b14-f48d709cb27c

Если стековая память должна быть закреплена, то это, похоже, нарушает идею «Это просто работает». В неуправляемом C ++ мы можем объявить буфер стека и затем передать указатель на него в функцию API. Однако, если переход к управляемому коду требует закрепления, это может подорвать «Это просто работает».

Это сбивает с толку то, как документы MSDN для pin_ptr говорят, что они предназначены только для предотвращения перемещения объектов, однако также возможно закрепить типы значений, которые, по-видимому, находятся в стеке и не должны двигаться в любом случае.

Я специально поднимаю вопрос о том, как стековая память обрабатывается одинаково в управляемом или неуправляемом коде. При отладке MSIL я обнаружил, что просмотр стека невозможен, и для этого нет средства просмотра стека. Я слышал, но не уверен, что в MSIL нет «реального» стека, и вместо этого CLR виртуальной машины можно оптимизировать, например, используя свободные регистры процессора вместо реальной памяти. Неясно, верно ли это, и применимо ли это к стеку, как при передаче параметров, или к стеку, как в локальной переменной памяти.

Странный эффект в приведенном выше примере проекта заключается в том, что pin_ptr на разрушающем объекте, похоже, решает проблему. однако объект находится на СТЕКЕ и не нуждается в закреплении. Может быть так, что / CLR интерпретирует pin_ptr как не только «не перемещать этот объект», но также «покидать эту область как истинную память и не пытаться зарегистрировать оптимизации для нее», что приведет к тому, что она останется чистой на время вывода

Мне бы особенно хотелось узнать, достаточно ли умен / CLR, чтобы избежать оптимизации памяти стека в методе во время вызова API, но, возможно, не даст мне такую ​​же грацию в приведенном выше примере из-за прямая загрузка NTDLL и способ объявления функции как typedef.

Я рассмотрел добавление атрибутов Marshalling в функцию typedef, но, похоже, не смог этого сделать. Хочу заметить, что в определениях WinAPI нет атрибутов MarshallAs.

Мне удалось проникнуть в проект выше с помощью __debugbreak () непосредственно перед вызовом NTDLL, однако это только дает мне режим управляемой отладки, который кажется неспособным войти в нативный код. Я не могу написать «asm int 3», потому что x64 не поддерживает его. Однако я вижу, что ошибочное значение NumberOfPairs передается в ячейку памяти, на которую указывает регистр, а не как сам регистр.

0 голосов
/ 09 июля 2009

Не является ли поэтому принципиально небезопасным передача любых указателей на любые типы значений в стеке любым неуправляемым функциям без предварительного их закрепления?

Да.

Это связано с тем, что GC удаляет / перемещает их асинхронно в ваш метод.

См. перемещение GC для описания того, как работает CLR GC.

...