We use Linux as our application development platform. Linux uses GNU (a non profit organization) tools:
The introductory lecture and the laboratory material is about basics to get the whole picture (not all details included). You can get a detailed and full information of all the tools using Linux man- and info command (info gcc, info make man ld )and /usr/doc/HOWTO.
The professional program development work needs (in addition to the software management and design process) understanding of few basic technologies not depending on the operating system:
#include <stdio.h>
short int i=0xe589;
main()
{
printf("helloi=%d\n",i)
}
______________________________________________
__________________________________________________
Compiling?
A compiler generates an object file from a source file
.
An object file?
Machine level instructions generated by a compiler
and information to a linker to solve unresolved symbols (for example calling
subprogram printf).
Example to understanding the output of the compiler at
binary level:
#include <sdtio.h>
int i = e589;
objdump --disassemble-all main.o__________________________
_________________________________main.o: file format elf32-i386Disassembly of section .text:
00000000 <main>:
0: 55 pushl %ebp
1: 89 e5 movl %esp,%ebp <-----a hex number same as the integer i in the example program BUT these are in the code segment meaning the processor interprets this as an instruction
3: 0f bf 05 00 00 movswl 0x0,%eax
8: 00 00
a: 50 pushl %eax
b: 68 00 00 00 00 pushl $0x0
10: e8 fc ff ff ff call 11 <main+0x11> <------here we call printf (address resolved by linker later)
15: 83 c4 08 addl $0x8,%esp
18: c9 leave
19: c3 ret
Disassembly of section .data:00000000 <i>:
0: 89 e5 movl %esp,%ebp <--------This is integer i not a program code
Disassembly of section .rodata:00000000 <.rodata>:
0: 68 65 6c 6c 6f pushl $0x6f6c6c65
5: 0a 20 orb (%eax),%ahobjdump --reloc main.o
main.o: file format elf32-i386
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
00000006 R_386_32 i
0000000c R_386_32 .rodata
00000011 R_386_PC32 printf
Run the program now:
Linking plenty of object modules to an executable:
Linking (static) with linker program ld:
Compiler starts the linker automatically in the previous
example.
To link directly with ld the first module
to be linked must be a so called start up library.
___________________________________
Libraries (archives):
How to create your own library and maintain and use it?
There are plenty of flags supported by ar, consult the manual page of ar.
How to use our brand new library?
The make command:
make(1)
make(1)
NAME
make - maintain, update,
and regenerate groups of programs
SYN OPSIS
make [-f makefile] [-bBdeiknpPqrsStuw]
[macro_name=value] [names]
DESCRIPTION
Makefile Structure
A makefile can contain four different
kinds of lines: target lines,
shell command lines, macro definitions,
and include lines.
Lines of the form string1 = string2 are macro definitions.
The following makefile says that excecutable pgm depends
on two files: a.o and
b.o, and
that they in turn depend on their corresponding source files
(a.c and
b.c) and a common file incl.h:
OBJS = a.o b.o
pgm: $(OBJS)
cc $(OBJS) -o pgm
<----Always tab-characer or nothing in the beginning of
a line, no spaces!
a.o: incl.h a.c
cc -c a.c
b.o: incl.h b.c
cc -c b.c
So, always on the left we have a file to be created if
the files after semicolon are changed.
Next line tells how this is to be done.
Important: The make command is very sensitive, you
must have always tab- character (no spaces) in the beginning of the command
line.
The following example is from my real-time operaing systems
course material. This makefile executed by the main makefile creates the
debugger task:
_debugger.o:$(OBJS)
ld -r -o $(TARGET) $(OBJS)
dep:
$(CC)
-M *.c >.dep
clean:
rm *.o
include .dep
.c.o:
$(CC) $(CCFLAGS) -c $< -o $@
This was not all about the make. To learn more:
int z;
main()
{
z=add(0x12345678, 0x11111111);
printf("%x ",z);
};
int add(int x,int y)
{
return x+y;
}
[root@localhost /linu>
GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details.GDB 4.16 (i386-redhat-linux), Copyright 1996 Free Software Foundation, Inc...
(gdb) break
add
Breakpoint 1 at 0x80484b7: file
disas.c, line 10.
(gdb) run
Starting program: /linux/a.out
Breakpoint 1, add (x=305419896,
y=286331153) at disas.c:10
10
return x+y;
(gdb) print
x
$1 = 305419896
(gdb) print
/x x
$2 = 0x12345678
////////////
(gdb) disassemble
main
Dump of assembler code for function
main:
0x8048480 <main>:
pushl %ebp //old
base pointer (from pointer to the
stack)
0x8048481 <main+1>:
movl %esp,%ebp //bp
gets new value current frame)
0x8048483 <main+3>:
pushl $0x11111111 //parameters
always in the stack
0x8048488 <main+8>:
pushl $0x12345678 //reverse
order
0x804848d <main+13>:
call 0x80484a4 <add>
0x8048492 <main+18>:
movl %eax,0x80495cc //return
value always in the eax
0x8048497 <main+23>:
pushl %eax
0x8048498 <main+24>:
pushl $0x80484fc
0x804849d <main+29>:
call 0x80483cc <printf>
0x80484a2 <main+34>:
leave
//get old base pointer
0x80484a3 <main+35>:
ret
//return to the library routine
End of assembler dump.
(gdb) disassemble
add
Dump of assembler code for function
add:
0x80484a4 <add>:
pushl %ebp
0x80484a5 <add+1>:
movl %esp,%ebp
0x80484a7 <add+3>:
movl 0x8(%ebp),%eax //get
the parameters from the stack
0x80484aa <add+6>:
addl 0xc(%ebp),%eax //return
value always in the eax
0x80484ad <add+9>:
leave
0x80484ae <add+10>:
ret
End of assembler dump.
(gdb)
We talk about:
Segments:
.text
/* the code */
.data
/* Initialized data */ .
.bss
/* Un initialized data */
Registers:
- stack pointer (Intel: sp, esp)
- program counter
- base pointer or frame pointer (Intel: bp, ebp)
The stack frame concept
![]()
/* The main file main_1.c */ int sub(int,int);
int tab[100];/* Data in un initialized segment (BSS)*/
char c;int a1=55555;/* Data in data segment */
void main(void)
{
int sum;
sum=add(0x12345678,0x11111111);
printf("Sum=%x\n",sum);
}/* Function add */
int add(int x, int y)
{
return(x+y);
}
gcc -S -O3 main_1.c /* Make assembly file, optimize (O3) */
cat main_1.s /* Show it */
.file "main_1.c"
.version "01.01"
gcc2_compiled.:
.globl a1
.data
.align 4
.type a1,@object
.size a1,4
a1:
.long 55555
.section .rodata
.LC0:
.string "Sum=%x\n"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
pushl $286331153
pushl $305419896
call add
pushl %eax
pushl $.LC0
call printf
leave
ret
.Lfe1:
.size main,.Lfe1-main
.comm tab,400,4
.comm c,1,1
.ident "GCC: (GNU) 2.7.2.3"
gcc -c add.c /* Compile - no optimization */
objdump --disassembble add.o
add_1.o: file format elf32-i386Disassembly of section .text:
00000000 <add>:
0: 55 pushl %ebp
1: 89 e5 movl %esp,%ebp
3: 8b 55 08 movl 0x8(%ebp),%edx /* Get prameter from stack */
6: 03 55 0c addl 0xc(%ebp),%edx
9: 89 d0 movl %edx,%eax /* Return value always in the first general register (eax)*/
b: eb 03 jmp 10 <add+0x10>
d: 8d 76 00 leal 0x0(%esi),%esi /* This is nothing, jmp to the next instruction */
10: c9 leave
11: c3 ret
/* New version of add (short_add.s), assembly source file extension is .s
works similarly */.section .text
.globl add
add:
movl 4(%esp),%eax
addl 8(%esp),%eax
ret
/* Now we can compile and run both versions */
gcc main_1.c add_1.c -o add
gcc main_1.c short_add.s -o short./add
Sum=23456789
./short
Sum=23456789
/* Now we optimize add gcc -c -O2 add_1.c
objdump --disassemble add_1.o */
add_1.o: file format elf32-i386
Disassembly of section .text:
00000000 <add>:
0: 55 pushl %ebp
1: 89 e5 movl %esp,%ebp
3: 8b 45 08 movl 0x8(%ebp),%eax
6: 03 45 0c addl 0xc(%ebp),%eax
9: c9 leave
a: c3 ret
/* Next we study segment structure.
We have a new assembly file data.s */.data
.globl x1
x1:
.long 1111
.globl y1
y1:
.long 2222
.bss
.globl z1
z1:
.long
objdump --syms data.o data.o: file format elf32-i386
SYMBOL TABLE:
00000000 l d .text 00000000
00000000 l d .data 00000000
00000000 l d .bss 00000000
00000000 l .data 00000000 x1
00000004 g .data 00000000 y1
00000000 g .bss 00000000 z1
/* Main program linked with assembly file data.s
data.s contains external variablesgcc main_2.c add_1.c data.s
*/
int add(int,int);int tab[100];
char c1;int a1=55555;
extern int x1, y1;
void main(void)
{
int a;
a=add(x1,y1);
printf("Sum=%d\n",a);
}
gcc main_2.c add_1.c data.s
./a.out
Sum=3333
Binary image
ld -nostdlib -oformat binary main_3.o add_1.o data.o -o bin /* main_3.c =main_2.c printf omitted - library routines are very long */
od -x bin
0000000 8955 83e5 04ec 38a1 0490 5008 34a1 0490 /* The code segment .text*/
0000020 5008 0de8 0000 8300 08c4 c089 4589 c9fc
0000040 90c3 9090 8955 8be5 0845 4503 c90c 90c3
0000060 0000 0000 0000 0000 0000 0000 0000 0000
*
0010060 d903 0000 0457 0000 08ae 0000 /*457 hex is decimal 1111 and 8ae is 2222*/ /* The data segment data */
Linker script (an example from the kernel WBS)
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS
{
.bss 0x05000: {
_bss_start = . ; *.o(.bss); _bss_end = . ;
}
.text 0x09000: {
_start = ABSOLUTE(.);
*.o(.text)
}
.data :{
*.o(.data)
*.COMMON
}
_end = .;
_size_of_kernel = _end - _start;
}