/*
Copyright (c) 2019, 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 "config.h"
#include <stdio.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif /* HAVE_MALLOC_H */
#ifdef HAVE_STDDEF_H
#include <stddef.h> /* ptrdiff_t */
#endif /* HAVE_STDDEF_H */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */
#include <string.h>
#ifdef HAVE_ELF_H
#include <elf.h>
#endif /* HAVE_ELF_H */
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* getcwd */
#endif /* HAVE_UNISTD_H */
#if 0
#include <sys/types.h> /* for open() */
#include <sys/stat.h> /* for open() */
#include <fcntl.h> /* for open() */
#include <errno.h>
#endif
#include "dwarf_incl.h"
#include "dwarf_alloc.h"
#include "dwarf_error.h"
#include "dwarf_util.h"
#include "dwarfstring.h"
#include "dwarf_debuglink.h"

#ifndef O_BINARY
#define O_BINARY 0
#endif /* O_BINARY */

#define MINBUFLEN 1000
#define TRUE  1
#define FALSE 0

#ifdef HAVE_UNUSED_ATTRIBUTE
#define  UNUSEDARG __attribute__ ((unused))
#else
#define  UNUSEDARG
#endif


#if _WIN32
#define NULL_DEVICE_NAME "NUL"
#else
#define NULL_DEVICE_NAME "/dev/null"
#endif /* _WIN32 */

#ifdef WORDS_BIGENDIAN
#define ASNAR(func,t,s)                         \
    do {                                        \
        unsigned tbyte = sizeof(t) - sizeof(s); \
        t = 0;                                  \
        func(((char *)&t)+tbyte ,&s[0],sizeof(s));  \
    } while (0)
#else /* LITTLE ENDIAN */
#define ASNAR(func,t,s)                         \
    do {                                        \
        t = 0;                                  \
        func(&t,&s[0],sizeof(s));               \
    } while (0)
#endif /* end LITTLE- BIG-ENDIAN */

static int
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 {
    char * js_fullpath;
    dwarfstring js_dirname;
    dwarfstring js_basepath;
    dwarfstring js_basename;
    dwarfstring js_cwd;
    dwarfstring js_originalfullpath;
    dwarfstring js_tmp;
    dwarfstring js_tmp2;
    dwarfstring js_tmpdeb;
    dwarfstring js_tmp3;
    dwarfstring js_buildid;
    dwarfstring js_buildid_filename;
};

#if 0
int
_dwarf_check_string_valid(
    void *areaptr,
    void *strptr,
    void *areaendptr,
    int suggested_error,
    int *errcode)
{
    Dwarf_Small *start = areaptr;
    Dwarf_Small *p = strptr;
    Dwarf_Small *end = areaendptr;
    ptrdiff_t diff =  0;

    if (p < start) {
        diff = start - p;
#ifdef  TESTING
        printf("Error  string start  pointer error: loc %"
            DW_PR_DSs
            " bytes before available area \n",(Dwarf_Signed)diff);
#endif  /* TESTING */
        *errcode = suggested_error;
        return DW_DLV_ERROR;
    }
    if (p >= end) {
        diff = p - start;
#ifdef  TESTING
        printf("Error  string end  pointer error, not terminated %"
            " before end of area. Length:  "
            DW_PR_DSs  "\n",(Dwarf_Signed)diff);
#endif  /* TESTING */
        *errcode = suggested_error;
        return DW_DLV_ERROR;
    }
    while (p < end) {
        if (*p == 0) {
            return DW_DLV_OK;
        }
        ++p;
    }
    diff =  p - start;
#ifdef  TESTING
    printf("Error string not terminated error:  not ended after %"
        DW_PR_DSs " bytes (past end of available bytes)\n",
        (Dwarf_Signed)diff);
#endif  /* TESTING */
    *errcode = DW_DLE_STRING_NOT_TERMINATED;
    return DW_DLV_ERROR;
}
#endif


