3

We are trying to implement the debugger program which will takes either PID or Program name as input and Invoke the gdb by using PID. Below is two small programs are written, Not able to figure out what is the exact problem here... after passing PID, it shows 5000+ instructions are executed as a result.

Debug.c

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
#include <syscall.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <unistd.h>
#include <errno.h>


/* Print a message to stdout, prefixed by the process ID
*/
void procmsg(const char* format, ...)
{
    va_list ap;
    fprintf(stdout, "[%d] ", getpid());
    va_start(ap, format);
    vfprintf(stdout, format, ap);
    va_end(ap);
}


void run_target(const char* programname)
{
    procmsg("target started. will run '%s'\n", programname);

    /* Allow tracing of this process */
    if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
        perror("ptrace");
        return;
    }

    /* Replace this process's image with the given program */
    execl(programname, programname, 0);
}


void run_debugger(pid_t child_pid)
{
    int wait_status;
    unsigned icounter = 0;
    procmsg("debugger started\n");

    /* Wait for child to stop on its first instruction */
    wait(&wait_status);

    while (WIFSTOPPED(wait_status)) {
        icounter++;
        struct user_regs_struct regs;
        ptrace(PTRACE_GETREGS, child_pid, 0, &regs);
        unsigned instr = ptrace(PTRACE_PEEKTEXT, child_pid, regs.eip, 0);

        procmsg("icounter = %u.  EIP = 0x%08x.  instr = 0x%08x\n",
                    icounter, regs.eip, instr);

        /* Make the child execute another instruction */
        if (ptrace(PTRACE_SINGLESTEP, child_pid, 0, 0) < 0) {
            perror("ptrace");
            return;
        }

        /* Wait for child to stop on its next instruction */
        wait(&wait_status);
    }

    procmsg("the child executed %u instructions\n", icounter);
}


int main(int argc, char** argv)
{
    pid_t child_pid_attach;

    if (argc < 2) {
        fprintf(stderr, "Expected a program name as argument\n");
        return -1;
    }
sscanf(argv[1],"%d",&child_pid_attach);

//Attaching to running process
if(ptrace(PTRACE_ATTACH,child_pid_attach,0,0)<)
{
perror("ptrace");
return;
}
else
{
printf("%d",child_pid_attach);
}
    if (child_pid_attach== 0)
        run_target(argv[1]);
    else if (child_pid_attach > 0)
        run_debugger(child_pid_attach);
    else {
        perror("fork");
        return -1;
    }

ptrace(PTRACE_DETACH,child_pid_attach,0,0);
    return 0;
}

The above program has been used to debug the process that is created by the following program (i.e sum of two numbers). test.c

#include<stdio.h>
void main()
{
int a, b, c;
scanf("%d", &a);
scanf("%d", &b);
printf("\n Sum of Two Numbers is:");
c=a+b;
printf("%d",c);
}

First, we are running ./test and then checking its pid. As a next step we are running ./Debug [pid]. As a result of the above execution, it is displaying that Child process has executed 5000+ instructions and it is printing the same instructions all the time.

Please do let me know if there is any other way to do it and do let me know how to read the data of another process. In this case "How can I read the data (values of variables) of process that is created by ./test?".

1
  • What is wrong with just using gdb? What would you like to happen when you give a program name? That it attaches to some process running that program, launch the program to debug it? If you really have some use case that hasn't been considered by the gdb developers, maybe you should discuss it with them (after possibly clarifying the proposal here).
    – vonbrand
    Jan 24, 2013 at 16:43

2 Answers 2

2

Actually, this is correct behavior.

Below is a quote from here:

The answer is very interesting. By default, gcc on Linux links programs to the C runtime libraries dynamically. What this means is that one of the first things that runs when any program is executed is the dynamic library loader that looks for the required shared libraries. This is quite a lot of code – and remember that our basic tracer here looks at each and every instruction, not of just the main function, but of the whole process.

1

"How can I read the data (values of variables) of process that is created by ./test?".

You may want to look into the the DWARF Debugging Format. Part 3 of the How Debuggers Work link below discusses DWARF briefly. There are other ways to resolve symbols, but why not do as GDB does and use DWARF? [edit: It is no trivial task to extract functions from the gdb source code to use them in simpler programs]. The source code is available anyway, check 'How GDB Loads Symbol files' link below, where the links point right at it. The third option would be manually parsing the ELF symbol tables with your own functions. It's uglier and is probably the more involved road but it doesn't rely on the debug symbols that dwarf provides.

As for breakpoints, you can set them using ptrace and trap = memory & 0xffffff00 | 0xcc after saving the instruction at the address and recovering the instruction after the trap is hit, as How Debuggers Work describes. The 0xcc byte is opcode int 3.

To see how gdb does it, this is the link for you: How GDB Loads symbol files

The following is solely to hint at where those 5000+ steps are coming from by running an assembly program with no linked libraries through a similar tracer:

;hello.asm

section .text
    global _start

_start:
    mov edx,5
    mov ecx,msg
    mov ebx,1
    mov eax,4
    int 0x80

    mov eax,1
    int 0x80

msg:
    db "Hello"

The program that I used to count is similar (from How Debuggers Work)

#include <sys/ptrace.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#include <sys/wait.h>

void run_debugger(pid_t child_pid)
{
    int wait_status;
    unsigned icounter = 0;
    printf("debugger started\n");

    /* Wait for child to stop on its first instruction */
    wait(&wait_status);

    while (WIFSTOPPED(wait_status)) {
        icounter++;
        /* Make the child execute another instruction */
        if (ptrace(PTRACE_SINGLESTEP, child_pid, 0, 0) < 0) {
            perror("ptrace");
            return;
        }

        /* Wait for child to stop on its next instruction */
        wait(&wait_status);
    }

    printf("\nthe child executed %u instructions\n", icounter);
}


void run_target(const char* programname)
{
    printf("target started. will run '%s'\n", programname);

    /* Allow tracing of this process */
    if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
        perror("ptrace");
        return;
    }

    /* Replace this process's image with the given program */
    execl(programname, programname, NULL);
}

int main(int argc, char** argv)
{
    pid_t child_pid;

    if (argc < 2) {
        fprintf(stderr, "Expected a program name as argument\n");
        return -1;
    }

    child_pid = fork();
    if (child_pid == 0)
        run_target(argv[1]);
    else if (child_pid > 0)
        run_debugger(child_pid);
    else {
        perror("fork");
        return -1;
    }

    return 0;
}

Compiled this as a.out and ran:

$ ./a.out helloasm
debugger started
target started. will run 'helloasm'
Hello
the child executed 7 instructions

vs.

#include <stdio.h>

int main()
{
        printf("Hello World\n");
        return 0;
}

Which totals 141 690 instructions.

3
  • While this is interesting, I am struggling to understand how it answers the question. Jun 28, 2017 at 21:05
  • That's a good point. I meant to just add an example to brilliantidiot's answer showing that in my trivial assembly program, because this tracer is not stepping through the dynamic library loader that looks for the required shared libraries that gcc uses to link, that our instruction count is much more reasonable for such a simple program. I'll make an edit to try to make what I'm saying clearer.
    – flerb
    Jun 29, 2017 at 22:29
  • I also missed the second part about reading variables, I'll fix my answer.
    – flerb
    Jun 29, 2017 at 22:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .