How to link file generated with --relocatable in a PIE executable? - c

I have a big text file that I want to include in a C program. I could just make it a string literal but it's pretty big and that would be cumbersome. So I'm currently linking like this:
$ ld -r -b binary -o /tmp/stuff.o /tmp/stuff.txt
$ clang -o myprogram main.o /tmp/stuff.o
Objdump output:
$ objdump -t /tmp/stuff.o
/tmp/stuff.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000006 g *ABS* 0000000000000000 _binary__tmp_stuff_txt_size
0000000000000006 g .data 0000000000000000 _binary__tmp_stuff_txt_end
0000000000000000 g .data 0000000000000000 _binary__tmp_stuff_txt_start
In the code, I do this (gotten from this question):
extern char _binary__tmp_stuff_txt_start[];
extern char _binary__tmp_stuff_txt_size[];
int f(void) {
size_t size = (size_t)_binary__tmp_stuff_txt_size;
do_stuff(size, _binary__tmp_stuff_txt_start);
}
Everything works great, but when I compile with GCC instead of Clang, it segfaults. Looking at it in GDB, the size variable initialized like this size_t size = (size_t)_binary__tmp_stuff_txt_size; is garbage. It seems that when GCC links, it passes the -pie flag to ld but Clang doesn't. I could fix this by just passing -no-pie to GCC, but it seems kindof sad that doing something so simple would prevent using PIE. Is there something I should change to make this work?

Related

How to fix undefined reference linker errors when static linking archive with gcc target_clones function multi-versioning

I am using the target_clones GCC attribute for run-time optimized SIMD versions of several functions, some declared static and others used by other objects in the same static library. The latter have declarations, with the target_clones attribute, in a header file. All of the objects build fine and are assembled into a static archive with ar. The final application linking stage fails though when including the static archive, with undefined reference errors to the versioned symbols for the library public functions.
UPDATE: Added a gcc bug report, in case that is what this is: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91664
I created a test application which illustrates this problem here https://github.com/elementgreen/fmv-test
It has 3 make targets. The first one "make works" just compiles the test app all in one step from .c files. The second "make also_works" compiles each .c file into object .o files and then links those with gcc -o. The third target "make borken" does not work and illustrates the problem. Each .c file is compiled into .o object files, then a static archive is created with ar, then gcc is used to link the static archive into the final application.
Here are the contents of the test application I put on github:
main.c:
#include <stdlib.h>
#include <string.h>
#include "fmv-test.h"
#define ARRAY_SIZE 100000
int
main (int argc, char **argv)
{
double *dArray;
dArray = malloc (ARRAY_SIZE * sizeof (double));
memset (dArray, 0, ARRAY_SIZE * sizeof (double));
fmv_test (dArray, ARRAY_SIZE);
return 0;
}
fmv-test.h:
#ifndef __FMV_TEST_H__
#define __FMV_TEST_H__
#define SIMD_CLONE __attribute__ ((__target_clones__ ("avx2","avx","sse4.1","sse2","default")))
double fmv_test (double *dArray, int size) SIMD_CLONE;
#endif
fmv-test.c:
#include "fmv-test.h"
static void internal_func (double *dArray, int size) SIMD_CLONE;
double
fmv_test (double *dArray, int size)
{
double result;
int i;
internal_func (dArray, size);
for (i = 0; i < size; i++)
result += dArray[i];
return result;
}
static void
internal_func (double *dArray, int size)
{
int i;
for (i = 0; i < size; i++)
dArray[i] += 1.0;
}
Makefile:
works:
#echo "This works"
gcc -o fmv-test fmv-test.c main.c
also_works:
#echo "This also works"
gcc -c fmv-test.c
gcc -c main.c
gcc -o fmv-test fmv-test.o main.o
borken:
#echo "This doesn't work"
gcc -c fmv-test.c
gcc -c main.c
ar cr fmv-test.a fmv-test.o main.o
gcc -o fmv-test-borken fmv-test.a
Linking a static archive with gcc function multi-versioning should work. Instead it fails with the following errors:
/usr/bin/ld: fmv-test.a(main.o): in function `fmv_test':
main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x1f): undefined reference to `fmv_test.avx2.0'
/usr/bin/ld: main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x3b): undefined reference to `fmv_test.avx.1'
/usr/bin/ld: main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x57): undefined reference to `fmv_test.sse4_1.2'
/usr/bin/ld: main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x71): undefined reference to `fmv_test.sse2.3'
/usr/bin/ld: main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x7a): undefined reference to `fmv_test.default.4'
collect2: error: ld returned 1 exit status
It seems the object files end up with different numbered endings on the various symbols, but these still manage to link up with the "also_works" target, so I don't think that is the problem (see below output from objdump). Is this a bug in gcc? I'm using version 8.3.0 on Ubuntu 19.04.
objdump -t fmv-test.o | grep fmv_test
0000000000000000 l F .text 0000000000000062 fmv_test.default.9
0000000000000239 l F .text 0000000000000062 fmv_test.avx2.4
000000000000029b l F .text 0000000000000062 fmv_test.avx.5
00000000000002fd l F .text 0000000000000062 fmv_test.sse4_1.6
000000000000035f l F .text 0000000000000062 fmv_test.sse2.7
0000000000000000 l d .text.fmv_test.resolver 0000000000000000 .text.fmv_test.resolver
00000000000003c1 g i .text 0000000000000080 internal_func._GLOBAL___fmv_test.ifunc
0000000000000000 w F .text.fmv_test.resolver 0000000000000080 fmv_test.resolver
0000000000000000 g i .text.fmv_test.resolver 0000000000000080 fmv_test
objdump -t main.o | grep fmv_test
0000000000000000 l d .text.fmv_test.resolver 0000000000000000 .text.fmv_test.resolver
0000000000000000 g i .text.fmv_test.resolver 0000000000000080 fmv_test
0000000000000000 w F .text.fmv_test.resolver 0000000000000080 fmv_test.resolver
0000000000000000 *UND* 0000000000000000 fmv_test.avx2.0
0000000000000000 *UND* 0000000000000000 fmv_test.avx.1
0000000000000000 *UND* 0000000000000000 fmv_test.sse4_1.2
0000000000000000 *UND* 0000000000000000 fmv_test.sse2.3
0000000000000000 *UND* 0000000000000000 fmv_test.default.4

