Get the compiler options from a compiled executable? - c++

It there a way to see what compiler and flags were used to create an executable file in *nix? I have an old version of my code compiled and I would like to see whether it was compiled with or without optimization. Google was not too helpful, but I'm not sure I am using the correct keywords.

gcc has a -frecord-gcc-switches option for that:
-frecord-gcc-switches
This switch causes the command line that was used to invoke the compiler to
be recorded into the object file that is being created. This switch is only
implemented on some targets and the exact format of the recording is target
and binary file format dependent, but it usually takes the form of a section
containing ASCII text.
Afterwards, the ELF executables will contain .GCC.command.line section with that information.
$ gcc -O2 -frecord-gcc-switches a.c
$ readelf -p .GCC.command.line a.out
String dump of section '.GCC.command.line':
[ 0] a.c
[ 4] -mtune=generic
[ 13] -march=x86-64
[ 21] -O2
[ 25] -frecord-gcc-switches
Of course, it won't work for executables compiled without that option.
For the simple case of optimizations, you could try using a debugger if the file was compiled with debug info. If you step through it a little, you may notice that some variables were 'optimized out'. That suggests that optimization took place.

If you compile with the -frecord-gcc-switches flag, then the command line compiler options will be written in the binary in the note section. See also the docs.

Another option is -grecord-gcc-swtiches (note, not -f but -g). According to gcc docs it'll put flags into dwarf debug info. And looks like it's enabled by default since gcc 4.8.
I've found dwarfdump program to be useful to extract those cflags. Note, strings program does not see them. Looks like dwarf info is compressed.

As long as the executable was compiled by gcc with -g option, the following should do the trick:
readelf --debug-dump=info /path/to/executable | grep "DW_AT_producer"
For example:
% cat test.c
int main() {
return 42;
}
% gcc -g test.c -o test
% readelf --debug-dump=info ./test | grep "DW_AT_producer"
<c> DW_AT_producer : (indirect string, offset: 0x2a): GNU C17 10.2.0 -mtune=generic -march=x86-64 -g
Sadly, clang doesn't seem to record options in similar way, at least in version 10.
Of course, strings would turn this up too, but one has to have at least some idea of what to look for as inspecting all the strings in real-world binary with naked eyes is usually impractical. E.g. with the binary from above example:
% strings ./test | grep march
GNU C17 10.2.0 -mtune=generic -march=x86-64 -g -O3

This is something that would require compiler support. You don't mention what compiler you are using but since you tagged your question linux I will assume you are using gcc -- which does not default the feature you're asking about (but -frecord-gcc-switches is an option to perform this).
If you want to inspect your binary, the strings command will show you everything that appears to be a readable character string within the file.

If you still have the compiler (same version) you used, and it is only one flag you're unsure about, you can try compiling your code again, once with and once without the flag. Then you can compare the executables. Your old one should be identical, or very similar, to one of the new ones.

I highly doubt it is possible:
int main()
{
}
When compiled with:
gcc -O3 -ffast-math -g main.c -o main
None of the parameters can be found in the generated object:
strings main | grep -O3
(no output)

Related

g++ arm-none-eabi upgrade from 4.9 to gcc 8.2. Generated binary do not fit any more in flash

