Ajay Brahmakshatriya
Ajay Brahmakshatriya

Reputation: 9203

Create non-PIC shared libraries with ld

I have a bunch of object files that have been compiled without the -fPIC option. So the calls to the functions do not use @PLT. (source code is C and is compiled with clang).

I want to link these object files into a shared library that I can load at runtime using dlopen. I need to do this because I have to do a lot of setup before the actual .so is loaded.

But every time I try to link with the -shared option, I get the error -

relocation R_X86_64_PC32 against symbol splay_tree_lookup can not be used when making a shared object; recompile with -fPIC

I have no issues recompiling from source. But I don't want to use -fPIC. This is part of a research project where we are working on a custom compiler. PIC wouldn't work for the type of guarantees we are trying to provide in the compiler.

Is there some flag I can use with ld so that it generate load time relocating libraries. In fact I am okay with no relocations. I can provide a base address for the library and dlopen can fail if the virtual address is not available.

The command I am using for compiling my c files are equivalent to -

clang -m64 -c foo.c

and for linking I am using

clang -m64 -shared *.o -o foo.so

I say equivalent because it is a custom compiler (forked off clang) and has some extra steps. But it is equivalent.

Upvotes: 6

Views: 2820

Answers (3)

The19thFighter
The19thFighter

Reputation: 101

It is possible to reduce the amount of relocations and generation of PLTs for example using the linker flag -Bsymbolic and to provide an integrated relocation routine. This article also explains very well the intrinsics and caveats about using this flag, as well as it's benefits.

An integrated relocation routine is used for example, when loading an EFI executable built using GNU-EFI. Or for example when building an EFI module for U-Boot. (Although the linker script for ARM32 contains a bug the maintainers of U-Boot refuse to fix, because they have no relocations in their code, so it doesn't even affect them)

It's basically a shared object packed into a PE32 format (see first hyperlink). The PE32 itself does not contain any relocations, however the integrated object does. When the EFI loader executes the GNU-EFI file, the entry point of the EFI (PE32) executable is being jumped to. (This would be the _init function in case of objects opened by dlopen) This function then performs all relocations, that follow.

ARM32:

Entry Point and PE32 Header

ELF Relocation Routine

Linker Script

X86_64:

Entry Point without PE32 Header

ELF Relocation Routine

Linker Script

With these files I managed to link an EFI shared object, with integrated ELF relocations, using lld (not ld, sorry):

ld.lld-12 -nostdlib -zexecstack -znocombreloc -T ${U_BOOT_ROOT}/arch/arm/lib/elf_arm_efi.lds -shared -Bsymbolic -znorelro -s ./target/armv6kz-none-eabihf/debug/libhello_world_arm_efi.a ${U_BOOT_ROOT}/lib/efi_loader/efi_crt0.o ${U_BOOT_ROOT}/lib/efi_loader/efi_reloc.o -o hello_world_arm_efi.so
arm-linux-gnueabi-objcopy -j .header -j .text -j .sdata -j .data -j .dynamic -j .dynsym  -j .rel* -j .rela* -j .reloc -O binary hello_world_arm_efi.so hello_world_arm_efi.efi

I'm convinced you can use the same mechanism to remove the relocations visible to the system loader and use your embedded custom relocator.

Furthermore the amount of relocations is greatly reduced by using -O2 and -Bsymbolic together. Normally, this should be sufficient to avoid most relocations, from my experience.

Upvotes: 0

jxh
jxh

Reputation: 70422

It is not possible to dynamically load your existing non PIC objects with the expectation of it working without problems.

If you cannot recompile the original code to create a proper shared library that supports PIC, then I suggest you create a service executable that links to a static library composed of those objects. The service executable can then provide IPC/RPC/REST API/shared memory/whatever to allow your object code to be used by your program.

Then, you can author a shared library which is compiled with PIC that provides wrapper APIs that launches and communicates with the service executable to perform the actual work.

On further thought, this wrapper API library may as well be static. The dynamic aspect of it is performed by launching the service executable.

Upvotes: 4

LegendofPedro
LegendofPedro

Reputation: 1414

Recompiling the library's object files with the -fpic -shared options would be the best option, if this is possible!


man ld says:

-i Perform an incremental link (same as option -r).

-r
--relocatable

Generate relocatable output---i.e., generate an output file that can in turn serve as input to ld. This is often called partial linking. As a side effect, in environments that support standard Unix magic numbers, this option also sets the output file’s magic number to "OMAGIC". If this option is not specified, an absolute file is produced. When linking C++ programs, this option will not resolve references to constructors; to do that, use -Ur.

When an input file does not have the same format as the output file, partial linking is only supported if that input file does not contain any relocations. Different output formats can have further restrictions; for example some "a.out"-based formats do not support partial linking with input files in other formats at all.

I believe you can partially link your library object files into a relocatable (PIC) library, then link that library with your source code object file to make a shared library.

ld -r -o libfoo.so *.o
cp libfoo.so /foodir/libfoo.so
cd foodir
clang -m32 -fpic -c foo.c
clang -m32 -fpic -shared *.o -o foo.so

Regarding library base address:

(Again from man ld)

--section-start=sectionname=org

Locate a section in the output file at the absolute address given by org. You may use this option as many times as necessary to locate multiple sections in the command line. org must be a single hexadecimal integer; for compatibility with other linkers, you may omit the leading 0x usually associated with hexadecimal values. Note: there should be no white space between sectionname, the equals sign ("="), and org.

You could perhaps move your library's .text section?

--image-base value

Use value as the base address of your program or dll. This is the lowest memory location that will be used when your program or dll is loaded. To reduce the need to relocate and improve performance of your dlls, each should have a unique base address and not overlap any other dlls. The default is 0x400000 for executables, and 0x10000000 for dlls. [This option is specific to the i386 PE targeted port of the linker]

Upvotes: -1

Related Questions