Wednesday, October 19, 2011

Using ptrace()'s GETREGSET on x86

Using ptrace()'s GETREGSET on x86

The following program shows how to obtain a snapshot of register values on x86 machine using ptrace()'s GETREGSET command.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <elf.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <linux/ptrace.h>

const char *str = "linux is awesome\n";

struct iovec {
        void *iov_base;
        unsigned int iov_len;
};

struct user_regs_struct32 {
        __u32 ebx, ecx, edx, esi, edi, ebp, eax;
        unsigned short ds, __ds, es, __es;
        unsigned short fs, __fs, gs, __gs;
        __u32 orig_eax, eip;
        unsigned short cs, __cs;
        __u32 eflags, esp;
        unsigned short ss, __ss;
};

int main(void)
{
        struct user_regs_struct32 uregs;
        struct iovec iov;
        pid_t c_pid, w_pid;
        int status;
        long ret;

        switch (c_pid = fork()) {
        case -1:
                perror("fork");
                exit(1);
                break;

        case 0:
                ret = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
                if (ret == -1) {
                        perror("ptrace");
                        exit(1);
                }

                raise(SIGUSR1);

                write(1, str, strlen(str));

                printf("Child process exiting.\n");

                exit(0);

                break;

        default:
                break;
        }

        /* Parent's code */
        w_pid = wait(&status);
        if (w_pid == -1) {
                perror("wait");
                goto out1;
        }

        ret = ptrace(PTRACE_SYSCALL, c_pid, NULL, NULL);
        if (ret == -1) {
                perror("ptrace");
                goto out1;
        }

        w_pid = wait(&status);
        if (w_pid == -1) {
                perror("wait");
                goto out1;
        }

        memset(&uregs, 0, sizeof(uregs));

        iov.iov_len = sizeof(uregs);
        iov.iov_base = &uregs;
        ret = ptrace(PTRACE_GETREGSET, c_pid, NT_PRSTATUS, &iov);
        if (ret == -1) {
                perror("ret");
                goto out1;
        }

        printf("uregs.orig_eax = %u.\n", uregs.orig_eax);

        exit(0);
out1:
        exit(1);
}

HTML generated by org-mode 7.3 in emacs 23

Saturday, January 15, 2011

