[PATCH] D65819: [Driver][Bundler] Improve bundling of object files.
Jonas Hahnfeld via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 14 02:48:01 PDT 2019
Hahnfeld added a comment.
Okay, so I wasn't happy with the current explanations because I don't like "fixing" an issue without understanding the problem. Here's a small reproducer:
$ cat main.cpp
#include "test.h"
int main(int argc, char *argv[]) {
m[1] = 2;
return 0;
}
$ cat test.h
#include <map>
#include <vector>
template <typename T>
struct B {
static std::vector<int> v;
virtual void f() {
v.push_back(1);
}
};
struct C : public B<int> {
C() { }
};
template <typename T>
std::vector<int> B<T>::v;
extern std::map<int, int> m;
$ cat test.cpp
#include "test.h"
std::map<int, int> m;
Compiling with `clang++ -c main.cpp`, `clang++ -c test.cpp`, `clang++ main.o test.o -o main` works fine and the resulting executable doesn't crash.
Now if instead partially linking `test.o` like `ld -r test.o -o test.o.ld-r` and then linking the executable with `clang++ main.o test.o.ld-r -o main.ld-r` outputs a binary that crashes at execution because the constructor of `std::map<int> m` is not called.
Digging in the object files reveals the reason:
$ readelf -S test.o | grep -n1 .init_array
281- 0000000000000008 0000000000000000 WAG 0 0 8
282: [138] .init_array INIT_ARRAY 0000000000000000 000009d0
283- 0000000000000008 0000000000000000 WAG 0 0 8
284: [139] .rela.init_array RELA 0000000000000000 00002460
285- 0000000000000018 0000000000000018 G 150 138 8
286: [140] .init_array INIT_ARRAY 0000000000000000 000009d8
287- 0000000000000008 0000000000000000 WA 0 0 8
288: [141] .rela.init_array RELA 0000000000000000 00002478
289- 0000000000000018 0000000000000018 150 140 8
$ readelf -S test.o.ld-r | grep -n1 .init_array
279- 0000000000000078 0000000000000000 A 0 0 4
280: [137] .init_array INIT_ARRAY 0000000000000000 00001270
281- 0000000000000010 0000000000000008 WAG 0 0 8
282: [138] .rela.init_array RELA 0000000000000000 00003b10
283- 0000000000000030 0000000000000018 IG 147 137 8
I haven't further looked into the contents of the sections, but I'd guess that the first `.init_array` in `test.o` contains the constructor for `template <typename T> std::vector<int> B<T>::v`. Because it might need to be merged with other TUs (in this case, it's also present in `main.o`) it is marked with a group flag (`G`). The second `.init_array` in `test.o` is probably the constructor for `std::map<int, int> m` and not marked with a group, but `ld -r` merges these two sections into one `.init_array`, now with a group. So when linking with `main.o` which also contains a constructor for `template <typename T> std::vector<int> B<T>::v`, the linker drops the call to the constructor of `std::map<int, int> m`:
$ readelf -S main | grep -n1 .init_array
43- 0000000000000160 0000000000000000 A 0 0 4
44: [19] .init_array INIT_ARRAY 0000000000006d98 00005d98
45- 0000000000000018 0000000000000008 WA 0 0 8
$ readelf -S main.ld-r | grep -n1 .init_array
43- 0000000000000160 0000000000000000 A 0 0 4
44: [19] .init_array INIT_ARRAY 0000000000006da0 00005da0
45- 0000000000000010 0000000000000008 WA 0 0 8
(note the difference in size of the two `.init_array` sections!)
Further notes:
- Obviously, linking in a different order like `clang++ test.o.ld-r main.o -o main.ld-r2` also results in a working executable, but that's not really a solution.
- Calling `ld -Ur` doesn't change anything:
$ ld -Ur test.o -o test.o.ld-Ur
$ md5sum test.o.ld-*
a4d5cead3209ef191d5c05de63e398de test.o.ld-r
a4d5cead3209ef191d5c05de63e398de test.o.ld-Ur
---
Long story short: This very much looks like a bug in `ld` when using partial linking. So the best thing that Clang can do to (kind of) workaround this problem is ensuring that bundling + unbundling results in the bitwise-same host object file. However, I think we should still use partial linking for easy access to the host object file, even if we don't extract it from there (other tools using it from there have to blame `ld -r`, not `clang-offload-bundler`, that the partially linked object file doesn't correctly call global initializers).
Repository:
rC Clang
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D65819/new/
https://reviews.llvm.org/D65819
More information about the cfe-commits
mailing list