#if 0
static int
does_file_exist(char *f)
{
    int fd = 0;

    fd = open(f,O_RDONLY|O_BINARY);
    if (fd < 0) {
        return DW_DLV_NO_ENTRY;
    }
    /* Here we could derive the crc to validate the file. */
    close(fd);
    return DW_DLV_OK;
}
#endif


static void
construct_js(struct joins_s * js)
{
    memset(js,0,sizeof(struct joins_s));
    dwarfstring_constructor(&js->js_basename);
    dwarfstring_constructor(&js->js_dirname);
    dwarfstring_constructor(&js->js_basepath);
    dwarfstring_constructor(&js->js_cwd);
    dwarfstring_constructor(&js->js_originalfullpath);
    dwarfstring_constructor(&js->js_tmp);
    dwarfstring_constructor(&js->js_tmp2);
    dwarfstring_constructor(&js->js_tmpdeb);
    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_basepath);
    dwarfstring_destructor(&js->js_basename);
    dwarfstring_destructor(&js->js_cwd);
    dwarfstring_destructor(&js->js_originalfullpath);
    dwarfstring_destructor(&js->js_tmp);
    dwarfstring_destructor(&js->js_tmp2);
    dwarfstring_destructor(&js->js_tmpdeb);
    dwarfstring_destructor(&js->js_tmp3);
    dwarfstring_destructor(&js->js_buildid);
    dwarfstring_destructor(&js->js_buildid_filename);
}

static char joinchar = '/';
static char* joinstr = "/";

int
_dwarf_pathjoinl(dwarfstring *target,dwarfstring * input)
{
    char *inputs = dwarfstring_string(input);
    char *targ = dwarfstring_string(target);
    size_t targlen = 0;

    if (!dwarfstring_strlen(target)) {
        dwarfstring_append(target,dwarfstring_string(input));
        return DW_DLV_OK;
    }
    targlen = dwarfstring_strlen(target);
    targ = dwarfstring_string(target);
    if (targ[targlen-1] != joinchar) {
        if (*inputs != joinchar) {
            dwarfstring_append(target,joinstr);
            dwarfstring_append(target,inputs);
        } else {
            dwarfstring_append(target,inputs);
        }
    } else {
        if (*inputs != joinchar) {
            dwarfstring_append(target,inputs);
        } else {
            dwarfstring_append(target,inputs+1);
        }
    }
    return DW_DLV_OK;
}
/*  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) {
        /* we know diff is postive in all cases */
        ptrdiff_t diff =  lastjoinchar - s;
        /* count the last join as mydirlen. */
        return (size_t)(diff+1);
    }
    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
