NASM 调用外部 C++ 函数
NASM call for external C++ function
我正在尝试从 NASM 调用外部 C++ 函数。当我在 google 上搜索时,我没有找到任何相关的解决方案。
C++
void kernel_main()
{
char* vidmem = (char*)0xb8000;
/* And so on... */
}
NASM
;Some calls before
section .text
;nothing special here
global start
extern kernel_main ;our problem
在 运行 编译这两个文件后,我收到此错误:kernel.asm(.text+0xe): undefined reference to kernel_main'
这里有什么问题?谢谢
到目前为止,还没有从汇编调用 C++ 函数的标准化方法。这是由于称为 name-mangling 的功能。 C++ 编译器工具链不会发出名称与代码中完全相同的符号。因此,您不知道代表用名称 kernel_main
或 kernelMain
编码的函数的符号的名称是什么。
为什么需要名称修改?
您可以在 C++ 中声明具有相同名称但在不同父名称空间下的多个实体(classes、函数、方法、名称空间等)。如果两个名称为局部名称的实体(例如,名称空间 SymbolDomain
中的 class SomeContainer
的局部名称是 SomeContainer
但全局名称是 SymbolDomain::SomeContainer
,这会导致符号冲突,至少可以在此讨论回答,好的)有相同的符号名称。
方法重载也会发生冲突,因此,classes 的方法也会发出(以某种形式)每个参数的类型。为了解决这个问题,C++ 工具链将以某种方式破坏 ELF 二进制对象中的实际名称。
那么,我不能在汇编中使用 C++ 损坏的名称吗?
是的,这是一种解决方案。您可以将 readelf -s fileName
与 kernel_main
的对象文件一起使用。您必须搜索与 kernel_main
具有某种相似性的符号。一旦你认为你得到了它,然后用 echo _ZnSymbolName | c++filt
确认它应该输出 kernel_main
.
您在程序集中使用此名称而不是 kernel_main
。
这个解决方案的问题在于,如果出于某种原因,您更改了参数、return 值或任何其他内容(我们不知道什么会影响名称修改),您的汇编代码可能休息。因此,您必须对此小心。另一方面,这不是一个好的做法,因为你要进入非标准的东西。
请注意,名称修改不是标准化的,并且因工具链而异。通过依赖它,您也坚持使用相同的编译器。
我不能做一些标准化的事情吗?
是的。您可以通过像这样
声明函数 extern "C"
来在 C++ 中使用 C 函数
extern "C" void kernelMain(void);
这是您的情况下的最佳解决方案,因为您的 kernel_main
已经是一个没有父 class 和命名空间的 C 风格函数。请注意,C 函数是用 C++ 编写的,并且仍然使用 C++ 功能(内部)。
其他解决方案包括使用宏间接寻址,如果确实需要,C 函数调用 C++ 函数。像这样的 -
///
/// Simple class containing a method to illustrate the concept of
/// indirection.
///
class SomeContainer
{
public:
int execute(int y)
{
}
}
#define _SepArg_ , // Comma macro, to pass into args, comma not used directly
///
/// Indirection for methods having return values and arguments (other than
/// this). For methods returning void or having no arguments, make something
/// similar).
///
#define _Generate_Indirection_RetEArgs(ret, name, ThisType, thisArg, eargs) \
extern "C" ret name ( ThisType thisArg, eargs ) \
{ \
return thisArg -> name ( eargs ); \
} \
_Generate_Indirection_RetEArgs(int, execute, SomeContainer, x, int y);
我正在尝试从 NASM 调用外部 C++ 函数。当我在 google 上搜索时,我没有找到任何相关的解决方案。
C++
void kernel_main()
{
char* vidmem = (char*)0xb8000;
/* And so on... */
}
NASM
;Some calls before
section .text
;nothing special here
global start
extern kernel_main ;our problem
在 运行 编译这两个文件后,我收到此错误:kernel.asm(.text+0xe): undefined reference to kernel_main'
这里有什么问题?谢谢
到目前为止,还没有从汇编调用 C++ 函数的标准化方法。这是由于称为 name-mangling 的功能。 C++ 编译器工具链不会发出名称与代码中完全相同的符号。因此,您不知道代表用名称 kernel_main
或 kernelMain
编码的函数的符号的名称是什么。
为什么需要名称修改?
您可以在 C++ 中声明具有相同名称但在不同父名称空间下的多个实体(classes、函数、方法、名称空间等)。如果两个名称为局部名称的实体(例如,名称空间 SymbolDomain
中的 class SomeContainer
的局部名称是 SomeContainer
但全局名称是 SymbolDomain::SomeContainer
,这会导致符号冲突,至少可以在此讨论回答,好的)有相同的符号名称。
方法重载也会发生冲突,因此,classes 的方法也会发出(以某种形式)每个参数的类型。为了解决这个问题,C++ 工具链将以某种方式破坏 ELF 二进制对象中的实际名称。
那么,我不能在汇编中使用 C++ 损坏的名称吗?
是的,这是一种解决方案。您可以将 readelf -s fileName
与 kernel_main
的对象文件一起使用。您必须搜索与 kernel_main
具有某种相似性的符号。一旦你认为你得到了它,然后用 echo _ZnSymbolName | c++filt
确认它应该输出 kernel_main
.
您在程序集中使用此名称而不是 kernel_main
。
这个解决方案的问题在于,如果出于某种原因,您更改了参数、return 值或任何其他内容(我们不知道什么会影响名称修改),您的汇编代码可能休息。因此,您必须对此小心。另一方面,这不是一个好的做法,因为你要进入非标准的东西。
请注意,名称修改不是标准化的,并且因工具链而异。通过依赖它,您也坚持使用相同的编译器。
我不能做一些标准化的事情吗?
是的。您可以通过像这样
声明函数extern "C"
来在 C++ 中使用 C 函数
extern "C" void kernelMain(void);
这是您的情况下的最佳解决方案,因为您的 kernel_main
已经是一个没有父 class 和命名空间的 C 风格函数。请注意,C 函数是用 C++ 编写的,并且仍然使用 C++ 功能(内部)。
其他解决方案包括使用宏间接寻址,如果确实需要,C 函数调用 C++ 函数。像这样的 -
///
/// Simple class containing a method to illustrate the concept of
/// indirection.
///
class SomeContainer
{
public:
int execute(int y)
{
}
}
#define _SepArg_ , // Comma macro, to pass into args, comma not used directly
///
/// Indirection for methods having return values and arguments (other than
/// this). For methods returning void or having no arguments, make something
/// similar).
///
#define _Generate_Indirection_RetEArgs(ret, name, ThisType, thisArg, eargs) \
extern "C" ret name ( ThisType thisArg, eargs ) \
{ \
return thisArg -> name ( eargs ); \
} \
_Generate_Indirection_RetEArgs(int, execute, SomeContainer, x, int y);