NASM 调用外部 C++ 函数

Ond*_*ngr 3 c++ compilation g++ nasm osdev

我正在尝试从 NASM 调用外部 C++ 函数。当我在谷歌上搜索时,我没有找到任何相关的解决方案。
C++

void kernel_main()
{
    char* vidmem = (char*)0xb8000;
    /* And so on... */
}
Run Code Online (Sandbox Code Playgroud)

国家安全管理协会

;Some calls before
section     .text
    ;nothing special here

global start
extern kernel_main ;our problem
Run Code Online (Sandbox Code Playgroud)

运行编译这两个文件后,我收到此错误:kernel.asm(.text+0xe): undefined reference to kernel_main' 这里有什么问题?谢谢。

Shu*_*Pal 5

到目前为止,还没有从汇编中调用 C++ 函数的标准化方法。这是由于一个名为name-mangling的功能。C++ 编译器工具链不会发出名称与代码中完全相同的符号。因此,您不知道代表用名称kernel_main或编码的函数的符号的名称是kernelMain什么。

为什么需要名称修改?

您可以在 C++ 中声明多个具有相同名称的实体(类、函数、方法、命名空间等),但在不同的父命名空间下。如果名称为 local name 的两个实体(例如class SomeContainer,命名空间中的本地名称SymbolDomainSomeContainer但全局名称是SymbolDomain::SomeContainer,至少在这个答案中说话,好吧)具有相同的符号名称,这会导致符号冲突。

方法重载也会发生冲突,因此,每个参数的类型也会(以某种形式)为类的方法发出。为了解决这个问题,C++ 工具链会以某种方式破坏 ELF 二进制对象中的实际名称。

那么,我不能在程序集中使用 C++ 损坏的名称吗?

是的,这是一种解决方案。您可以readelf -s fileName将目标文件用于kernel_main. 您必须搜索与kernel_main. 一旦你认为你得到了它,然后确认echo _ZnSymbolName | c++filt应该输出kernel_main.

您在程序集中使用此名称而不是kernel_main.

此解决方案的问题在于,如果出于某种原因,您更改了参数、返回值或其他任何内容(我们不知道是什么影响了名称修改),您的汇编代码可能会中断。因此,你必须小心这一点。另一方面,这不是一个好习惯,因为您要使用非标准的东西。

请注意,名称修改不是标准化的,并且因工具链而异。通过依赖它,您也坚持使用相同的编译器。

我不能做一些标准化的事情吗?

是的。您可以通过extern "C"像这样声明函数来在 C++ 中使用 C 函数

extern "C" void kernelMain(void);
Run Code Online (Sandbox Code Playgroud)

这是您的最佳解决方案,因为您kernel_main已经是一个没有父类和命名空间的 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);
Run Code Online (Sandbox Code Playgroud)