Поскольку схемы искажения не стандартизированы, на этот вопрос нет однозначного ответа; ближе всего к реальному ответу было бы посмотреть на искаженные имена, сгенерированные наиболее распространенными схемами искажения. Насколько мне известно, это схемы GCC и MSVC в алфавитном порядке, так что...
ССЗ:
Чтобы проверить это, мы можем использовать простую программу.
#include <string>
#include <cstdlib>
std::string foo(int x) { return "hello"; }
//int foo(int x) { return x; }
int main() {
// Assuming executable file named "a.out".
system("nm a.out");
}
Скомпилируйте и запустите с помощью GCC или Clang, и он выведет список содержащихся в нем символов. В зависимости от того, какая из функций раскомментирована, результаты будут такими:
// GCC:
// ----
std::string foo(int x) { return "hello"; } // _Z3fooB5cxx11i
// foo[abi:cxx11](int)
int foo(int x) { return x; } // _Z3fooi
// foo(int)
// Clang:
// ------
std::string foo(int x) { return "hello"; } // _Z3fooi
// foo(int)
int foo(int x) { return x; } // _Z3fooi
// foo(int)
Схема GCC содержит относительно мало информации, не считая возвращаемых типов:
- Тип символа:
_Z
для функции.
- Имя:
3foo
для ::foo
.
- Параметры:
i
для int
.
Однако, несмотря на это, они различаются при компиляции с помощью GCC (но не с Clang), потому что GCC указывает, что версия std::string
использует cxx11
ABI.
Обратите внимание, что он по-прежнему отслеживает тип возвращаемого значения и следит за совпадением подписей; просто для этого не используется искаженное имя функции.
МСВК:
Чтобы проверить это, мы можем использовать простую программу, как указано выше.
#include <string>
#include <cstdlib>
std::string foo(int x) { return "hello"; }
//int foo(int x) { return x; }
int main() {
// Assuming object file named "a.obj".
// Pipe to file, because there are a lot of symbols when <string> is included.
system("dumpbin/symbols a.obj > a.txt");
}
Скомпилируйте и запустите с помощью Visual Studio, и a.txt
выведет список содержащихся в нем символов. В зависимости от того, какая из функций раскомментирована, результаты будут такими:
std::string foo(int x) { return "hello"; }
// ?foo@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z
// class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl foo(int)
int foo(int x) { return x; }
// ?foo@@YAHH@Z
// int __cdecl foo(int)
Схема MSVC содержит все объявление, включая то, что не указано явно:
- Имя:
foo@
для ::foo
, затем @
для завершения.
- Тип символа: Все после
@
, завершающего имя.
- Тип и статус члена:
Y
для функции, не являющейся членом.
- Соглашение о вызовах:
A
для __cdecl
.
- Return type:
H
for int
.
?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@
(за которым следует @
для завершения) для std::basic_string<char, std::char_traits<char>, std::allocator<char>>
(сокращенно std::string
).
- Список параметров:
H
для int
(за которым следует @
для завершения).
- Спецификатор исключения:
Z
для throw(...)
; это опущено из разделенных имен, если это не что-то другое, вероятно, потому, что MSVC все равно просто игнорирует его.
Это позволяет ему ныть, если объявления не идентичны в каждой единице компиляции.
Как правило, большинство компиляторов будут использовать одну из этих схем (или иногда их вариант) при работе с *nix или Windows соответственно, но это не гарантируется. Например...
- Clang, насколько мне известно, будет использовать схему GCC для *nix или схему MSVC для Windows.
- Intel C++ использует схему GCC для Linux и Mac и схему MSVC (с некоторыми незначительными вариациями) для Windows.
- У компиляторов Borland и Watcom свои схемы.
- Компиляторы Symantec и Digital Mars обычно используют схему MSVC с небольшими изменениями.
- Старые версии GCC и многие инструменты UNIX используют модифицированную версию схемы искажения cfront.
- И так далее...
Схемы, используемые другими компиляторами, взяты из PDF Agner Fog.
Примечание:
Изучив сгенерированные символы, становится очевидным, что схема искажения GCC не обеспечивает такого же уровня защиты от Макиавелли, как MSVC. Рассмотрим следующее:
// foo.cpp
#include <string>
// Simple wrapper class, to avoid encoding `cxx11 ABI` into the GCC name.
class MyString {
std::string data;
public:
MyString(const char* const d) : data(d) {}
operator std::string() { return data; }
};
// Evil.
MyString foo(int i) { return "hello"; }
// -----
// main.cpp
#include <iostream>
// Evil.
int foo(int);
int main() {
std::cout << foo(3) << '\n';
}
Если мы скомпилируем каждый исходный файл отдельно, то попытаемся связать объектные файлы вместе...
- GCC:
MyString
из-за того, что он не является частью cxx11
ABI, приводит к тому, что MyString foo(int)
искажается как _Z3fooi
, как и int foo(int)
. Это позволяет связать объектные файлы и создать исполняемый файл. Попытка запустить его вызывает segfault.
- MSVC: компоновщик будет искать
?foo@@YAHH@Z
; поскольку вместо этого мы указали ?foo@@YA?AVMyString@@H@Z
, связывание не удастся.
Учитывая это, схема изменения, включающая тип возвращаемого значения, более безопасна, хотя функции нельзя перегружать исключительно из-за различий в типе возвращаемого значения.
person
Justin Time - Reinstate Monica
schedule
24.11.2016