4

I'm currently debugging a program (using lldb) that uses a static variable to toggle debug logging on or off. Weirdly, some of the log messages are not showing (while others are).

I'm setting a hardware breakpoint on the address of the variable when it is initially set: idevicedebug.c#L232, debug.c#L46

However, if I step into one of the logging functions that isn't working, not only does the hardware breakpoint not hit, if I print the address of the static variable, it's totally different.

Called here for example: idevicedebug.c#L322, the address of debug_level is different than what it was in internal_set_debug_level initially (and the value is now 0 instead of 1): debug.c#L87

Am I misunderstanding how static variables work? Could the binary be linked to two versions of the same code somehow? I'm unsure how to debug this further.

EDIT: Here is a rundown of the code in question (edited for brevity) a CLI utility for sending GDB packet commands to an iOS device over a USB connection:

idevicedebug.c:

int main(int argc, char *argv[])
{
    /* ... */
    int i;
    int debug_level = 0;
    /* ... */


    /* parse command line arguments */
    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
            debug_level++;
            idevice_set_debug_level(debug_level); // <- local debug_level is 1
            continue;
        }
    }

    /* ... */

    /* set maximum packet size */
    debug_info("Setting maximum packet size..."); // <- this does not print to stdout

    /* ... */

debug.h:

/* ... */

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && !defined(STRIP_DEBUG_CODE)
#define debug_info(...) debug_info_real (__func__, __FILE__, __LINE__, __VA_ARGS__)
#elif defined(__GNUC__) && __GNUC__ >= 3 && !defined(STRIP_DEBUG_CODE)
#define debug_info(...) debug_info_real (__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
#else
#define debug_info(...)
#endif

void debug_info_real(const char *func,
                                            const char *file,
                                            int line,
                                            const char *format, ...);

/* ... */

void internal_set_debug_level(int level);

#endif

debug.c:

/* ... */

static int debug_level;

void internal_set_debug_level(int level)
{
    debug_level = level; // <- static debug_level set to 1
}

/* ... */

void debug_info_real(const char *func, const char *file, int line, const char *format, ...)
{
#ifndef STRIP_DEBUG_CODE
    va_list args;
    char *buffer = NULL;

    if (!debug_level) // <- this is 0 despite being set previously
        return;       // breakpoint in internal_set_debug_level shows
                      // debug_level at 0x00000001000b75fc (== 1), yet
                      // here at 0x00000001000041b4 (== 0)

    /* run the real fprintf */
    va_start(args, format);
    (void)vasprintf(&buffer, format, args);
    va_end(args);

    debug_print_line(func, file, line, buffer);

    free(buffer);
#endif
}

lldb output:

Process 29615 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2
    frame #0: 0x00000001000ae0c7 libimobiledevice.6.dylib`internal_set_debug_level(level=1) at debug.c:46
   43
   44   void internal_set_debug_level(int level)
   45   {
-> 46           debug_level = level;
   47   }
   48
Target 0: (idevicedebug) stopped.
(lldb) p debug_level
(int) $0 = 0
(lldb) p &debug_level
(int *) $1 = 0x00000001000b75fc
(lldb) watch set expression (int *)0x00000001000b75fc
Watchpoint created: Watchpoint 1: addr = 0x1000b75fc size = 8 state = enabled type = w
    new value: 0x0000000000000000
(lldb) n

Watchpoint 1 hit:
old value: 0x0000000000000000
new value: 0x0000000000000001
Process 29615 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
    frame #0: 0x00000001000ae0d0 libimobiledevice.6.dylib`internal_set_debug_level(level=1) at debug.c:47
   44   void internal_set_debug_level(int level)
   45   {
   46           debug_level = level;
-> 47   }
Target 0: (idevicedebug) stopped.
(lldb) c
Process 29615 resuming
Process 29615 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    frame #0: 0x0000000100001e58 idevicedebug`main(argc=4, argv=0x00007fff5fbfef50) at idevicedebug.c:359
   357
   358                          /* set maximum packet size */
-> 359                          debug_info("Setting maximum packet size...");
   360
Target 0: (idevicedebug) stopped.
(lldb) s
Process 29615 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x0000000100002fff idevicedebug`debug_info_real(func="main", file="idevicedebug.c", line=359, format="Setting maximum packet size...") at debug.c:85
   82   {
   83   #ifndef STRIP_DEBUG_CODE
   84           va_list args;
-> 85           char *buffer = NULL;
   86
   87           if (!debug_level)
   88                   return;
Target 0: (idevicedebug) stopped.
(lldb) p debug_level
(int) $3 = 0
(lldb) p &debug_level
(int *) $4 = 0x00000001000041b4
user1234
  • 7,860
  • 4
  • 27
  • 36
  • 2
    Are you sure you are not somehow confusing the static `debug_level` variable declared in `debug.c` with local `debug_level` variable declared in `main`? – AnT stands with Russia Oct 03 '18 at 21:19
  • @AnT I don't think so, since they both should be set to 1, and in the second invocation I posted the value is 0 (which is the issue). – user1234 Oct 03 '18 at 21:23
  • But what is `debug_info`? According to your description, call to `debug_info` from line 322 lands in `debug_info_real`. So, is `debug_info` some kind of wrapper for `debug_info_real`? Where is it defined? – AnT stands with Russia Oct 03 '18 at 21:24
  • 1
    Post your code here, not just at a remote site. Please reduce it to a [mcve] rather than posting the entire thing. – Barmar Oct 03 '18 at 21:25
  • @AnT it's defined here: https://github.com/libimobiledevice/libimobiledevice/blob/b34e3435c21d06b3e5a4e7b5246fb6ddb6641a9f/common/debug.h#L29 – user1234 Oct 03 '18 at 21:26
  • @Barmar I'm not sure how to do that, since it's not my code (I'm debugging a 3rd party program that I've compiled and isn't working). – user1234 Oct 03 '18 at 21:27
  • I've updated the question with inline code examples and the lldb output if it helps. Thanks! – user1234 Oct 03 '18 at 21:49
  • Maybe you forgot to compile the library with `-fPIC`. Shared libraries will behave strangely if it gets swapped out, and then restored. – jxh Oct 03 '18 at 22:11

1 Answers1

2

You have no problem understanding how static variables work.

Nothing sure, but apparently the problem lies with the linking stage. Probably a mix of static/dynamic linking. According to your debugger output, you are including the same code twice :

  • libimobiledevice.la includes common/debug.c (through common/libinternalcommon.la)
  • idevicedebug seems to include it statically (through libinternalcommon too), and dynamically through libimobiledevice.

I would try removing common/libinternalcommon.la from idevicedebug_LDFLAGS in tools/Makefile.am.

autogen.sh fails on my machine, so I can't help more, but you get the idea.

xhienne
  • 5,738
  • 1
  • 15
  • 34