I recently updated my Linux laptop from Ubuntu 16.04 to 18.04.
I had a STM32 (Cortex-M4) Makefile based project that compiled correctly with the arm-none-eabi g++ version provided by Ubuntu. The generated file required 47620 bytes in the .text section.
With the Ubuntu upgrade, I have also installed an up-to-date version of gcc (from ARM website). Version is 8.2.1.
When I compile the same project (make clean && make), the generated binary do not fit in flash (97424 bytes required, more than twice!). The project is exactly the same (sources, link script, startup files, Makefile).
The compiler options are: -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -DSTM32F303x8 -DARMCM4 -O0 -g -Wall -fexceptions -Wno-deprecated.
The linker options are -mthumb -mcpu=cortex-m4 -Tstm32f303K8.ld -mfloat-abi=hard -mfpu=fpv4-sp-d16 --specs=nosys.specs -lm -Wl,--start-group -lm -Wl,--end-group -Wl,--gc-sections -Lsys -Xlinker -Map=test.elf.map
When I look at the .Map generated file, all the user functions take approximatively the same size (new version saves 8 bytes!). But after, it includes C++ sepcific parts, and one is more than 26Kb (from map file):
.text 0x00000000080079e8 0x683c /usr/local/gcc-arm-none-eabi-8-2018-q4-major/bin/../lib/gcc/arm-none-eabi/8.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libstdc++.a(cp-demangle.o)
0x000000000800e13c __cxa_demangle
Note: there is no problem with C only projects, only with C++. The library included are the same (gcc 4.9.3 -> armv7e-m/fpu, and gcc 8.2.1 -> thumb/v7e-m+fp/hard):
libm.a libstdc++.a libc.a libnosys.a libgcc.a
Is there a way to get rid of that so that I can compile and flash my (no so old) project?
regards,
I found a solution using the libstdc++_nano (instead of implicit libstc++). With that, the code size is reduced from 84kb to 26kb!
LDFLAGS += -lstdc++_nano
It just works. Thanks #Henrik, #Matthieu and #EOF for your support!
It might be related to exception handling, as std::terminate(), which is used with exceptions, might call the demangling routine. If you don't need exceptions then try disabling them with -fno-exceptions as described here.
Another solution might be to look at the GCC headers:
Demangling routine.
ABI-mandated entry point in the C++ runtime library for demangling.
[...]
returns a pointer to the start of the NUL-terminated demangled
name, or NULL if the demangling fails. The caller is
responsible for deallocating this memory using free.
The prototype is:
char*
__cxa_demangle(const char* __mangled_name, char* __output_buffer,
size_t* __length, int* __status);
So you could probably just supply your own dummy function returning NULL (Given that all library functions are weak, and can be overridden). I'll advise you to look at the disassembled code first though, and find out how and why it is being called in the first place, since it might change behaviour to just discard functionality).
They also give other advise in This forum post, which might be useful for you as well:
Optimize for size with -Os instead of -O0 (possibly add the -Og option instead, if you prefer easily debuggable code, it is often both smaller and faster than -O0).
Optimize at link-time with -flto while compiling and linking.
Maybe disable RTTI if not used.

Code size is doubled when compiling with GCC ARM Embedded?

I've just ported a STM32 microcontroller project from Keil uVision (using Keil ARM Compiler) to CooCox CoIDE (using GCC ARM Embedded compiler).
Problem is, the code size is the double size when compiled in CoIDE with GCC compared to Keil uVision.
How can this be? What can I do?
Code size in Keil: 54632b (.text)
Code size in CoIDE: 100844b (.text)
GCC compiler flags:
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -g2 -Wl,-Map=project.map -Os
-Wl,--gc-sections -Wl,-TC:\arm-gcc-link.ld -g -o project.elf -L -lm
I am suspecting CoIDE and GCC to compile a lot of functions and files, that are present in the project, though aren't used (yet). Is it possible that it compiles whole files even if I only use 1 function out of 20 in there? (even though I have -Os)..
Hard to say which files are really compiled/linked in your final binary from the information you give. I suppose it takes all the C files it finds on your project if you did not explicitly specified which one to compile or if you don't use your own Makefile.
But from the compiler options you give, the linker flag --gc-sections won't do much garbage if you don't have the following compiler flags: -ffunction-sections -fdata-sections. Try to add those options to strip all unused functions and data at link time.
Since the question was tagged with C++, I wonder if you would like to disable exceptions and RTTI. Those take quite a bit of code. Add -fno-exceptions -fno-rtti to linker flags.

Use CXX to query preprocessor defines?

