Using make, how do I protect against this behavior with .c files in an assembly project? - makefile

I'm making a super easy NASM x86_64 assembly program mytest.asm
BITS 64
GLOBAL _start
SECTION .text
_start:
mov rax, 60 ; 'exit' system call
mov rdi, 42 ; exit with error code 42
syscall
All this program does is exit with status 42. I made a very simple Makefile for it,
AS=nasm
ASFLAGS=-f elf64
.PHONY: all
all: mytest
%.o : %.asm
$(AS) $(ASFLAGS) -o "$#" "$<"
% : %.o
$(LD) $(LDFLAGS) -o "$#" "$<"
When executed, this works fine,
nasm -f elf64 -o "mytest.o" "mytest.asm"
ld -m elf_x86_64 -o "mytest" "mytest.o"
rm mytest.o
Unless there is a file by the same name with .c, for example here mytest.c
// exits with status 66
int main () {
__asm__ (
"mov $60, %%rax\n\t"
"mov $66, %%rdi\n\t"
"syscall\n\t"
::: "%rax", "%rdi"
);
}
If I run make with the following file the C code actually gets compiled to mytest.
And awkwardly, $(LDARGS) gets sent to $(CC) and not to $(LD). This seems insecure to me, anyone with this very typical Makefile can generate executable from code name by inserting a .c file with the same name?
Is this behavior documented by GNU Make?
GNU Make 4.1
Built for x86_64-pc-linux-gnu
Ideally, the presence of the .c files wouldn't affect the assembly build stream.

This comes from Make’s built-in rules. Make knows how to build executables from C files, so when you ask it to build mytest (which is what make does since you have an all target requiring mytest), it builds up its dependency tree and notes that:
it can build mytest from mytest.c, following its built-in rule;
it can build mytest from mytest.o, following the rule you’ve specified;
it can build mytest.o from mytest.c, following its built-in rule;
it can build mytest.o from mytest.asm, following the rule you’ve specified.
The first rule wins (I’m not sure what the precedence is), and that’s what it does.
You can disable this using the -r option:
make -r mytest
will always use your rules to build the program.
You can also cancel built-in rules either
individually by redefining them:
% : %.c
at the end of your Makefile will disable that built-in rule and result in the behaviour you’re after;
globally by specifying MAKEFLAGS += -r in your Makefile.

Related

what are the correct flags to mix assembly and C without losing .data constants

I am working on a utility in assembler (x86, NASM 2.13, gcc 7.50, Ubuntu 18.X) to be called from C. The problem that I am running into is that the string constant addresses seem to be lost in the making process, so messages are not printing. I can see the string constants in the final binary.
This is the sample assembler code
SECTION .data
error_len_message: db "test", 10
error_len_message_len: equ $-error_len_message
SECTION .text
global test_func
test_func:
mov eax, 4
mov ebx, 1
mov ecx, error_len_message
mov edx, error_len_message_len
int 80H
The header file would be trivial as it would only need the signature used in main.c
#include "....."
int main(void) {
test_func();
return 0;
}
The make file of the executable is as follows:
util_tests: ../asm/utilities/utilities.o
gcc -Wall -o ./build/$# main.c $?
../asm/utilities/utilities.o:
cd ../asm/utilities && make && cd ../../util_tests;
clean:
cd ../asm/utilities && make clean && cd ../../util_tests;
The make file of the utilities is as follows:
utilities.o: test/test.o
ld $? -o $#
test/test.o:
cd test && make && cd ..;
clean:
cd test && make clean && cd ..;
rm *.o
The make file of test assembler code is:
test.o: test.asm
nasm -f elf64 -g -F dwarf $?
clean:
rm *.o
I have no trouble turning this into an executable using assembler only and printing strings defined in .data section. Make file:
test: test.o
ld -o test $^
test.o: *.asm
nasm -f elf64 -g -F stabs $?
clean:
rm *.o
I can successfully define and use variables in the .bss section (not shown for brevity)
The problem, I believe, is in the way I am building the utility. Thanks in advance!
So after adding the pertinent "default rel" to the assembler source file, removing direct calls to int 80H, and adding calls printf, the linker fails because it also tries to relocate "printf#glibc_x.x.x". After trying linker options to ignore unresolved symbols, it seems like a dead end.
So it seems there are only two choices, assemble in 32 bit to avoid problems with "int 80H", or return status/results and let the caller deal with the rest. The following assemble, link, and gdb displays labels and offsets matching correctly
DEFAULT REL
SECTION .text
global silly
silly:
lea r9, [message]
ret
SECTION .data
message: db "A Message", 10
Makefile:
silly.so: silly.o
ld -shared $? -o $#
silly.o: silly.asm
nasm -f elf64 -g -F dwarf $?
clean:
rm *.o
rm *.so
Hopefully, this would be useful to anyone

