Search code examples
cassemblygnu-assembler

How to .set a .globl symbol in GAS?


I have a function original_fun defined in one file (original_fun.c), and I need to refer to it as global_alias in file use_alias.c. I tried the following:

#  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;
}

But the symbol global_alias is not exported:

$ 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)

And objdump reports:

$ 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

(The motivating example is that original_fun is a mangled C++ name, and I would like to export it under a cleaner name, so that it can be easily used from assembler.)

The GAS manual (https://sourceware.org/binutils/docs/as/Set.html) states the following:

If you .set a global symbol, the value stored in the object file is the last value stored into it.

This might be related to my problem, but I am not sure I understand this correctly.


Solution

  • The basic reason why what you're trying to do doesn't work is because gas doesn't know the "value" of original_fun, as it is an external symbol. When assembling references to original_fun, it can only emit relocation directives, which is different from emitting the actual value of it.

    What .set then does instead is to locally make global_alias a kind-of soft alias for original_fun, so that it works similar to it. What happens is that every use of global_alias in the same file emits a relocation to original_fun instead. However, such a mechanic cannot meaningfully be exported as the actual value of global_alias, as there is no way to express that idea in ELF. (I don't know if there may some object file-format that is capable of expressing a symbol as being an alias for another symbol, nor do I know if gas would support that feature when assembling to that format.)

    If you want to make global_alias work as you describe it, the .set directive would need to be in the same assembly unit as the actual definition of original_fun. Technically, if you use GCC, then you could do this with top-level inline assembly:

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

    That is clearly not portable to different compilers, however.