C Bit Fields: Full example code

The following two examples demonstrate the use of bit fields to reduce memory consumption of certain applications.

In the first example we create a compressed ‘bit’ struct and on the second we create a weird struct representation for bytes to show that the size of that struct is significantly less that the original one.

First example: Using bit fields to create a ‘bit’ structure

bits_bit_fields.c (compressed) (2 downloads)
#include <stdio.h>

// Weird structure to represent a 'bit'
typedef struct {
    unsigned char value;
} bit;

// 'bit' structure using bit fields
typedef struct {
    unsigned char value : 1;
} bit_bit_field;

int main( ) {

    printf( "Memory size occupied by 'bit' struct : %zu bytes\n", sizeof(bit));
    printf( "Memory size occupied by 'bit_bit_field' struct : %zu bytes\n", sizeof(bit_bit_field));

    bit bits[8];
    bit_bit_field bits_bit_field[8];

    printf( "Memory size occupied by 'bits' array : %zu bytes\n", sizeof(bits));
    printf( "Memory size occupied by 'bits_bit_field' array : %zu bytes\n", sizeof(bits_bit_field));

    // Setting the value of the first 'bit' and then printing it.
    // We will use various values for this test to show that when you set a value to a bit field
    // that is greater that the allowed size it will fill it using the last bits only.
    // It will not spill data though to neighbouring 'bits'.
    unsigned char value;
    for (value = 0; value < 4; value++)
    {
        printf("Input Value: %d\n", value);
        int bits_i;
        const int bits_length = (sizeof(bits) / sizeof(bit));
        for (bits_i = 0; bits_i < bits_length; bits_i++)
        {
            if (bits_i % 2)
            {
                bits[bits_i].value = 0;
            }
            else
            {
                bits[bits_i].value = value;
            }
            printf("%d", bits[bits_i].value);
        }
        printf("\n");

        int bits_bit_field_i;
        const int bits_bit_field_length = (sizeof(bits_bit_field) / sizeof(bit_bit_field));
        for (bits_bit_field_i = 0; bits_bit_field_i < bits_bit_field_length; bits_bit_field_i++)
        {
            if (bits_bit_field_i % 2)
            {
                bits_bit_field[bits_bit_field_i].value = 0;
            }
            else
            {
                bits_bit_field[bits_bit_field_i].value = value;
            }
            printf("%d", bits_bit_field[bits_bit_field_i].value);
        }
        printf("\n");
    }
    return 0;
}
bits_bit_fields.c (compressed) (2 downloads)

Execution output

Memory size occupied by 'bit' struct : 1 bytes
Memory size occupied by 'bit_bit_field' struct : 1 bytes
Memory size occupied by 'bits' array : 8 bytes
Memory size occupied by 'bits_bit_field' array : 8 bytes
Input Value: 0
00000000
00000000
Input Value: 1
10101010
10101010
Input Value: 2
20202020
00000000
Input Value: 3
30303030
10101010

Second example: Using bit fields to create a ‘byte’ structure where each ‘bit’ is another named member

bytes_bit_fields.c (compressed) (1 download)
#include <stdio.h>

// Weird structure to represent a 'byte'
typedef struct {
    unsigned char bit_0;
    unsigned char bit_1;
    unsigned char bit_2;
    unsigned char bit_3;
    unsigned char bit_4;
    unsigned char bit_5;
    unsigned char bit_6;
    unsigned char bit_7;
} byte;

// 'byte' structure using bit fields
// Unfortunately we cannot declare an array where the values are bit fields,
// so we have to declare each member separately.
// We instruct the compiler to use only one bit per element.
typedef struct {
    unsigned char bit_0 : 1;
    unsigned char bit_1 : 1;
    unsigned char bit_2 : 1;
    unsigned char bit_3 : 1;
    unsigned char bit_4 : 1;
    unsigned char bit_5 : 1;
    unsigned char bit_6 : 1;
    unsigned char bit_7 : 1;
} byte_bit_field;