GNU make generate assembly first, them compile them to .o and link

SOURCE=a.c b.c c.c
ASM=$(patsubst %.c,%.s, $(SOURCE))
all:%.o
gcc -o test $^
$(ASM):%.c
gcc -S -o $# $<
%.o:%.s
gcc -c -o$# $<
I want to generate assembly code (.s) first, then compile the assembly code to object (.o), then link them.
But it seems above makefile code does not work. What is the correct code?
When asking questions, does not work is never very useful... if it worked you probably wouldn't be asking a question! :-) Instead you should always show the command you ran and the output you received (or at least the failing part of the output if it's long). Please cut and paste the actual text rather than paraphrasing messages. Also, including the version of the make program you're using (make --version) and the platform you're running on is often helpful.
Luckily this time we can figure out the problem without this information:
This:
$(ASM):%.c
gcc -S -o $# $<
where ASM is a.s b.s c.s, is not a pattern rule because the targets don't contain a pattern character %. That means the prerequisite %.c is not treated as a pattern, but as an actual file name, literally %.c which obviously doesn't exist.
Similarly, this:
all: %.o
has the same problem: all is a target, so this depends on the literal file named %.o which doesn't exist, and can't be created.
Also as a general rule every recipe that creates a target must create the actual target you told make it would, so this all rule is wrong because the target name is all but the recipe creates the target test.
Finally, it's a very bad idea to name your program test because test is a common UNIX program and a shell built-in, so if you run test it won't do the right thing (if you run ./test it will work).
You want to have all depend on the program you want to build, say mytest, and mytest should depend on the actual .o files:
all: mytest
mytest: $(SOURCE:.c=.o)
gcc -o $# $^
Next, you need to define a pattern rule that knows how to create an assembly file from a source file:
%.s : %.c
gcc -S -o $# $<
That, along with your other pattern rules, is all you need: make will figure it all out from that.
Finally, make has a built-in rule that tells it how to build object files directly from source files. It's best to get rid of this to force make to use your rules; add this to your makefile to delete it:
%.o : %.c

gcc / make - creating make files for creating assembly and linking

To perform an experiment I want to modify the assembly code of the OpenWRT Project (by inserting NOPs between the regular, meaningful code).
Thous I need to create the assembly files (.s files) by compiling with gcc's -S flag in the first run, execute a shell script modifying the assembly files and call the linker in the third step to create the executable binaries. Beside step 2, is there a way to accomplish steps 1 & 3 by an appropriate make file modification/configuration, i.e. create one make file for compiling (creating .s files) and another to conduct linking?
Thanks for enlightenment &
Happy a new year! :)
use this syntax:
target [target...] : [dependent ....]
[ command ...]
example:
%.S: %.c
$(CC) -S $# -c $<
%.o: %.S
$(CC) -o $# -c $<
which means to build the target foo.o make should build foo.S and for foo.S build foo.c
$# means target name
$< means targets first dependency

What is the importance of target in makefile?