Tango theme for IDLE (Python's default IDE)

Tango theme for IDLE (Python's default IDE)

Add the following text to ~/.idlerc/config-highlight.cfg.

[tango]
definition-foreground = #fce94f
error-foreground = #fa8072
string-background = #2e3436
keyword-foreground = #8cc4ff
normal-foreground = #ffffff
comment-background = #2e3436
hit-foreground = #ffffff
break-foreground = #000000
builtin-background = #2e3436
stdout-foreground = #eeeeec
cursor-foreground = #fce94f
hit-background = #2e3436
comment-foreground = #73d216
hilite-background = #edd400
definition-background = #2e3436
stderr-background = #2e3436
break-background = #2e3436
console-foreground = #87ceeb
normal-background = #2e3436
builtin-foreground = #ad7fa8
stdout-background = #2e3436
console-background = #2e3436
stderr-foreground = #ff3e40
keyword-background = #2e3436
string-foreground = #e9b96e
hilite-foreground = #2e3436
error-background = #2e3436

In the IDLE window, go to Options->Configure IDLE. Click on the Highlighting tab. Select the a Custom Theme. From the drop down menu below, select tango. Click on Apply.

HTML generated by org-mode 7.3 in emacs 23

Sunday, December 26, 2010

Get list of network interfaces using rtnetlink sockets

Get list of network interfaces using rtnetlink sockets

/**
 * @file get_if_list.c
 *
 * @brief Print the list of network interfaces available on the system.
 *
 * The network interface list is obtained using rtnetlink sockets.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <net/if.h>

#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>


#define IF_LIST_SIZE 10


static int32_t create_rtnetlink_sock(int32_t *sd);
static int32_t send_if_list_req(int32_t sd);
static int32_t recv_if_list_resp(int32_t sd,
                                 char iflist[IF_LIST_SIZE][IF_NAMESIZE]);

int main(void)
{
        char iflist[IF_LIST_SIZE][IF_NAMESIZE];
        int32_t sd;
        int32_t ret;

        ret = create_rtnetlink_sock(&sd);
        if (ret == -1) {
                fprintf(stderr, "Unable to create rtnetlink socket.\n");
                goto out1;
        }

        ret = send_if_list_req(sd);
        if (ret == -1) {
                fprintf(stderr, "Unable to send GET INTERFACE request.\n");
                goto out2;
        }

        ret = recv_if_list_resp(sd, iflist);
        if (ret == -1) {
                fprintf(stderr, "Unable to receive INTERFACE list request.\n");
                goto out2;
        }

        close(sd);

        exit(0);
out2:
        close(sd);
out1:
        exit(1);
}

/**
 * @brief Create a rtnetlink socket.
 *
 * @param[out] sd Will be filled with newly created socket's descriptor.
 *
 * @return 0 on success; -1 on failure.
 */
static int32_t create_rtnetlink_sock(int32_t *sd)
{
        struct sockaddr_nl nl;
        int32_t ret;

        if (sd == NULL) {
                fprintf(stderr, "%s: Invalid arguments.\n", __func__);
                goto out1;
        }

        *sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
        if (*sd == -1) {
                perror("socket");
                goto out1;
        }

        memset(&nl, 0, sizeof(nl));
        nl.nl_family = AF_NETLINK;
        ret = bind(*sd, (struct sockaddr *)&nl, sizeof(nl));
        if (ret == -1) {
                perror("bind");
                goto out2;
        }

        return 0;

out2:
        close(*sd);
out1:
        return -1;
}


/**
 * @brief Construct and send a request for obtaining the list of
 * network interfaces.
 *
 * @param[in] sd rtnetlink socket descriptor to be used for communication.
 *
 * @return 0 on success; -1 on failure.
 */
static int32_t send_if_list_req(int32_t sd)
{
        struct {
                struct nlmsghdr nh;
                struct ifinfomsg ifinfomsg;
        } req;
        ssize_t ssize;

        memset(&req, 0, sizeof(req));
        req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfomsg));
        req.nh.nlmsg_type = RTM_GETLINK;
        req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
        req.ifinfomsg.ifi_family = AF_UNSPEC;

        ssize = send(sd, &req, sizeof(req), 0);
        if (ssize == -1) {
                perror("send");
                return -1;
        }

        return 0;
}


/**
 * @brief Receive and process the rtnetlink response (containing the list
 * of interfaces).
 *
 * @param[in] sd rtnetlink socket descriptor to be used for communication.
 *
 * @return 0 on success; -1 on failure.
 */
static int32_t recv_if_list_resp(int32_t sd,
                                 char iflist[IF_LIST_SIZE][IF_NAMESIZE])
{
        struct ifinfomsg *ifinfomsg;
        struct nlmsghdr *nh;
        struct nlmsgerr *nlme;
        struct rtattr *rta;
        struct {
                struct nlmsghdr nh;
                uint8_t payload[1024];
        } resp;
        ssize_t ssize;
        int32_t len;
        int32_t attr_len;

        ssize = recv(sd, &resp, sizeof(resp), 0);
        if (ssize == -1) {
                perror("recv");
                return -1;
        }

        nh = &(resp.nh);
        if (nh->nlmsg_type == NLMSG_ERROR) {
                nlme = NLMSG_DATA(nh);
                fprintf(stderr, "Got error %d.\n", nlme->error);
                return -1;
        }

        len = ssize;
        printf("Available interfaces:\n");
        for (;NLMSG_OK(&(resp.nh), len); nh = NLMSG_NEXT(nh, len)) {
                ifinfomsg = NLMSG_DATA(nh);
                rta = IFLA_RTA(ifinfomsg);
                attr_len = IFLA_PAYLOAD(nh);
                for (; RTA_OK(rta, attr_len); rta = RTA_NEXT(rta, attr_len)) {
                        if (rta->rta_type == IFLA_IFNAME) {
                                printf("%s ", (char *)RTA_DATA(rta));
                        }
                }
                if (nh->nlmsg_type == NLMSG_DONE) {
                        break;
                }
        }

        printf("\n");

        return 0;
}