int main( ) {

    printf( "Memory size occupied by 'byte' struct : %zu bytes\n", sizeof(byte));
    printf( "Memory size occupied by 'byte_bit_field' struct : %zu bytes\n", sizeof(byte_bit_field));

    byte bytes[8];
    byte_bit_field bytes_bit_field[8];

    printf( "Memory size occupied by 'bytes' array : %zu bytes\n", sizeof(bytes));
    printf( "Memory size occupied by 'bytes_bit_field' array : %zu bytes\n", sizeof(bytes_bit_field));

    // Setting the value of the first 'bit' and then printing it.
    // We will use various values for this test to show that when you set a value to a bit field
    // that is greater that the allowed size it will fill it using the last bits only.
    // It will not spill data though to neighbouring 'bits'.
    unsigned char value;
    for (value = 0; value < 4; value++)
    {
        printf("Input Value: %d\n", value);
        int bytes_i;
        const int bytes_length = (sizeof(bytes) / sizeof(byte));
        for (bytes_i = 0; bytes_i < bytes_length; bytes_i++)
        {
            if (bytes_i % 2)
            {
                bytes[bytes_i].bit_3 = 0;
            }
            else
            {
                bytes[bytes_i].bit_3 = value;
            }
            printf(" %d  ", bytes[bytes_i].bit_3);
        }
        printf("\n");

        int bytes_bit_field_i;
        const int bytes_bit_field_length = (sizeof(bytes_bit_field) / sizeof(byte_bit_field));
        for (bytes_bit_field_i = 0; bytes_bit_field_i < bytes_bit_field_length; bytes_bit_field_i++)
        {
            if (bytes_bit_field_i % 2)
            {
                bytes_bit_field[bytes_bit_field_i].bit_3 = 0;
            }
            else
            {
                bytes_bit_field[bytes_bit_field_i].bit_3 = value;
            }
            printf("%d%d%d ",
                   bytes_bit_field[bytes_bit_field_i].bit_2,
                   bytes_bit_field[bytes_bit_field_i].bit_3,
                   bytes_bit_field[bytes_bit_field_i].bit_4);
        }
        printf("\n");
    }
    return 0;
}
bytes_bit_fields.c (compressed) (1 download)

Execution output

Memory size occupied by 'byte' struct : 8 bytes
Memory size occupied by 'byte_bit_field' struct : 1 bytes
Memory size occupied by 'bytes' array : 64 bytes
Memory size occupied by 'bytes_bit_field' array : 8 bytes
Input Value: 0
 0   0   0   0   0   0   0   0  
000 000 000 000 000 000 000 000 
Input Value: 1
 1   0   1   0   1   0   1   0  
010 000 010 000 010 000 010 000 
Input Value: 2
 2   0   2   0   2   0   2   0  
000 000 000 000 000 000 000 000 
Input Value: 3
 3   0   3   0   3   0   3   0  
010 000 010 000 010 000 010 000

grep: How to match lines using any of multiple patterns

Recently, we needed to filter the results of ps x using two different patterns.
The first pattern was ./ where we needed to match that exact character sequence.
The . period character is treated as a special character in regular expressions (it matches a single character of any value, except for the end of line), so we decided to use the -F parameter to remove this special handling.
Doing this change prevented us from writing a regular expression that uses the OR | operator.

-F (or --fixed-strings) is a matching control option that instructs grep to interpret the patterns as a list of fixed strings (instead of regular expressions), separated by newlines, any of which is to be matched.
We tried assigning the different patterns as different lines to a variable and then using them on the pipe, like in the following example:

patterns="./
banana";
ps x | grep -F $patterns;

..but it failed.

Solution

grep supports a matching control option -e that allows us to define multiple patterns using different strings.

-e PATTERN (or --regexp=PATTERN) uses the value PATTERN as the pattern. If this option is used  multiple times or it is combined with the -f (--file) option, grep will search for all patterns given.

