# Expect script for creating PDB files when linking. # Copyright (C) 2022-2023 Free Software Foundation, Inc. # # This file is part of the GNU Binutils. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, # MA 02110-1301, USA. if {![istarget i*86-*-mingw*] && ![istarget x86_64-*-mingw*]} { return } proc get_pdb_name { pe } { global OBJDUMP set exec_output [run_host_cmd "$OBJDUMP" "-p $pe"] if ![regexp -line "^\\(format RSDS signature (\[0-9a-fA-F\]{32}) age 1 pdb (.*)\\)$" $exec_output full sig pdb] { return "" } return $pdb } proc get_pdb_guid { pe } { global OBJDUMP set exec_output [run_host_cmd "$OBJDUMP" "-p $pe"] if ![regexp -line "^\\(format RSDS signature (\[0-9a-fA-F\]{32}) age 1 pdb (.*)\\)$" $exec_output full sig pdb] { return "" } return $sig } proc check_pdb_info_stream { pdb guid } { global ar set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0001"] if ![string match "" $exec_output] { return 0 } set fi [open tmpdir/0001] fconfigure $fi -translation binary # check version set data [read $fi 4] binary scan $data i version if { $version != 20000404 } { close $fi return 0 } # skip signature (timestamp) read $fi 4 # check age set data [read $fi 4] binary scan $data i age if { $age != 1 } { close $fi return 0 } # check GUID set data [read $fi 16] binary scan $data H2H2H2H2H2H2H2H2H* guid1 guid2 guid3 guid4 guid5 guid6 guid7 guid8 guid9 set data "$guid4$guid3$guid2$guid1$guid6$guid5$guid8$guid7$guid9" if { $data ne $guid } { close $fi return 0 } # skip names string set data [read $fi 4] binary scan $data i names_length read $fi $names_length # read number of names entries set data [read $fi 4] binary scan $data i num_entries # skip number of buckets read $fi 4 # skip present bitmap set data [read $fi 4] binary scan $data i bitmap_length read $fi [expr $bitmap_length * 4] # skip deleted bitmap set data [read $fi 4] binary scan $data i bitmap_length read $fi [expr $bitmap_length * 4] # skip names entries read $fi [expr $num_entries * 8] # skip uint32_t read $fi 4 # read second version set data [read $fi 4] binary scan $data i version2 if { $version2 != 20140508 } { close $fi return 0 } close $fi return 1 } proc check_type_stream { pdb stream } { global ar set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb $stream"] if ![string match "" $exec_output] { return 0 } set fi [open tmpdir/$stream] fconfigure $fi -translation binary # check version set data [read $fi 4] binary scan $data i version if { $version != 20040203 } { close $fi return 0 } # check header size set data [read $fi 4] binary scan $data i header_size if { $header_size != 0x38 } { close $fi return 0 } # skip type_index_begin and type_index_end read $fi 8 # read type_record_bytes set data [read $fi 4] binary scan $data i type_record_bytes close $fi # check stream length set stream_length [file size tmpdir/$stream] if { $stream_length != [ expr $header_size + $type_record_bytes ] } { return 0 } return 1 } proc check_dbi_stream { pdb } { global ar set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0003"] if ![string match "" $exec_output] { return 0 } set fi [open tmpdir/0003] fconfigure $fi -translation binary # check signature set data [read $fi 4] binary scan $data i signature if { $signature != -1 } { close $fi return 0 } # check version set data [read $fi 4] binary scan $data i version if { $version != 19990903 } { close $fi return 0 } # check age set data [read $fi 4] binary scan $data i age if { $age != 1 } { close $fi return 0 } # skip fields read $fi 12 # read substream sizes set data [read $fi 4] binary scan $data i mod_info_size set data [read $fi 4] binary scan $data i section_contribution_size set data [read $fi 4] binary scan $data i section_map_size set data [read $fi 4] binary scan $data i source_info_size set data [read $fi 4] binary scan $data i type_server_map_size # skip MFC type server index seek $fi 4 current set data [read $fi 4] binary scan $data i optional_dbg_header_size set data [read $fi 4] binary scan $data i ec_substream_size close $fi # check stream length set stream_length [file size tmpdir/0003] if { $stream_length != [expr 0x40 + $mod_info_size + $section_contribution_size + $section_map_size + $source_info_size + $type_server_map_size + $optional_dbg_header_size + $ec_substream_size] } { return 0 } return 1 } proc get_section_stream_index { pdb } { global ar set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0003"] if ![string match "" $exec_output] { return -1 } set fi [open tmpdir/0003] fconfigure $fi -translation binary # skip fields seek $fi 24 # read substream sizes set data [read $fi 4] binary scan $data i mod_info_size set data [read $fi 4] binary scan $data i section_contribution_size set data [read $fi 4] binary scan $data i section_map_size set data [read $fi 4] binary scan $data i source_info_size set data [read $fi 4] binary scan $data i type_server_map_size # skip type server index seek $fi 4 current set data [read $fi 4] binary scan $data i optional_dbg_header_size if { $optional_dbg_header_size < 12 } { close $fi return -1 } # skip data seek $fi [expr 12 + $mod_info_size + $section_contribution_size + $section_map_size + $source_info_size + $type_server_map_size + 10] current set data [read $fi 2] binary scan $data s section_stream_index close $fi return $section_stream_index } proc check_section_stream { img pdb } { global ar # read sections stream set index [get_section_stream_index $pdb] if { $index == -1 } { return 0 } set index_str [format "%04x" $index] set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb $index_str"] if ![string match "" $exec_output] { return 0 } set stream_length [file size tmpdir/$index_str] set fi [open tmpdir/$index_str] fconfigure $fi -translation binary set stream_data [read $fi $stream_length] close $fi # read sections from PE file set fi [open $img] fconfigure $fi -translation binary # read PE offset read $fi 0x3c set data [read $fi 4] binary scan $data i pe_offset # read number of sections seek $fi [expr $pe_offset + 6] set data [read $fi 2] binary scan $data s num_sections # read size of optional header seek $fi 12 current set data [read $fi 2] binary scan $data s opt_header_size # read section headers seek $fi [expr $opt_header_size + 2] current set section_data [read $fi [expr $num_sections * 40]] close $fi # compare if { $stream_data ne $section_data} { return 0 } return 1 } proc get_publics_stream_index { pdb } { global ar set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0003"] if ![string match "" $exec_output] { return -1 } set fi [open tmpdir/0003] fconfigure $fi -translation binary # skip fields seek $fi 16 # read substream sizes set data [read $fi 2] binary scan $data s index close $fi return $index } proc get_sym_record_stream_index { pdb } { global ar set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0003"] if ![string match "" $exec_output] { return -1 } set fi [open tmpdir/0003] fconfigure $fi -translation binary # skip fields seek $fi 20 # read substream sizes set data [read $fi 2] binary scan $data s index close $fi return $index } proc check_publics_stream { pdb } { global ar global objdump global srcdir global subdir set publics_index [get_publics_stream_index $pdb] if { $publics_index == -1 } { return 0 } set index_str [format "%04x" $publics_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb $index_str"] if ![string match "" $exec_output] { return 0 } set exp [file_contents "$srcdir/$subdir/pdb1-publics.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] if ![string match $exp $got] { return 0 } set sym_record_index [get_sym_record_stream_index $pdb] if { $sym_record_index == -1 } { return 0 } set index_str [format "%04x" $sym_record_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb $index_str"] if ![string match "" $exec_output] { return 0 } set exp [file_contents "$srcdir/$subdir/pdb1-sym-record.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] if ![string match $exp $got] { return 0 } return 1 } proc test1 { } { global as global ld global srcdir global subdir if ![ld_assemble $as $srcdir/$subdir/pdb1.s tmpdir/pdb1.o] { unsupported "Build pdb1.o" return } if ![ld_link $ld "tmpdir/pdb1.exe" "--pdb=tmpdir/pdb1.pdb --gc-sections -e foo tmpdir/pdb1.o"] { fail "Could not create a PE image with a PDB file" return } if ![string equal [get_pdb_name "tmpdir/pdb1.exe"] "pdb1.pdb"] { fail "PDB filename not found in CodeView debug info" return } pass "PDB filename present in CodeView debug info" if [check_pdb_info_stream tmpdir/pdb1.pdb [get_pdb_guid "tmpdir/pdb1.exe"]] { pass "Valid PDB info stream" } else { fail "Invalid PDB info stream" } if [check_type_stream tmpdir/pdb1.pdb "0002"] { pass "Valid TPI stream" } else { fail "Invalid TPI stream" } if [check_type_stream tmpdir/pdb1.pdb "0004"] { pass "Valid IPI stream" } else { fail "Invalid IPI stream" } if [check_dbi_stream tmpdir/pdb1.pdb] { pass "Valid DBI stream" } else { fail "Invalid DBI stream" } if [check_section_stream tmpdir/pdb1.exe tmpdir/pdb1.pdb] { pass "Valid section stream" } else { fail "Invalid section stream" } if [check_publics_stream tmpdir/pdb1.pdb] { pass "Valid publics stream" } else { fail "Invalid publics stream" } } proc test_mod_info { mod_info } { # check filenames in mod_info set off 64 set obj1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $obj1] + 1] set ar1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $ar1] + 1] if [string match "*pdb2a.o" $obj1] { pass "Correct name for first object file" } else { fail "Incorrect name for first object file" } if [string equal $obj1 $ar1] { pass "Correct archive name for first object file" } else { fail "Incorrect archive name for first object file" } if { [expr $off % 4] != 0 } { set off [expr $off + 4 - ($off % 4)] } incr off 64 set obj2 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $obj2] + 1] set ar2 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $ar2] + 1] if [string match "*pdb2b.o" $obj2] { pass "Correct name for second object file" } else { fail "Incorrect name for second object file" } if [string match "*pdb2b.a" $ar2] { pass "Correct archive name for second object file" } else { fail "Incorrect archive name for second object file" } if { [expr $off % 4] != 0 } { set off [expr $off + 4 - ($off % 4)] } incr off 64 set obj3 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $obj3] + 1] set ar3 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $ar3] + 1] if [string equal $obj3 "* Linker *"] { pass "Correct name for dummy object file" } else { fail "Incorrect name for dummy object file" } if [string equal $ar3 ""] { pass "Correct archive name for dummy object file" } else { fail "Incorrect archive name for dummy object file" } } proc test_section_contrib { section_contrib } { global objdump global srcdir global subdir set fi [open tmpdir/pdb2-sc w] fconfigure $fi -translation binary puts -nonewline $fi $section_contrib close $fi set exp [file_contents "$srcdir/$subdir/pdb2-section-contrib.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb2-sc"] if [string equal $exp $got] { pass "Correct section contribution substream" } else { fail "Incorrect section contribution substream" } } proc test2 { } { global as global ar global ld global srcdir global subdir if ![ld_assemble $as $srcdir/$subdir/pdb2a.s tmpdir/pdb2a.o] { unsupported "Build pdb2a.o" return } if ![ld_assemble $as $srcdir/$subdir/pdb2b.s tmpdir/pdb2b.o] { unsupported "Build pdb2b.o" return } set exec_output [run_host_cmd "$ar" "cr tmpdir/pdb2b.a tmpdir/pdb2b.o"] if ![string match "" $exec_output] { unsupported "Create pdb2b.a" return } if ![ld_link $ld "tmpdir/pdb2.exe" "--pdb=tmpdir/pdb2.pdb --gc-sections -e foo tmpdir/pdb2a.o tmpdir/pdb2b.a"] { unsupported "Create PE image with PDB file" return } set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb2.pdb 0003"] if ![string match "" $exec_output] { return 0 } set fi [open tmpdir/0003] fconfigure $fi -translation binary seek $fi 24 set data [read $fi 4] binary scan $data i mod_info_size set data [read $fi 4] binary scan $data i section_contrib_size seek $fi 32 current set mod_info [read $fi $mod_info_size] set section_contrib [read $fi $section_contrib_size] close $fi test_mod_info $mod_info test_section_contrib $section_contrib } proc find_named_stream { pdb name } { global ar set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0001"] if ![string match "" $exec_output] { return 0 } set fi [open tmpdir/0001] fconfigure $fi -translation binary seek $fi 0x1c set data [read $fi 4] binary scan $data i string_len set strings [read $fi $string_len] set string_off 0 while {[string first \000 $strings $string_off] != -1 } { set str [string range $strings $string_off [expr [string first \000 $strings $string_off] - 1]] if { $str eq $name } { break } incr string_off [expr [string length $str] + 1] } if { [string length $strings] == $string_off } { # string not found close $fi return 0 } set data [read $fi 4] binary scan $data i num_entries seek $fi 4 current set data [read $fi 4] binary scan $data i present_bitmap_len seek $fi [expr $present_bitmap_len * 4] current set data [read $fi 4] binary scan $data i deleted_bitmap_len seek $fi [expr $deleted_bitmap_len * 4] current for {set i 0} {$i < $num_entries} {incr i} { set data [read $fi 4] binary scan $data i offset if { $offset == $string_off } { set data [read $fi 4] binary scan $data i value close $fi return $value } seek $fi 4 current } close $fi return 0 } proc test3 { } { global as global ar global ld global objdump global srcdir global subdir if ![ld_assemble $as $srcdir/$subdir/pdb-strings1.s tmpdir/pdb-strings1.o] { unsupported "Build pdb-strings1.o" return } if ![ld_assemble $as $srcdir/$subdir/pdb-strings2.s tmpdir/pdb-strings2.o] { unsupported "Build pdb-strings2.o" return } if ![ld_link $ld "tmpdir/pdb-strings.exe" "--pdb=tmpdir/pdb-strings.pdb tmpdir/pdb-strings1.o tmpdir/pdb-strings2.o"] { unsupported "Create PE image with PDB file" return } set index [find_named_stream "tmpdir/pdb-strings.pdb" "/names"] if { $index == 0 } { fail "Could not find /names stream" return } else { pass "Found /names stream" } set index_str [format "%04x" $index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-strings.pdb $index_str"] if ![string match "" $exec_output] { return 0 } set exp [file_contents "$srcdir/$subdir/pdb-strings.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] if ![string match $exp $got] { fail "Strings table was not as expected" } else { pass "Strings table was as expected" } } proc extract_c13_info { pdb mod_info } { global ar binary scan [string range $mod_info 34 35] s module_sym_stream binary scan [string range $mod_info 36 39] i sym_byte_size binary scan [string range $mod_info 40 43] i c11_byte_size binary scan [string range $mod_info 44 47] i c13_byte_size set index_str [format "%04x" $module_sym_stream] set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb $index_str"] if ![string match "" $exec_output] { return "" } set fi [open tmpdir/$index_str] fconfigure $fi -translation binary seek $fi [expr $sym_byte_size + $c11_byte_size] set data [read $fi $c13_byte_size] close $fi return $data } proc test4 { } { global as global ar global ld global objdump global srcdir global subdir if ![ld_assemble $as $srcdir/$subdir/pdb3a.s tmpdir/pdb3a.o] { unsupported "Build pdb3a.o" return } if ![ld_assemble $as $srcdir/$subdir/pdb3b.s tmpdir/pdb3b.o] { unsupported "Build pdb3b.o" return } if ![ld_link $ld "tmpdir/pdb3.exe" "--pdb=tmpdir/pdb3.pdb --gc-sections -e main tmpdir/pdb3a.o tmpdir/pdb3b.o"] { unsupported "Create PE image with PDB file" return } # read relevant bits from DBI stream set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb3.pdb 0003"] if ![string match "" $exec_output] { fail "Could not extract DBI stream" return } else { pass "Extracted DBI stream" } set fi [open tmpdir/0003] fconfigure $fi -translation binary seek $fi 24 # read substream sizes set data [read $fi 4] binary scan $data i mod_info_size set data [read $fi 4] binary scan $data i section_contribution_size set data [read $fi 4] binary scan $data i section_map_size set data [read $fi 4] binary scan $data i source_info_size seek $fi 24 current set mod_info [read $fi $mod_info_size] seek $fi [expr $section_contribution_size + $section_map_size] current set source_info [read $fi $source_info_size] close $fi # check source info substream set fi [open tmpdir/pdb3-source-info w] fconfigure $fi -translation binary puts -nonewline $fi $source_info close $fi set exp [file_contents "$srcdir/$subdir/pdb3-source-info.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb3-source-info"] if [string match $exp $got] { pass "Correct source info substream" } else { fail "Incorrect source info substream" } # check C13 info in first module set c13_info [extract_c13_info "tmpdir/pdb3.pdb" [string range $mod_info 0 63]] set fi [open tmpdir/pdb3-c13-info1 w] fconfigure $fi -translation binary puts -nonewline $fi $c13_info close $fi set exp [file_contents "$srcdir/$subdir/pdb3-c13-info1.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb3-c13-info1"] if [string match $exp $got] { pass "Correct C13 info for first module" } else { fail "Incorrect C13 info for first module" } # check C13 info in second module set fn1_end [string first \000 $mod_info 64] set fn2_end [string first \000 $mod_info [expr $fn1_end + 1]] set off [expr $fn2_end + 1] if { [expr $off % 4] != 0 } { set off [expr $off + 4 - ($off % 4)] } set c13_info [extract_c13_info "tmpdir/pdb3.pdb" [string range $mod_info $off [expr $off + 63]]] set fi [open tmpdir/pdb3-c13-info2 w] fconfigure $fi -translation binary puts -nonewline $fi $c13_info close $fi set exp [file_contents "$srcdir/$subdir/pdb3-c13-info2.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb3-c13-info2"] if [string match $exp $got] { pass "Correct C13 info for second module" } else { fail "Incorrect C13 info for second module" } } proc test5 { } { global as global ar global ld global objdump global srcdir global subdir if ![ld_assemble $as $srcdir/$subdir/pdb-types1a.s tmpdir/pdb-types1a.o] { unsupported "Build pdb-types1a.o" return } if ![ld_assemble $as $srcdir/$subdir/pdb-types1b.s tmpdir/pdb-types1b.o] { unsupported "Build pdb-types1b.o" return } if ![ld_link $ld "tmpdir/pdb-types1.exe" "--pdb=tmpdir/pdb-types1.pdb tmpdir/pdb-types1a.o tmpdir/pdb-types1b.o"] { unsupported "Create PE image with PDB file" return } set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types1.pdb 0002"] if ![string match "" $exec_output] { fail "Could not extract TPI stream" return } else { pass "Extracted TPI stream" } # check values in TPI header, and save anything interesting set fi [open tmpdir/0002] fconfigure $fi -translation binary seek $fi 8 current set data [read $fi 4] binary scan $data i first_type if { $first_type != 0x1000 } { fail "Incorrect first type value in TPI stream." } else { pass "Correct first type value in TPI stream." } set data [read $fi 4] binary scan $data i end_type # end_type is one greater than the last type in the stream if { $end_type != 0x102c } { fail "Incorrect end type value in TPI stream." } else { pass "Correct end type value in TPI stream." } set data [read $fi 4] binary scan $data i type_list_size set data [read $fi 2] binary scan $data s hash_stream_index seek $fi 2 current set data [read $fi 4] binary scan $data i hash_size if { $hash_size != 4 } { fail "Incorrect hash size in TPI stream." } else { pass "Correct hash size in TPI stream." } set data [read $fi 4] binary scan $data i num_buckets if { $num_buckets != 0x3ffff } { fail "Incorrect number of buckets in TPI stream." } else { pass "Correct number of buckets in TPI stream." } set data [read $fi 4] binary scan $data i hash_list_offset set data [read $fi 4] binary scan $data i hash_list_size set data [read $fi 4] binary scan $data i skip_list_offset set data [read $fi 4] binary scan $data i skip_list_size seek $fi 8 current set type_list [read $fi $type_list_size] close $fi set fi [open tmpdir/pdb-types1-typelist w] fconfigure $fi -translation binary puts -nonewline $fi $type_list close $fi # check type list set exp [file_contents "$srcdir/$subdir/pdb-types1-typelist.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-typelist"] if ![string match $exp $got] { fail "Incorrect type list in TPI stream." } else { pass "Correct type list in TPI stream." } # extract hash list and skip list set index_str [format "%04x" $hash_stream_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types1.pdb $index_str"] if ![string match "" $exec_output] { fail "Could not extract TPI hash stream." } else { pass "Extracted TPI hash stream." } set fi [open tmpdir/$index_str] fconfigure $fi -translation binary seek $fi $hash_list_offset set hash_list [read $fi $hash_list_size] seek $fi $skip_list_offset set skip_list [read $fi $skip_list_size] close $fi # check hash list set fi [open tmpdir/pdb-types1-hashlist w] fconfigure $fi -translation binary puts -nonewline $fi $hash_list close $fi set exp [file_contents "$srcdir/$subdir/pdb-types1-hashlist.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-hashlist"] if ![string match $exp $got] { fail "Incorrect hash list in TPI stream." } else { pass "Correct hash list in TPI stream." } # check skip list set fi [open tmpdir/pdb-types1-skiplist w] fconfigure $fi -translation binary puts -nonewline $fi $skip_list close $fi set exp [file_contents "$srcdir/$subdir/pdb-types1-skiplist.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-skiplist"] if ![string match $exp $got] { fail "Incorrect skip list in TPI stream." } else { pass "Correct skip list in TPI stream." } } proc test6 { } { global as global ar global ld global objdump global srcdir global subdir if ![ld_assemble $as $srcdir/$subdir/pdb-types2a.s tmpdir/pdb-types2a.o] { unsupported "Build pdb-types2a.o" return } if ![ld_assemble $as $srcdir/$subdir/pdb-types2b.s tmpdir/pdb-types2b.o] { unsupported "Build pdb-types2b.o" return } if ![ld_link $ld "tmpdir/pdb-types2.exe" "--pdb=tmpdir/pdb-types2.pdb tmpdir/pdb-types2a.o tmpdir/pdb-types2b.o"] { unsupported "Create PE image with PDB file" return } set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types2.pdb 0004"] if ![string match "" $exec_output] { fail "Could not extract IPI stream" return } else { pass "Extracted IPI stream" } # check values in IPI header, and save anything interesting set fi [open tmpdir/0004] fconfigure $fi -translation binary seek $fi 8 current set data [read $fi 4] binary scan $data i first_type if { $first_type != 0x1000 } { fail "Incorrect first type value in IPI stream." } else { pass "Correct first type value in IPI stream." } set data [read $fi 4] binary scan $data i end_type # end_type is one greater than the last type in the stream if { $end_type != 0x100f } { fail "Incorrect end type value in IPI stream." } else { pass "Correct end type value in IPI stream." } set data [read $fi 4] binary scan $data i type_list_size set data [read $fi 2] binary scan $data s hash_stream_index seek $fi 2 current set data [read $fi 4] binary scan $data i hash_size if { $hash_size != 4 } { fail "Incorrect hash size in IPI stream." } else { pass "Correct hash size in IPI stream." } set data [read $fi 4] binary scan $data i num_buckets if { $num_buckets != 0x3ffff } { fail "Incorrect number of buckets in IPI stream." } else { pass "Correct number of buckets in IPI stream." } set data [read $fi 4] binary scan $data i hash_list_offset set data [read $fi 4] binary scan $data i hash_list_size set data [read $fi 4] binary scan $data i skip_list_offset set data [read $fi 4] binary scan $data i skip_list_size seek $fi 8 current set type_list [read $fi $type_list_size] close $fi set fi [open tmpdir/pdb-types2-typelist w] fconfigure $fi -translation binary puts -nonewline $fi $type_list close $fi # check type list set exp [file_contents "$srcdir/$subdir/pdb-types2-typelist.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types2-typelist"] if ![string match $exp $got] { fail "Incorrect type list in IPI stream." } else { pass "Correct type list in IPI stream." } # extract hash list and skip list set index_str [format "%04x" $hash_stream_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types2.pdb $index_str"] if ![string match "" $exec_output] { fail "Could not extract IPI hash stream." } else { pass "Extracted IPI hash stream." } set fi [open tmpdir/$index_str] fconfigure $fi -translation binary seek $fi $hash_list_offset set hash_list [read $fi $hash_list_size] seek $fi $skip_list_offset set skip_list [read $fi $skip_list_size] close $fi # check hash list set fi [open tmpdir/pdb-types2-hashlist w] fconfigure $fi -translation binary puts -nonewline $fi $hash_list close $fi set exp [file_contents "$srcdir/$subdir/pdb-types2-hashlist.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types2-hashlist"] if ![string match $exp $got] { fail "Incorrect hash list in IPI stream." } else { pass "Correct hash list in IPI stream." } # check skip list set fi [open tmpdir/pdb-types2-skiplist w] fconfigure $fi -translation binary puts -nonewline $fi $skip_list close $fi set exp [file_contents "$srcdir/$subdir/pdb-types2-skiplist.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types2-skiplist"] if ![string match $exp $got] { fail "Incorrect skip list in IPI stream." } else { pass "Correct skip list in IPI stream." } } proc test7 { } { global as global ar global ld global objdump global srcdir global subdir if ![ld_assemble $as $srcdir/$subdir/pdb-types3a.s tmpdir/pdb-types3a.o] { unsupported "Build pdb-types3a.o" return } if ![ld_assemble $as $srcdir/$subdir/pdb-types3b.s tmpdir/pdb-types3b.o] { unsupported "Build pdb-types3b.o" return } if ![ld_link $ld "tmpdir/pdb-types3.exe" "--pdb=tmpdir/pdb-types3.pdb tmpdir/pdb-types3a.o tmpdir/pdb-types3b.o"] { unsupported "Create PE image with PDB file" return } set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types3.pdb 0004"] if ![string match "" $exec_output] { fail "Could not extract IPI stream" return } else { pass "Extracted IPI stream" } set fi [open tmpdir/0004] fconfigure $fi -translation binary seek $fi 16 current set data [read $fi 4] binary scan $data i type_list_size set data [read $fi 2] binary scan $data s hash_stream_index seek $fi 10 current set data [read $fi 4] binary scan $data i hash_list_offset set data [read $fi 4] binary scan $data i hash_list_size set data [read $fi 4] binary scan $data i skip_list_offset set data [read $fi 4] binary scan $data i skip_list_size seek $fi 8 current set type_list [read $fi $type_list_size] close $fi set fi [open tmpdir/pdb-types3-typelist w] fconfigure $fi -translation binary puts -nonewline $fi $type_list close $fi # check type list set exp [file_contents "$srcdir/$subdir/pdb-types3-typelist.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-typelist"] if ![string match $exp $got] { fail "Incorrect type list in IPI stream." } else { pass "Correct type list in IPI stream." } # extract hash list and skip list set index_str [format "%04x" $hash_stream_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types3.pdb $index_str"] if ![string match "" $exec_output] { fail "Could not extract IPI hash stream." } else { pass "Extracted IPI hash stream." } set fi [open tmpdir/$index_str] fconfigure $fi -translation binary seek $fi $hash_list_offset set hash_list [read $fi $hash_list_size] seek $fi $skip_list_offset set skip_list [read $fi $skip_list_size] close $fi # check hash list set fi [open tmpdir/pdb-types3-hashlist w] fconfigure $fi -translation binary puts -nonewline $fi $hash_list close $fi set exp [file_contents "$srcdir/$subdir/pdb-types3-hashlist.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-hashlist"] if ![string match $exp $got] { fail "Incorrect hash list in IPI stream." } else { pass "Correct hash list in IPI stream." } # check skip list set fi [open tmpdir/pdb-types3-skiplist w] fconfigure $fi -translation binary puts -nonewline $fi $skip_list close $fi set exp [file_contents "$srcdir/$subdir/pdb-types3-skiplist.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-skiplist"] if ![string match $exp $got] { fail "Incorrect skip list in IPI stream." } else { pass "Correct skip list in IPI stream." } } proc test8 { } { global as global ar global ld global objdump global srcdir global subdir if ![ld_assemble $as $srcdir/$subdir/pdb-syms1a.s tmpdir/pdb-syms1a.o] { unsupported "Build pdb-syms1a.o" return } if ![ld_assemble $as $srcdir/$subdir/pdb-syms1b.s tmpdir/pdb-syms1b.o] { unsupported "Build pdb-syms1b.o" return } if ![ld_link $ld "tmpdir/pdb-syms1.exe" "--pdb=tmpdir/pdb-syms1.pdb tmpdir/pdb-syms1a.o tmpdir/pdb-syms1b.o"] { unsupported "Create PE image with PDB file" return } # get index of globals stream and records stream set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb 0003"] if ![string match "" $exec_output] { fail "Could not extract DBI stream" return } else { pass "Extracted DBI stream" } set fi [open tmpdir/0003] fconfigure $fi -translation binary seek $fi 12 set data [read $fi 2] binary scan $data s globals_index seek $fi 6 current set data [read $fi 2] binary scan $data s records_index seek $fi 2 current set data [read $fi 4] binary scan $data i mod_info_size seek $fi 36 current set mod_info [read $fi $mod_info_size] close $fi # get index of first and second module streams binary scan [string range $mod_info 34 35] s mod1_index set off 64 set obj1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $obj1] + 1] set ar1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $ar1] + 1] if { [expr $off % 4] != 0 } { set off [expr $off + 4 - ($off % 4)] } incr off 34 binary scan [string range $mod_info $off [expr $off + 1]] s mod2_index # check globals stream set index_str [format "%04x" $globals_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"] if ![string match "" $exec_output] { fail "Could not extract globals stream" return } else { pass "Extracted globals stream" } set exp [file_contents "$srcdir/$subdir/pdb-syms1-globals.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] if [string match $exp $got] { pass "Correct globals stream" } else { fail "Incorrect globals stream" } # check records stream set index_str [format "%04x" $records_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"] if ![string match "" $exec_output] { fail "Could not extract records stream" return } else { pass "Extracted records stream" } set exp [file_contents "$srcdir/$subdir/pdb-syms1-records.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] if [string match $exp $got] { pass "Correct records stream" } else { fail "Incorrect records stream" } # check symbols in first module set index_str [format "%04x" $mod1_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"] if ![string match "" $exec_output] { fail "Could not extract first module's symbols" return } else { pass "Extracted first module's symbols" } set exp [file_contents "$srcdir/$subdir/pdb-syms1-symbols1.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] if [string match $exp $got] { pass "Correct symbols in first module's stream" } else { fail "Incorrect symbols in first module's stream" } # check symbols in second module set index_str [format "%04x" $mod2_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"] if ![string match "" $exec_output] { fail "Could not extract second module's symbols" return } else { pass "Extracted second module's symbols" } set exp [file_contents "$srcdir/$subdir/pdb-syms1-symbols2.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] if [string match $exp $got] { pass "Correct symbols in second module's stream" } else { fail "Incorrect symbols in second module's stream" } } proc test9 { } { global as global ar global ld global objdump global srcdir global subdir if ![ld_assemble $as $srcdir/$subdir/pdb-syms2.s tmpdir/pdb-syms2.o] { unsupported "Build pdb-syms2.o" return } if ![ld_link $ld "tmpdir/pdb-syms2.exe" "--pdb=tmpdir/pdb-syms2.pdb tmpdir/pdb-syms2.o"] { unsupported "Create PE image with PDB file" return } # get index of module stream set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms2.pdb 0003"] if ![string match "" $exec_output] { fail "Could not extract DBI stream" return } else { pass "Extracted DBI stream" } set fi [open tmpdir/0003] fconfigure $fi -translation binary seek $fi 24 set data [read $fi 4] binary scan $data i mod_info_size seek $fi 36 current set mod_info [read $fi $mod_info_size] close $fi binary scan [string range $mod_info 34 35] s module_index # check module records set index_str [format "%04x" $module_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms2.pdb $index_str"] if ![string match "" $exec_output] { fail "Could not extract module symbols" return } else { pass "Extracted module symbols" } set exp [file_contents "$srcdir/$subdir/pdb-syms2-symbols1.d"] set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] if [string match $exp $got] { pass "Correct symbols in module stream" } else { fail "Incorrect symbols in module stream" } # check linker symbols set off 64 set obj1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $obj1] + 1] set ar1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] incr off [expr [string length $ar1] + 1] if { [expr $off % 4] != 0 } { set off [expr $off + 4 - ($off % 4)] } incr off 34 binary scan [string range $mod_info $off [expr $off + 1]] s linker_syms_index set index_str [format "%04x" $linker_syms_index] set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms2.pdb $index_str"] if ![string match "" $exec_output] { fail "Could not extract linker symbols" return } else { pass "Extracted linker symbols" } set syms [file_contents "tmpdir/$index_str"] # check S_OBJNAME set off 4 binary scan [string range $syms $off [expr $off + 1]] s sym_len binary scan [string range $syms [expr $off + 2] [expr $off + 3]] s sym_type if { $sym_type != 0x1101 } { fail "First linker symbol was not S_OBJNAME" } else { pass "First linker symbol was S_OBJNAME" set linker_fn [string range $syms [expr $off + 8] [expr [string first \000 $syms [expr $off + 8]] - 1]] if ![string equal $linker_fn "* Linker *"] { fail "Incorrect linker object name" } else { pass "Correct linker object name" } } incr off [expr $sym_len + 2] # check S_COMPILE3 binary scan [string range $syms $off [expr $off + 1]] s sym_len binary scan [string range $syms [expr $off + 2] [expr $off + 3]] s sym_type if { $sym_type != 0x113c } { fail "Second linker symbol was not S_COMPILE3" } else { pass "Second linker symbol was S_COMPILE3" } incr off [expr $sym_len + 2] # check S_ENVBLOCK binary scan [string range $syms $off [expr $off + 1]] s sym_len binary scan [string range $syms [expr $off + 2] [expr $off + 3]] s sym_type if { $sym_type != 0x113d } { fail "Third linker symbol was not S_ENVBLOCK" } else { pass "Third linker symbol was S_ENVBLOCK" } } test1 test2 test3 test4 test5 test6 test7 test8 test9