【问题标题】:NASM call for external C++ functionNASM 调用外部 C++ 函数
【发布时间】:2018-09-24 09:18:05
【问题描述】:

我正在尝试从 NASM 调用外部 C++ 函数。当我在谷歌上搜索时,我没有找到任何相关的解决方案。
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' 这里有什么问题?谢谢。

【问题讨论】:

  • void kernel_main() 应该是 extern "C" void kernel_main() 以避免 c++ 名称修改。 extern kernel_main 应该是 extern _kernel_main 因为 C 代码生成。
  • AFAIK 从根本上讲是不可能的。 IE。 c++ 没有标准化的 ABI,名称(符号)被破坏。您需要专门标记它们以某种标准方式导出,例如c 风格通过extern "C"。阅读这个:stackoverflow.com/questions/1041866/… BTW。我的意思是不可能在二进制级别上以纯 c++ 方式接口 c++,因为没有,您需要遵循一些其他机制,这是非常可能的。
  • 我所知道的没有标准化语言有标准 ABI - 当然 C 没有:stackoverflow.com/questions/4489012/does-c-have-a-standard-abi
  • extern _kernel_main 将取决于所使用的工具链。如果使用通用的 elf g++ 编译器,则不会有前导下划线。如果工具链针对本机 32 位 Windows 或 MacOS,那就不同了。我建议为 OSDev 工作使用通用 elf 交叉编译器的原因之一正是出于这个原因 - 避免为特定平台生成本机代码的工具链的细微差别。通用 ELF 可执行文件不需要 _ 前缀作为全局可访问符号。

标签: c++ compilation g++ nasm osdev


【解决方案1】:

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

为什么需要重命名?

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

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

那么,我不能在程序集中使用 C++ 重整名称吗?

是的,这是一种解决方案。您可以将readelf -s fileNamekernel_main 的目标文件一起使用。您必须搜索与kernel_main 有一些相似性的符号。一旦你认为你明白了,然后用echo _ZnSymbolName | c++filt 确认它应该输出kernel_main

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

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

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

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

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

extern "C" void kernelMain(void);

这是您的最佳解决方案,因为您的 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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多