Dependency related linker options
2021-06-13 16:00:00 Author: maskray.me(查看原文) 阅读量:98 收藏

UNDER CONSTRUCTION

-z defs

-z defs (alias --no-undefined) tells the linker to report an error for an unresolved undefined symbol from a relocatable object file. Executable links (-no-pie and -pie) default to -z defs while shared object links (-shared) default to -z undefs.

"Unresolved" means that (a) no other relocatable object file linked into the component provides a definition, and (b) no shared object linked into the component provides a definition.

When an undefined symbol in A is provided by a link-time shared object B, we can say that A depends on B. The linker will record this fact by adding a DT_NEEDED dynamic tag. If B has a DT_SONAME, the DT_NEEDED value is the SONAME; otherwise the DT_NEEDED value is the path of B (either absolute or relative).

I can think of several reasons that the loose default for -shared links was chosen.

  1. ELF supports interposition. Say, a shared object has an undefined symbol. The symbol may be provided by an arbitrary shared object or by the executable at runtime. By not requiring the dependencies to be fully specified, runtime can have flexibility.

  2. There may be mutual references between two shared objects A and B. We cannot break the tie with a regular approach if fully dependencies need to be specified. A variant is that the executable has A as its dependency while A also references symbols from the executable.

For (a), such unbounded flexibility does not quite fit into a build system. With a modular design, the libraries should have well-defined roles and dependencies. We do not substitute an arbitrary shared object for a link-time shared object. When we need such flexibility, we can define an interface and make several shared objects implement the interface.

Having fully specified dependencies makes the shipped shared object A convenient to use. It is likely that an executable link does not use A's dependency B. It would feel awkward if the executable link needs to additionally link against B when linking against A.

If you have read my article about ELF interposition, you should know that having the dependency information makes direct bindings possible, which can improve symbol lookup time for the dynamic loader. No ELF system other than Solaris has implemented direct bindings, though.

  1. indicates a bad layering of libraries. A and B are no longer isolated components. A change in A may affect B and vice versa. The unit testing for A needs to involves B. With archives, you will need --start-group A.a B.a --end-group with GNU ld and gold. Actually merging A and B is often a better strategy.

If we don't merge A and B, http://blog.darlinghq.org/2018/07/mach-o-linking-and-loading-tricks.html mentions that the Mach-O approach for such circular dependencies is usually to link the libraries twice.

1
2
3
4
ld -o libfoo.dylib foo.o -flat_namespace -undefined suppress
ld -o libbar.dylib bar.o -flat_namespace -undefined suppress
ld -o libfoo.dylib foo.o libbar.dylib
ld -o libbar.dylib bar.o libfoo.dylib

The ELF counterpart is:

1
2
3
4
ld -shared foo.o -o foo.so
ld -shared bar.o -o bar.so
ld -shared -z defs foo.o bar.so -o foo.so
ld -shared -z defs bar.o foo.so -o bar.so

A build system may support archives as well as shared objects. An archive is a collection of regular object files, with special archive member selection semantics in the linker. (We will discuss the archive member selection later.) If A.a needs definitions from B.a and you do not supply B.a when linking a dependent executable (ld ... A.a instead of ld ... A.a b.a), you know that you may be welcomed by undefined reference to (GNU ld) or undefined symbol: (LLD). In practice a build system needs to track dependencies for archives.

--no-allow-shlib-undefined

--no-allow-shlib-undefined tells the linker to report an error for an unresolved undefined symbol from a shared object. Executable links (-no-pie and -pie) default to -z defs while shared object links (-shared) default to -z undefs.

E.g. for ld -shared a.o b.so -o a.so, if b.so has an undefined symbol not defined by a.so or b.so's dependencies, the linker will report an error.

gold and ld.lld do not recursively load DT_NEEDED tags. Instead, they report an error only when b.so's DT_NEEDED list is all on the linker command line. Say, b.so depends on c.so and d.so. ld.lld -shared a.o b.so c.so -o a.so will not error for an unresolved undefined symbol in b.so because d.so is not the linker command line. ld.lld -shared a.so b.so c.so d.so -o a.so may error.

If a build system is -z defs clean, it will also be --no-allow-shlib-undefined clean. If a build system cannot use -z defs, --no-allow-shlib-undefined can catch some propagated problems.

1
2
3
4
5
6
7

void f();
void g() { f(); }


void g();
int main() { g(); }

ld b.o a.so will report an error for the undefined symbol f in a.so.

This concept is required to understand --warn-backrefs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
undef = {};
for file in input {
if file is a relocatable object file {
for sym in file's global symbols {
if sym is defined
undef.erase(sym);
else if sym has STB_GLOBAL binding
undef.insert(sym);
}
} else if file is a shared object {
for sym in file's .dynsym {
if sym is defined
undef.erase(sym);
else if sym has STB_GLOBAL binding
undef.insert(sym);
}
} else if file is an archive {
bool cont = true;
while (cont) {
cont = false;
for member in file {
if member is not a regular object file || member is extracted { continue; }

// A member is extracted if it defines a previously undefined symbol.
bool extract = false;
for sym in member's global symbols
if sym is not defined && undef.count(sym)
extract = true;
if !extract { continue; }

cont = true;
for sym in member's global symbols {
if sym is defined
undef.erase(sym);
else if sym has STB_GLOBAL binding
undef.insert(sym);
}
}

// The extraction of one member may cause other members to be extracted.
// Use a loop.
}
}
}

--warn-backrefs


文章来源: http://maskray.me/blog/2021-06-13-dependency-related-linker-options
如有侵权请联系:admin#unsafe.sh