Vector in C++ module causes useless Bad file data GCC output
Asked Answered
F

1

6

TL;DR: GCC 11.2.0 (image f7ea55625e09) + C++20 + <vector>'s std::vector<anything> cause useless output. How to get out something I can work with?

Compilation works in:

  • module cache
  • single file
  • separate module file
In module imported at main.cpp:4:1: import mymodule;
mymodule: error: failed to read compiled module: Bad file data eh???????
mymodule: note: compiled module file is 'gcm.cache/mymodule.gcm' exists, 124 912 Bytes 
mymodule: fatal error: returning to the gate for a mechanical issue ????????
compilation terminated.

For the fatal (gate) I found only these references (1, 2), from which everything looks okay for my case.


I've tried various simple things with the new C++ modules (C++20, GCC 11.2) and it makes me wonder whether I'm just encountering a compiler bug / missing implementation or not getting something very simple.

Here is a simple C++ code with vector<string>, it compiles just fine with basic flags and outputs what's expected:

# create module cache for system headers
for item in iostream string vector
do
    g++ -fmodules-ts -std=c++20 -x c++-system-header $item
done
g++ -Wall -Wextra -Wpedantic -std=c++20 -fmodules-ts main.cpp
// main.cpp
import <iostream>;
import <string>;
import <vector>;

int main() {
    std::vector<std::string> vec = std::vector<std::string>{};
    vec.push_back("Hello");
    vec.push_back("world");

    for (auto& item : vec) {
        std::cout << item << std::endl;
    }
}
$ ./a.out 
Hello
world

Here I move the vector creation into a new function, compiles fine, works fine. Still no separate module except for the system headers.

// main.cpp
import <iostream>;
import <string>;
import <vector>;

std::vector<std::string> create() {
    std::vector<std::string> vec = std::vector<std::string>{};
    vec.push_back("Hello");
    vec.push_back("world");
    return vec;
}

int main() {
    std::vector<std::string> vec = create();

    for (auto& item : vec) {
        std::cout << item << std::endl;
    }
}

And here I move the function to a separate, exported function in a separate module file.

g++ -Wall -Wextra -Wpedantic -std=c++20 -fmodules-ts -c mymodule.cpp
// mymodule.cpp
export module mymodule;
import <string>;
import <vector>;

export std::vector<std::string> create() {
    std::vector<std::string> vec = std::vector<std::string>{};
    vec.push_back("Hello");
    vec.push_back("world");
    return vec;
}

which compiles just fine, but when adding to the main.cpp,

import <iostream>;
import <string>;
import <vector>;
import mymodule;

int main() {
    std::vector<std::string> vec = create();

    for (auto& item : vec) {
        std::cout << item << std::endl;
    }
}

I get only this:

g++ -Wall -Wextra -Wpedantic -std=c++20 -fmodules-ts mymodule.cpp main.cpp
In module imported at main.cpp:4:1: import mymodule;
mymodule: error: failed to read compiled module: Bad file data eh???????
mymodule: note: compiled module file is 'gcm.cache/mymodule.gcm' exists, 124 912 Bytes 
mymodule: fatal error: returning to the gate for a mechanical issue ????????
compilation terminated.
# file gcm.cache/mymodule.gcm
ELF 32-bit LSB no file type, no machine, version 1 (SYSV)
# file gcm.cache/usr/local/include/c++/11.2.0/iostream.gcm
ELF 32-bit LSB no file type, no machine, version 1 (SYSV)
# file a.out
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped                                                                             

And it doesn't seem to be a problem with simple containers nor with the import declarations of the <vector> alone:

// mymodule.cpp
export module mymodule;
import <string>;
import <vector>;

export std::string create() {
    return "world";
}

// main.cpp
import <iostream>;
import <string>;
import <vector>;
import mymodule;

int main() {
    std::string vec = create();
    std::cout << vec << std::endl;
}

And I've tried playing with (and with no real effect):

  • .cpp vs .mpp extensions, though it shouldn't matter
  • clearing the cache before each compilation (ref)
  • global module fragment (7) placement i.e. module; [stuff]; export module mymodule;
  • compiling separately (-c mymodule.cpp + -c main.cpp) to link manually (fails the same way on main.cpp)
  • (re)moving export from the function
  • not calling the function, just importing (import mymodule; to trigger compilation)
  • switching from std::vector<std::string> to std::vector<int> to see whether the template's argument list causes the problem
  • switching from std::vector<std::string> to std::pair<int, int> and then to std::pair<std::string, std::string> (with <utility> header module cache) to see whether just <vector> is broken for me

And it looks like <vector> header is causing the problem. Any ideas how can I pry open GCC to give me something better than "naaah, can't do"? At the least I can generate assembly with -S (3k+ lines) or use hexdump / objdump for gcm.cache/mymodule.gcm and look at the binary, but I'm not sure what to look for because of the useless output.

Edit: It looks like a problem with the architectures perhaps?

  • using -m64 does nothing for the module cache, remains 32bit
  • using -m32 (apk install -y g++-multilib on 64bit) returns the same output

