【问题标题】:How to .set a .globl symbol in GAS?如何在 GAS 中设置 .global 符号?
【发布时间】:2015-03-28 07:42:29
【问题描述】:

我在一个文件 (original_fun.c) 中定义了一个函数 original_fun,我需要在文件 use_alias.c 中将其称为 global_alias。我尝试了以下方法:

#  set_alias.s
.globl global_alias
.set global_alias, original_fun

// original_fun.c
#include <stdio.h>
void original_fun(int x) 
{
  printf("original_fun(%d)\n", x);
}

// use_alias.c
extern void global_alias(int x);
int main()
{
  global_alias(42);
  return 0;
}

但是符号global_alias没有被导出:

$ as set_alias.s -o set_alias.o
$ clang original_fun.c -c -o original_fun.o
$ clang use_alias.c -c -o use_alias.o
$ clang set_alias.o original_fun.o use_alias.o -o result
use_alias.o: In function `main':
use_alias.c:(.text+0x1d): undefined reference to `global_alias'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

还有objdump 报告:

$ objdump -rt set_alias.o
set_alias.o:     file format elf32-i386
SYMBOL TABLE:
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000         *UND*  00000000 original_fun

(鼓舞人心的例子是original_fun 是一个错位的 C++ 名称,我想以更简洁的名称导出它,以便在汇编程序中轻松使用它。)

GAS 手册 (https://sourceware.org/binutils/docs/as/Set.html) 声明如下:

如果您 .set 一个全局符号,则存储在目标文件中的值是最后存储的值。

这可能与我的问题有关,但我不确定我是否理解正确。

【问题讨论】:

  • 我的答案是使用extern "C" 或重新考虑您的设计。为什么需要从程序集中调用函数?不使用重载会解决问题吗?这是唯一被声明为extern "C" 的重载吗?您可以使用声明为extern "C" 的入口函数吗?如果该函数需要您使用汇编生成的数据,为什么这不在一个可以从original_fun 调用的自包含汇编过程中,而不是像您尝试做的相反的方式?
  • 我需要从汇编中调用函数(并引用常量),因为汇编器是从编译器输出的,而 C++ 是运行时库的语言。即使没有重载,函数的名称也会被破坏,因为编译器永远无法确定以后或在另一个文件中不会有任何重载。

标签: c assembly gnu-assembler


【解决方案1】:

您尝试做的事情不起作用的基本原因是gas 不知道original_fun 的“值”,因为它是一个外部符号。在组装original_fun的引用时,它只能发出重定位指令,这与发出它的实际值不同。

然后.set 所做的是在本地使global_alias 成为original_fun 的一种软别名,这样它的工作原理就与它类似。发生的情况是,在同一文件中每次使用 global_alias 都会将重定位到 original_fun 。然而,这样的机制不能有意义地导出为global_alias 的实际值,因为没有办法在 ELF 中表达这个想法。 (我不知道是否有一些目标文件格式能够将一个符号表示为另一个符号的别名,我也不知道gas 在组装成该格式时是否支持该功能。)

如果您想让global_alias 像您描述的那样工作,.set 指令需要与original_fun 的实际定义位于同一汇编单元中。从技术上讲,如果您使用 GCC,那么您可以使用顶级内联汇编来做到这一点:

void original_fun(int x) 
{
    printf("original_fun(%d)\n", x);
}
asm(".set global_alias, original_fun");

然而,这显然不能移植到不同的编译器。

【讨论】:

  • FWIW,而 ELF 符号表确实无法表达这种“外部别名”又名“间接符号”,无论是 COFF(因此是 Windows)还是 Mach-O(因此是 macOS)support them,并且现代版本的 MASM aka ML 中的 ALIAS 语句可以生成一个。 (GNU 和 LLVM 都不能。)
【解决方案2】:

启发性的例子是 original_fun 是一个错位的 C++ 名称,我想以更简洁的名称导出它,以便在汇编程序中轻松使用它。

作为替代答案,更清洁的解决方案可能是使用extern "C"

extern "C" {
    void original_fun(int x) 
    {
        printf("original_fun(%d)\n", x);
    }
}

这不仅需要更少的源文件,而且 AFAIK 还可以在不同的 C++ 编译器之间移植。

【讨论】:

  • 这就是我现在正在做的事情,但我想避免将我的一半功能放在extern "C" 中,而其余的在我的namespace 中。
  • @honzasp:为什么不呢?既然您无论如何都在为它们创建顶级符号,那么您会失去什么?请注意,您仍然可以将它们放在 namespace 块中;与extern "C"兼容。
  • 如果我在全局命名空间(extern "C")中声明该函数,我需要添加一个前缀以避免可能的名称冲突,而且我发现有些名称带有前缀而有些不带前缀很难看: ) (这是我需要别名的唯一原因)
【解决方案3】:

如果你 .set 一个全局符号,存储在目标文件中的值是 最后存储的值。

阅读本文我相信这意味着您正在尝试将别名设置为无法完成的变量,我会尝试使用但我现在无法测试!

const auto& new_fn_name = old_fn_name;

【讨论】:

  • 引用只是一个伪装的指针,所以汇编标签new_fn_name指向一个字内存,这里保存着old_fn_name的真实地址。
猜你喜欢
  • 2011-10-13
  • 1970-01-01
  • 2021-04-05
  • 2015-07-13
  • 2020-08-17
  • 2021-12-12
  • 1970-01-01
  • 2020-07-02
相关资源
最近更新 更多