/* Copyright (c) 2019-2021, David Anderson All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include /* printf() */ #include /* free() malloc() */ #include /* memcpy() memset() strcmp() strdup() strlen() */ #ifdef HAVE_STDINT_H #include /* uintptr_t */ #endif /* HAVE_STDINT_H */ #if defined _WIN32 #ifdef HAVE_STDAFX_H #include "stdafx.h" #endif /* HAVE_STDAFX_H */ #include /* getcwd */ #elif defined HAVE_UNISTD_H #include /* getcwd */ #endif /* _WIN32 */ #include "dwarf.h" #include "libdwarf.h" #include "libdwarf_private.h" #include "dwarf_base_types.h" #include "dwarf_safe_strcpy.h" #include "dwarf_opaque.h" #include "dwarf_global.h" #include "dwarf_alloc.h" #include "dwarf_error.h" #include "dwarf_util.h" #include "dwarf_string.h" #include "dwarf_debuglink.h" #ifndef O_BINARY #define O_BINARY 0 #endif /* O_BINARY */ #define MINBUFLEN 1000 static char joinchar = '/'; static char* joinstr = "/"; static char joincharw = '\\'; /* Large enough it is unlikely dwarfstring will ever do a malloc here, Windows paths usually < 256 characters. */ #if defined (_WIN32) static char winbuf[256]; #endif /* _WIN32 */ static int is_full_path(char *path) { unsigned char c = path[0]; unsigned char c1 = 0; int okdriveletter = FALSE; if (!c) { /* empty string. */ return FALSE; } if (c == joinchar) { return TRUE; } if (c >= 'c' && c <= 'z') { okdriveletter = TRUE; } else if (c >= 'C' && c <= 'Z') { okdriveletter = TRUE; } if (!okdriveletter) { return FALSE; } c1 = path[1]; if (!c1) { /* No second character */ return FALSE; } if (c1 == ':') { /* Windows full path, we assume We just assume nobody would be silly enough to name a linux/posix directory starting with "C:" */ return TRUE; } /* No kind of full path name */ return FALSE; } static int _dwarf_extract_buildid(Dwarf_Debug dbg, struct Dwarf_Section_s * pbuildid, unsigned *type_returned, char **owner_name_returned, unsigned char **build_id_returned, unsigned *build_id_length_returned, Dwarf_Error *error); struct joins_s { dwarfstring js_dirname; dwarfstring js_basenamesimple; dwarfstring js_basesimpledebug; dwarfstring js_binname; dwarfstring js_cwd; dwarfstring js_originalfullpath; dwarfstring js_linkstring; dwarfstring js_tmp2; dwarfstring js_tmp3; dwarfstring js_buildid; dwarfstring js_buildid_filename; }; static void construct_js(struct joins_s * js) { memset(js,0,sizeof(struct joins_s)); dwarfstring_constructor(&js->js_dirname); dwarfstring_constructor(&js->js_basenamesimple); dwarfstring_constructor(&js->js_basesimpledebug); dwarfstring_constructor(&js->js_cwd); dwarfstring_constructor(&js->js_originalfullpath); dwarfstring_constructor(&js->js_linkstring); dwarfstring_constructor(&js->js_tmp2); dwarfstring_constructor(&js->js_tmp3); dwarfstring_constructor(&js->js_buildid); dwarfstring_constructor(&js->js_buildid_filename); } static void destruct_js(struct joins_s * js) { dwarfstring_destructor(&js->js_dirname); dwarfstring_destructor(&js->js_basenamesimple); dwarfstring_destructor(&js->js_basesimpledebug); dwarfstring_destructor(&js->js_cwd); dwarfstring_destructor(&js->js_originalfullpath); dwarfstring_destructor(&js->js_linkstring); dwarfstring_destructor(&js->js_tmp2); dwarfstring_destructor(&js->js_tmp3); dwarfstring_destructor(&js->js_buildid); dwarfstring_destructor(&js->js_buildid_filename); } /* Altering Windows \ to posix / which is sort of useful in mingw64 msys2. Simply assuming no one would use \ in a file/directory name in posix. */ static void transform_to_posix_slash(dwarfstring * out, char *in) { char *cp = in; int c = 0; for (c = *cp ; c ; ++cp,c = *cp) { if ( *cp == joincharw ) { dwarfstring_append_length(out,joinstr,1); } else { dwarfstring_append_length(out,cp,1); } } } /* We change c: or C: to /c for example */ static void transform_leading_windowsletter(dwarfstring *out, char *in) { char *cp = in; char c = 0; int isupper = FALSE; int islower = FALSE; if (!cp) { /* Nothing to do here. */ return; } c = *cp++; /* Unsure if a, b are normal Windows drive letters. */ if (c && (c >= 'C') && (c <= 'Z')) { isupper = TRUE; } else if (c && (c >= 'c') && (c <= 'z')) { islower = TRUE; } if (isupper || islower) { int c2 = 0; /* ++ here to move past the colon in the input */ c2 = *cp++; if (c2 && (c2 == ':')) { /* Example: Turn uppercase C to c */ char newstr[4]; char newletter = c; if (isupper) { char distance = 'a' - 'A'; newletter = c + distance; } newstr[0] = '/'; newstr[1] = newletter; newstr[2] = 0; dwarfstring_append(out,newstr); } else { cp = in; } } else { cp = in; } transform_to_posix_slash(out,cp); } int _dwarf_pathjoinl(dwarfstring *target,dwarfstring * input) { char *inputs = dwarfstring_string(input); char *targ = dwarfstring_string(target); size_t targlenszt = 0; #if defined (_WIN32) /* Assuming we are in mingw msys2 or equivalent. If we are reading a Windows object file but running non-Windows this won't happen but debuglink won't be useful anyway. */ dwarfstring winput; dwarfstring_constructor_static(&winput,winbuf,sizeof(winbuf)); transform_to_posix_slash(&winput,inputs); targlenszt = dwarfstring_strlen(target); inputs = dwarfstring_string(&winput); if (!targlenszt) { dwarfstring_append(target,inputs); return DW_DLV_OK; } #else /* !_Windows */ targlenszt = dwarfstring_strlen(target); #endif /* _WIN32 */ if (!targlenszt) { dwarfstring_append(target,inputs); return DW_DLV_OK; } targ = dwarfstring_string(target); if (!is_full_path(targ+targlenszt-1)) { if (!is_full_path(inputs)) { dwarfstring_append(target,joinstr); dwarfstring_append(target,inputs); } else { dwarfstring_append(target,inputs); } } else { if (!is_full_path(inputs)) { dwarfstring_append(target,inputs); } else { dwarfstring_append(target,inputs+1); } } #if defined (_WIN32) dwarfstring_destructor(&winput); #endif /* _WIN32 */ return DW_DLV_OK; } /* Find the length of the prefix before the final part of the path. ASSERT: the last character in s is not a / */ static size_t mydirlen(char *s) { char *cp = 0; char *lastjoinchar = 0; size_t count =0; for (cp = s ; *cp ; ++cp,++count) { if (*cp == joinchar) { lastjoinchar = cp; } } if (lastjoinchar) { /* ptrdiff_t is generated but not named */ Dwarf_Unsigned sizetoendjoin = (lastjoinchar >= s)? (Dwarf_Unsigned)(uintptr_t)lastjoinchar - (Dwarf_Unsigned)(uintptr_t)s :(Dwarf_Unsigned)0xffffffff; /* count the last join as mydirlen. */ if (sizetoendjoin == 0xffffffff) { /* impossible. */ return 0; } return sizetoendjoin; } return 0; } struct dwarfstring_list_s { dwarfstring dl_string; struct dwarfstring_list_s *dl_next; }; static void dwarfstring_list_constructor(struct dwarfstring_list_s *l) { dwarfstring_constructor(&l->dl_string); l->dl_next = 0; } static int dwarfstring_list_add_new(struct dwarfstring_list_s * base_entry, struct dwarfstring_list_s *prev, dwarfstring * input, struct dwarfstring_list_s ** new_out, int *errcode) { struct dwarfstring_list_s *next = 0; if (prev) { next = ( struct dwarfstring_list_s *) malloc(sizeof(struct dwarfstring_list_s)); if (!next) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } dwarfstring_list_constructor(next); } else { next = base_entry; } dwarfstring_append(&next->dl_string, dwarfstring_string(input)); if (prev) { prev->dl_next = next; } *new_out = next; return DW_DLV_OK; } /* destructs passed in entry (does not free it) and all those on the dl_next list (those are freed). */ static void dwarfstring_list_destructor(struct dwarfstring_list_s *l) { struct dwarfstring_list_s *curl = l; struct dwarfstring_list_s *nextl = l; nextl = curl->dl_next; dwarfstring_destructor(&curl->dl_string); curl->dl_next = 0; curl = nextl; for ( ; curl ; curl = nextl) { nextl = curl->dl_next; dwarfstring_destructor(&curl->dl_string); curl->dl_next = 0; free(curl); } } static void prepare_linked_name(dwarfstring *out, dwarfstring *dirname, dwarfstring *extradir, dwarfstring *linkname) { dwarfstring_append(out, dwarfstring_string(dirname)); if (extradir) { _dwarf_pathjoinl(out,extradir); } _dwarf_pathjoinl(out,linkname); } static void build_buildid_filename(dwarfstring *target, unsigned buildid_length, unsigned char *buildid) { dwarfstring tmp; unsigned bu = 0; unsigned char *cp = 0; dwarfstring_constructor(&tmp); dwarfstring_append(&tmp,".build-id"); dwarfstring_append(&tmp,joinstr); cp = buildid; for (bu = 0; bu < buildid_length; ++bu ,++cp) { dwarfstring_append_printf_u(&tmp, "%02x",*cp); if (bu == 0) { dwarfstring_append(&tmp,"/"); } } _dwarf_pathjoinl(target,&tmp); dwarfstring_append(target,".debug"); dwarfstring_destructor(&tmp); return; } #if 0 static void dump_bytes(const char *msg,unsigned char * start, unsigned len) { Dwarf_Small *end = start + len; Dwarf_Small *cur = start; printf("%s (0x%lx) ",msg,(unsigned long)start); for (; cur < end; cur++) { printf("%02x", *cur); } printf("\n"); } #endif /*0*/ /* If the compiler or the builder of the object file being read has made a mistake it is possible for a constructed name to match the original file path, yet we do not want to add that to the search path. */ static int duplicatedpath(dwarfstring* l, dwarfstring*r) { if (strcmp(dwarfstring_string(l), dwarfstring_string(r))) { return FALSE; } /* Oops. somebody made a mistake */ return TRUE; } static int _dwarf_do_debuglink_setup(char *link_string_in, dwarfstring * link_string_fullpath_out, char ** global_prefixes_in, unsigned length_global_prefixes_in, struct dwarfstring_list_s **last_entry, struct joins_s *joind, struct dwarfstring_list_s* base_dwlist, int *errcode) { int res = 0; if (!link_string_in) { return DW_DLV_OK; } { /* First try dir of executable */ struct dwarfstring_list_s *now_last = 0; unsigned global_prefix_number = 0; dwarfstring_append(&joind->js_linkstring, link_string_in); dwarfstring_reset(&joind->js_tmp3); dwarfstring_reset(&joind->js_tmp2); prepare_linked_name(&joind->js_tmp3, &joind->js_dirname, 0,&joind->js_linkstring); dwarfstring_append( link_string_fullpath_out, dwarfstring_string(&joind->js_tmp3)); if (!duplicatedpath(&joind->js_originalfullpath, &joind->js_tmp3)) { res = dwarfstring_list_add_new( base_dwlist, *last_entry,&joind->js_tmp3, &now_last,errcode); if (res != DW_DLV_OK) { return res; } *last_entry = now_last; } dwarfstring_reset(&joind->js_tmp2); dwarfstring_reset(&joind->js_tmp3); dwarfstring_append(&joind->js_tmp3,".debug"); prepare_linked_name(&joind->js_tmp2, &joind->js_dirname, &joind->js_tmp3,&joind->js_linkstring); if (! duplicatedpath(&joind->js_originalfullpath, &joind->js_tmp2)) { res = dwarfstring_list_add_new( base_dwlist, *last_entry,&joind->js_tmp2, &now_last,errcode); if (res != DW_DLV_OK) { return res; } *last_entry = now_last; } else { } /* Now look in the global locations. */ for (global_prefix_number = 0; global_prefix_number < length_global_prefixes_in; ++global_prefix_number) { char * prefix = global_prefixes_in[global_prefix_number]; dwarfstring_reset(&joind->js_tmp2); dwarfstring_reset(&joind->js_tmp3); dwarfstring_append(&joind->js_tmp2, prefix); prepare_linked_name(&joind->js_tmp3, &joind->js_tmp2, &joind->js_dirname, &joind->js_linkstring); if (!duplicatedpath(&joind->js_originalfullpath, &joind->js_tmp3)) { res = dwarfstring_list_add_new( base_dwlist, *last_entry,&joind->js_tmp3, &now_last,errcode); if (res != DW_DLV_OK) { return res; } *last_entry = now_last; } else { } } } return DW_DLV_OK; } static int _dwarf_do_buildid_setup(unsigned buildid_length, char ** global_prefixes_in, unsigned length_global_prefixes_in, struct dwarfstring_list_s **last_entry, struct joins_s *joind, struct dwarfstring_list_s* base_dwlist, int *errcode) { unsigned global_prefix_number = 0; int res = 0; if (!buildid_length) { return DW_DLV_OK; } for (global_prefix_number = 0; buildid_length && (global_prefix_number < length_global_prefixes_in); ++global_prefix_number) { char * prefix = 0; struct dwarfstring_list_s *now_last = 0; prefix = global_prefixes_in[global_prefix_number]; dwarfstring_reset(&joind->js_buildid); dwarfstring_append(&joind->js_buildid,prefix); _dwarf_pathjoinl(&joind->js_buildid, &joind->js_buildid_filename); res = dwarfstring_list_add_new( base_dwlist, *last_entry,&joind->js_buildid, &now_last,errcode); if (res != DW_DLV_OK) { return res; } *last_entry = now_last; } return DW_DLV_OK; } /* New September 2019. Access to the GNU section named .gnu_debuglink See https://sourceware.org/gdb/onlinedocs/ gdb/Separate-Debug-Files.html The default global path was set to /usr/lib/debug by set_global_paths_init() called at libdwarf object init time. So there is always the default global path present. From the above web page: For the “debug link” method, GDB looks up the named file in the directory of the executable file, then in a subdirectory of that directory named .debug, and finally under each one of the global debug directories, in a subdirectory whose name is identical to the leading directories of the executable’s absolute file name. (On MS-Windows/MS-DOS, the drive letter of the executable’s leading directories is converted to a one-letter subdirectory, i.e. d:/usr/bin/ is converted to /d/usr/bin/, because Windows filesystems disallow colons in file names.) For the “build ID” method, GDB looks in the .build-id subdirectory of each one of the global debug directories for a file named nn/nnnnnnnn.debug, where nn are the first 2 hex characters of the build ID bit string, and nnnnnnnn are the rest of the bit string. (Real build ID strings are 32 or more hex characters, not 10.) GDB can automatically query debuginfod servers using build IDs in order to download separate debug files that cannot be found locally. */ int _dwarf_construct_linkedto_path( char **global_prefixes_in, unsigned length_global_prefixes_in, char *pathname_in, char *link_string_in, /* from debug link */ dwarfstring *link_string_fullpath_out, unsigned char *buildid, /* from gnu buildid */ unsigned buildid_length, /* from gnu buildid */ char ***paths_out, unsigned *paths_out_length, int *errcode) { char * pathname = pathname_in; int res = 0; struct joins_s joind; size_t dirnamelen = 0; struct dwarfstring_list_s base_dwlist; struct dwarfstring_list_s *last_entry = 0; dwarfstring_list_constructor(&base_dwlist); construct_js(&joind); dirnamelen = mydirlen(pathname); if (dirnamelen) { /* Original dirname, before cwd (if needed) */ dwarfstring_append_length(&joind.js_tmp2, pathname,dirnamelen); } if (!is_full_path(pathname)) { /* Meaning a/b or b, not /a/b or /b , so we apply cwd */ char buffer[2000]; unsigned buflen= sizeof(buffer); char *wdret = 0; buffer[0] = 0; wdret = getcwd(buffer,buflen); if (!wdret) { /* printf("getcwd() issue. Do nothing. " " line %d %s\n",__LINE__,__FILE__); */ dwarfstring_list_destructor(&base_dwlist); destruct_js(&joind); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } dwarfstring_append(&joind.js_cwd,buffer); } /* Applies to the leading chars in the named file */ if (dwarfstring_strlen(&joind.js_tmp2) > 0) { transform_leading_windowsletter(&joind.js_dirname, dwarfstring_string(&joind.js_tmp2)); } dwarfstring_reset(&joind.js_tmp2); /* Creating a real basename. No slashes. */ dwarfstring_append(&joind.js_basenamesimple, pathname+dirnamelen); dwarfstring_append(&joind.js_basesimpledebug, dwarfstring_string(&joind.js_basenamesimple)); dwarfstring_append(&joind.js_basesimpledebug,".debug"); /* Now js_dirname / js_basenamesimple are reflecting a full path */ /* saves the full path to the original executable */ dwarfstring_append(&joind.js_originalfullpath, dwarfstring_string(&joind.js_dirname)); _dwarf_pathjoinl(&joind.js_originalfullpath, &joind.js_basenamesimple); build_buildid_filename(&joind.js_buildid_filename, buildid_length, buildid); /* The GNU buildid method of finding debug files. No crc calculation required ever. */ res = _dwarf_do_buildid_setup(buildid_length, global_prefixes_in, length_global_prefixes_in, &last_entry, &joind, &base_dwlist,errcode); if (res == DW_DLV_ERROR) { dwarfstring_list_destructor(&base_dwlist); destruct_js(&joind); return res; } /* The debug link method of finding debug files. A crc calculation is required for full checking. Which can be slow if an object file is large. */ res = _dwarf_do_debuglink_setup(link_string_in, link_string_fullpath_out, global_prefixes_in, length_global_prefixes_in, &last_entry, &joind,&base_dwlist,errcode); if (res == DW_DLV_ERROR) { dwarfstring_list_destructor(&base_dwlist); destruct_js(&joind); return res; } /* Now malloc space for the pointer array and the strings they point at so a simple free by our caller will clean up everything. Copy the data from base_dwlist to the new area. */ { struct dwarfstring_list_s *cur = 0; char **resultfullstring = 0; unsigned long count = 0; size_t pointerarraysize = 0; size_t sumstringlengths = 0; size_t totalareasize = 0; size_t setptrindex = 0; size_t setstrindex = 0; cur = &base_dwlist; for ( ; cur ; cur = cur->dl_next) { ++count; pointerarraysize += sizeof(void *); sumstringlengths += dwarfstring_strlen(&cur->dl_string) +1; } /* Make a final null pointer in the pointer array. */ pointerarraysize += sizeof(void *); totalareasize = pointerarraysize + sumstringlengths +8; resultfullstring = (char **)malloc(totalareasize); setstrindex = pointerarraysize; if (!resultfullstring) { dwarfstring_list_destructor(&base_dwlist); destruct_js(&joind); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } memset(resultfullstring,0,totalareasize); cur = &base_dwlist; for ( ; cur ; cur = cur->dl_next,++setptrindex) { char **iptr = (char **)((char *)resultfullstring + setptrindex*sizeof(void *)); char *sptr = (char*)resultfullstring + setstrindex; char *msg = dwarfstring_string(&cur->dl_string); size_t slen = dwarfstring_strlen(&cur->dl_string); _dwarf_safe_strcpy(sptr,totalareasize - setstrindex, msg,slen); setstrindex += slen +1; *iptr = sptr; } *paths_out = resultfullstring; *paths_out_length = count; } dwarfstring_list_destructor(&base_dwlist); destruct_js(&joind); return DW_DLV_OK; } static int _dwarf_extract_debuglink(Dwarf_Debug dbg, struct Dwarf_Section_s * pdebuglink, char ** name_returned, /* static storage, do not free */ unsigned char ** crc_returned, /* 32bit crc , do not free */ Dwarf_Error *error) { Dwarf_Small *ptr = 0; Dwarf_Small *endptr = 0; size_t namelenszt = 0; unsigned m = 0; unsigned incr = 0; Dwarf_Small *crcptr = 0; int res = DW_DLV_ERROR; Dwarf_Unsigned secsize = 0; if (!pdebuglink->dss_data) { return DW_DLV_NO_ENTRY; } secsize = pdebuglink->dss_size; ptr = pdebuglink->dss_data; endptr = ptr + secsize; res = _dwarf_check_string_valid(dbg,ptr, ptr, endptr, DW_DLE_FORM_STRING_BAD_STRING, error); if ( res != DW_DLV_OK) { return res; } namelenszt = strlen((const char*)ptr); m = (namelenszt+1) %4; if (m) { incr = 4 - m; } crcptr = (unsigned char *)ptr +namelenszt +1 +incr; if ((crcptr +4) != (unsigned char*)endptr) { _dwarf_error(dbg,error,DW_DLE_CORRUPT_GNU_DEBUGLINK); return DW_DLV_ERROR; } *name_returned = (char *)ptr; *crc_returned = crcptr; return DW_DLV_OK; } /* The definition of .note.gnu.buildid contents (also used for other GNU .note.gnu. sections too. */ struct buildid_s { char bu_ownernamesize[4]; char bu_buildidsize[4]; char bu_type[4]; char bu_owner[1]; }; static int _dwarf_extract_buildid(Dwarf_Debug dbg, struct Dwarf_Section_s * pbuildid, unsigned * type_returned, char **owner_name_returned, unsigned char **build_id_returned, unsigned * build_id_length_returned, Dwarf_Error *error) { Dwarf_Small * ptr = 0; Dwarf_Small * endptr = 0; int res = DW_DLV_ERROR; struct buildid_s *bu = 0; Dwarf_Unsigned namesize = 0; Dwarf_Unsigned descrsize = 0; Dwarf_Unsigned type = 0; Dwarf_Unsigned finalsize; Dwarf_Unsigned secsize = 0; if (!pbuildid->dss_data) { return DW_DLV_NO_ENTRY; } secsize = pbuildid->dss_size; ptr = pbuildid->dss_data; if (secsize < sizeof(struct buildid_s)) { _dwarf_error(dbg,error,DW_DLE_CORRUPT_NOTE_GNU_DEBUGID); return DW_DLV_ERROR; } endptr = ptr + secsize; /* We hold gh_content till all is closed as we return pointers into it if all goes well. */ bu = (struct buildid_s *)ptr; ASNAR(dbg->de_copy_word,namesize, bu->bu_ownernamesize); ASNAR(dbg->de_copy_word,descrsize,bu->bu_buildidsize); if (descrsize >= secsize) { _dwarf_error_string(dbg,error, DW_DLE_BUILD_ID_DESCRIPTION_SIZE, "DW_DLE_BUILD_ID_DESCRIPTION_SIZE Size is much too " "large to be correct. Corrupt Dwarf"); return DW_DLV_ERROR; } if ((descrsize+8) >= secsize) { _dwarf_error_string(dbg,error, DW_DLE_BUILD_ID_DESCRIPTION_SIZE, "DW_DLE_BUILD_ID_DESCRIPTION_SIZE Size is too " "large to be correct. Corrupt Dwarf"); return DW_DLV_ERROR; } if (descrsize >= DW_BUILDID_SANE_SIZE) { _dwarf_error_string(dbg,error, DW_DLE_BUILD_ID_DESCRIPTION_SIZE, "DW_DLE_BUILD_ID_DESCRIPTION_SIZE Size is too " "large to be sane. Corrupt Dwarf"); return DW_DLV_ERROR; } ASNAR(dbg->de_copy_word,type, bu->bu_type); /* descrsize test is no longer appropriate, other lengths than 20 may exist. --Wl,--build-id= can have at least one of 'fast', 'md5', 'sha1', 'uuid' or possibly others so we should not check the length. */ res = _dwarf_check_string_valid(dbg, (Dwarf_Small *)&bu->bu_owner[0], (Dwarf_Small *)&bu->bu_owner[0], endptr, DW_DLE_CORRUPT_GNU_DEBUGID_STRING, error); if ( res != DW_DLV_OK) { return res; } if ((strlen(bu->bu_owner) +1) != namesize) { _dwarf_error(dbg,error, DW_DLE_CORRUPT_GNU_DEBUGID_STRING); return DW_DLV_ERROR; } finalsize = sizeof(struct buildid_s)-1 + namesize + descrsize; if (finalsize > secsize) { _dwarf_error(dbg,error, DW_DLE_CORRUPT_GNU_DEBUGID_SIZE); return DW_DLV_ERROR; } *type_returned = (unsigned)type; *owner_name_returned = &bu->bu_owner[0]; if (descrsize >= secsize) { /* In case value insanely large */ dwarfstring m; dwarfstring_constructor(&m); dwarfstring_append_printf_u(&m, "DW_DLE_CORRUPT_NOTE_GNU_DEBUGID buildid description" "length %u larger than the section size. " "Corrupt object section",descrsize); _dwarf_error_string(dbg,error, DW_DLE_CORRUPT_GNU_DEBUGID_SIZE, dwarfstring_string(&m)); dwarfstring_destructor(&m); return DW_DLV_ERROR; } if ((descrsize+8) >= secsize) { /* In case a bit too large */ dwarfstring m; dwarfstring_constructor(&m); dwarfstring_append_printf_u(&m, "DW_DLE_CORRUPT_NOTE_GNU_DEBUGID buildid description" "length %u larger than is appropriate. " "Corrupt object section",descrsize); _dwarf_error_string(dbg,error, DW_DLE_CORRUPT_GNU_DEBUGID_SIZE, dwarfstring_string(&m)); dwarfstring_destructor(&m); return DW_DLV_ERROR; } *build_id_length_returned = (unsigned)descrsize; *build_id_returned = (unsigned char *)ptr + sizeof(struct buildid_s)-1 + namesize; return DW_DLV_OK; } /* Caller frees space returned by debuglink_fillpath_returned and The following return pointers into the dbg itself and are only valid while that dbg is open. debuglink_path_returned. crc_returned, buildid_owner_name_returned, buildid_returned, */ int dwarf_gnu_debuglink(Dwarf_Debug dbg, char ** debuglink_path_returned, /* do not free*/ unsigned char ** crc_returned, char ** debuglink_fullpath_returned, /* caller frees */ unsigned * debuglink_fullpath_length_returned, unsigned * buildid_type_returned , char ** buildid_owner_name_returned, unsigned char ** buildid_returned, unsigned * buildid_length_returned, char *** paths_returned, unsigned * paths_count_returned, Dwarf_Error* error) { dwarfstring debuglink_fullpath; int res = DW_DLV_ERROR; char * pathname = 0; int errcode = 0; struct Dwarf_Section_s * pdebuglink = 0; struct Dwarf_Section_s * pbuildid = 0; if (dbg->de_gnu_debuglink.dss_size) { pdebuglink = &dbg->de_gnu_debuglink; res = _dwarf_load_section(dbg, pdebuglink,error); if (res == DW_DLV_ERROR) { return res; } } if (dbg->de_note_gnu_buildid.dss_size) { pbuildid = &dbg->de_note_gnu_buildid; res = _dwarf_load_section(dbg, pbuildid,error); if (res == DW_DLV_ERROR) { return res; } } if (!pdebuglink && !pbuildid) { *debuglink_fullpath_returned = strdup(dbg->de_path); *debuglink_fullpath_length_returned = (unsigned)strlen(dbg->de_path); return DW_DLV_OK; } if (pbuildid) { /* buildid does not involve calculating a crc at any point */ int buildidres = 0; buildidres = _dwarf_extract_buildid(dbg, pbuildid, buildid_type_returned, buildid_owner_name_returned, buildid_returned, buildid_length_returned, error); if (buildidres == DW_DLV_ERROR) { return buildidres; } } if (pdebuglink) { int linkres = 0; linkres = _dwarf_extract_debuglink(dbg, pdebuglink, debuglink_path_returned, crc_returned, error); if (linkres == DW_DLV_ERROR) { return linkres; } } dwarfstring_constructor(&debuglink_fullpath); pathname = (char *)dbg->de_path; if (pathname && paths_returned) { /* Caller wants paths created and returned. */ res = _dwarf_construct_linkedto_path( (char **)dbg->de_gnu_global_paths, dbg->de_gnu_global_path_count, pathname, *debuglink_path_returned, &debuglink_fullpath, *buildid_returned, *buildid_length_returned, paths_returned, paths_count_returned, &errcode); if (res == DW_DLV_ERROR) { dwarfstring_destructor(&debuglink_fullpath); return res; } if (dwarfstring_strlen(&debuglink_fullpath)) { *debuglink_fullpath_returned = strdup(dwarfstring_string(&debuglink_fullpath)); *debuglink_fullpath_length_returned = (unsigned)dwarfstring_strlen(&debuglink_fullpath); } } else if (paths_count_returned) { *paths_count_returned = 0; } else { /* nothing special to do */ } dwarfstring_destructor(&debuglink_fullpath); return DW_DLV_OK; } /* This should be rarely called and most likely only once (at dbg init time from dwarf_generic_init.c, see set_global_paths_init()). Maybe once or twice later. */ int dwarf_add_debuglink_global_path(Dwarf_Debug dbg, const char *pathname, Dwarf_Error *error) { unsigned glpath_count_in = 0; unsigned glpath_count_out = 0; char **glpaths = 0; char *path1 = 0; glpath_count_in = dbg->de_gnu_global_path_count; glpath_count_out = glpath_count_in+1; glpaths = (char **)malloc(sizeof(char *)* glpath_count_out); if (!glpaths) { _dwarf_error(dbg,error,DW_DLE_ALLOC_FAIL); return DW_DLV_ERROR; } if (glpath_count_in) { memcpy(glpaths, dbg->de_gnu_global_paths, sizeof(char *)*glpath_count_in); } path1 = strdup(pathname); if (!path1) { free(glpaths); _dwarf_error(dbg,error,DW_DLE_ALLOC_FAIL); return DW_DLV_ERROR; } free(dbg->de_gnu_global_paths); glpaths[glpath_count_in] = path1; dbg->de_gnu_global_paths = (const char **)glpaths; dbg->de_gnu_global_path_count = glpath_count_out; return DW_DLV_OK; }