Edit 2: So I rewrote it a bit to make it compatible with Clang 12 (b978a93) with a help of this article but it's not 1:1 and is rather kind of butchering (string_view note), but maybe I'm not seeing the broader picture or something is missing.

I don't think I should be including <string_view> though as that should have been included automatically. Otherwise even if I write my module, I can just start copy-pasting every #include from the implementation until there's none left so I can ensure the file order (then again what'd be the module's point).

// mymodule.cpp
module;
// no proper "import" available yet, so switching to the old includes
#include <string>
#include <vector>
#include <iostream>
export module mymodule;

export std::vector<std::string> create() {
    std::vector<std::string> vec = std::vector<std::string>{};
    vec.push_back("Hello");
    vec.push_back("world");
    return vec;
}

// need to wrap printing for a string_view for some reason
export void printme(std::string &item) {
    std::cout << item << std::endl;
}
// main.cpp
// includes needed for "auto" and usage of those types
// and were needed also for print(create()) call
// so something seems broken over here
#include <vector>
#include <string>
import mymodule;

int main() {
    auto vec = create();
    for (std::string item : vec) {
        // string_view:142:2: note: declaration of
        // 'basic_string_view<_CharT, _Traits>' does not match
        // std::cout << item << std::endl;
        printme(item);
    }
}
clang++ -std=c++20 -c mymodule.cpp -Xclang -emit-module-interface -fimplicit-modules -fimplicit-module-maps -o mymodule.pcm
clang++ -std=c++20 -fprebuilt-module-path=. -fimplicit-modules -fimplicit-module-maps mymodule.cpp main.cpp

So the issue seems to be GCC specific and most likely is a bug judging by the architecture switching (hardcoding/wrong code branch in GCC?). Maybe worth revisiting after >11.2.0.

Farther answered 23/12, 2021 at 1:39 Comment(9)
https://mcmap.net/q/1547977/-how-to-use-modules-in-c-20-using-g/681865Aught
Pure speculation: Could the module have been compiled in x86 mode instead of x64 mode?Kassala
@Aught yeah, I've seen that one and tried everything suggested over there too. :/ Adding to the question as a ref.Farther
@TedLyngmo Yes, it's in 32bit and a.out in 64bit. But that doesn't seem to impact it for the std::pair swap.Farther
What happens if you compile it in x64 mode instead? I'm really not very experienced when it comes to modules, but I expect it'll be closer to what you need.Kassala
@TedLyngmo Seems like a hardcoded arch within GCC to me? -m64 does nothing for the module cache and -m32 returns the same output with Bad file data.Farther
@PeterBadida Darn... Have you tried it with other compilers? modules are pretty new and I expect that the level of standard compliance isn't 100% in every compiler.Kassala
@TedLyngmo I haven't tried with others, but I have found a Docker image with clang 12. Just some simple commands for compiling the module resulted in gthr-default.h and weakref declaration must have internal linkage, so I'll get some sleep and try to check again with a clear head tomorrow. I'll update the question. Maybe it's just GCC specific, perhaps a regression or something. Except possible clang++ alternative, I don't have access to other compilers, so suggestions are welcome. The modules have quite a pull, it'd be nice to have them working. :)Farther
I can confirm this issue on alpine:20220316 with gcc version 11.2.1 20220219 (Alpine 11.2.1_git20220219). Few notes: 1. It is a member of a struct with of std::vector<std::string_view> 2. std::vector<int> is OK 3. std::vector<std::string> is NOK 4. std::vector<empty_class_in_same_module> is NOK 5. using simple_type_alias=std::vector<std::string_view> is NOK 6. template<class t> struct vector_factory {using type=...}; is NOK 7. std::deque<std::string_view> is NOK 8. std::list<std::string_view> is NOK UPDATE: I think it is more related to `std::string_view, std::string, constCrossed
S
3

I'm writing a shared library using C++20 modules (gcc 11.2.0) and have the same "Bad file data" error for std::vector with custom type. In my case, as already mentioned by @balázs-Árva this error occurs for most containers: maps, lists, etc.

Following your references I found this issue and now have a temporary solution with just this line in module.

namespace std _GLIBCXX_VISIBILITY(default){}

This works in my library with other containers. I've looked for a proper solution using compiler options but haven't any success.

Files / commands

// mymodule.cpp
export module mymodule;
import <string>;
import <vector>;

namespace std _GLIBCXX_VISIBILITY(default){}

export std::vector<std::string> create() {
    std::vector<std::string> vec = std::vector<std::string>{};
    vec.push_back("Hello");
    vec.push_back("world");
    return vec;
}
// main.cpp
import <iostream>;
import <string>;
import <vector>;
import mymodule;

int main() {
    std::vector<std::string> vec = create();
    for (auto& item : vec) {
        std::cout << item << std::endl;
    }
}
g++ -fmodules-ts -std=c++20 -x c++-system-header iostream
g++ -fmodules-ts -std=c++20 -x c++-system-header string
g++ -fmodules-ts -std=c++20 -x c++-system-header vector
g++ -fmodules-ts -std=c++20 mymodule.cpp main.cpp
$ ./a.out 
Hello
world

P.S. I can't comment, so posted it as answer, hope it will bring up them.

Snowman answered 25/4, 2022 at 22:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.