Is there a way to detect inline function ODR violations?
Asked Answered
B

2

21

So I have this code in 2 separate translation units:

// a.cpp
#include <stdio.h>
inline int func() { return 5; }
int proxy();
int main() { printf("%d", func() + proxy()); }

// b.cpp
inline int func() { return 6; }
int proxy() { return func(); }

When compiled normally the result is 10. When compiled with -O3 (inlining on) I get 11.

I have clearly done an ODR violation for func().

It showed up when I started merging sources of different dll's into fewer dll's.

I have tried:

  • GCC 5.1 -Wodr (which requires -flto)
  • gold linker with -detect-odr-violations
  • setting ASAN_OPTIONS=detect_odr_violation=1 before running an instrumented binary with the address sanitizer.

Asan can supposedly catch other ODR violations (global vars with different types or something like that...)

This is a really nasty C++ issue and I am amazed there isn't reliable tooling for detecting it.

Pherhaps I have misused one of the tools I tried? Or is there a different tool for this?

EDIT:

The problem remains unnoticed even when I make the 2 implementations of func() drastically different so they don't get compiled to the same amount of instructions.

This also affects class methods defined inside the class body - they are implicitly inline.

// a.cpp
struct A { int data; A() : data(5){} };

// b.cpp
struct A { int data; A() : data(6){} };

Legacy code with lots of copy/paste + minor modifications after that is a joy.

Bresee answered 30/7, 2015 at 11:24 Comment(5)
I would wonder what the use case for having inline functions in a .cpp file would be. I can't think of any possible advantage.Backwater
@Backwater why the inline keyword? legacy code. Also consider this: struct A { A(){} }; - here the constructor is defined inside the struct definition and is implicitly inline. 2 such structs may have identical layout but different inline methods...Bresee
Perhaps the easiest way to find these ODR violations in this legacy code is to remove all inline specifiers from functions defined in .cpp files and examine any resulting linkage errors.Backwater
You may find this article helpful: Devirtualization in C++, part 7 (Enforcing One Definition Rule)Housewares
I love how you use "legacy" to mean "broken". This code was never a good idea.Bolt
F
5

The simplest way to detect such concerns is to copy all the functions into a single compilation unit (create one temporarily if needed). Any C++ compiler will then be able to detect and report duplicate definitions when compiling that file.

Frameup answered 30/7, 2015 at 11:54 Comment(1)
This is my current way of solving such issues but it isn't always easy to make a unity build - lots of legacy code + anonymous statics + preprocessor. Also this doesn't help with symbols from static libraries.Bresee
C
6

The tools are imperfect.

I think Gold's check will only notice when the symbols have different types or different sizes, which isn't true here (both functions will compile to the same number of instructions, just using a different immediate value).

I'm not sure why -Wodr doesn't work here, but I think it only works for types, not functions, i.e. it will detect two conflicting definitions of a class type T but not your func().

I don't know anything about ASan's ODR checking.

Crinose answered 30/7, 2015 at 11:46 Comment(0)
F
5

The simplest way to detect such concerns is to copy all the functions into a single compilation unit (create one temporarily if needed). Any C++ compiler will then be able to detect and report duplicate definitions when compiling that file.

Frameup answered 30/7, 2015 at 11:54 Comment(1)
This is my current way of solving such issues but it isn't always easy to make a unity build - lots of legacy code + anonymous statics + preprocessor. Also this doesn't help with symbols from static libraries.Bresee

© 2022 - 2024 — McMap. All rights reserved.