I am learning how to create makefile on a Linux distro.
I am using the following code (I know it can be written in a small form, but the long form is intentional) to properly understand the behavior of makefile
test: test.o
cc -o test test.o
test.o: test.c
cc -c test.c
clean:
rm test.o
Now, when I use make and make clean in the shell, they are working as intended.
However, I want to know the importance of target in makefile. Hence, started by changing test.o: test.c line to test2.o: test.c and typed make in the shell; my initial guess was that there would be a file in my home directory called test2.o, but that's not the case, I still see test.o being created again.
So, the above behavior begs my question, what is the important of target component in makefile?
The 'target' is the file which Make checks to determine whether it needs to execute the commands associated with the target at all.
I.e. if you change test.o: test.c to test2.o: test.c, Make sees that test2.o does not exist and hence executes the command cc -c test.c -- which still only creates test.o. Hence, if you re-run make, you will see that the compiler is executed again because test.o still does not exist.
In the original version, test.o: test.c, the compiler will only be executed if test.o does not exist, or if the modification time of test.c is newer than that of test.o.
The target becomes available in the commands section as a variable $#, which can be used to define what gets built.
In your makefile you had:
test2.o: test.c
cc -c test.c
Because you didn't tell the compiler what the output would be as part of the cc command, it created test.o from test.c, which is the default behaviour. If you run cc -c file.c it will generate file.o by default.
You need to specify the destination file as part of the commands run for generating the target, so:
test2.o: test.c
cc -c test.c -o $#
Would cause the compiler to generate the test2.o file appropriately.
At a fundamental level, a makefile is nothing more that a set of targets, dependencies for the targets and the sets of commands for making those targets. You have to ensure that as part of the build process, the final product from a set of commands is the target in order to have a properly functioning makefile.
The compiler doesn't know anything about the fact that it's being run in the makefile.
There are a bunch of automatic rules, pre-created by the default make system. These include rules for making .o files from .c files - it knows that it needs to compile a file using the following rule and commands:
%.o: %.c
# commands to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<
where COMPILE.c:
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
and OUTPUT_OPTION is:
OUTPUT_OPTION = -o $#
CC defaults to cc, CFLAGS defaults to empty, CPPFLAGS defaults to empty and TARGET_ARCH defaults to empty. You can see these definitions using make -p
So the resulting command is:
cc -c -o $# $<
Where $# is the name of the target and $< is the first item in the list of dependencies. This pattern matches all target files called <something>.o where there is an existing file called <something>.c. If there's a request to build test.o as a target then it will compile a file called test.c, because that file exists and matches these rules.
tl;dr
Your test2.o rule is never executed. test.o is created by make's implicit rule.
Let's take this apart.
test.o: test.c
cc -c test.c
This is a rule.
The general syntax for a rule is:
targets : prerequisites
recipee
So, test.o is the target, and test.c the prerequisite.
If:
the target (file) does not exist, or
(one of) the prerequisite(s) is newer than the target,
the recipee is executed (which should, but does not have to, create the target).
So, let's look at your Makefile:
test: test.o
cc -o test test.o
test.o: test.c
cc -c test.c
When you say make test, you want to create the target test. This target has test.o as prerequisite.
For test.o exists another rule, with test.c as prerequisite. So that rule gets checked and executed first (compiling your source to object code), before the test prerequisite is checked, and the recipee run if required (linking your object code to executable format).
Hence, started by changing test.o: test.c line to test2.o: test.c and typed make in the shell; my initial guess was that there would be a file in my home directory called test2.o, but that's not the case, I still see test.o being created again.
No target has a test2.o prerequisite, and you did not ask for that to be build specifically (make test2.o), so the recipee for test2.o is never executed.
But test still has test.o as a prerequisite. As there is no explicit rule for a target of that name in your Makefile, make substitutes it with its implicit rule for creating a .o file from an existing .c file...
The default output file from cc -c test.c is test.o. If you want it to create test2.o, you need to tell it explicitly:
test2.o: test.c
cc -o test2.o -c test.c
cc doesn't know anything about the makefile or what target it's being run from.
The importance of targets is that they're used for finding all the dependencies. So the first rule in your makefile says that test is dependent on test.o: before you can create test, you first need to create test.o, and if test.o has changed, you need to rebuild test.
The commands below the target are expected to do whatever it takes to create the target. But you have to code that explicitly (although there are some macros that can automatically substitute targets and dependencies into the command line -- these are mostly useful when the target contains a wildcard pattern).

