Skip to content

Commit bbf785e

Browse files
committed
review(coff): name /bigobj, and drop a constant the DATA rule stopped using
Two things a read-through caught. `/bigobj` objects are a different container — machine 0 and `0xFFFF` where the section count would be — and the reader fell through to "unsupported COFF machine 0x0000". True and useless: it blames the reader for a flag the project passed. Named now, with the two ways out (drop /bigobj, or annotate the surface and need no generated .def at all). `kScnMemWrite` was left over from a first version whose DATA rule keyed on writability. It does not: a `const` in `.rdata` is a variable and consumers read it as one, so the rule keys on "not executable" instead. The constant was documenting a decision that is no longer taken.
1 parent f9ca398 commit bbf785e

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

src/build/coff_exports.cppm

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ constexpr std::uint16_t kMachineArm64 = 0xaa64;
115115

116116
constexpr std::uint8_t kSymClassExternal = 2; // IMAGE_SYM_CLASS_EXTERNAL
117117
constexpr std::uint16_t kSymTypeFunction = 0x20; // DTYPE_FUNCTION << 4
118-
constexpr std::uint32_t kScnMemWrite = 0x80000000u; // IMAGE_SCN_MEM_WRITE
119118
constexpr std::uint32_t kScnMemExecute = 0x20000000u; // IMAGE_SCN_MEM_EXECUTE
120119

121120
std::uint16_t rd16(std::span<const std::byte> b, std::size_t off) {
@@ -201,6 +200,23 @@ read_exports(std::span<const std::byte> bytes)
201200
return std::unexpected("not a COFF object: shorter than a file header");
202201

203202
const auto machine = rd16(bytes, 0);
203+
204+
// `/bigobj` objects are a DIFFERENT container: machine 0 and a `0xFFFF`
205+
// where the section count would be, followed by a class GUID and a much
206+
// larger header. Named here rather than left to fall out as "unsupported
207+
// machine 0x0000", which is true and useless — the reader would be blamed
208+
// for a flag the project passed.
209+
if (machine == 0 && bytes.size() >= 4 && rd16(bytes, 2) == 0xFFFF) {
210+
return std::unexpected(
211+
"this is a /bigobj object, whose header layout differs from an "
212+
"ordinary COFF one.\n"
213+
" mcpp's export reader does not parse it. Build the shared library "
214+
"without /bigobj,\n"
215+
" or mark its public surface with __declspec(dllexport) — an "
216+
"annotated library needs\n"
217+
" no generated .def at all.");
218+
}
219+
204220
if (!is_supported_machine(machine)) {
205221
return std::unexpected(std::format(
206222
"unsupported COFF machine 0x{:04x}. mcpp reads i386, amd64, arm, "

tests/unit/test_coff_exports.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,21 @@ TEST(CoffExports, RefusesATruncatedSymbolTable) {
214214
EXPECT_NE(r.error().find("past the end"), std::string::npos);
215215
}
216216

217+
TEST(CoffExports, NamesBigobjRatherThanCallingItAnUnknownMachine) {
218+
// A `/bigobj` object is a different container: machine 0 and 0xFFFF where
219+
// the section count would be. Falling through to "unsupported machine
220+
// 0x0000" is true and useless — it blames the reader for a flag the project
221+
// passed.
222+
auto obj = make_obj({ Sym{ .name = "f" } });
223+
obj[0] = std::byte{0}; obj[1] = std::byte{0};
224+
obj[2] = std::byte{0xFF}; obj[3] = std::byte{0xFF};
225+
auto r = read_exports(obj);
226+
ASSERT_FALSE(r);
227+
EXPECT_NE(r.error().find("bigobj"), std::string::npos);
228+
// And it says what to do, or it is a dead end.
229+
EXPECT_NE(r.error().find("dllexport"), std::string::npos);
230+
}
231+
217232
TEST(CoffExports, RefusesSomethingThatIsNotAnObject) {
218233
std::vector<std::byte> tiny(4, std::byte{0});
219234
EXPECT_FALSE(read_exports(tiny));

0 commit comments

Comments
 (0)