[PATCH] D65819: [Driver][Bundler] Improve bundling of object files.

Alexey Bataev via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 14 02:54:56 PDT 2019


I said this before that most probably this is the bug in partial linking, made by ld. But clang-offload-bundler is also nit quite correct here since the original host code is actually not unbundled. I'll fix the patch later today to keep the partial linking for the host object, but really unbundle it when required.

Best regards,
Alexey Bataev

> 14 авг. 2019 г., в 5:48, Jonas Hahnfeld via Phabricator <reviews at reviews.llvm.org> написал(а):
> 
> 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