Strange make implicit rule

I have written a small makefile for a few simple C programs that compiles them and then tests their execution time:
CC = gcc
CFLAGS = -Wall
PTEST = /usr/bin/time -f "%Us"
ARCH=-march=native
OPTIMIZATION=
NOPTIMIZATION=
%comp : %.c
$(CC) $(CFLAGS) $(NOPTIMIZATION) -o $* $<
$(CC) $(CFLAGS) $(OPTIMIZATION) -o $*_opt $<
$(CC) $(CFLAGS) $(NOPTIMIZATION) $(ARCH) -o $*_arch $<
$(CC) $(CFLAGS) $(OPTIMIZATION) $(ARCH) -o $*_opt_arch $<
%test:
#echo ---$<---
#echo Bez optymalizacji, bez podowania architektury
#$(PTEST) ./$*
#echo Bez optymalizacji, uwzgledniana architektura
#$(PTEST) ./$*_arch
#echo Opcja $(OPTIMIZATION), bez podawania architektury
#$(PTEST) ./$*_opt
#echo Opcja $(OPTIMIZATION), uwzgledniania architektura
#$(PTEST) ./$*_opt_arch
loop%:OPTIMIZATION=-O2
logic%:OPTIMIZATION=-O1
math%:OPTIMIZATION=-O1 -ffast-math
recursive%:OPTIMIZATION=-O2 -foptimize-sibling-calls
recursive%:NOPTIMIZATION=-O2 -fno-optimize-sibling-calls
#all: loopcomp logiccomp mathcomp recursivecomp looptest logictest mathtest recursivetest
loop:loopcomp looptest
clean:
rm -rf loop loop_opt loop_arch loop_opt_arch \
logic logic_opt logic_arch logic_opt_arch \
math math_opt math_arch math_opt_arch \
recursive recursive_opt recursive_arch recursive_opt_arch
When I type make loop it compiles and tests them but then it invokes strange implicit rule that does this:
gcc -Wall loop.c loopcomp looptest -o loop
gcc: error: loopcomp: No such file or directory
gcc: error: looptest: No such file or directory
I know that this is make implicit rule because when I invoke make -r loop everything goes OK. I can't figure it out: which built-in implicit rule is make trying to invoke and how can I override it, preferably without adding -r option when invoking make? If it is possible, I would like to override it or somehow diasable make implicit rules inside makefile.
If you don't want to create a file called "loop" and you just want to be able to say "make loop" as a way to bundle up other targets (like "make all") then you should declare "loop" to be phony and make won't search for implicit rules:
.PHONY: loop
loop: loopcomp looptest
If you don't want to do that but want to ensure that a given target doesn't undergo implicit rule search, then you should declare an explicit rule for it. A simple way to do that is add a do-nothing recipe, like this:
loop: loopcomp looptest
#:
(the ":" command is the shell's "do-nothing" command).
Your default target in the makefile is:
loop: loopcomp looptest
This tells make that to build loop, it must first ensure that loopcomp and looptest are up to date, and then it must find a way to build loop. Since there is a file loop.c, it invokes its default %.c: rule to build loop:
gcc -Wall loop.c loopcomp looptest -o loop
This includes the two files (programs) you told it that loop depends on.
While you have a loop.c, I think you're likely to run into this problem.
There does not seem to be a way to say in the makefile "do not use any built-in rules". If there was, you'd expect it to be a 'Special Built-in Target Name' (§4.8 of the GNU Make manual for version 3.82), such as .DEFAULT.
Your only remaining hope is that declaring .PHONY: loop might suppress this. Otherwise, rewrite the default target rule as:
check-loop: loopcomp looptest
This is a mind-boggling makefile. Porting that to anything other than GNU make will not be trivial.

Resources