2

I am using https://github.com/sudar/Arduino-Makefile project with https://github.com/ladislas/Bare-Arduino-Project to compile my Arduino projects from Linux command line. I put as much as possible functionality to libraries for reuse (and use the same libraries from many projects too).

Now I had run to some problems and I am not sure, if I am using the language (C/wiring/...) properly, as a lot of #define macros are used. I think, that seeing the resulting code would help me greeatly in finding, wheather some specific lines are compiled into what I want them to be compiled. I am interested mainly in the library code, even if it would mean to dump full program together with all used standard libraries. (I can find my way in large files.)

I would prefere intermediate files, as I suppose they could contain more info than just dissassembled .HEX file, but if this assumption is not correct, and I can get the same from .HEX, than I can use that way as well.

Question: How to get intermediate files (Ideally both "plain C" after preprocessing all macros AND "plain ASM" after compiling the code) ideally with corresponding lines of original code as comments inside.

Subquestion: In the above, how to disable optimalisation as much as possible, to get the code more verbatim similar to original C (I know, that it would hurt both speed and size and I do not care - I just want to be sure compiler does undertand my code the same way as I myself and if not, then see, where I made mistakes)

Thanks for all suggestions :)

  • seeing the resulting code would help me greeatly in finding, wheather some specific lines are compiled into what I want them to be compiled. - what, exactly, do you "want" them to be compiled into? The compiler generally compiles your C++ code accurately into machine code. Are you suggesting you could find flaws in the way it does that? – Nick Gammon Mar 6 '17 at 6:29
  • Exactly the other way. "Computer does every time exactly what you command it to do and never what you want it to do." I have some idea, I write a code and it does not work. So it means, there are some bugs. Some are in logic, some are in syntax, where I write command, which does something other, than I had in mind, when I wrote it. But seing the generated code means I see the same as computer and then I can see, that this value should not be here but there and should read this way. So that I wrote the command wrong. And should write it different way. Because I made some bad assumptions – gilhad Mar 6 '17 at 7:46
  • For example here stackoverflow.com/questions/42617079/… I had bad types in the code, so it does not worked exactly as I needed. Writing some (uint8_t) here and (uint32_t) there solved problem. Finding where and what to write took much more time, than to do it then. Seeining the generated code can show me, where are bytes loaded to registers and where are converted to int16_t (which I did not want), then shifted, and that the shifts should be in 32-bits, not 16-bits blocks. Faster to read the result code, than to test values – gilhad Mar 6 '17 at 7:51
1

One method I've used for creating assembly listings for Arduino sketches when using the Arduino IDE is as follows:
• Click Verify to compile the sketch
• Locate the the temporary build directory
• Use avr-objdump with -CSI... switches to decode the ELF file

Here are some commands you can put into a shell file to automate that procedure, on a Linux system:

item=*.ino
[ -z "$item" ] && echo ino file not found && exit
BDIR=/tmp/$(ls -t /tmp | egrep -m1 build.*tmp)
BASE=$(basename $item .ino)
avr-objdump -CSI$PWD $BDIR/$BASE.cpp.elf > $BASE.ino.asm

The above assumes the current working directory (ie, $PWD) is the directory with the sketch in it; variable item is the sketch name; variable BASE is the basename of the sketch, with .ino stripped off; $BDIR is the most-recently-used temporary build directory (which, on Ubuntu Linux systems, has a path that starts with /tmp/build); and the resulting deassembly is placed in $BASE.ino.asm for viewing.

To try this manually, as you may need to do on systems where the above shell commands don't work, say

avr-objdump -CSIddd bbb/sss.cpp.elf > sss.ino.asm

in a shell or at a command console, replacing ddd with the sketch's source directory name; bbb with the temporary build directory's name; and sss with the sketch's name.


Edit 2: Using Sudar's Arduino Makefile probably is the best starting point for looking at source code that has been pre-processed, or that has been compiled (into assembly language). This makefile system is fairly easy to install and use.

When you have the makefile system installed and working, you can generate pre-processed code (code that's been run through the pre-processor to incorporate #include files and to expand macros) by adding the following line within a makefile:

CPPFLAGS += -E

The -E switch tells avr-gcc to stop after preprocessing. According to man gcc its effect is:

Stop after the preprocessing stage; do not run the compiler proper. The output is in the form of preprocessed source code, which is sent to the standard output.

To generate assembly language, add the following line within a makefile:

CPPFLAGS += -S

The -S switch tells avr-gcc to stop after compiling. According to man gcc its effect is:

Stop after the stage of compilation proper; do not assemble. The output is in the form of an assembler code file for each non-assembler input file specified. ... By default, the assembler file name for a source file is made by replacing the suffix .c, .i, etc., with .s.

If you are skilled at makefile writing, it may be possible to add a target with rules to generate assembly or pre-processed code for inspection. Actually, Sudar's Arduino Makefile appears to have assembly-listing targets and rules already (as below for .pde; others for .ino and .cpp) although I haven't gotten them to work:

# generated assembly
$(OBJDIR)/%.s: %.pde $(COMMON_DEPS) | $(OBJDIR)
        @$(MKDIR) $(dir $@)
        $(CXX) -x c++ -include $(ARDUINO_HEADER) -MMD -S -fverbose-asm $(CPPFLAGS) $(CXXFLAGS) $< -o $@

[I now see that Edgar Bonet has mentioned this too.]

  • Thanks, the -S flag is nice. Do you know about way to get preprocessed files too? This one dissassemble the compiled code, but I would like also to see the preprocessed code, where macros are expanded, but it is still the text, not binary translated back. (so I can see eq. a=16|4|2, not just loading 22 to some memory location) – gilhad Mar 6 '17 at 8:34
  • @gilhad, I wasn't sure what you meant by preprocessed. See -s or -S flag to avr-gcc, I don't remember which, will look tomorrow – James Waldby - jwpat7 Mar 6 '17 at 8:47
  • accepted & upvoted. Just for record it can be called from command line like CPPFLAGS+=' -E ' make respective CPPFLAGS+=' -S ' make after make clean (to force generating code for everything) – gilhad Mar 7 '17 at 9:11
2

You can get the generated assembly by passing the -S option to avr-g++. The makefile you are using has a rule for that, so you would just type

make build-*/my_file.s

to get the assembly for my_file.cpp.

For the pre-processed C++, you use the -E option. There is no rule for that in the makefile, so you will have to write yo own, or manually invoke the compiler. At a minimum:

avr-g++ -mmcu=my_mcu -E my_file.cpp > my_file.i

You may want to add -DF_CPU=... -DARDUINO=... just like the makefile does when compiling the same file.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.