I'm following this post, in which it is clearly shown how to add an interpreter to a shared object.
Now I'm trying to do the same but without add the line
const char interp_path[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";
i.e. I would like the code to be
#include <stdio.h>
#include <stdlib.h>
void print_version()
{
printf("Library version 1.0\n");
exit(0);
}
compiled with
gcc -shared -fPIC -Wl,--entry=print_version -o lib.so lib.c
and to add an interpreter to my lib.so
.
patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 lib.so
. This results in the following error:patchelf: cannot find section '.interp'. The input file is most likely statically linked
A
flag [ 1] .interp PROGBITS 0000000000000318 000318 00001c 00 A 0 0 1
I tried
echo -n /lib64/ld-linux-x86-64.so.2 > tempfile
objcopy --add-section .interp=tempfile --set-section-flags .interp=alloc,readonly lib.so libi.so
which complains telling me that objcopy: libi.so: warning: allocated section `.interp' not in segment
. Despite with readelf
I get the correct header section and patchelf
does not complain any more, I cannot get my libi.so
as I want.
Can you help me?
As I said in the comment,
Simply adding the .interp section is not enough and the interpreter will not be found by the loader, because it is not part of a loadable segment, thus is not loaded on memory. Along with the section, you also have to add/update a program header and make it of PT_INTERP type and set its values according to the interpreter string's attributes (which is now located in the .interp section).
Unfortunately I didn't find any useful tool for this purpose when I was writing the comment... You can use libraries like LIEF to add your custom PT_INTERP
program header (I'm not sure if this library supports it though).
If you want to write if from scratch, it will not be conceptually difficult. I assume that your code is Position-Independent (As you said, your target is a shared object). You have to append the new PT_INTERP
program header to the end of the program header table and then update the following headers accordingly (You can extract header information using libbfd
):
Add the sizeof(Elf64_Phdr)
or sizeof(Elf32_Phdr)
to e_entry
and e_shoff
, because the Entry Point and the base of section header table will be shifted after appending the new phdr. Also add 1
to e_phnum
(Which stores the number of program headers).
Because the section and section header's data is usually placed after the program header table, the shift affects the correct offsets for the sections' positions; thus you also have to add the sizeof(Elf64_Phdr)
or sizeof(Elf32_Phdr)
to the sh_offset
field of each of the section headers.
Because the sections' data has shifted, you also have to update the p_offset
field for all of the program headers that use offsets after the program header table. This means you have to skip program headers like PT_PHDR
, whose data is located before the program header table and are not affected by the shift.
To create the program header itself, you can use the program header structure available in elf.h
(Elf64_Phdr
or Elf32_Phdr
). Initialize the structure in this way:
p_type = 3 // Set p_type to PT_INTERP
p_flags = PF_R // Set permission for .interp to Readable
p_offset = (Your .interp offset)
p_vaddr = (Your .interp virtual address)
p_paddr = (Your .interp virtual address)
p_filesz = 28 // strlen(Interpreter string) + 1
p_memsz = 28
p_align = 1
You can then write the initialized structure on a file and then inject it into the end of the program header (As I said above).