Chapter 2.12: Compilation, Assembling, Linking
and Program Execution
ITSC 3181 Introduction to Computer Architecture
https://passlab.github.io/ITSC3181/
Department of Computer Science
Yonghong Yan
yyan7@uncc.edu
https://passlab.github.io/yanyh/
A Translation Hierarchy for C
2
Compilation Process in C
Compilation process: gcc hello.c -o hello
Constructing an executable image for an application
Multiple stages
Command:
gcc <options> <source_file.c>
Compiler Tool
gcc (GNU Compiler)
man gcc (on Linux m/c)
icc (Intel C compiler)
3
4 Stages of Compilation Process
Preprocessing
gcc -E hello.c -o hello.i
hello.c à hello.i
Compilation (after preprocessing)
gcc -S hello.i -o hello.s
Assembling (after compilation)
gcc -c hello.s -o hello.o
Linking object files
gcc hello.o -o hello
Output à Executable (a.out)
Run à ./hello (Loader)
4
4 Stages of Compilation Process
1. Preprocessing (Those with # …)
Expansion of Header files (#include … )
Substitute macros and inline functions (#define …)
2. Compilation
Generates assembly language, .s file
Verification of functions usage using prototypes
Header files: Prototypes declaration
3. Assembling
Generates re-locatable object file (contains m/c instructions), .o file
nm app.o
0000000000000000 T main
U puts
nm or objdump tool used to view object files
5
4 Stages of Compilation Process (contd..)
4. Linking
Generates executable file (nm tool used to view exe file)
Binds appropriate libraries
Static Linking
Dynamic Linking (default)
Loading and Execution (of an executable file)
Evaluate size of code and data segment
Allocates address space in the user mode and transfers them
into memory
Load dependent libraries needed by program and links them
Invokes Process Manager à Program registration
6
Compiling a C Program
gcc <options> program_name.c
Options:
-----------
-Wall: Shows all warnings
-o output_file_name: By default a.out executable file is
created when we compile our program with gcc. Instead, we
can specify the output file name using "-o" option.
-g: Include debugging information in the binary.
man gcc
Four stages into one
7
Preprocessing
Things with #
#include <stdio.h>
#define REAL float
Others
Processes the C source files BEFORE handing it to compiler.
`Pre`-process
gcc E
cpp
8
File Inclusion
Recall : #include <filename>
#include <foo.h>
System directories
#include “foo.h
Current directories
gcc I/usr/include to specify where to search those
header files
gcc I/usr/include sum_full.c o sum
Preprocessing replaces the line #include <foo.h>” with the
content of the file foo.h
9
Macros
Define and replaced by preprocessing
Every occurrence of REAL will be replaced with float before
compilation.
10
About printf in C
printf(“format string”,vars);
Format string?
“This year is %d\n”
“Your score is %d\n”
Conversion by %
%d : int
%f : float, double
%c : char
%s : char *, string
%e : float, double in scientific form
11
Library
Files
Object
Files
Assembly
Source
Files
C/C++ Source
and Header
Files
Tools and Steps for Program Execution
Makefile
C/C++ Source
and Header
Files
Assembly
Source
Files
Linker
Script
File
User-created files
preprocessor
compiler assembler
Make Utility
Object
Files
Shared
Object
File
Linkable
Image File
Executable
Image File
Link Map
File
Linker and Locator
Library
Files
Archive Utility
12
Code Can be in Assembly Language
Assembly language either is written by a programmer or is
the output of a compiler.
13
High-Level Program, Assembly Code and Binary
14
Hand-On, sum x86_64
A method in assembly
.globl: a global symbol
.type
.cfi_startproc
.cfi_endproc
ret: return
for loop
check i<N, if true continue, else
goto end;
loop body
i++
end
15
https://passlab.github.io/ITSC3181/ex ercises/sum/sum_full.c
https://passlab.github.io/ITSC3181/ex ercises/sum/sum_full_x86.s
Sum, RISC-V
and MIPS
Mainly different
instructions
for loop
check i<N, if true,
continue, else goto end;
loop body
i++
end
16
RISC-V Version MIPS Version
Sum, x86_64
Number of instructions per loop
iteration
Count it
17
CPU Time 𝑠
=
# Instructions
Program
×
# Clock cycles
Instruction
×
Second s
Clock cycle
When to Use Assembly Language
Advantage: Speed, size and predictable
No compiler middle-man
Fit for mission-critical, embedded domain, e.g. space shuttle or
car control
Hybrid approach
Non-critical part in high-level language
Critical part in assembly language
Explore special instructions
E.g. those special-purpose instructions that can do more than
one thing
18
Drawbacks of Assembly Language
Assembly language has many (and more) disadvantages that
strongly argue against its wide-spread use.
Machine-specific code, i.e. assembly code are not portable
Rewrite for new or different architectures
Harder than high level language to write large code or software
Harder to keep a high-level software structure
Harder to read and debug
Most compilers are good enough to convince that you do not need
to write assembly code for general-purpose applications
Except embedded or IoT domain
19
Assembler
Translates file of assembly
language statements into a file of
binary machine instructions and
binary data.
Two main steps:
Find memory address for symbols
(e.g. functions).
Translate each assembly
statement by combining the
numeric equivalents of opcodes,
register specifiers, and labels into
a legal instruction
Binary
Produce object files
20
Object File
21
#include <stdio.h>
int a[10]={0,1,2,3,4,5,6,7,8,9};
int b[10];
int main(int argc, char* argv[]){
int i;
static int k = 3;
for(i = 0; i < 10; i++) {
printf("%d\n",a[i]);
b[i] = k*a[i];
}
}
ELF Format: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
Contents of Object File for the Sample C program
22
Offset Contents Comment
Header section
0 124 number of bytes of Machine code section
4 44 number of bytes of initialized data section
8 40 number of bytes of Uninitialized data section (array b[])
(not part of this object module)
12 60 number of bytes of Symbol table section
16 44 number of bytes of Relocation information section
Machine code section (124 bytes)
20 X code for the top of the for loop (36 bytes)
56 X code for call to printf() (22 bytes)
68 X code for the assignment statement (10 bytes)
88 X code for the bottom of the for loop (4 bytes)
92 X code for exiting main() (52 bytes)
Initialized data section (44 bytes)
144 0 beginning of array a[]
148 1
:
176 8
180 9 end of array a[] (40 bytes)
184 3 variable k (4 bytes)
Symbol table section (60 bytes)
188 X array a[] : offset 0 in Initialized data section (12 bytes)
200 X variable k : offset 40 in Initialized data section (10 bytes)
210 X array b[] : offset 0 in Uninitialized data section (12 bytes)
222 X main : offset 0 in Machine code section (12 bytes)
234 X printf : external, used at offset 56 of Machine code section (14 bytes)
Relocation information section (44 bytes)
248 X relocation information
Some Terms
Object file vs Executable
Object file is the file for binary format of machine instructions,
not linked with others, nor positioned (in memory) for execution
Executable is binary format of object files that are linked and
positioned ready for execution.
Symbol
Names, e.g. global function name, variable name
Library
Archive or package of multiple object files
23
Inspect an ELF Object File or Executable
Executable and Linkable Format (ELF)
https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
readelf and objdump command in Linux to inspect
object/executable file or disassembly
Only objdump can do disassembly
nm command to display symbol information
Try sum_full.o and sum example
sum_full.o is an object file
sum is an executable
24
Linking
Linker (ld command) searches a collection of object files and
program libraries to find nonlocal routines used in a program,
combines them into a single executable file, and resolves
references between routines in different files.
25
Linking Multiple files to make executable file
Two programs, prog1.c and prog2.c for one single task
To make single executable file using following instructions
First, compile these two files with option "-c"
gcc -c prog1.c
gcc -c prog2.c
-c: Tells gcc to compile and assemble the code, but not link.
We get two files as output, prog1.o and prog2.o
Then, we can link these object files into single executable file
using below instruction.
gcc -o prog prog1.o prog2.o
Now, the output is prog executable file.
We can run our program using
./prog
26
Linking with other libraries
Normally, compiler will read/link libraries from /usr/lib
directory to our program during compilation process.
Library are precompiled object files
To link our programs with libraries like pthreads and realtime
libraries (rt library).
gcc <options> program_name.c -lpthread -lrt
-lpthread: Link with pthread library à libpthread.so file
-lrt: Link with rt library à librt.so file
Option here is "-l<library>"
Another option "-L<dir>" used to tell gcc compiler search for
library file in given <dir> directory.
27
Compile Multiple Files and Link to One Executable
Split the sum_full.c into two files
sum.c that only contains the definition of sum method
Also the “#define REAL float line on top
Remove the sum definition from sum_full.c, but still keep sum
method declaration (referred too as function signature)
Compile both together and generate sum executable
Compile in one step: gcc sum_full.c sum.c -o sum
The command compiles each *.c file one by one into object
files and then link the two object files into one executable
Compile in multiple steps: compile each .c file one by one
and link together
28
Compile in One Step
29
Compile in Multiple Steps
30
Try readelf
31
Try objdump for both object file and executable
32
objdump -D” to disassembly: convert binary
object code back to symbolic assembly code
33
nm: list symbols from
object files
T: define a symbol
U: undefined symbol
Linker to link
Address are relative
34
Static Linking
If multiple program want to use read_timer functions
They all include the full definition in their source code
Duplicate: If the function changes, we need to change each file
Separate reader_timer in a new file, compile and statically linked
with other object files to create executables
Duplicate the same object in multiple executables.
Dynamic linking at the runtime
Create a dynamic library that provides reader_timer
implementation
Tell ld to link the library at the runtime
Runtime load and link them on the fly and execute
35
Static Library vs Shared (Dynamic) Library
Static library needs to be duplicated in every executable
Bigger code size, better optimized
Shared library are loaded on the fly during the execution
Smaller code size, performance hits of loading shared memory
Combine both
36
Hands-On for dynamic linking
Sum example for static and dynamic linking: from sum.c and
sum_full.c created in the last exercise,
Create a new file read_timer.c that includes the read_timer and
read_timer_ms definition in the file
Leave only the read_timer and read_timer_ms declaration in the
sum_full.c
They are the interface of the two methods.
Compile read_timer.c into a dynamic library
The library name is my_read_timer, and the library file is
libmy_read_timer.so. You can choose any name.
Compile sum.c and sum_full.c and link with lib my_read_timer
gcc sum_full.c sum.c -o sum -L. -lmy_read_timer
Use ldd command to list dependent libraries
37
Build Steps with Dynamic Library
38
-L<>: to tell where to find the library
file, in this case, the current folder (.)
-l<...>: to tell the library file name, which
will be expanded to lib<>.so file
Linking error: cannot find
reader_timer
implementation when linking
from sum_full.o
Linking error: do not know where
to find the libread_timer.so file.
ldd command to list the dependent libraries
39
Loading a File for Execution
Steps:
It reads the executable’s header to determine the
size of the text and data segments.
It creates a new address space for the program. is
address space is large enough to hold the text and
data segments, along with a stack segment (see
Section A.5).
It copies instructions and data from the executable
into the new address space.
It copies arguments passed to the program onto
the stack.
It initializes the machine registers. In general, most
registers are cleared, but the stack pointer must be
assigned the address of the rst free stack location
(see Section A.5).
It jumps to a start-up routine that copies the
program’s arguments from the stack to registers
and calls the program’s main routine. If the main
routine returns, the start-up routine terminates
the program with the exit system call.
40
Memory Layout of A Process
ABI
41
MIPS architecture process memory
X86 architecture process memory
ELF format of an executable
Linux Process Memory in 32-bit System (4G space)
Code (machine instructions)
à
Text segment
Static variables
à
Data or BSS segment
Function variables
à
stack (i, A[100] and B)
A is a variable that stores memory address, the memory for As 100 int elements is in the stack
B is a memory address, it is stored in stack, but the memory B points to is in heap (100 int elements)
Dynamic allocated memory using malloc or C++ “new
à
heap (B[100)
42
#include <stdio.h>
static char *gonzo = “God’s own prototype”;
static char *userName;
int main(int argc, char* argv[]){
int i; /* stack */
int A[100]; /* stack */
int *B = (int*)malloc(sizeof(int)*100); //heap
for(i = 0; i < 100; i++) {
A[i] = i*i;
B[i] = A[i] * 20;
printf(”A[i]: %d, B[i]: %d\n",A[i], B[i]);
}
}
Stack size limit. If 8MB, “int
A[10,000,000]” won’t work.
Check the Memory Map of a Process
Given a process ID:
pmap <pid>
cat /proc/<pid>/maps
43