build_buildid_filename(dwarfstring *target,
    unsigned buildid_length,
    unsigned char *buildid)
{
    dwarfstring tmp;
    unsigned bu = 0;
    unsigned char *cp  = 0;
    char lbuf[10];

    dwarfstring_constructor(&tmp);
    cp = buildid;
    for (bu = 0; bu < buildid_length; ++bu ,++cp) {
        sprintf(lbuf,"%02x",*cp);
        dwarfstring_append(&tmp,lbuf);
        if (bu == 0) {
            dwarfstring_append(&tmp,"/");
        }
    }
    dwarfstring_append(&tmp,".debug");
    _dwarf_pathjoinl(target,&tmp);
    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


/*  New September 2019.  Access to the GNU section named
    .gnu_debuglink
    See
    https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html

*/
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,
   UNUSEDARG unsigned char *crc_in, /* from debug_link, 4 bytes */
   unsigned char *buildid, /* from gnu buildid */
   unsigned       buildid_length, /* from gnu buildid */
   char        ***paths_out,
   unsigned      *paths_out_length,
   int *errcode)
{
    char * depath = 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;
    unsigned global_prefix_number = 0;

    dwarfstring_list_constructor(&base_dwlist);
    construct_js(&joind);
    build_buildid_filename(&joind.js_buildid_filename,
        buildid_length, buildid);
    dirnamelen = mydirlen(depath);
    if (dirnamelen) {
        dwarfstring_append_length(&joind.js_dirname,
            depath,dirnamelen);
    }
    dwarfstring_append(&joind.js_basepath,depath+dirnamelen);
    dwarfstring_append(&joind.js_basename,link_string_in);
    if (depath[0] != joinchar) {
        char  buffer[2000];
#ifdef TESTING
        buffer[0] = 0;
        /*  For testing lets use a fake (consistent)
            base dir.  */
        strcpy(buffer,"/fake/dir/path");
#else
        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;
        }
#endif /* TESTING */
        dwarfstring_append(&joind.js_cwd,buffer);
        buffer[0] = 0;
    }

    {
        /*  Builds the full path to the original
            executable, but absent executable name. */
        dwarfstring_append(&joind.js_originalfullpath,
            dwarfstring_string(&joind.js_cwd));
        _dwarf_pathjoinl(&joind.js_originalfullpath,
            &joind.js_dirname);
        _dwarf_pathjoinl(&joind.js_originalfullpath,
            &joind.js_basepath);
#ifdef TESTING
        printf("originalfullpath    : %s\n",
            dwarfstring_string(&joind.js_originalfullpath));
#endif
    }
    {
        /*  There is perhaps a directory prefix in the
            incoming pathname.
            So we add that to js_cwd. */
        res = _dwarf_pathjoinl(&joind.js_cwd,
            &joind.js_dirname);
        /* This is used in a couple search paths. */
    }
    for (global_prefix_number = 0;
        buildid_length &&
        (global_prefix_number < length_global_prefixes_in);
        ++global_prefix_number) {
        char * prefix = 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);
        if (!strcmp(dwarfstring_string(&joind.js_originalfullpath),
            dwarfstring_string(&joind.js_buildid))) {
#ifdef TESTING
            printf("duplicated output string %s\n",
                dwarfstring_string(&joind.js_buildid));
#endif /* TESTING */
            /* duplicated name. spurious match. */
        } else {
            struct dwarfstring_list_s *now_last = 0;
            res = dwarfstring_list_add_new(
                &base_dwlist,
                last_entry,&joind.js_buildid,
                &now_last,errcode);
            if(res != DW_DLV_OK) {
                dwarfstring_list_destructor(&base_dwlist);
                destruct_js(&joind);
                return res;
            }
            last_entry = now_last;
        }
    }
    if (link_string_in) {
        /* js_cwd is a leading / directory name. */
        {
            dwarfstring_reset(&joind.js_tmp);
            dwarfstring_append(&joind.js_tmp,
                dwarfstring_string(&joind.js_cwd));
            /* If we add basename do we find what we look for? */
            res = _dwarf_pathjoinl(&joind.js_tmp,&joind.js_basename);
            /* We return the original link as full path this way. */
            dwarfstring_append(link_string_fullpath_out,
                dwarfstring_string(&joind.js_tmp));
            if (!strcmp(dwarfstring_string(&joind.js_originalfullpath),
                dwarfstring_string(&joind.js_tmp))) {
#ifdef TESTING
                printf("duplicated output string %s\n",
                    dwarfstring_string(&joind.js_tmp));
#endif /* TESTING */
                /* duplicated name. spurious match. */
            } else if (res == DW_DLV_OK) {
                struct dwarfstring_list_s *now_last = 0;
                res = dwarfstring_list_add_new(
                    &base_dwlist,
                    last_entry,&joind.js_tmp,
                    &now_last,errcode);
                if(res != DW_DLV_OK) {
                    dwarfstring_list_destructor(&base_dwlist);
                    destruct_js(&joind);
                    return res;
                }
                last_entry = now_last;
            }
        }
        {
            dwarfstring_reset(&joind.js_tmp2);
            dwarfstring_reset(&joind.js_tmpdeb);

            dwarfstring_append(&joind.js_tmp2,
                dwarfstring_string(&joind.js_cwd));
            dwarfstring_append(&joind.js_tmpdeb,".debug");
            res = _dwarf_pathjoinl(&joind.js_tmp2,&joind.js_tmpdeb);
            if (res == DW_DLV_OK) {
                res = _dwarf_pathjoinl(&joind.js_tmp2,
                    &joind.js_basename);
                /*  this the second search path
                    after global directories
                    search for nn/nnnnn....debug.   */
                if (!strcmp(dwarfstring_string(
                    &joind.js_originalfullpath),
                    dwarfstring_string(&joind.js_tmp2))) {
#ifdef TESTING
                printf("duplicated output string %s\n",
                    dwarfstring_string(&joind.js_tmp2));
#endif /* TESTING */
                    /* duplicated name. spurious match. */
                } else if(res == DW_DLV_OK) {
                    struct dwarfstring_list_s *now_last = 0;
                    res = dwarfstring_list_add_new(
                        &base_dwlist,
                        last_entry,&joind.js_tmp2,
                        &now_last,errcode);
                    if(res != DW_DLV_OK) {
                        dwarfstring_list_destructor(&base_dwlist);
                        destruct_js(&joind);
                        return res;
                    }
                    last_entry = now_last;
                }
            }
        }
        /*  Not found above, 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_tmp3);
            dwarfstring_append(&joind.js_tmp3, prefix);
            res = _dwarf_pathjoinl(&joind.js_tmp3, &joind.js_cwd);
            if (res == DW_DLV_OK) {
                res = _dwarf_pathjoinl(&joind.js_tmp3,
                    &joind.js_basename);
                if (!strcmp(dwarfstring_string(
                    &joind.js_originalfullpath),
                    dwarfstring_string(&joind.js_tmp3))) {
                    /* duplicated name. spurious match. */
#ifdef TESTING
                    printf("duplicated output string %s\n",
                        dwarfstring_string(&joind.js_tmp3));
#endif /* TESTING */
                } else if (res == DW_DLV_OK) {
                    struct dwarfstring_list_s *now_last = 0;
                    res = dwarfstring_list_add_new(
                        &base_dwlist,
                        last_entry,&joind.js_tmp3,
                        &now_last,errcode);
                    if(res != DW_DLV_OK) {
                        dwarfstring_list_destructor(&base_dwlist);
                        destruct_js(&joind);
                        return res;
                    }
                    last_entry = now_last;
                }
            }
        }
    }

    {
        struct dwarfstring_list_s *cur = 0;
        char **resultfullstring = 0;

        unsigned long count = 0;
        unsigned long pointerarraysize = 0;
        unsigned long sumstringlengths = 0;
        unsigned long totalareasize = 0;
        unsigned long setptrindex = 0;
        unsigned long 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) {
#ifdef TESTING
            printf("Malloc fail making final paths. Length %lu"
                " bytes.\n",totalareasize);
#endif /* TESTING */
            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;

            strcpy(sptr,dwarfstring_string(&cur->dl_string));
            setstrindex += dwarfstring_strlen(&cur->dl_string)+1;
            *iptr = sptr;
        }
        *paths_out = resultfullstring;
        *paths_out_length = count;
    }
    dwarfstring_list_destructor(&base_dwlist);
    destruct_js(&joind);
    return DW_DLV_OK;
}