Compile and run the program as shown below:

[mwnn@schumi rtnetlink]$ cc -g -Wall get_if_list.c -oget_if_list
[mwnn@schumi rtnetlink]$ ./get_if_list
Available interfaces:
lo eth0

HTML generated by org-mode 7.3 in emacs 23

Tuesday, September 7, 2010

Debug cores generated by release mode executables

Debug cores generated by release mode executables

Release mode executables are generated by either compiling the code without using the -g compiler switch or by stripping the final executable. When a core (due to say, SIGSEGV) is generated by such an executable how do we obtain a backtrace using GDB. The solution is pretty simple; Compile a debug version of the same executable using the -g switch (the rest of the compiler options must be the same as those passed during the generation of the release mode executable). Use this executable when examining the core file using GDB. This works because the .text section of both executables (release mode and debug mode) remains the same. The debug mode executable has .debug* sections along with the .symtab section; The other sections are an exact replica as those found in the release mode executable.

Author: mwnn <mwnnlin AT gmail DOT com>

Date: 2010-09-07 23:43:35 IST

HTML generated by org-mode 6.33x in emacs 23

Monday, May 17, 2010

Generate core files with meta-information as part of their names

Generate core files with meta-information as part of their names

A core file is generated whenever an application encounters a segmentation fault. When working on a multi-process application, the process responsible for generating a core can be recognized using the file command.

$ file core

A better way to do the same would be to have this meta information as part of the filename of the core file. This can be done by writing into the /proc/sys/kernel/core_pattern file. The following command generates core files with process name as part of the core filename generated:

# sysctl kernel.core_pattern=core.%e

To keep the changes across power cyles add kernel.core_pattern=core.%e to the file /etc/sysctl.conf. On Slackware, this file has to be created explicitly.

Author: mwnn <mwnnlin AT gmail DOT com>

HTML generated by org-mode 6.33x in emacs 23

Saturday, May 15, 2010

Make MPlayer use ALSA as the default audio driver

Make MPlayer use ALSA as the default audio driver

MPlayer can write decoded audio to many audio drivers. To get a list of output audio drivers supported on your machine execute the following command.

$ mplayer -ao help

Among the choices listed I prefer to use ALSA(Advanced Linux Sound Architecture). The -ao switch allows us to specify ALSA as the output audio driver to be used by MPlayer. An example usage is provided below:

$ mplayer -ao alsa linux_can.mp3

Specifying the -ao switch all the time can be a pain in the neck. This problem can be solved by copying the file /etc/mplayer.conf to ~/.mplayer/config. MPlayer reads this file on startup and initializes itself based on the settings provided in the file. In the file, the ao configuration value can be found under audio settings section. Uncomment the line and set the value for ao as alsa. The edited line should look like the one shown below:

# Specify default audio driver (see -ao help for a list).
ao=alsa

Now MPlayer will use ALSA on every invocation.

Author: mwnn <mwnnlin AT gmail DOT com>

HTML generated by org-mode 6.33x in emacs 23

Thursday, May 13, 2010

IPv6 address assignment & bind() issues

IPv6 address assignment & bind() issues

The C program given below assigns an IPv6 address to a specified network interface.

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>

#define IPV6_BINARY_LEN 128

int32_t set_ip_addr(const char *dev, const char *ip, int32_t prefix_len);

