sub readDWARFInfo()

in src/main/abi-symbols/abi-dumper.pl [1025:1337]


sub readDWARFInfo($)
{
    my $Path = $_[0];
    
    my $Dir = getDirname($Path);
    my $Name = getFilename($Path);
    
    if(not checkCmd($EU_READELF)) {
        exitStatus("Not_Found", "can't find \"$EU_READELF\" command");
    }
    
    if(-s $Path > 1024*1024*100) {
        $TooBig = 1;
    }
    
    my $AddOpt = "";
    if(not defined $AddrToName)
    { # disable search of symbol names
        $AddOpt .= " -N";
    }
    
    my $Sect = `$EU_READELF_L -S \"$Path\" 2>\"$TMP_DIR/error\"`;
    
    if($Sect!~/\.z?debug_info/)
    { # No DWARF info
        if(my $DebugFile = getDebugFile($Path, "gnu_debuglink"))
        {
            my $DPath = $DebugFile;
            my $DName = getFilename($DPath);
            
            printMsg("INFO", "Found link to $DName (gnu_debuglink)");
            
            if(my $DDir = getDirname($Path))
            {
                $DPath = $DDir."/".$DPath;
            }
            
            my $Found = undef;
            
            if(defined $SearchDirDebuginfo)
            {
                if(-f $SearchDirDebuginfo."/".$DName) {
                    $Found = $SearchDirDebuginfo."/".$DName;
                }
                else
                {
                    my @Files = findFiles($SearchDirDebuginfo, "f");
                    
                    foreach my $F (@Files)
                    {
                        if(getFilename($F) eq $DName)
                        {
                            $Found = $F;
                            last;
                        }
                    }
                }
            }
            elsif(-f $DPath
            and $DPath ne $Path) {
                $Found = $DPath;
            }
            
            if($Found and $Found ne $Path)
            {
                printMsg("INFO", "Reading debug-info file $DName linked from gnu_debuglink");
                return readDWARFInfo($Found);
            }
            else
            {
                printMsg("ERROR", "missed debug-info file $DName linked from gnu_debuglink (try --search-debuginfo=DIR option)");
                return 0;
            }
        }
        return 0;
    }
    elsif(not defined $AltDebugInfoOpt)
    {
        if($Sect=~/\.gnu_debugaltlink/)
        {
            if(my $AltObj = getDebugAltLink($Path))
            {
                $AltDebugInfo = $AltObj;
                readAltInfo($AltObj);
            }
            else {
                exitStatus("Error", "can't read gnu_debugaltlink");
            }
        }
    }
    
    if($AltDebugInfo)
    {
        if($TooBig) {
            printMsg("WARNING", "input object is compressed and large, may require a lot of RAM memory to process");
        }
    }
    
    printMsg("INFO", "Reading debug-info");
    
    my $ExtraPath = undef;
    
    # ELF header
    if($ExtraInfo)
    {
        mkpath($ExtraInfo);
        $ExtraPath = $ExtraInfo."/elf-header";
    }
    
    if($ExtraPath)
    {
        system($EU_READELF_L." -h \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
        open(HEADER, $ExtraPath);
    }
    else {
        open(HEADER, $EU_READELF_L." -h \"$Path\" 2>\"$TMP_DIR/error\" |");
    }
    
    my %Header = ();
    while(<HEADER>)
    {
        if(/\A\s*([\w ]+?)\:\s*(.+?)\Z/) {
            $Header{$1} = $2;
        }
    }
    close(HEADER);
    
    $SYS_ARCH = $Header{"Machine"};
    
    if($SYS_ARCH=~/80\d86/
    or $SYS_ARCH=~/i\d86/)
    { # i386, i586, etc.
        $SYS_ARCH = "x86";
    }
    
    if($SYS_ARCH=~/amd64/i
    or $SYS_ARCH=~/x86\-64/i)
    { # amd64
        $SYS_ARCH = "x86_64";
    }
    
    initRegs();
    
    # ELF sections
    if($ExtraInfo)
    {
        mkpath($ExtraInfo);
        $ExtraPath = $ExtraInfo."/elf-sections";
    }
    
    if($ExtraPath)
    {
        system($EU_READELF_L." -S \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
        open(HEADER, $ExtraPath);
    }
    
    # source info
    if($ExtraInfo)
    {
        mkpath($ExtraInfo);
        $ExtraPath = $ExtraInfo."/debug_line";
    }
    
    if($ExtraPath)
    {
        system($EU_READELF_L." $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
        open(SRC, $ExtraPath);
    }
    else {
        open(SRC, $EU_READELF_L." $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |");
    }
    
    my $Offset = undef;
    my $DirTable_Def = undef;
    my %DirTable = ();
    
    while(<SRC>)
    {
        if(defined $AddDirs)
        {
            if(/Directory table/i)
            {
                $DirTable_Def = 1;
                %DirTable = ();
                next;
            }
            elsif(/File name table/i)
            {
                $DirTable_Def = undef;
                next;
            }
            
            if(defined $DirTable_Def)
            {
                if(/\A\s*([^\[\]\(\)]+?)\Z/) {
                    $DirTable{keys(%DirTable)+1} = $1;
                }
                elsif(/\A\s*(\d+)\s+(.+?)\s+\(\d+\)\Z/)
                { # F34
                    $DirTable{$1} = $2;
                }
            }
        }
        
        if(index($_, "Table")!=-1
        and /Table at offset (\w+)/) {
            $Offset = $1;
        }
        elsif(defined $Offset)
        {
            my ($Num, $Dir, $File) = ();
            
            if(/(\d+)\s+(\d+)\s+\d+\s+\d+\s+([^ ]+)/) {
                ($Num, $Dir, $File) = ($1, $2, $3);
            }
            elsif(/(\d+)\s+([^ ]+)\s+\(\d+\)\,\s+(\d+)/)
            { # F34
                ($Num, $File, $Dir) = ($1, $2, $3);
            }
            
            if($File)
            {
                chomp($File);
                
                if(defined $AddDirs)
                {
                    if(my $DName = $DirTable{$Dir})
                    {
                        $File = $DName."/".$File;
                    }
                }
                
                $SourceFile{$Offset}{$Num} = $File;
            }
        }
    }
    close(SRC);
    
    # debug_loc
    if($ExtraInfo)
    {
        mkpath($ExtraInfo);
        $ExtraPath = $ExtraInfo."/debug_loc";
    }
    
    if($ExtraPath)
    {
        system($EU_READELF_L." $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
        open(LOC, $ExtraPath);
    }
    else {
        open(LOC, $EU_READELF_L." $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" |");
    }
    
    my $Offset = undef;
    
    while(<LOC>)
    {
        if(/\A \[\s*(\w+)\].*\[\s*\w+\]\s*(.+)\Z/) {
            $DebugLoc{$1} = $2;
        }
        elsif(/\A \[\s*(\w+)\]/) {
            $DebugLoc{$1} = "";
        }
        elsif(/Offset:\s+(.+?),/)
        { # F34
            $Offset = $1;
        }
        elsif($Offset and /\A\s+\[\s*\w+\]\s*(.+)\Z/)
        { # F34
            $DebugLoc{$Offset} = $1;
        }
    }
    close(LOC);
    
    # dwarf
    if($ExtraInfo)
    {
        mkpath($ExtraInfo);
        $ExtraPath = $ExtraInfo."/debug_info";
    }
    
    my $INFO_fh;
    
    if($Dir)
    { # to find ".dwz" directory (Fedora)
        chdir($Dir);
    }
    if($ExtraPath)
    {
        system($EU_READELF_L." $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
        open($INFO_fh, $ExtraPath);
    }
    else {
        open($INFO_fh, $EU_READELF_L." $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" |");
    }
    chdir($ORIG_DIR);
    
    readDWARFDump($INFO_fh, 1);
    
    if(my $Err = readFile("$TMP_DIR/error"))
    { # eu-readelf: cannot get next DIE: invalid DWARF
        if($Err=~/invalid DWARF/i)
        {
            if($Loud) {
                printMsg("ERROR", $Err);
            }
            exitStatus("Invalid_DWARF", "invalid DWARF info");
        }
    }
    
    return 1;
}