static int
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;
    unsigned namelen = 0;
    unsigned m = 0;
    unsigned incr = 0;
    Dwarf_Small *crcptr = 0;
    int res = DW_DLV_ERROR;
    Dwarf_Unsigned secsize = 0;

    if (!pdebuglink->dss_data) {
        res = _dwarf_load_section(dbg, pdebuglink,error);
        if (res != DW_DLV_OK) {
            return res;
        }
    }
    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;
    }
    namelen = (unsigned)strlen((const char*)ptr);
    m = (namelen+1) %4;
    if (m) {
        incr = 4 - m;
    }
    crcptr = (unsigned char *)ptr +namelen +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
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) {
        res = _dwarf_load_section(dbg, pbuildid,error);
        if (res != DW_DLV_OK) {
            return res;
        }
    }
    secsize = pbuildid->dss_size;
    ptr = pbuildid->dss_data;
    if (secsize < sizeof(struct buildid_s)) {
#ifdef  TESTING
        printf("ERROR section .note.gnu.build-id too small: "
            " section length: 0x%" DW_PR_DUx
            " minimum struct size 0x%" DW_PR_DUx  "\n",
            secsize,(Dwarf_Unsigned) sizeof(struct buildid_s));
#endif /* TESTING */
        _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);
    ASNAR(dbg->de_copy_word,type,     bu->bu_type);
    if (descrsize != 20) {
        _dwarf_error(dbg,error,DW_DLE_CORRUPT_NOTE_GNU_DEBUGID);
        return DW_DLV_ERROR;
    }
    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 res;
    }

    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 = type;
    *owner_name_returned = &bu->bu_owner[0];
    *build_id_length_returned = descrsize;
    *build_id_returned = (unsigned char *)ptr +
        sizeof(struct buildid_s)-1 + namesize;
    return DW_DLV_OK;
}

