{"id":51,"date":"2013-08-06T04:07:19","date_gmt":"2013-08-06T03:07:19","guid":{"rendered":"http:\/\/sysprogs.com\/blog\/?p=51"},"modified":"2016-07-16T22:25:39","modified_gmt":"2016-07-16T21:25:39","slug":"resolving-library-symbol-load-errors-when-debugging-with-cross-toolchains","status":"publish","type":"post","link":"https:\/\/sysprogs.com\/w\/resolving-library-symbol-load-errors-when-debugging-with-cross-toolchains\/","title":{"rendered":"Resolving library symbol load errors when debugging with cross-toolchains"},"content":{"rendered":"<p>Using cross-compilation toolchains to build code for your embedded Linux boards (such as <a href=\"http:\/\/gnutoolchains.com\/raspberry\/\">Raspberry PI<\/a>) can be cool. It&#8217;s faster than building your code on a slow embedded box with few RAM and disk space, it&#8217;s easier to edit files directly, it&#8217;s more comfortable to have all necessary files at hand.<\/p>\n<p>There&#8217;s one common problem, however. If you use your cross-compilation toolchain to debug a remote Linux box using GDBServer, you may end up in a situation when GDB silently fails to load symbols for your libraries unless you manually execute the <a href=\"http:\/\/visualgdb.com\/gdbreference\/commands\/sharedlibrary\">sharedlibrary<\/a> command. This article explains why this happens and how to resolve it.<br \/>\n<!--more--><\/p>\n<h2>Shared library load events on Linux<\/h2>\n<p>Unlike Windows where library load\/unload events are generated by the operating system and caught via <strong>WaitForDebugEvent()<\/strong>, linux does not offer an OS-wide mechanism for handling those events. Instead, the dynamic linker (typically called <strong>ld-linux.so<\/strong>) provides a special function called <strong>_dl_debug_state()<\/strong> that is called each time a library load\/unload event occurs. Here&#8217;s the definition of the function from Glibc 2.17:<\/p>\n<pre>\/* This function exists solely to have a breakpoint set on it by the\r\n\u00a0\u00a0 debugger.\u00a0 The debugger is supposed to find this function's address by\r\n\u00a0\u00a0 examining the r_brk member of struct r_debug, but GDB 4.15 in fact looks\r\n\u00a0\u00a0 for this particular symbol name in the PT_INTERP file.\u00a0 *\/\r\nvoid\r\n_dl_debug_state (void)\r\n{\r\n}<\/pre>\n<p>Each time the dynamic linker is asked to load or unload a library, it calls the <strong>_dl_debug_state()<\/strong> function. E.g. here&#8217;s code from <strong>_dl_map_object_from_fd()<\/strong> that notifies debugger that a library was loaded:<\/p>\n<pre>\u00a0\u00a0\u00a0\u00a0\u00a0 \/* Notify the debugger we have added some objects.\u00a0 We need to\r\n\u00a0\u00a0 \u00a0 call _dl_debug_initialize in a static program in case dynamic\r\n\u00a0\u00a0 \u00a0 linking has not been used before.\u00a0 *\/\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 r-&gt;r_state = RT_ADD;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 _dl_debug_state ();<\/pre>\n<p>If a debugger (such as GDB) sets a breakpoint on the <strong>_dl_debug_state()<\/strong> function, it should be able to detect all library load\/unload events and act accordingly (e.g. load the necessary symbols). Let&#8217;s see how GDB accomplishes this.<\/p>\n<h2>The GDB approach<\/h2>\n<p>To see how exactly GDB handles the shared library events, we&#8217;ll use a Windows cross-build of GDB 7.5.1 to debug a Linux program using gdbserver.<\/p>\n<p>In order to distinguish the breakpoints internally created to watch for shared library events, GDB creates them with a special tag using the <strong>create_solib_event_breakpoint()<\/strong> function. If we set a breakpoint on it, let a properly configured GDB attach to a remote Linux target, we&#8217;ll see that the function is called by the <strong>enable_break()<\/strong> function in <strong>solib-svr4.c<\/strong> that is in turn called by the <strong>svr4_solib_create_inferior_hook()<\/strong> function.<\/p>\n<p>The <strong>enable_break()<\/strong> function checks the <strong>debug_base<\/strong> field of the <strong>svr4_info<\/strong> structure. The value comes from parsing the <strong>.dynamic<\/strong> section of the executable (<strong>elf_locate_base()<\/strong> function does the job) and is typically NULL on Linux. In this case GDB will use a different algorithm to find the address:<\/p>\n<p>First of all, GDB will find the name of the dynamic linker library (e.g. <strong>\/lib\/ld-linux.so.2<\/strong>) by calling <strong>find_program_interpreter()<\/strong> that reads it from the main executable file&#8217;s headers.<\/p>\n<p>Then GDB will use its symbol engine to open the <strong>ld-linux.so<\/strong> file (via a call to <strong>solib_bfd_open()<\/strong>). Once the file is opened locally, GDB will use the symbol information from the file to find the address of the function used by the dynamic linker to report library events. GDB will do this by trying to locate one of several predefined functions in the symbol table:<\/p>\n<pre>\/* Now try to set a breakpoint in the dynamic linker.\u00a0 *\/\r\nfor (bkpt_namep = solib_break_names; *bkpt_namep != NULL; bkpt_namep++)\r\n{\r\n\u00a0 sym_addr = gdb_bfd_lookup_symbol (tmp_bfd, cmp_name_and_sec_flags,\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0(void *) *bkpt_namep);\r\n\u00a0 if (sym_addr != 0)\r\n\u00a0\u00a0 \u00a0break;\r\n}<\/pre>\n<p>Here&#8217;s the list of the predefined symbol names that GDB expects to find:<\/p>\n<pre>static const char * const solib_break_names[] =\r\n{\r\n\u00a0 \"r_debug_state\",\r\n\u00a0 \"_r_debug_state\",\r\n\u00a0 \"_dl_debug_state\",\r\n\u00a0 \"rtld_db_dlactivity\",\r\n\u00a0 \"__dl_rtld_db_dlactivity\",\r\n\u00a0 \"_rtld_debug_state\",\r\n\r\n\u00a0 NULL\r\n};<\/pre>\n<p>The <strong>_dl_debug_state()<\/strong> function mentioned above is the third entry in the list.<\/p>\n<h2>The problem<\/h2>\n<p>The big problem with this approach is that GDB uses the local copy of the <strong>ld-linux.so<\/strong> to determine the address of <strong>_dl_debug_state()<\/strong> and then sets a breakpoint at this address inside the remote copy of <strong>ld-linux.so<\/strong>. Everything works like a charm if those copies are identical, however if your local copy gets out of sync with your Linux box (e.g. after updating your Linux system), things get nasty: GDB will silently put a breakpoint at a wrong place without issuing any warning and your library load events will be silently ignored. The easiest way to tell whether your files are out of sync is to run the following command against both local and remote versions of <strong>ld-linux.so:<\/strong><\/p>\n<pre>readelf -s \/lib\/ld-linux.so.2 | grep _dl_debug_state<\/pre>\n<p>The <strong>readelf -s<\/strong> command will dump the symbols listed in the <strong>ld-linux.so<\/strong> file. The grep tool will filter out everything except the line related to <strong>_dl_debug_state<\/strong>:<\/p>\n<pre>17: 0000f9f0 2 FUNC GLOBAL DEFAULT\u00a0\u00a0 11 _dl_debug_state@@GLIBC_PRIVATE<\/pre>\n<p>If the address of _dl_debug_state (<strong>0000f9f0 <\/strong>in the example above) turns out to be different on your remote Linux box and the machine with the cross-toolchain, you have found the cause of your problem.<\/p>\n<h2>Known solutions<\/h2>\n<p>One way of solving this would be to keep your cross-toolchain sysroot folder synchronized with the remote Linux box to ensure that the versions of <strong>ld-linux.so<\/strong> on both sides are binary identical. If you don&#8217;t want to do this, you can tell GDB to automatically download the binaries from your remote machine instead of using the local copies in the cross-toolchain sysroot directory. This is achieved using the <a href=\"http:\/\/visualgdb.com\/gdbreference\/commands\/set_sysroot\">set sysroot<\/a> command:<\/p>\n<pre>set sysroot remote:\/<\/pre>\n<p>This will let GDB use the root directory (\/) on the remote machine (running gdbserver) as the source of the symbol files. Note that you will need to have a fresh build of gdbserver and a fast connection between the two machines to use this option efficiently.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using cross-compilation toolchains to build code for your embedded Linux boards (such as Raspberry PI) can be cool. It&#8217;s faster than building your code on a slow embedded box with few RAM and disk space, it&#8217;s easier to edit files directly, it&#8217;s more comfortable to have all necessary files at hand. There&#8217;s one common problem, &hellip; <a href=\"https:\/\/sysprogs.com\/w\/resolving-library-symbol-load-errors-when-debugging-with-cross-toolchains\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Resolving library symbol load errors when debugging with cross-toolchains<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/posts\/51"}],"collection":[{"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/comments?post=51"}],"version-history":[{"count":2,"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/posts\/51\/revisions"}],"predecessor-version":[{"id":8577,"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/posts\/51\/revisions\/8577"}],"wp:attachment":[{"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/media?parent=51"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/categories?post=51"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sysprogs.com\/w\/wp-json\/wp\/v2\/tags?post=51"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}