Pages

Tuesday, August 18, 2015

GCC Compilation Process

GCC compiles a C/C++ program into executable in 4 steps as shown in the diagram below. For example a: "gcc -o hello.exe hello.c" is carried out as follows:

Lets understand this with the  a simple example.

#include <stdio.h>
int main(){
   printf("This is an example of compilation process using gcc");
   return 0;
}

1. Preprocessing: via the GNU C Preprocessor(cpp.exe)  which includes the headers(#include) and expands the macros(#define). The command is cpp hello.exe > hello.i . Note here that the command has cpp not gcc as we are using cpp.exe which is the C preprocessor.

The resultant intermediate file "hello.i" contains the expanded source code. Click on the link below to see how the file looks like.

http://pastebin.com/YBr6yt1s

2. Compilation: The compiler compiles the pre-processed source code into an assembly code for a specified processor.

The command is : gcc -S hello.i

The -S option specifies the pre-processed source code, instead of object code.The resultant assembly is "hello.s".

3. Assembly :  The assembler (as.exe) converts assembly code into machine code.

The command is : as -o hello.o hello.s

4. Linker: Finally the linker links the object code with the library code to produce an executable file.

ld -o hello.exe hello.o ...libraries...

We can see the detailed compilation process by enabling -v(verbose) option.

gcc -v hello.c -o hello.exe

Saturday, August 15, 2015

Function Pointers in C

Pointers are used to point to address of some variable. Pointers can be de-referenced to get the value of the data that the pointer points to. In addition to data, pointers can also be used to store the address of a function. Not only this, it can also be de-referenced to execute the function.

Lets understand with the following example:

#include <stdio.h>

int add(int x, int y){
 return x+y;
}

int main(){

    int c;
    int (*p)(int,int);
    p = add;  // p = &add can also be used to assign the address of  add function to p.
    c=  p(2,3); // c= (*p)(2,3) can also be used to de-reference the pointer p and run the function.
    printf("%d\n",c);
}

The add function adds two integers x and y and returns the sum of two integers.

In function main, the function pointer is defined using the syntax : int (*p) (int, int).
This means that, the pointer variable p is the function pointer for a function with two integers as the input variable and an integer as the return value.

The syntax, p= add ; or p= &add; is used to store the address of add function in the pointer variable p.

The syntax, c = p(2,3) or c= *p(2,3) can be used to call the function add by de-referencing the pointer p and the return value is stored in c as c is an int variable.


Sunday, August 9, 2015

Windows Programming in C

The best tutorial that I have come across till now to get started with windows programming in C is:

http://www.winprog.org/tutorial/simple_window.html

The program compiles perfectly and the output is a basic window.
Although, we do not use C to do windows programming, it's good to understand what happens behind the scene when we create windows application using frameworks like .NET.

The code is as follows:

#include <windows.h>

const char g_szClassName[] = "myWindowClass";

// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    //Step 1: Registering the Window Class
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Step 2: Creating the Window
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "The title of my window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
        NULL, NULL, hInstance, NULL);

    if(hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // Step 3: The Message Loop
    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

Saturday, August 8, 2015

printf in C

Format:
int printf ( const char * format, ... );

Function:
This prints formatted output to stdout.
Writes the C string pointed by format to the standard output (stdout). If format includes format
specifiers (subsequences beginning with %), the additional arguments following format are formatted and inserted in the resulting string replacing their respective specifiers.

Return Value:
printf is a function and like any other function it has input parameters and a return value.
On success, the total number of characters written is returned. On failure, a negative number is returned. Thus the following C program makes perfect sense.

#include  

int main(void); // Declare main() and the fact that this program doesn’t use any passed parameters
int main()
{

    int i;
    int nCount = 0; // Always initialize your auto variables
    char szString[] = “We want to impress you %d\n”;

    for (i = 0; i < 5; i++)
    {
        nCount += printf(szString, i + 1);   //The return value of printf is 25 which is added to nCount
    }
    return (nCount); // Brackets around all return values
}

Output:

We want to impress you 1
The number of characters printed is 25
We want to impress you 2
The number of characters printed is 50
We want to impress you 3
The number of characters printed is 75
We want to impress you 4
The number of characters printed is 100
We want to impress you 5
The number of characters printed is 125
We want to impress you 6
The number of characters printed is 150

Scanset in C

scanf family functions support scanset specifiers which are represented by %[]. Inside scanset, we can specify single character or range of characters. While processing scanset, scanf will process only those characters which are part of scanset. We can define scanset by putting characters inside squre brackets. Please note that the scansets are case-sensitive.

For Eg:

If we want to get only capital letters from stdin we can use the following:

scanf("%[A-Z]s", str);

If we want to get only small letters from stdin we can use the following:

scanf("%[a-z]s", str);

If first character of scanset is ‘^’, then the specifier will stop reading after first occurrence of that character. For example, given below scanset will read all characters but stops after first occurrence of ‘o’

scanf("%[^o]s", str);

If we want to read a line from stdin into the buffer pointed to by s until either a terminating newline or EOF found.

scanf("%[^\n]s", str);

Thursday, August 6, 2015

How are integers stored in binary

In a C program, when we use the following statements:

int a = 12;

The operating system allocates 4 bytes of memory for a and stores 12 in binary format in those 4 bytes.

In binary 12 = 1100, which when expanded to store in 4 bytes will be as follows:
00000000 00000000 00000000 00001100.

Lets assume the memory address to store these 4 bytes are 1001,1002,1003,1004
The rightmost bit is called the least significant bit and the leftmost bit is the most significant bit. Each byte is stored in a different memory address. If we store the bytes with the least significant byte in the lowest memory address ie store 00001100 in 1001, this method of storing in memory is called little endian. On the other hand, if we store the most significant byte in the lowest memory address ie store 00000000 in 1001, it is called big endian.

Integers can be positive or negative. One way to store negative numbers in binary is to set the most significant bit to 1 to denote that it is a negative number. So -12 is stored as follows:

10000000 00000000 00000000 00001100.
So, we have only 31 bits for the numbers. Thus the largest integer that can be stored is 2^31-1= 65535 and the lowest integer that can be stored is -2^31-1= -65535

However, there are two major problems when we store integers like this.

1. Lets suppose we want to store values from -3 to +3. There are 7 numbers in this range.

0 - 000
1 - 001
2 - 010
3 - 011
-0 - 100
-1 - 101
-2 - 110
-3 - 111

Here we can see that 0 has two representations which can create a big confusion.

2. Arithmetic operations do not give the correct results.

If we add +1 and -1, it should output 0. Here,

 001 + 101 = 110 which is -2 and that is wrong.

So, to avoid these we use 2's complement to store negative numbers. In 2's complement, to get the binary representation for a negative number, its positive conterpart is first complemented and 1 is added to the 1's complement.

The 2's complement of 1(001) is 111(110+1).

The above range can now be represented as follows:


0 - 000
1 - 001
2 - 010
3 - 011
-1 - 111
-2 - 110
-3 - 101
-4 - 100

Thus, we can see that both of the above problems are solved using the 2's complement. Also, we can store one more number as there is only 1 representation for 0. The range of number which can be represented using this method is -2^31 to 2^31-1. 

Friday, June 26, 2015

Linux Kernel Module - Creating, Inserting and Removing Kernel Object

Consider the following C program stored in the file simple_module.c


#include <linux/init.h>
#include <linux/module.h>

int simple_module_init(void){
  printk(KERN_ALERT "Inside the init function");
  return 0;
}

void simple_module_exit(void){
  printk(KERN_ALERT "Inside the exit function");
}

module_init(simple_module_init);
module_exit(simple_module_exit);
The above example is the simplest example of a kernel module. It has an init and an exit function. The init function is called when the module is initialized and the exit function is called when the module is removed.

To run this program we create a Makefile which is as follows:

obj-m := simple_module.o

To compile this function we run the following command,

"make -C /lib/modules/$(uname -r)/build M=$PWD modules", where C gives the directory of the build and M means the module, PWD means the reference files are in the present directory.

When this command is run, it creates a simple_module.ko file, ko = kernel object.

To load this module into the kernel, the command is :

sudo insmod simple_module.ko

We can see if the module is loaded using the following command:

lsmod

To remove the module from the kernel, we use the following command:

sudo rmmod simple_module

The log messages can be seen in /var/log/syslog or /var/log/messages.

Use the following command to see the tail of the file and follow the changes in file

tail -f /var/log/syslog