/* */
int dwarf_gnu_debuglink(Dwarf_Debug dbg,
    char     **  debuglink_path_returned,
    unsigned char **  crc_returned,
    char     **  debuglink_fullpath_returned,
    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 linkres = DW_DLV_ERROR;
    int res = DW_DLV_ERROR;
    char * pathname = 0;
    int buildidres = 0;
    int errcode = 0;
    struct Dwarf_Section_s * pdebuglink = 0;
    struct Dwarf_Section_s * pbuildid = 0;

    if(!dbg) {
        _dwarf_error(dbg,error,DW_DLE_DBG_NULL);
        return DW_DLV_ERROR;
    }
    if (dbg->de_gnu_debuglink.dss_size) {
        pdebuglink = &dbg->de_gnu_debuglink;
    }
    if (dbg->de_note_gnu_buildid.dss_size) {
        pbuildid = &dbg->de_note_gnu_buildid;
    }
    if (!pdebuglink && !pbuildid) {
        return DW_DLV_NO_ENTRY;
    }
    if (pdebuglink) {
        linkres = extract_debuglink(dbg,
            pdebuglink,
            debuglink_path_returned,
            crc_returned,
            error);
        if (linkres == DW_DLV_ERROR) {
            return linkres;
        }
    }
    if (pbuildid) {
        buildidres = extract_buildid(dbg,
            pbuildid,
            buildid_type_returned,
            buildid_owner_name_returned,
            buildid_returned,
            buildid_length_returned,
            error);
        if (buildidres == DW_DLV_ERROR) {
            return buildidres;
        }
    }

    dwarfstring_constructor(&debuglink_fullpath);
    pathname = (char *)dbg->de_path;
    if (pathname && paths_returned) {
        res =  _dwarf_construct_linkedto_path(
            (char **)dbg->de_gnu_global_paths,
            dbg->de_gnu_global_path_count,
            pathname,
            *debuglink_path_returned,
            &debuglink_fullpath,
            *crc_returned,
            *buildid_returned,
            *buildid_length_returned,
            paths_returned,
            paths_count_returned,
            &errcode);
        if(res != DW_DLV_OK) {
            dwarfstring_destructor(&debuglink_fullpath);
            return res;
        }
        if (dwarfstring_strlen(&debuglink_fullpath)) {
            *debuglink_fullpath_returned =
                strdup(dwarfstring_string(&debuglink_fullpath));
            *debuglink_fullpath_length_returned =
                dwarfstring_strlen(&debuglink_fullpath);
        }
    } else if (paths_count_returned) {
        *paths_count_returned = 0;
    }
    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;
    const char **glpaths = 0;
    const char * path1 = 0;

    glpath_count_in = dbg->de_gnu_global_path_count;
    glpath_count_out = glpath_count_in+1;
    glpaths = (const 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((char *)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;
}