In the end, our command was transformed to the following, which worked just fine!

ps x | grep -F -e "./" -e "banana";

A small note on how we use valgrind

valgrind is a suite of tools for debugging and profiling programs.
We use it for debugging and profiling Linux executable files.

Despite the fact that it can do a whole lot of stuff, usually we use it as follows (when we do not forget) to test our applications for memory leaks:

valgrind --show-leak-kinds=all --leak-check=full $application $application_arguments;

The options we chose are the following:

  • --show-leak-kinds=all It will show all leaks (definite, indirect, possible, reachable) in the full leak search (see next bullet)
  • --leak-check=full this option instructs valgrind to search for memory leaks when the client program finishes.
    Each individual leak will be shown in detail and be counted as an error.

These options are extremely useful as they will catch a lot of little leaks that you might have missed (e.g. closing a file, freeing some memory, …)


List available functions in a shared library .so file

Recently, we wanted to see if a certain function call was available in a shared library (.so).

To do so we used the command nm.
nm lists symbols from object files.

We used the command nm -D /libs/mylib.so.1.
The parameter -D (or --dynamic) displays the dynamic symbols rather than the normal symbols.  (This is only meaningful for dynamic objects, such as certain types of shared libraries.)

We got a huge list which was similar to this

...
000000000000e6e0 T sudo_SHA512Update
000000000000eb20 T sudo_sig2str
000000000000b970 T sudo_strlcat
000000000000b910 T sudo_strlcpy
0000000000006f60 T sudo_strsplit_v1
00000000000070a0 T sudo_strtobool_v1
0000000000007330 T sudo_strtoid_v1
0000000000007570 T sudo_strtomode_v1
000000000000bb20 T sudo_strtonum
000000000000ac20 T sudo_term_cbreak_v1
000000000000adb0 T sudo_term_copy_v1
000000000021339c B sudo_term_erase
00000000002133a0 B sudo_term_kill
000000000000a920 T sudo_term_noecho_v1
000000000000aa80 T sudo_term_raw_v1
000000000000a860 T sudo_term_restore_v1
00000000000052a0 T sudo_vfatal_nodebug_v1
00000000000052d0 T sudo_vfatalx_nodebug_v1
0000000000005480 T sudo_vwarn_nodebug_v1
00000000000054b0 T sudo_vwarnx_nodebug_v1
00000000000055c0 T sudo_warn_gettext_v1
00000000000052f0 T sudo_warn_nodebug_v1
00000000000055a0 T sudo_warn_set_conversation_v1
...

We filtered out all elements that had the value T or t on the second column as those objects are symbol in the text (code) section and we found the function call we wanted there!


Fedora 25 with GNOME 3: Making a Wi-Fi hotspot

Recently we tried to create a Wi-Fi hotspot on Fedora 25 running GNOME 3.

When we clicked on the Use as Hotspot... button  on the network manager it did not activate the hotspot.
Actually, nothing changed after we clicked on the button.
We tried this several times, some while being disconnected from all networks, others with having the Wi-Fi device disabled etc. None of the tests payed out.

To mitigate the problem, we used nm-connection-editor to create the hotspot configuration and then activate it from the network manager.

After we starter nm-connection-editor, we pressed the Add button to create a new configuration:

From the prompt, we selected the option Wi-Fi and then clicked on the Create... button.

In the newly appeared window, we filled in

  • the Connection name (which is not used by the system, it is only for us to identify which configuration this is),
  • then the SSID (which is the name of the network you will create and connect to),
  • we set Mode to Hotspot

Then we switched to the Wi-Fi Security tab where we filled in the type of protection we want the hotspot to have and the password for it.

We clicked Save and then we closed the Network Connections window as well.

From the network manager, we clicked on Use as Hotspot... button and then the Turn On button on the confirmation popup to finish the activation.