What is *ABS* section and when to use?

// foo.c
int main() { return 0; }
When I compiled the code above I noticed some symbols located in *ABS*:
$ gcc foo.c
$ objdump -t a.out | grep ABS
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000000000 l df *ABS* 0000000000000000 foo.c
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000000000 l df *ABS* 0000000000000000
Looks like they're some debug symbols but isn't debug info are stored in somewhere like .debug_info section?
According to man objdump:
*ABS* if the section is absolute (ie not connected with any section)
I don't understand it since no example is given here.
Question here shows an interesting way to pass some extra symbols in *ABS* by --defsym. But I think it could be easier by passing macros.
So what is this *ABS* section and when would someone use it?
EDIT:
Absolute symbols don't get relocated, their virtual addresses (0000000000000000 in the example you gave) are fixed.
I wrote a demo but it seems that the addresses of absolute symbols can be modified.
// foo.c
#include <stdio.h>
extern char foo;
int main()
{
printf("%p\n", &foo);
return 0;
}
$ gcc foo.c -Wl,--defsym,foo=0xbeef -g
$ objdump -t a.out | grep ABS
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000000000 l df *ABS* 0000000000000000 foo.c
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000000000 l df *ABS* 0000000000000000
000000000000beef g *ABS* 0000000000000000 foo
# the addresses are not fixed
$ ./a.out
0x556e06629eef
$ ./a.out
0x564f0d7aeeef
$ ./a.out
0x55c2608dceef
# gdb shows that before entering main(), &foo == 0xbeef
$ gdb a.out
(gdb) p &foo
$1 = 0xbeef <error: Cannot access memory at address 0xbeef>
(gdb) br main
Breakpoint 1 at 0x6b4: file foo.c, line 7.
(gdb) r
Starting program: /home/user/a.out
Breakpoint 1, main () at foo.c:7
7 printf("%p", &foo);
(gdb) p &foo
$2 = 0x55555555feef <error: Cannot access memory at address 0x55555555feef>
If you look at other symbols you might find an index (or section name if the reader does the mapping for you) in place of *ABS*. This is a section index in the section headers table. It points to the section header of a section the symbol is defined in (or SHN_UNDEF (zero) if it is undefined in the object you are looking at). So the value (virtual address) of a symbol will be adjusted by the same value its containing section is adjusted during loading. (This process is called relocation.) Not so for absolute symbols (having special value SHN_ABS as their st_shndx). Absolute symbols don't get relocated, their virtual addresses (0000000000000000 in the example you gave) are fixed.
Such absolute symbols are sometimes used to store some meta information. In particular, the compiler can create symbols with symbol names equivalent to the names of translation units it compiles. Such symbols aren't needed for linking or running the program, they are just for humans and binary processing tools.
As for your question w.r.t the reason this isn't stored in .debug_info section (and why this info is emitted even though no debug switches were specified), the answer is that it is a separate thing; it is just the symbol table (.symtab). It is also needed for debugging, sure, but it's primary purpose is linking of object (.o) files. By default it is preserved in linked executables/libraries. You can get rid of it with strip.
Much of what I wrote here is in man 5 elf.
I don't think doing what you are doing (with --defsym) is supported/supposed to work with dynamic linking. Looking at the compiler output (gcc -S -masm=intel), I see this
lea rsi, foo[rip]
Or, if we look at objdump -M intel -rD a.out (linking with -q to preserve relocations), we see the same thing: rip-relative addressing is used to get the address of foo.
113d: 48 8d 35 ab ad 00 00 lea rsi,[rip+0xadab] # beef <foo>
1140: R_X86_64_PC32 foo-0x4
The compiler doesn't know that it's going to be an absolute symbol, so it produces the code it does (as for a normal symbol). rip is the instruction pointer, so it depends on the base address of the segment containing .text after the program is mapped into memory by ld.so.
I found this answer shedding light on the proper use-case for absolute symbols.

