I am currently learning C with the book "Learn C The Hard Way" and looked into reverse engineering as well. What I want to do is read through the compiler generated assembly for the C programs I have written. I know the option for gcc here is gcc -S -masm=intel file.c.
My question now is: can I automatically have gcc create the .s file and the .out file using the Makefile?
My previous Makefiles always looked like this:
CC=gcc
CLFAGS=-Wall -g
all: file
clean:
rm -f file
How can I extend my Makefile to make it work?
You could do something like the following:
CC=gcc
CFLAGS=-Wall -Werror -Wextra -O2 -g
SOURCE=file.c
all: binary assembly
binary: $(SOURCE)
$(CC) $(CFLAGS) $(SOURCE)
assembly: $(SOURCE)
$(CC) $(CFLAGS) -fverbose-asm -S -masm=intel $(SOURCE)
clean:
rm -f file.s a.out
And use it like this:
$ make # same as `make all`
$ make all # makes both executable (binary) and assembly
$ make binary # makes binary file only
$ make assembly # makes assembly only
It seems that you can execute other labels from other labels. However
how do they differ from just writing the desired filename like I did?
Remember that Makefiles consist of "rules" with the format:
target: dependencies
system command(s)
See more here.
The make program decides on what should be done based on how you invoke it and how you define your rules. For example, the first rule with target all depends on binary and assembly, so make checks those rules, and carries out the relevant steps (in this case executing gcc).
How does gcc differentiate between them?
The gcc program is a compiler, which is a completely separate program. It is merely invoked by make, just like rm is for the clean rule. It has no involvement in processing the Makefile itself, so it doesn't need to "differentiate" anything -- make does all the processing of the Makefile.
Also, the $() variables you use, do they only exist for gcc or could
you also define them globally in your operating system?
The variables have nothing to do with gcc. The make program parses the Makefile and performs the necessary substitutions before invoking the commands (e.g. gcc).
The variables could be used elsewhere in the Makefile too, for example with the rm command. They are not specific to any command in particular.
You can also make use of environment variables in a Makefile, as explained in this post.
Related
Is it possible to configure automake to generate a Makefile which, in addition to building the .o files and linked binary, also has targets for %.s? I want to be able to review the compiler output in a text format without having to invoke binutils on the .o files.
Specifically, if I have main.c as a source file, I want to be able to run make main.s. The desired recipe would be the same as that for main.o, but using CC1 := $(CC) -S.
The question is a little XY.
You want to be able make the intermediate assembly file foo.s, where
the source file foo.c is one of the sources in an autotooled project, using
a makefile that is generated by the project's ./configure script. You
assume that to do this you must do something to the automake inputs -
the Makefile.ams? - that will cause ./configure to generate Makefiles
that include assembly targets *.s matching all object targets *.o.
Well you could, but then your project would not be a regular autotooled
project as usually distributed, and there is no need to make it irregular
to get what you want.
The GCC option -save-temps
exists to let developers see the intermediate files of compilation - the preprocessor
output, the assembly.
$ gcc -c -o foo.o foo.c
outputs foo.o
$ gcc -save-temps -c -o foo.o foo.c
outputs:
foo.o
foo.i # preprocessed source
foo.s # assembly
As I expect you know, GNU Make receives compiler options from the make-variable
CFLAGS, and automake respects this convention, independently of and in addition to any compiler
options prescribed by the project's autotooling. So, if you would otherwise generate
makefiles with:
$ ./configure ...
then, to add -save-temps to the C compiler options, generate makefiles instead
with:
$ ./configure CFLAGS=-save-temps ...
And if you are already using CFLAGS, e.g.
$ ./configure CFLAGS="-g -O0" ...
then append -save-temps:
$ ./configure CFLAGS="-g -O0 -save-temps" ...
Then,
$ make main.o
will make main.o, main.i and main.s up-to-date.
To disable -save-temps, of course, rerun ./configure, removing it from
the CFLAGS.
If the project involves C++ compilation, then CXXFLAGS affects the C++
compiler in the same way that CFLAGS affects the C compiler. Note that
the generated preprocessed C++ sources will be called *.ii, not *.i.
With -save-temps enabled, make clean will not delete the *.i and *.s
files. You may not care, since compilation will always clobber them. If you
do care, you may take advantage of automake's standard phony target clean-local,
which is supported to let an autotooling maintainer extend the behaviour of
clean. Add the following recipe to the Makefile.am of each source directory
in the project:
clean-local:
$(RM) *.i *.ii *.s
Then update the autotooling and regenerate Makefiles:
$ autoreconf
$ ./configure ...
While the COMPILE variable in the generated Makefile.in is technically an internal detail, and this solution relies on the compiler to understand -c -S, adding:
.c.s:
$(COMPILE) -c -S $<
to the Makefile.am has worked for as long as I've been using the autotools. It might also be convenient to add:
clean-local:
rm -f *.s
I find this useful in development to have a look at the assembly output for specific configure and CC, CFLAGS options.
The COMPILE variable will be defined as something like:
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
Similarly, for C++ source we have:
.cc.s:
$(CXXCOMPILE) -c -S $<
This is my absolute first time ever making a makefile, and I'm really trying to understand the process.
I'm trying to create a very simple makefile for a C++ project whose structure is as follows:
root folder
makefile
readme
src folder
...source files all here...
include folder
...header files for external libraries here...
lib folder
...external lib files all here...
bin folder
...output directory for built executable...
obj folder
...object files all here...
I followed the tutorial here.
Here's my makefile:
IDIR=include .
CC=g++
CFLAGS=-I$(IDIR)
ODIR=bin/obj
LDIR=lib
LIBS=none
SRC=src
_DEPS=hello.h
DEPS=$(patsubst %,$(IDIR)/,%(_DEPS))
_OBJ=file1.o file2.o
OBJ=$(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: $(SRC)/%.cpp $(DEPS)
$(CC) -c -o $# $< $(CFLAGS) # $(LIBS)
test_proj: $(OBJ)
$(CC) -o $# $^ $(CFLAGS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
When I run make on this, I get the following error:
g++ -o .o
g++: fatal error: no input files
compilation terminated.
<builtin>: recipe for target '.o' failed
mingw32-make.exe: *** [.o] Error 1
I'm using GNU Make 3.82.90 built for i686-pc-mingw32, if that matters at all.
Can anyone point out whatever ridiculous error I'm making?
IDIR=include .
is the first problem. Replace it by:
IDIR=include
With your code CFLAGS is expanded as:
-Iinclude .
It does not make sense, I'm afraid. The second problem is:
DEPS=$(patsubst %,$(IDIR)/,%(_DEPS))
which should probably be:
DEPS=$(patsubst %,$(IDIR)/%,$(_DEPS))
and would expand as:
DEPS=include/hello.h
if you fix the first problem, else as:
DEPS=include ./hello.h
which does not make sense neither. The cumulated effect of these two errors are strange recipes (I didn't try to expand them by hand) that probably trigger a make implicit rule with wrong parameters.
IDIR=include .
CC=g++
CFLAGS=-I$(IDIR)
This is wrong. First, for C++ code, use CXX not CC and CXXFLAGS not CFLAGS. Run make -p to understand the builtin rules of your make.
Then -I$(IDIR) does not "distribute" the -I, and IDIR is never used elsewhere. So I suggest to start your Makefile with:
CXX=g++
MY_CXX_LANG_FLAGS= -std=c++11
MY_CXX_WARN_FLAGS= -Wall -Wextra
MY_CXX_INCL_FLAGS= -I. -Iinclude
MY_CXX_MACRO_FLAGS= -DMYFOO=32
### replace with -O2 for a release build below
MY_CXX_OPTIM_FLAGS= -g
CXXFLAGS= $(MY_CXX_LANG_FLAGS) $(MY_CXX_WARN_FLAGS) \
$(MY_CXX_INCL_FLAGS) $(MY_CXX_MACRO_FLAGS)
I won't improve your Makefile, but I do suggest to upgrade to GNU make version 4 if possible (and compiling make 4.1 from its source code is worthwhile in 2015) for that purpose. If possible enable GUILE scripting in it.
If you are forced to use make 3.82 debug your Makefile using remake (with -x); if you can afford a make version 4 use its --trace option
BTW, you might consider using automatic dependencies, that is generating dependencies by passing -M or -MG (etc) flags of g++, see that.
At last, a simple project for a small program (less than a hundred thousands of source lines) might just put all (a few dozens of) its files in the current directory (then the Makefile could be simpler); your proposed directory structure might be arcane for a simple project (but could worth the pain if you have millions of C++ source lines of code). I've given several simple examples of Makefile, e.g. this & that. And GNU make source code itself has a less complex file tree that what you want.
BTW, I strongly disagree with the opinions of that answer (which I did upvote, since it is helpful). I don't feel that GNU make is senile, but I regret that, instead of using recent features available on recent versions (4.x) of make, many people prefer to use complex and arcane Makefile generators (like cmake) instead of coding a clever Makefile (for make version 4 specifically).
At last, you could use other builders, e.g. omake, icmake, ....
Is there a way of telling gcc to use the c99 standard when compiling c files as a default?
I want to avoid giving it the -std=c99 parameter all the time.
I assume I can do this by creating an alias in the .bashrc file, but seems to be rather inelegant.
You may call c99 instead of gcc (wrapper for gcc, if it's available on your system) or try to modify your gcc spec file. More information here: http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Spec-Files.html
Here's an unexpected answer. Use a Makefile!
Pros:
Simply type make to build
All options are automatically handled in the build process.
While you're at it, go ahead and enable all warnings, as is good to do. See this.
Easy to scale up to multiple source files
Can handle multi-step builds involving different tools
Cons:
Another tool to learn, another thing to get wrong.
Consider this source:
#include <stdio.h>
int main() {
printf("Hello!\n");
int x = 4;
printf("%d\n", x);
return 0;
}
You could make a Makefile like this:
(Disclaimer, I don't actually know how to write them)
CC=gcc
CFLAGS=-Wall -pedantic -std=c99
LDFLAGS=
SOURCES=$(wildcard *.c)
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=hello
.PHONY: clean
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $#
.cpp.o:
$(CC) $(CFLAGS) $< -o $#
clean:
rm -f *.o $(EXECUTABLE)
And it builds for me.
Likewise, if you remove the -std=c99, it shouldn't be valid C89 code, and indeed, typing make brings up the build error.
Custom compilation suggests you have at a working knowledge of compilers, standards, and basic flags / options. For that reason, I suggest you set shell variables in your .bashrc, .tcshrc, etc., and use them on the command line.
Since the choice of dialect can have an effect on linking: CC="gcc -std=c99", lets you invoke separate compilation commands using $CC -c -O2 ... foo.c, and is also picked up as the default for configure scripts, etc. Of course, you can always override a configure script with CC="gcc -std=c90" or CC="clang". The same applies to a preferred CFLAGS value, e.g.,
CFLAGS="-pipe -W -Wall -O2 -march=core2"
Allows for $CC $CFLAGS -c foo.c commands, and both environment variables are used by default with configure scripts, unless you explicitly override them. I think this is more useful than aliases. But perhaps I've just grown used to my own setup:)
Both of the proposed solutions are, in my opinion, almost what you want, but neither quite gets there.
Makefile solution
As seen here, by defining variables in your Makefile but not defining targets, you can use the make command like a customized pass-through to GCC. So if you create a Makefile in your "sandbox" directory (or wherever you're compiling outside of a real build system) and define the C*FLAGS vars, you'll essentially get what you want. An example Makefile:
CFLAGS=-Wall -std=c99
CXXFLAGS=-Wall -std=c++14
Now, make foo will turn foo.c into an executable called foo.
If you want to do this trick in multiple directories, put your makefile in a known location--say, ~/sandbox--and create the following alias (or something like it) in your .bashrc:
alias usestdmake="ln -s ~/sandbox/Makefile"
Then you can quickly compile a single file anywhere on your machine:
usestdmake
make foo
This has the added advantage of giving the output executable an appropriate name (foo in this case). It has the disadvantage of disabling tab-completion for your compile command (make fo<tab> does nothing, at least on my system).
Pure bashrc solution
The CC/CFLAGS variables mentioned in Brett Hale's answer are fairly standard, so it might be a good idea to define them in your bashrc. You can then use these variables inside of aliases.
In your .bashrc:
CFLAGS="-Wall -std=c99"
CC=gcc
# Use single-ticks to ensure that the variables are evaluated when the alias is evaluated.
alias mycc='$CC $CFLAGS'
On the command line:
cc foo.c # Compile with default CFLAGS
CFLAGS="$CFLAGS -O2" cc foo.c # Compile with modified CFLAGS
I'm pretty new to Makefiles; thus, I encountered a question for which I can't come up with a good google search to help answer.
I am running a virtual OS which has a distro of fedora setup by someone else. If I construct my own Makefile in a directory, I can setup my .c files to compile however I like. Yet, if I simply run make test, whereby in my directory exists test.c, I will get the following : clang -ggdb3 -std=c99 -Wall -Werror test.c -lcs50 -lm -o test.
My question following this observation was where does this default, seemingly universal, make behavior come from? In other words, where does this Makefile, if it is one, sit on my file system?
make has several predefined implicit rules. Two of which are:
Compiling C programs
n.o is made automatically from n.c with a recipe of the form ‘$(CC) $(CPPFLAGS) $(CFLAGS) -c’.
Linking a single object file
n is made automatically from n.o by running the linker (usually called ld) via the C compiler. The precise recipe used is ‘$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)’.
Note, make is smart enough to effectively concatenate the above two into one rule when it makes sense:
... could be done by using the ‘.o’ object files as intermediates, but it is faster to do the compiling and linking in one step, so that's how it's done.
You can dump the predefined rules with make -pn. e.g.:
$ make -pn -f /dev/null | grep -A3 '^%: %.c$'
make: *** No targets. Stop.
%: %.c
# commands to execute (built-in):
$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $#
$
This goes for GNU make, which normally is the default make implementation on linux.
There's no default Makefile on your file system containing the default rules.
There are however implicit rules built into make that are in effect whether you supply a makefile or not, and what make does when invoked
is documented here.
These rules knows e.g. how to build an executable from a .c source file. You can learn about those implicit rules here,
e.g make has this default rule when building an executable:
n is made automatically from n.o by running the linker (usually called
ld) via the C compiler. The precise recipe used is ‘$(CC) $(LDFLAGS)
n.o $(LOADLIBES) $(LDLIBS)’
Meaning if you run make test it will try to create an executable test from the file test.o, and you can set the respective CC/LDFLAGS/etc. variables that will be used when linking.
And as another implicit rule it can build a .o file from a .c file, so the above will look for test.o, and try to rebuild that using the rule:
n.o is made automatically from n.c with a recipe of the form ‘$(CC)
$(CPPFLAGS) $(CFLAGS) -c’.
I.e. the implicit rules when running make test will first compile test.c and then link test.o using the compiler you specify with the CC envirnment variable(or the default compiler cc) and the various compiler/linker flags if you set then as environment variables.
.
See, the problem is that I'm supposed to use an executable driver program (vdriver) to test the C source file I wrote (myfile.c) containing a collection of methods the driver program will use. I used gcc to compile them together (and also any files they depend on) and then ran "gdb vdriver"
Apparently, I am getting a segfault somewhere in myfile.c. The "dissasemble"-produced assembly code can even display the whole method in assembly and point to which instruction just segfaulted.
However, due to the complexity (and length) of the assembly code, I think it would be much more effective to view this line where the segfault occurred in C.
However, running the command "list *$eip" results in:
No source file for address 0x804a3d3
Does anyone know how to make this work?
Compile with debugging info.
gcc -ggdb -c source.c -o source.o ...
Update: It looks like you're having trouble invoking GCC as well. I suggest writing a Makefile, and taking a quick look through the GCC manual for what -c and -o mean.
CC = gcc
CFLAGS = -ggdb -Wall # or whatever flags you want, read the manual
# List all files, with *.c changed to *.o (Make will figure the rest out)
my_app : file1.o file2.o file3.o file4.o
$(CC) -o my_app $^
# The above line should start with a tab, not spaces
clean :
rm -f my_app *.o
# List dependencies like this (technically optional)
# But if you don't do it, "make" might not re-make things that need it
file1.o : file1.c header.h header2.h
file2.o : file2.c header.h