After this, the network manager changed its screen and showed a page which had all the necessary information that are needed to connect to our newly created hotspot.

Note:

In case you cannot connect because the password verification fails even though you are providing the correct password, you can always do the ugly hack of setting up a hotspot with no security to get your job done…


Git: Create a branch on your local machine, switch to it and then push it on the server

Following are a couple of simple commands that we use daily to create a new branch on git and push it to the git server.

The following command will create locally a new branch called branch_name and switch to it.

git checkout -b branch_name;

The following command will push the local branch you are currently switched to on the git server.
It will be made available to the server using the name branch_name.

git push --set-upstream origin branch_name;

How to find the program interpreter that a Linux application requests 1

Recently we tried to execute an application and we got the following error:
-bash: ./main: No such file or directory
This error occurred because our application was trying to use an interpreter that was not available on that machine.
We used the readelf utility that displays information about ELF files (including the interpreter information) to resolve our issue.
Specifically we used readelf -l ./main which displays the information contained in the file’s segment headers, if it has any.
(You can replace the parameter -l with --program-headers or --segments, they are the same).

From the data that was produced we only needed the following line:

[Requesting program interpreter: /lib/ld-linux-armhf.so.3]
so we used grep to filter out all other lines and then cut and tr to get the data after the : character (second column) and then remove all spaces and the ] character from the result.
The full and final command we used was:
readelf -l ./main | grep 'Requesting' | cut -d':' -f2 | tr -d ' ]';

Device has MAC address X, instead of configured address Y.

Recently, we tried to take a network interface down on a VM, when we executed the command

ifdown eth0

we got the following error:

ERROR    : [/etc/sysconfig/network-scripts/ifdown-eth] Device  has MAC address 00:00:00:00:00:00
 00:45:4D:04:02:36
 00:45:4D:04:02:40, instead of configured address 00:45:4D:16:0B:29. Ignoring.

We used ifconfig eth0 to check the information on the network interfaces which resulted in the following data:

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
 inet 10.1.20.115  netmask 255.255.255.0  broadcast 10.1.20.255
 inet6 fe80::215:5dff:fe01:236  prefixlen 64  scopeid 0x20<link>
 ether 00:45:4d:04:02:36  txqueuelen 1000  (Ethernet)
 RX packets 10103919  bytes 5474528935 (5.0 GiB)
 RX errors 0  dropped 0  overruns 0  frame 0
 TX packets 6541413  bytes 1190276207 (1.1 GiB)
 TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

We saw that the value ether for eth0 was 00:45:4d:04:02:36, which was one of the addresses the warning mentioned that are valid.

Apparently, we had an error in the following configuration file:

/etc/sysconfig/network-scripts/ifcfg-eth0

The value for HWADDR was set to 00:45:4D:16:0B:29 which was wrong (most likely this file was copied here from another source), after we updated the value to 00:45:4d:04:02:36 we were able to use the device normally.


Fedora: Start a Virtual Machine using a physical hard disk

Recently, we wanted to start a Virtual Machine running a Windows installation from a physical hard disk.

We could not find a way for GNOME Boxes to achieve this, so we installed qemu to do so.

We installed qemu using:


sudo dnf install qemu -y;

Configuring our command to start the Virtual Machine from the physical hard drive:

  • Our hard disk was identified on the physical machine as /dev/sda so we set the -hda parameter to that value.
  • Then we added the parameter -boot c to instruct the virtual machine to boot from the first hard disk.
  • The default guest start-up RAM size was 128 MiB, so we set the parameter -m to 4096 to give to the virtual machine 4GB of RAM.
  • Finally we added the -snapshot parameter which instructed the system to write to temporary files instead of the disk image files (all disk images are considered as read only).
    In this case, the raw disk image used are not written back. When sectors are written, they are written in a temporary file created in /tmp.
    You can however force the write back to the raw disk images by using the commit monitor command (or C-a s in the serial console).