int main(int argc, char *argv[])
{
        struct sockaddr_in6 sa;
        int32_t size;
        int32_t sd;
        int32_t ret;

        if (argc != 4) {
                fprintf(stderr, "Usage: %s <dev> <ip address> <prefix Length>.\n",
                        argv[0]);
                goto out1;
        }

        ret = set_ip_addr(argv[1], argv[2], atoi(argv[3]));
        if (ret == -1) {
                fprintf(stderr, "Unable to set IP address.\n");
                goto out1;
        }

        sd = socket(AF_INET6, SOCK_DGRAM, 0);
        if (sd == -1) {
                perror("socket");
                goto out1;
        }

        memset(&sa, 0, sizeof(sa));
        sa.sin6_family = AF_INET6;
        sa.sin6_port = htons(49153);
        sa.sin6_scope_id = 0;
        ret = inet_pton(AF_INET6, argv[2], sa.sin6_addr.s6_addr);
        if (ret != 1) {
                perror("inet_pton");
                goto out2;
        }

        ret = bind(sd, (struct sockaddr *)&sa, sizeof(sa));
        if (ret == -1) {
                perror("bind");
                goto out2;
        }

        printf("Bind success!\n");

        close(sd);

        exit(0);
out2:
        close(sd);
out1:
        exit(1);
}

struct in6_ifreq {
        struct in6_addr ifr6_addr;
        uint32_t        ifr6_prefixlen;
        int32_t         ifr6_ifindex;
};

int32_t set_ip_addr(const char *dev, const char *ip, int32_t prefix_len)
{
        struct in6_ifreq in6_ifreq;
        struct ifreq ifreq;
        uint8_t ip_bin[IPV6_BINARY_LEN];
        int32_t sd;
        int32_t ret;

        if ((dev == NULL) || (ip == NULL) || (prefix_len < 0)) {
                fprintf(stderr, "%s: Invalid arguments.\n", __func__);
                goto out1;
        }

        ret = inet_pton(AF_INET6, ip, ip_bin);
        if (ret != 1) {
                perror("inet_pton");
                goto out1;
        }

        sd = socket(AF_INET6, SOCK_DGRAM, 0);
        if (sd == -1) {
                perror("socket");
                goto out1;
        }

        memset(&ifreq, 0, sizeof(ifreq));
        strncpy(ifreq.ifr_name, dev, IFNAMSIZ - 1);
        ret = ioctl(sd, SIOCGIFINDEX, &ifreq);
        if (ret == -1) {
                perror("ioctl");
                goto out2;
        }

        memset(&in6_ifreq, 0, sizeof(in6_ifreq));
        ret = inet_pton(AF_INET6, ip, &in6_ifreq.ifr6_addr.s6_addr);
        if (ret != 1) {
                perror("inet_pton");
                goto out2;
        }

        in6_ifreq.ifr6_ifindex = ifreq.ifr_ifindex;
        in6_ifreq.ifr6_prefixlen = prefix_len;

        ret = ioctl(sd, SIOCSIFADDR, &in6_ifreq);
        if (ret == -1) {
                perror("ioctl");
                goto out2;
        }

        close(sd);

        return 0;
out2:
        close(sd);
out1:
        return -1;
}

Compile and execute the program as shown below:

$ gcc -Wall set_ipv6_addr.c -oset_ipv6_addr
$ ./set_ipv6_addr eth0 2001:db8:0:145::243 64

The bind() system call in the above program fails and errno is set to EADDRNOTAVAIL.

To solve this issue (a workaround rather than a correct fix :-() follow the instructions given below:

  1. Open the file net/ipv6/addrconf.c in the Linux kernel.
  2. In the function ipv6_add_addr(), replace the following line
ifa->flags = flags | IFA_F_TENTATIVE;

with

ifa->flags = flags | IFA_F_NODAD;

Recompile the kernel and execute the previously listed program after booting into the new kernel.

HTML generated by org-mode 7.3 in emacs 23