Is -ffunction-sections -fdata-sections and --gc-sections not working?

I want to remove unused functions from code while compiling. Then I write some code (main.c):
#include <stdio.h>
const char *get1();
int main()
{
puts( get1() );
}
and getall.c:
const char *get1()
{
return "s97symmqdn-1";
}
const char *get2()
{
return "s97symmqdn-2";
}
const char *get3()
{
return "s97symmqdn-3";
}
Makefile
test1 :
rm -f a.out *.o *.a
gcc -ffunction-sections -fdata-sections -c main.c getall.c
ar cr libgetall.a getall.o
gcc -Wl,--gc-sections main.o -L. -lgetall
After run make test1 && objdump --sym a.out | grep get , I only find the next 2 lines output:
0000000000000000 l df *ABS* 0000000000000000 getall.c
0000000000400535 g F .text 000000000000000b get1
I guess the get2 and get3 was removed. But when I open the a.out by vim, I found s97symmqdn-1 s97symmqdn-2 s97symmqdn-3 exists.
Is the function get2 get3 removed really ? How I can remove the symbol s97symmqdn-2 s97symmqdn-3 ? Thank you for your reply.
My system is centos7 and gcc version is 4.8.5
The compilation options -ffunction-sections -fdata-sections and linkage option --gc-sections
are working correctly in your example. Your static library is superfluous, so it can
be simplified to:
$ gcc -ffunction-sections -fdata-sections -c main.c getall.c
$ gcc -Wl,--gc-sections main.o getall.o -Wl,-Map=mapfile
in which I'm also asking for the linker's mapfile.
The unused functions get2 and get3 are absent from the executable:
$ nm a.out | grep get
0000000000000657 T get1
and the mapfile shows that the unused function-sections .text.get2 and .text.get3 in which get2 and get3 are
respectively defined were discarded in the linkage:
mapfile (1)
...
Discarded input sections
...
.text.get2 0x0000000000000000 0xd getall.o
.text.get3 0x0000000000000000 0xd getall.o
...
Nevertheless, as you found, all three of the string literals "s97symmqdn-(1|2|3)"
are in the program:
$ strings a.out | egrep 's97symmqdn-(1|2|3)'
s97symmqdn-1
s97symmqdn-2
s97symmqdn-3
That is because -fdata-sections applies just to the same data objects that
__attribute__ ((__section__("name"))) applies to1, i.e. to the definitions
of variables that have static storage duration. It is not applied to anonymous string literals like your
"s97symmqdn-(1|2|3)". They are all just placed in the .rodata section as usual,
and there we find them:
$ objdump -s -j .rodata a.out
a.out: file format elf64-x86-64
Contents of section .rodata:
06ed 73393773 796d6d71 646e2d31 00733937 s97symmqdn-1.s97
06fd 73796d6d 71646e2d 32007339 3773796d symmqdn-2.s97sym
070d 6d71646e 2d3300 mqdn-3.
--gc-sections does not allow the linker to discard .rodata from the program
because it is not an unused section: it contains "s97symmqdn-1", referenced
in the program by get1 as well as the unreferenced strings "s97symmqdn-2"
and "s97symmqdn-3"
Fix
To get these three string literals separated into distinct data sections, you
need to assign them to distinct named objects, e.g.
getcall.c (2)
const char *get1()
{
static const char s[] = "s97symmqdn-1";
return s;
}
const char *get2()
{
static const char s[] = "s97symmqdn-2";
return s;
}
const char *get3()
{
static const char s[] = "s97symmqdn-3";
return s;
}
If we recompile and relink with that change, we see:
mapfile (2)
...
Discarded input sections
...
.text.get2 0x0000000000000000 0xd getall.o
.text.get3 0x0000000000000000 0xd getall.o
.rodata.s.1797
0x0000000000000000 0xd getall.o
.rodata.s.1800
0x0000000000000000 0xd getall.o
...
Now there are two new discarded data-sections, which contain
the two string literals we don't need, as we can see in the object file:
$ objdump -s -j .rodata.s.1797 getall.o
getall.o: file format elf64-x86-64
Contents of section .rodata.s.1797:
0000 73393773 796d6d71 646e2d32 00 s97symmqdn-2.
and:
$ objdump -s -j .rodata.s.1800 getall.o
getall.o: file format elf64-x86-64
Contents of section .rodata.s.1800:
0000 73393773 796d6d71 646e2d33 00 s97symmqdn-3.
Only the referenced string "s97symmqdn-1" now appears anywhere in the program:
$ strings a.out | egrep 's97symmqdn-(1|2|3)'
s97symmqdn-1
and it is the only string in the program's .rodata:
$ objdump -s -j .rodata a.out
a.out: file format elf64-x86-64
Contents of section .rodata:
06f0 73393773 796d6d71 646e2d31 00 s97symmqdn-1.
[1] Likewise, -function-sections has the same effect as qualifying the
definition of every function foo with __attribute__ ((__section__(".text.foo")))