In the end our command was as follows:


sudo qemu-kvm -snapshot -m 4096 -boot c -hda /dev/sda;


Start CLion as root on Fedora

Recently we needed to start CLion as root on Fedora to allow the application we were developing to bind the DHCP service of the system.
When we tried to start CLion as root we got the following error:

java.awt.AWTError: Can't connect to X11 window server using ':0' as the value of the DISPLAY variable.

Specifically, the whole error log was as follows:

[[email protected] bin]$ sudo ./clion.sh 
[sudo] password for george: 
No protocol specified

Start Failed: Failed to initialize graphics environment

java.awt.AWTError: Can't connect to X11 window server using ':0' as the value of the DISPLAY variable.
    at sun.awt.X11GraphicsEnvironment.initDisplay(Native Method)
    at sun.awt.X11GraphicsEnvironment.access$200(X11GraphicsEnvironment.java:65)
    at sun.awt.X11GraphicsEnvironment$1.run(X11GraphicsEnvironment.java:115)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.awt.X11GraphicsEnvironment.<clinit>(X11GraphicsEnvironment.java:74)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at java.awt.GraphicsEnvironment.createGE(GraphicsEnvironment.java:103)
    at java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:82)
    at sun.awt.X11.XToolkit.<clinit>(XToolkit.java:126)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at java.awt.Toolkit$2.run(Toolkit.java:860)
    at java.awt.Toolkit$2.run(Toolkit.java:855)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.awt.Toolkit.getDefaultToolkit(Toolkit.java:854)
    at java.awt.Toolkit.getEventQueue(Toolkit.java:1734)
    at java.awt.EventQueue.isDispatchThread(EventQueue.java:1043)
    at javax.swing.SwingUtilities.isEventDispatchThread(SwingUtilities.java:1361)
    at javax.swing.text.StyleContext.reclaim(StyleContext.java:454)
    at javax.swing.text.StyleContext.addAttribute(StyleContext.java:311)
    at javax.swing.text.html.StyleSheet.addAttribute(StyleSheet.java:578)
    at javax.swing.text.StyleContext$NamedStyle.addAttribute(StyleContext.java:1501)
    at javax.swing.text.StyleContext$NamedStyle.setName(StyleContext.java:1312)
    at javax.swing.text.StyleContext$NamedStyle.<init>(StyleContext.java:1259)
    at javax.swing.text.StyleContext.addStyle(StyleContext.java:107)
    at javax.swing.text.StyleContext.<init>(StyleContext.java:87)
    at javax.swing.text.html.StyleSheet.<init>(StyleSheet.java:166)
    at javax.swing.text.html.HTMLEditorKit.getStyleSheet(HTMLEditorKit.java:391)
    at com.intellij.util.ui.UIUtil.<clinit>(UIUtil.java:102)
    at com.intellij.ide.plugins.PluginManager.start(PluginManager.java:73)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.ide.Bootstrap.main(Bootstrap.java:39)
    at com.intellij.idea.Main.main(Main.java:81)

This error occurred because the default configuration of the X server permissions did not allow the root to connect to it.
To verify this, we used xhost X server access control program to check the permissions.
Executing xhost with no command line arguments gave us a message indicating whether or not access control was currently enabled, followed by the list of those users allowed to connect.
For example in our case the output was as follows:


[[email protected] bin]$ xhost
access control enabled, only authorized clients can connect
SI:localuser:george

To add root to the list of users that was allowed to start an X application we executed the following command:


[[email protected] bin]$ xhost +si:localuser:root
localuser:root being added to access control list

Executing xhost again, we got the updated list which included the root


[[email protected] bin]$ xhost
access control enabled, only authorized clients can connect
SI:localuser:root
SI:localuser:george

After this, we were able to start CLion using sudo with no problems.


[[email protected] bin]$ sudo ./clion.sh

Note: This patch is not permanent, we actually execute it once at every restart of the machine.