Почему иногда используется неправильное соглашение о вызовах? - PullRequest
5 голосов
/ 11 августа 2011

Я использовал функцию «StartServiceCtrlDispatcher» для регистрации функции обратного вызова (называемой ServiceMain) в Windows, но объявленная мной функция обратного вызова скомпилирована с неверным соглашением о вызовах.

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

Теперь, когда я обнаружил ошибку, все заработало, но я просто не понимаю, почему на некоторых компьютерах она работала правильно, без сбоев?

Спасибо! : -)

Ответы [ 4 ]

6 голосов
/ 11 августа 2011

Это все очень специфично для Windows, мы не говорим здесь о стандартном C ++.

Извлекая документацию StartServiceDispatcher, она имеет только один аргумент и объявлена ​​какWINAPI что в свою очередь означает __stcall соглашение о вызовах.

Для автономных функций __stdcall является одним из двух основных соглашений о вызовах.Другой - __cdecl.Разница в уровне машинного кода заключается в том, кто просто восстанавливает указатель стека: с __stdcall это сама функция, а с __cdecl это код вызова.

Когда функция на самом деле __stdcall, новызывается так, как если бы это было __cdecl, ситуация в том, что существует две попытки восстановить указатель стека: одна на выходе из функции и одна в вызывающем коде.Тот, что в функции, будет успешным.В зависимости от того, как выполняется попытка в вызывающем коде, он может полностью испортить ситуацию (например, если просто добавить требуемое смещение, относиться к указателю стека как относительному), или он может не оказать вредного влияния.Но, скорее всего, это создаст беспорядок, поскольку предположение о значении указателя стека при возврате из функции неверно.

Когда функция на самом деле равна __cdecl, она сама не будет восстанавливать указатель стека, посколькуэто ответственность вызывающего кода.И если вызывающий код рассматривает его как __stdcall, то вызывающий код также не восстановит его, так как с точки зрения вызывающего кода функция делает это.В результате, если вы не получите преждевременный сбой (из-за неправильных предположений), должно получиться так, что повторные вызовы, скажем, в цикле, будут использовать пространство стека.

Это все очень неопределенное поведение.

И одно свойство Неопределенного поведения - то, что он может делать все что угодно, в том числе, очевидно, работающий…

Приветствия & hth.,

2 голосов
/ 11 августа 2011

Соглашения о вызовах отличаются деталями, например, какие регистры сохраняются. Если вам не удалось сохранить в этих регистрах что-то, что вам все еще нужно, тогда не имело значения, что они были стерты, когда этого не требовалось. Точно так же, если ваше соглашение о вызовах отличается от того, как оно работает с возвращаемыми значениями, если вы ничего не возвращаете, то это не имеет значения.

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

1 голос
/ 23 мая 2012

Компьютеры, на которых произошло сбой приложения, могли использовать .NET Framework версии 4.

Посмотрите на следующую статью: http://msdn.microsoft.com/en-us/library/ee941656.aspx

В разделе «Взаимодействие - вызов платформы» говорится следующее:

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

0 голосов
/ 11 августа 2011

Все это связано с тем, что является текущим в памяти.Предположим, у вас есть две функции, подобные этой:

void stdcall f1(...) { ... }

void cdecl f2(...) { ... }

stdcall - соглашение о вызовах Windows, в то время как cdecl используется большинством компиляторов.Разница между ними заключается в том, кто несет ответственность за очистку стека после вызова.В stdcall вызываемый (f1 или f2) делает, в cdecl делает вызывающий.

В конце концов, стек заполняется неизвестными значениями.Поэтому, когда он очищен (неправильно), следующее значение, к которому вы получаете доступ в стеке, не определено.Это вполне может быть приемлемой ценностью или очень плохой.В принципе, так работает переполнение стека (ошибка, а не сайт).

...