Search code examples
cgcclinkerldelf

Link an ELF binary with a c program


Given only access to a standalone ELF program I want to be able to call a function within the program from my own program. Let's say the below code is main.c

#include <stdio.h>

extern int mystery(int a,int b);

int main() {
        int a = 0;
        int b = 1;
        printf("mystery(a,b) = %d\n",mystery(a,b));
        return 0;
}

The function mystery exists in some elf file not_my_program. What I'm trying to do is something along the lines of

gcc main.c not_my_program

However this gives me an undefined reference error to mystery . I've looked for methods on forums and found that converting this elf file into a shared object file is not possible. I've also looked into compiling main.c into a relocatable object file with

gcc -c main.c

and then using ld to link the elf with main.o but I could not figure out how to do it. The elf is 32 bit but I've omitted the -m32 flag. If the flag is different for ld please let me know. Any help would be very much appreciated.

edit: output of readelf -h not_my_program

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x10e0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          15116 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         11
  Size of section headers:           40 (bytes)
  Number of section headers:         30
  Section header string table index: 29

Solution

  • This hacky way worked with a very simple case.

    [ aquila ~ ] $ cat 1.c
    int func (int a) { return a * (a-1) ; }
    int main(int argc) { return func (argc) ; }
    [ aquila ~ ] $ cc 1.c
    [ aquila ~ ] $ ./a.out ; echo $?
    0
    [ aquila ~ ] $ readelf -s a.out | grep func
        43: 0000000000400487    19 FUNC    GLOBAL DEFAULT   11 func
    [ aquila ~ ] $ cat 2.c
    #include <stdlib.h>
    static __attribute__((constructor)) void main() {
      int (*func)() = (int (*)())0x0000000000400487;
      exit(func(3));
    }
    [ aquila ~ ] $ cc -fPIC -shared 2.c -o a.so
    [ aquila ~ ] $ LD_PRELOAD=./a.so ./a.out ; echo $?
    6
    

    The caller in 2.c is made into a constructor with an exit so that the main program's main() is not called, in an attempt to limit the execution of the code other than the caller and func() itself. The return value being 6 instead of 0 shows both that the call worked and that the main program's main() did not get called.