Moving to different Linux build system, getting error: undefined symbol: stat

This may just be an issue with the build system I am migrating to, but I'll include differences in the two systems and how I encountered the problem.
My old build system is a SLES 10 machine. The gcc/cpp/g++ version is 4.1.0
My new system is on SLES 11 SP4, and the gcc/cpp/g++ version is 4.3.4.
I am building a shared library; building and linking work fine on the new system. However, at load time on the new system, I get the following:
error ./mysharedlib.so: undefined symbol: stat
Since the stat() function is included from /usr/include/sys/stat.h, I looked at glibc on both systems. Old:
# rpm -q -f /usr/include/sys/stat.h
glibc-devel-2.4-31.2
and new:
# rpm -q -f /usr/include/sys/stat.h
glibc-devel-2.11.3-17.95.2
I also looked at objdump output related to stat() on the old system:
# objdump -T mysharedlib.so | grep stat
0000000000000000 D *UND* 0000000000000000 __xstat
# objdump -x mysharedlib.so | grep stat
00000000000e3f8a l F .text 0000000000000024 stat
0000000000000000 *UND* 0000000000000000 __xstat
And the new system:
# objdump -T mysharedlib.so | grep stat
0000000000000000 D *UND* 0000000000000000 stat
0000000000000000 D *UND* 0000000000000000 lstat
# objdump -x mysharedlib.so | grep stat
0000000000000000 *UND* 0000000000000000 stat
0000000000000000 *UND* 0000000000000000 lstat
This tells me that on the old system, stat() was defined as a local function in the .text section of my actual shared object. Stat is undefined in mysharedlib on the new system.
I did find some information on feature_test_macros and thought that might resolve the issue, so I included features.h before stat.h and updated my makefile to define _XOPEN_SOURCE:
cc -D_XOPEN_SOURCE=500
This didn't resolve the problem.
I also tried adding "-lc" to my ld flags to link in libc. This seemed like it should work, since that's where stat() is defined(I think), but it did not.
At this point, I found this StackOverflow question:
Why does -O to gcc cause "stat" to resolve?
So I tried adding -O to my makefile when invoking g++ on the file that calls stat(). This seems to resolve the problem. I probably don't know enough about resolving symbols; however, this seems a bit hack-ish to me. Am I way off base there? If not, what's the correct way to resolve the load time error on the new system?
The problem you are facing is most likely the result of building your shared library with ld. User-level code on UNIX systems should never use ld directly. You should use the compiler driver (g++ in your case) to perform the link instead.
Example:
// t.c
#include <sys/stat.h>
void fn(const char *p)
{
struct stat st;
stat(p, &st);
}
gcc -fPIC -c t.c
ld -shared -o t.so t.o
nm t.so | grep stat
U stat ## problem: this library is not linked correctly
Compare to correctly linked library:
gcc -shared -o t.so t.o
nm t.so | grep stat
0000000000000700 t stat
0000000000000700 t __stat
U __xstat##GLIBC_2.2.5
To find where the above local stat symbol came from, you could do this:
gcc -shared -o t.so t.o -Wl,-y,stat
t.o: reference to stat
/usr/lib/x86_64-linux-gnu/libc_nonshared.a(stat.oS): definition of stat
Finally, the reason U stat disappears with optimization:
gcc -E t.c | grep -A2 ' stat '
extern int stat (const char *__restrict __file,
struct stat *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
gcc -E t.c -O | grep -A2 ' stat '
__attribute__ ((__nothrow__ , __leaf__)) stat (const char *__path, struct stat *__statbuf)
{
return __xstat (1, __path, __statbuf);
That's right: you get different preprocessed source depending on the optimization level.

Is extern optional?

I am sure I am going crazy, but consider the following C code:
// file1.c
int first;
void f(void)
{ first = 2; }
// file2.c
#include <stdio.h>
int first;
void f();
int main(void)
{
first = 1;
f();
printf("%d", first);
}
These two files, for some reason will compile and link together, and print 2. I was always under the impression that unless I labelled one or the other (but not both) definitions of first with extern, this wouldn't compile, and that was in fact the whole point of extern!
It only compiles because first is only declared twice, there are not actually two places in memory but only one. Just initialize the one first with int first=4; and the other with int first=5; and your linker will show you the error, e.g. GCC:
b.o:b.c:(.data+0x0): multiple definition of `_first'
a.o:a.c:(.data+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
Under normal conditions (no extra gcc flags) you should be fine to compile this code as:
gcc file1.c file2.c
What's going to happen is the compiler will see that you have two global variables named the same thing and neither is initialized. Then it will place your uninitialized global variables in the "common" section of the code**. In other words it's going to have only 1 copy of the "first" variable. This happens because the default for gcc is -fcommon
If you were to compile with the -fno-common flag you'd now receive the error you were thinking of:
/tmp/ccZNeN8c.o:(.bss+0x0): multiple definition of `first'
/tmp/cc09s2r7.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status
To resolve this you'd add extern to all but one of the variables.
WARNING:
Now let's say you had two global uninitialized arrays of different sizes:
// file1.c
int first[10];
// file2.c
int first[20];
Well guess what, compiling them with gcc -Wall file1.c file2.c produces no warnings or errors and the variable was made common even though it's differently sized!!!
//objdump from file1.c:
0000000000000028 O *COM* 0000000000000020 first
//objdump from file2.c:
0000000000000050 O *COM* 0000000000000020 first
This is one of the dangers of global variables.
**If you look at an objdump of the *.o files (you have to compile with gcc -c to generate them) you'll see first placed in the common (*COM*) section:
mike#mike-VirtualBox:~/C$ objdump -t file2.o
a.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 file2.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000004 O *COM* 0000000000000004 first
0000000000000000 g F .text 0000000000000039 main
0000000000000000 *UND* 0000000000000000 f
0000000000000000 *UND* 0000000000000000 printf

Resources