0

How can I force the inlining of a function, but define it in a C++ file ?

This is a question that's been asked in the past, for example here: Moving inline methods from a header file to a .cpp files

The answers there, in short, go as follows: "inline used to mean [remove function call overhead at the expense of .text size], now it means [relax ODR], so don't use inline for anything that's not ODR related, the compiler knows better".

I'm aware of that, however in my somewhat exotic case, I don't care about performance.

I'm programming an embedded device and, should someone break through the other layers of security, I want to make it as obnoxious as possible to reverse engineer this part of the code, and one thing this implies is that I don't want function calls (that aren't called numerous times anyway) to expose the function boundaries, which are natural delimitations of pieces of code that achieve something on their own.

However, I would also like to keep my code orderly and not have code in my header files.

I see that I can use __attribute((force_inline)) to force inlining, but then I get warnings if those functions don't have an inline attribute too: warning: always_inline function might not be inlinable [-Wattributes]

Suppressing the attributes warning is an option, but I'd rather only take it once I'm sure there are no clean way to do this.

Hence the question: how can I have a forcibly inlined function whose declaration is in a header, but definition is in a source file, without suppressing all attributes warnings ? Is that impossible ?

Julien BERNARD
  • 653
  • 6
  • 17
  • You can only inline a function whose source is defined. If the C++ compiler does not know what the actual function is, how can it inline it? And if the source of the function is put into a C++ file, how could the compiler know it when it's compiling a completely different C++ file? Can you explain how you expect this to work? – Sam Varshavchik Apr 17 '22 at 21:52
  • An inline function can be in a .cpp file, but it must be the same file that calls it. – HolyBlackCat Apr 17 '22 at 21:53
  • @SamVarshavchik With link time optimizations – Julien BERNARD Apr 17 '22 at 21:54
  • It sounds like you can put the definition into another header file – Artyer Apr 17 '22 at 21:54
  • Which can only happen after everything is compiled, and it's too late for the compiler to reject anything. Like the actual function turning out to be recursive, making it non-inlinable. – Sam Varshavchik Apr 17 '22 at 21:57
  • You do have a point... The compiler doesn't exactly pass the linker a call graph... I didn't think this through. Anyway yeah I can just make multiple header files, good point. Thanks all for your replies. – Julien BERNARD Apr 17 '22 at 22:09
  • @JulienBERNARD I was using LTO at one time, but I have to admit I don't know its limitations. I gave up on it because of extended link times and humungous additional (discardable) build products. – Paul Sanders Apr 17 '22 at 22:33

1 Answers1

1

Inlining can only be asked. Sometimes a bit forcefully. But you can never guarantee that the function WILL be inlined finally - because reasons, sometimes quite obscure ones.

Here what's MSVC documentation says (I've highlighted the important parts):

The compiler treats the inline expansion options and keywords as suggestions. There's no guarantee that functions will be inlined. You can't force the compiler to inline a particular function, even with the __forceinline keyword. When compiling with /clr, the compiler won't inline a function if there are security attributes applied to the function.

C++ standard says:

No matter how you designate a function as inline, it is a request that the compiler is allowed to ignore: the compiler might inline-expand some, all, or none of the places where you call a function designated as inline.

GCC documentation is a bit less crystal-clear about non-inlinable functions, but cases exists anyway.

The only "real" way to force inlining is quite ugly, since it rely on inlining it before compilation... Yeah, old-style preprocessor macros. The Evil Itself. Or by using a dirty hack with a #include replacing the function call (and inserting C++ code instead)... It may be a bit safer than a macro, regarding double evaluations, but other side-effects can be even worse since it must rely on "global" variables to work.

Does it worth the pain? Probably not. In particular for "obfuscation", because it won't be as "secure" as you think it will be. Yes, an explicit function call is easier to trace. But it won't change anything: reverse engineering don't rely on that to be done. In fact, obfuscation is near never a good (or even working...) solution. I used to think that... a long, very long time ago. I proved to myself that it was near useless. On my own "secured" code. Breaking the code took me much less time than it took me to "protect" it...

Wisblade
  • 1,483
  • 4
  • 13