Search
The exercise will be based on following C code. It prints number representation in computer memory. We shall modify the code during the class.
/* Simple program to examine how are different data types encoded in memory */ #include <stdio.h> /* * The macro determines size of given variable and then * prints individual bytes of the value representation */ #define PRINT_MEM(a) print_mem((unsigned char*)&(a), sizeof(a)) void print_mem(unsigned char *ptr, int size) { int i; printf("address = 0x%016lx\n", (long unsigned int)ptr); for (i = 0; i < size; i++) { printf("0x%02x ", *(ptr+i)); // == printf("0x%02x ", ptr[i]); } printf("\n"); } int main() { /* try for more types: long, float, double, pointer */ unsigned int unsig = 5; int sig = -5; /* Read GNU C Library manual for conversion syntax for other types */ /* https://www.gnu.org/software/libc/manual/html_node/Formatted-Output.html */ printf("value = %d\n", unsig); PRINT_MEM(unsig); printf("\nvalue = %d\n", sig); PRINT_MEM(sig); return 0; }
/opt/apo/binrep/print_binrep
To compile the program:
gcc -Wall -pedantic -o print_binrep ./print_binrep.c
Makefile
Make is a build automation tool which builds targets (i.e. executable program) from specified components and sources. Where only one source file needs to be compiled to build executable, manual compiler invocation or use of shell script to invoke short commands sequence is appropriate each time when build is requires. But processing of more files is required for larger projects and they are often of different types – for example, source codes in C and assembler, documentation in XML and perhaps something else. Manually translating each file separately and with another translator and combining the result together is a tedious and error prone large amount of work. That is why the program make was invented. Its basic idea is that it describes how to produce specified file types and determines which files are needed to “make” another file.
The make program works by reading the Makefile file, where the rules for compilation are written, and it invokes compilers, or more precisely system shell lines, commands, as needed.
Makefile consists of four line types:
The most simplified Makefile for simple C language hello world programu compilation follows.
hello: hello.c
It consist of only single line with explicit dependency which specifies that to produce program hello (name without extension is expected to be program executable) source file hello.c is required. The list of filenames or even more generic targets is specified on the left of colon and it is specified that it depends on result of processing components on the right (again files or generic targets) of the colon.
hello
hello.c
The 'make includes set of predefined rules which specify what should be called in above case. If implicit rules do not exists or we need to override them then given Makefile will contain:
'make
hello: hello.c gcc -Wall -o hello hello.c
The second line specifies command to generate target specified in the dependency line. Each command has to start with tabulator character. Each time when make is invoked and file hello is missing or is older than hell.c the command is invoked.
make
hell.c
Implicit rules tell how to generate one type of file from another type. These rules are mostly defined using variables. If we want to change the implicit rule just a bit, we don't need to define it again, but just change the values of the variables that are used in the rules.
CFLAGS = -g -Wall CC = m68k-elf-gcc hello: hello.c
The CFLAGS variable specifies switches which should be passed in addition to filenames to C language compiler. The actual C compiler executable/shell command is specified by CC variable [3].
CFLAGS
CC
For larger project, it is common to extend Makefile to include additional targets:
all: hello clean: rm -f hello hello: hello.c gcc -Wall -g -o hello hello.c .PHONY: clean depend
The all is specified the first and becomes default target controlling execution when make' is invoked without parameters. The clean target is usual name for the target which use during invocation leads to remove/clean of all final and intermediate products of compilation. The special .PHONY'' target is used to denote targets which are not expected to generate file with corresponding name but serve only as targets to invoke other targets nad files builds.
all
make' is invoked without parameters. The
target is usual name for the target which use during invocation leads to remove/clean of all final and intermediate products of compilation. The special
Much more could be written about the make program. It can used to translate even very complex projects, such as the Linux kernel itself. There is usually no need to create complex Makefiles manually. Tools like Meson, CMake, autoconf or some IDE generate Makefiles automatically.
#!/usr/bin/python3 import struct a = 1234.56789 b = -11.1111111111111111111111 c = a + b buf = struct.pack('<f', c) print ('C float LE:' + ' '.join(["{0:02x}".format(b) for b in buf])) print ('C float LE:' + str(struct.unpack('<f', buf)[0])) buf = struct.pack('>f', c) print ('C float BE:' + ' '.join(["{0:02x}".format(b) for b in buf])) print ('C float BE:' + str(struct.unpack('>f', buf)[0])) buf = struct.pack('<d', c) print ('C double LE:' + ' '.join(["{0:02x}".format(b) for b in buf])) print ('C double LE:' + str(struct.unpack('<d', buf)[0])) buf = struct.pack('>d', c) print ('C double BE:' + ' '.join(["{0:02x}".format(b) for b in buf])) print ('C double BE:' + str(struct.unpack('>d', buf)[0]))
See Python struct module for more struct — Interpret bytes as packed binary data.