【问题标题】:How to write and compile c++ program which declares a function which has its definition in the assembly module?如何编写和编译声明一个在汇编模块中定义的函数的c++程序?
【发布时间】:2021-03-17 07:09:00
【问题描述】:

我需要编写一个带有函数定义的汇编程序模块,并将其与我的 C++ 程序链接,其中是在汇编中实现的函数的声明。我不知道如何编写一个 C++ 文件和一个汇编程序模块来正确编译它。

我写了一个 C++ 文件

#include <iostream>
using namespace std;

void copy(unsigned int * source, unsigned int * dest, int n);

int main() {
    unsigned int source[] = {1, 2, 3, 4};
    unsigned int dest[4];
    copy(source, dest, 4);
    return 0;
}

然后我通过命令检查该函数的程序集名称:

g++ -masm=intel -S main.cpp -o file.asm

然后我将找到的结果插入到组装模块中

segment .text
global _Z4copyPjS_i@PLT
_Z4copyPjS_i@PLT:
ret

我想用汇编模块编译 cpp 文件(函数复制的实现在哪里)但我不知道该怎么做? 我试过了:

nasm -felf64 -o copy.o copy.asm
g++ main.cpp -o main.o
g++ -o main copy.o main.o 

但是在第二个命令中,我得到一个错误:

/usr/bin/ld: tmp/ccWFOX8p.o: in function 'main':
main.cpp:(.text+0x4b): undefined reference to 'copy(unsigned int*, unsigned int*, int)'
collect2: error: ld returned 1 exit status

【问题讨论】:

  • 最好在 C++ 代码中将函数声明为extern "C"。这样你就不必处理名称修改了。
  • @Nesrine Hadj Khelil - 请不要将 code formatting 用于 C++ 之类的名称。仅适用于您在 C++ 源代码中实际找到的文本,例如“return 语句”。您的编辑不应该被批准;我认为你的意思是好的,但这实际上并不是一种改进。错误消息的引用而不是代码块也不是更好。 (这没有错,但不值得在编辑中更改,尤其是在没有其他需要修复的情况下。)

标签: c++ assembly g++ x86-64 nasm


【解决方案1】:

@PLT 不是损坏的 asm 符号名称的一部分。 call symbol@plt 表示调用应该通过 PLT(过程链接表)间接调用,以防函数只在共享库中定义。

您的 asm 文件应定义 _Z4copyPjS_i。 (或者如果你不需要重载或成员函数,在原型中使用extern "C",那么你可以定义copy)。


gcc -fPIE(即the default on most current distros)通过 PLT 调用当前编译单元中未定义的函数,否则如果函数仅在共享库中定义,则链接将失败。但是,如果链接器确实在另一个 .o 中找到了该符号,它会直接放松为 call copy,而不是通过 PLT 存根。因此,您不需要使用__attribute__((visibility("hidden"))) 来使同一可执行文件的各个部分之间的函数调用高效。 (https://gcc.gnu.org/wiki/Visibility)。

https://godbolt.org/z/qrMK8P。如果您省略 -fPIE(或明确使用 -fno-pie),您将看到 GCC 仅发出 call _Z4copyPjS_i

如果您使用 gcc -fno-plt (https://godbolt.org/z/7s756d) 编译,“放松”call [QWORD PTR _Z4copyPjS_i@GOTPCREL[rip]]call _Z4copyPjS_i 仍然会发生。但它会短 1 个字节,并且在链接时更改没有重定位信息的其他偏移量为时已晚(例如调用中可能的相对分支)。因此,链接器使用67 地址大小前缀填充,如果您在构建gcc -fPIE -fno-plt foo.cpp asm.o 或其他任何内容后检查objdump -drwC -Mintel,则使其成为addr32 call rel32

【讨论】:

    【解决方案2】:
    g++ main.cpp -o main.o
    

    编译main.cpp 并尝试将其链接到名为main.o 的可执行文件中,但由于没有提供包含copy 实现的文件而失败。只编译使用-c 选项:

    g++ -c main.cpp -o main.o
    

    您可以在一个命令中编译和链接:

    g++ -o main copy.o main.cpp
    

    【讨论】:

      【解决方案3】:

      谢谢@Peter Cordes 和@Timothy Baldwin!

      总结一下: 我应该在 g++ 生成的函数名中丢弃@PLT

      我应该使用选项-c进行编译

      函数名称是_Z4copyPjS_i 编译:

      nasm -felf64 -o copy.o copy.asm
      g++ -c main.cpp -o main.o
      g++ -o main copy.o main.o
      

      【讨论】:

      • 或者您可以将两个 g++ 命令简化为一个,正如 Timothy 所示:g++ -o main copy.o main.cpp 将编译为临时的 .o 并链接它。这是 GCC 的“前端”的一部分。但是,是的,您确实希望避免使用已链接的可执行文件作为另一个链接命令的输入。出于某种原因,它不仅仅是错误,我猜是因为 gcc 创建的可执行文件仍然具有 ELF 节元数据,而不仅仅是程序头中的段。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多