Я добавлю еще один ответ, чтобы ответить на некоторые из обсуждений, которые имели место.
C ABI (двоичный интерфейс приложения) первоначально вызывал передачу аргументов в стеке в обратном порядке (т. Е. Толкает справа налево), где вызывающая сторона также освобождает хранилище стека. Современный ABI фактически использует регистры для передачи аргументов, но многие из искажающих соображений восходят к передаче оригинального стека.
Оригинальный ABI Pascal, напротив, выдвигал аргументы слева направо, и вызываемый должен был выдвигать аргументы. Оригинальный C ABI превосходит оригинальный Pascal ABI в двух важных моментах. Порядок проталкивания аргументов означает, что смещение стека первого аргумента всегда известно, что позволяет функциям с неизвестным числом аргументов, где ранние аргументы управляют количеством других аргументов (alaprintf
).
Второй способ превосходства C ABI - это поведение в том случае, если вызывающий и вызываемый абоненты не согласны с тем, сколько аргументов существует. В случае C, если вы на самом деле не обращаетесь к аргументам после последнего, ничего плохого не происходит. В Паскале неправильное количество аргументов извлекается из стека, и весь стек поврежден.
Оригинальный Windows 3.1 ABI был основан на Pascal. Как таковой, он использовал Паскаль ABI (аргументы в порядке слева направо, Callee Pops). Поскольку любое несоответствие номера аргумента может привести к повреждению стека, была сформирована схема искажения. Каждое имя функции было искажено числом, указывающим размер в байтах его аргументов. Итак, на 16-битной машине, следующая функция (синтаксис C):
int function(int a)
Был искалечен function@2
, потому чтоint
имеет ширину два байта. Это было сделано для того, чтобы в случае несоответствия объявления и определения компоновщик не смог найти функцию, а не повредил стек во время выполнения. И наоборот, если программа соединяется, то вы можете быть уверены, что в конце вызова выбрано правильное количество байтов из стека.
32-битная Windows и далее использовать stdcall
вместо этого ABI. Это похоже на Паскаль ABI, за исключением того, что порядок нажатия такой же, как в C, справа налево. Как и в Паскаль ABI, искажение имени меняет размер байтов аргументов в имя функции, чтобы избежать повреждения стека.
В отличие от утверждений, сделанных в другом месте, C ABI не искажает имена функций, даже в Visual Studio. И наоборот, функции искажения, украшенные stdcall
спецификацией ABI, не уникальны для VS. GCC также поддерживает этот ABI, даже при компиляции для Linux. Это широко используется Wine , который использует собственный загрузчик, чтобы разрешить связывание исполняемых файлов Linux скомпилированных библиотек Windows во время выполнения.