I have a GNUmakefile that respects CXX and CXXFLAGS. It also performs some platform and architecture tests. Currently, the makefile assumes the host and target are the same:
IS_X86 = $(shell uname -m | $(EGREP) -c "i.86|x86|i86|amd64")
In an effort to improve robustness, I want to ask the tools what it is compiling for. I've come up with the following, but I'm not sure it is correct.
$ export CXX=clang++
$ export CXXFLAGS="-DNDEBUG -g2 -O3 -m32"
$ $CXX $CXXFLAGS -dM -E - < /dev/null | egrep "(i386|x86_64)"
#define __i386 1
#define __i386__ 1
#define i386 1
$ export CXX=clang++
$ export CXXFLAGS="-DNDEBUG -g2 -O3"
$ $CXX $CXXFLAGS -dM -E - < /dev/null | egrep "(i386|x86_64)"
#define __x86_64 1
#define __x86_64__ 1
My question is, will the above - with CXX and CXXFLAGS - work reliably to detect a target? Or do I need something else?
Here's the two reasons I ask. First, my experience with Autotools indicates something different. When Autotools performs a test like above, they test CPP, and sometimes CPP or CXX needs to include --isysroot (or other hacks) to get things configured properly.
Second, some toolchains, like Clang, integrate other components (like a preprocessor or assembler), so I can't use CPP directly under all circumstances.
In fact, doing something as simple as $CXX -Wa,-v - </dev/null (ask assembler for its version) results in an "unsupported option" error under Clang when using its integrated assembler. (Cf., With integrated assembler enabled, fail to fetch version string of assembler).
And just in case: this is not an Autools or Cmake project. It does not use Boost or any other libraries. Its a stand alone C++03 project.
My question is, will the above - with CXX and CXXFLAGS - work reliably to detect a target?
The answer is Yes, it will. The preprocessor or compielr driver (passing down to preprocessor) will mostly yield expected target defines with all else being equal. Notable exception is GCC and ARMv8/Aarch64, which is missing a slew of expected defines.
The thing to avoid is uname -m (and friends). Uname reports information on the host, and not the target.

What are some of the most commonly used gcc/g++ flags for information (not just optimization)

I have found -E very useful to see preprocessor output and debug macros, and I have seen -fdump-class-hierarchy to look at the v-tables of a class hierarchy...I know there are flags to dump asm output as well..what are some other widely (or perhaps a bit unknown but very handy) flags akin to these?
Few flags which I like:
-x language: used to compile file with extension other than .c or .cpp
-s - dump asm.
-g - debug build.
gcc -O3 -Q --help=optimizers | grep disabled - will give you all optimization flags which remain diabled even after -O3
Wonderful place for all wonderful options

Compiling previously preprocessed file changes output

I have a source file which I preprocess using the options -E and -P (using GCC 4.1.2 for a vxWorks-based embedded platform). All other options are the same as when I compile the file. These options are:
-Wall
-march=pentium
-nostdinc
-O0
-fno-builtin
-fno-defer-pop
-g
-c
-o
as well as all include-paths. Now when I compile this preprocessed file, the resulting object-file is much smaller (about 30%) than when I compile the original directly. And when I then link the program, the linker complains about missing symbols (all in user-code), which again does not happen when using the original source-file. Why is there a difference? Is there any way to make this work?
You're sure you're not missing any -D defines from your command line? Your result would be consistent with parts not being compiled due to conditionals.
Another possibility (since you don't name the compiler specifically) is that you're using a generic gcc -E rather than the arch-specific cross compiler for your vxWorks environment. The cross-gcc will predefine some variables that you'll need for gcc -E.
When compiling the preprocessed output, try passing the -fpreprocessed option to tell GCC not to preprocess again.
The only difference I can think of is macros that result in expanding to an identifier that's a macro name that has already been expanded - the preprocessor stops expansion at that point, but if you ran the preprocessor again, the identifier would be expanded again. I would have expected any instances of this to probably cause a compiler error, but who knows?