ld: Write DEBUG_S_FILECHKSMS entries in PDBs

This commit is contained in:
Mark Harmstone 2022-12-09 01:52:32 +00:00 committed by Alan Modra
parent f559276dc5
commit 803561cb74
8 changed files with 709 additions and 12 deletions

429
ld/pdb.c
View File

@ -46,6 +46,7 @@ struct string
struct string *next;
uint32_t hash;
uint32_t offset;
uint32_t source_file_offset;
size_t len;
char s[];
};
@ -58,6 +59,18 @@ struct string_table
htab_t hashmap;
};
struct mod_source_files
{
uint16_t files_count;
struct string **files;
};
struct source_files_info
{
uint16_t mod_count;
struct mod_source_files *mods;
};
/* Add a new stream to the PDB archive, and return its BFD. */
static bfd *
add_stream (bfd *pdb, const char *name, uint16_t *stream_num)
@ -400,6 +413,135 @@ get_arch_number (bfd *abfd)
return IMAGE_FILE_MACHINE_I386;
}
/* Validate the DEBUG_S_FILECHKSMS entry within a module's .debug$S
section, and copy it to the module's symbol stream. */
static bool
copy_filechksms (uint8_t *data, uint32_t size, char *string_table,
struct string_table *strings, uint8_t *out,
struct mod_source_files *mod_source)
{
uint8_t *orig_data = data;
uint32_t orig_size = size;
uint16_t num_files = 0;
struct string **strptr;
bfd_putl32 (DEBUG_S_FILECHKSMS, out);
out += sizeof (uint32_t);
bfd_putl32 (size, out);
out += sizeof (uint32_t);
/* Calculate the number of files, and check for any overflows. */
while (size > 0)
{
struct file_checksum *fc = (struct file_checksum *) data;
uint8_t padding;
size_t len;
if (size < sizeof (struct file_checksum))
{
bfd_set_error (bfd_error_bad_value);
return false;
}
len = sizeof (struct file_checksum) + fc->checksum_length;
if (size < len)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
data += len;
size -= len;
if (len % sizeof (uint32_t))
padding = sizeof (uint32_t) - (len % sizeof (uint32_t));
else
padding = 0;
if (size < padding)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
num_files++;
data += padding;
size -= padding;
}
/* Add the files to mod_source, so that they'll appear in the source
info substream. */
strptr = NULL;
if (num_files > 0)
{
uint16_t new_count = num_files + mod_source->files_count;
mod_source->files = xrealloc (mod_source->files,
sizeof (struct string *) * new_count);
strptr = mod_source->files + mod_source->files_count;
mod_source->files_count += num_files;
}
/* Actually copy the data. */
data = orig_data;
size = orig_size;
while (size > 0)
{
struct file_checksum *fc = (struct file_checksum *) data;
uint32_t string_off;
uint8_t padding;
size_t len;
struct string *str = NULL;
string_off = bfd_getl32 (&fc->file_id);
len = sizeof (struct file_checksum) + fc->checksum_length;
if (len % sizeof (uint32_t))
padding = sizeof (uint32_t) - (len % sizeof (uint32_t));
else
padding = 0;
/* Remap the "file ID", i.e. the offset in the module's string table,
so it points to the right place in the main string table. */
if (string_table)
{
char *fn = string_table + string_off;
size_t fn_len = strlen (fn);
uint32_t hash = calc_hash (fn, fn_len);
void **slot;
slot = htab_find_slot_with_hash (strings->hashmap, fn, hash,
NO_INSERT);
if (slot)
str = (struct string *) *slot;
}
*strptr = str;
strptr++;
bfd_putl32 (str ? str->offset : 0, &fc->file_id);
memcpy (out, data, len + padding);
data += len + padding;
size -= len + padding;
out += len + padding;
}
return true;
}
/* Add a string to the strings table, if it's not already there. */
static void
add_string (char *str, size_t len, struct string_table *strings)
@ -420,6 +562,7 @@ add_string (char *str, size_t len, struct string_table *strings)
s->next = NULL;
s->hash = hash;
s->offset = strings->strings_len;
s->source_file_offset = 0xffffffff;
s->len = len;
memcpy (s->s, str, len);
@ -479,10 +622,15 @@ parse_string_table (bfd_byte *data, size_t size,
/* Parse the .debug$S section within an object file. */
static bool
handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
uint8_t **dataptr, uint32_t *sizeptr,
struct mod_source_files *mod_source)
{
bfd_byte *data = NULL;
size_t off;
uint32_t c13_size = 0;
char *string_table = NULL;
uint8_t *buf, *bufptr;
if (!bfd_get_full_section_contents (mod, s, &data))
return false;
@ -498,6 +646,8 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
off = sizeof (uint32_t);
/* calculate size */
while (off + sizeof (uint32_t) <= s->size)
{
uint32_t type, size;
@ -526,9 +676,63 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
switch (type)
{
case DEBUG_S_FILECHKSMS:
c13_size += sizeof (uint32_t) + sizeof (uint32_t) + size;
if (c13_size % sizeof (uint32_t))
c13_size += sizeof (uint32_t) - (c13_size % sizeof (uint32_t));
break;
case DEBUG_S_STRINGTABLE:
parse_string_table (data + off, size, strings);
string_table = (char *) data + off;
break;
}
off += size;
if (off % sizeof (uint32_t))
off += sizeof (uint32_t) - (off % sizeof (uint32_t));
}
if (c13_size == 0)
{
free (data);
return true;
}
/* copy data */
buf = xmalloc (c13_size);
bufptr = buf;
off = sizeof (uint32_t);
while (off + sizeof (uint32_t) <= s->size)
{
uint32_t type, size;
type = bfd_getl32 (data + off);
off += sizeof (uint32_t);
size = bfd_getl32 (data + off);
off += sizeof (uint32_t);
switch (type)
{
case DEBUG_S_FILECHKSMS:
if (!copy_filechksms (data + off, size, string_table,
strings, bufptr, mod_source))
{
free (data);
return false;
}
bufptr += sizeof (uint32_t) + sizeof (uint32_t) + size;
break;
}
@ -540,6 +744,23 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
free (data);
if (*dataptr)
{
/* Append the C13 info to what's already there, if the module has
multiple .debug$S sections. */
*dataptr = xrealloc (*dataptr, *sizeptr + c13_size);
memcpy (*dataptr + *sizeptr, buf, c13_size);
free (buf);
}
else
{
*dataptr = buf;
}
*sizeptr += c13_size;
return true;
}
@ -547,11 +768,15 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
data for each object file. */
static bool
populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
struct string_table *strings)
struct string_table *strings,
uint32_t *c13_info_size,
struct mod_source_files *mod_source)
{
uint8_t int_buf[sizeof (uint32_t)];
uint8_t *c13_info = NULL;
*sym_byte_size = sizeof (uint32_t);
*c13_info_size = 0;
/* Process .debug$S section(s). */
@ -559,17 +784,36 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
{
if (!strcmp (s->name, ".debug$S") && s->size >= sizeof (uint32_t))
{
if (!handle_debugs_section (s, mod, strings))
if (!handle_debugs_section (s, mod, strings, &c13_info,
c13_info_size, mod_source))
{
free (c13_info);
free (mod_source->files);
return false;
}
}
}
/* Write the signature. */
bfd_putl32 (CV_SIGNATURE_C13, int_buf);
if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
{
free (c13_info);
return false;
}
if (c13_info)
{
if (bfd_bwrite (c13_info, *c13_info_size, stream) != *c13_info_size)
{
free (c13_info);
return false;
}
free (c13_info);
}
/* Write the global refs size. */
@ -584,9 +828,11 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
/* Create the module info substream within the DBI. */
static bool
create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
uint32_t *size, struct string_table *strings)
uint32_t *size, struct string_table *strings,
struct source_files_info *source)
{
uint8_t *ptr;
unsigned int mod_num;
static const char linker_fn[] = "* Linker *";
@ -631,32 +877,54 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
len += 4 - (len % 4);
*size += len;
source->mod_count++;
}
*data = xmalloc (*size);
ptr = *data;
source->mods = xmalloc (source->mod_count
* sizeof (struct mod_source_files));
memset (source->mods, 0,
source->mod_count * sizeof (struct mod_source_files));
mod_num = 0;
for (bfd *in = coff_data (abfd)->link_info->input_bfds; in;
in = in->link.next)
{
struct module_info *mod = (struct module_info *) ptr;
uint16_t stream_num;
bfd *stream;
uint32_t sym_byte_size;
uint32_t sym_byte_size, c13_info_size;
uint8_t *start = ptr;
stream = add_stream (pdb, NULL, &stream_num);
if (!stream)
{
for (unsigned int i = 0; i < source->mod_count; i++)
{
free (source->mods[i].files);
}
free (source->mods);
free (*data);
return false;
}
if (!populate_module_stream (stream, in, &sym_byte_size,
strings))
strings, &c13_info_size,
&source->mods[mod_num]))
{
for (unsigned int i = 0; i < source->mod_count; i++)
{
free (source->mods[i].files);
}
free (source->mods);
free (*data);
return false;
}
@ -679,7 +947,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
bfd_putl16 (stream_num, &mod->module_sym_stream);
bfd_putl32 (sym_byte_size, &mod->sym_byte_size);
bfd_putl32 (0, &mod->c11_byte_size);
bfd_putl32 (0, &mod->c13_byte_size);
bfd_putl32 (c13_info_size, &mod->c13_byte_size);
bfd_putl16 (0, &mod->source_file_count);
bfd_putl16 (0, &mod->padding);
bfd_putl32 (0, &mod->unused2);
@ -741,6 +1009,8 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
memset (ptr, 0, 4 - ((ptr - start) % 4));
ptr += 4 - ((ptr - start) % 4);
}
mod_num++;
}
return true;
@ -855,6 +1125,114 @@ create_section_contrib_substream (bfd *abfd, void **data, uint32_t *size)
return true;
}
/* The source info substream lives within the DBI stream, and lists the
source files for each object file (i.e. it's derived from the
DEBUG_S_FILECHKSMS parts of the .debug$S sections). This is a bit
superfluous, as the filenames are also available in the C13 parts of
the module streams, but MSVC relies on it to work properly. */
static void
create_source_info_substream (void **data, uint32_t *size,
struct source_files_info *source)
{
uint16_t dedupe_source_files_count = 0;
uint16_t source_files_count = 0;
uint32_t strings_len = 0;
uint8_t *ptr;
/* Loop through the source files, marking unique filenames. The pointers
here are for entries in the main string table, and so have already
been deduplicated. */
for (uint16_t i = 0; i < source->mod_count; i++)
{
for (uint16_t j = 0; j < source->mods[i].files_count; j++)
{
if (source->mods[i].files[j])
{
if (source->mods[i].files[j]->source_file_offset == 0xffffffff)
{
source->mods[i].files[j]->source_file_offset = strings_len;
strings_len += source->mods[i].files[j]->len + 1;
dedupe_source_files_count++;
}
source_files_count++;
}
}
}
*size = sizeof (uint16_t) + sizeof (uint16_t);
*size += (sizeof (uint16_t) + sizeof (uint16_t)) * source->mod_count;
*size += sizeof (uint32_t) * source_files_count;
*size += strings_len;
*data = xmalloc (*size);
ptr = (uint8_t *) *data;
/* Write header (module count and source file count). */
bfd_putl16 (source->mod_count, ptr);
ptr += sizeof (uint16_t);
bfd_putl16 (dedupe_source_files_count, ptr);
ptr += sizeof (uint16_t);
/* Write "ModIndices". As the LLVM documentation puts it, "this array is
present, but does not appear to be useful". */
for (uint16_t i = 0; i < source->mod_count; i++)
{
bfd_putl16 (i, ptr);
ptr += sizeof (uint16_t);
}
/* Write source file count for each module. */
for (uint16_t i = 0; i < source->mod_count; i++)
{
bfd_putl16 (source->mods[i].files_count, ptr);
ptr += sizeof (uint16_t);
}
/* For each module, write the offsets within the string table
for each source file. */
for (uint16_t i = 0; i < source->mod_count; i++)
{
for (uint16_t j = 0; j < source->mods[i].files_count; j++)
{
if (source->mods[i].files[j])
{
bfd_putl32 (source->mods[i].files[j]->source_file_offset, ptr);
ptr += sizeof (uint32_t);
}
}
}
/* Write the string table. We set source_file_offset to a dummy value for
each entry we write, so we don't write duplicate filenames. */
for (uint16_t i = 0; i < source->mod_count; i++)
{
for (uint16_t j = 0; j < source->mods[i].files_count; j++)
{
if (source->mods[i].files[j]
&& source->mods[i].files[j]->source_file_offset != 0xffffffff)
{
memcpy (ptr, source->mods[i].files[j]->s,
source->mods[i].files[j]->len);
ptr += source->mods[i].files[j]->len;
*ptr = 0;
ptr++;
source->mods[i].files[j]->source_file_offset = 0xffffffff;
}
}
}
}
/* Stream 4 is the debug information (DBI) stream. */
static bool
populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
@ -865,19 +1243,37 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
{
struct pdb_dbi_stream_header h;
struct optional_dbg_header opt;
void *mod_info, *sc;
uint32_t mod_info_size, sc_size;
void *mod_info, *sc, *source_info;
uint32_t mod_info_size, sc_size, source_info_size;
struct source_files_info source;
source.mod_count = 0;
source.mods = NULL;
if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
strings))
strings, &source))
return false;
if (!create_section_contrib_substream (abfd, &sc, &sc_size))
{
for (unsigned int i = 0; i < source.mod_count; i++)
{
free (source.mods[i].files);
}
free (source.mods);
free (mod_info);
return false;
}
create_source_info_substream (&source_info, &source_info_size, &source);
for (unsigned int i = 0; i < source.mod_count; i++)
{
free (source.mods[i].files);
}
free (source.mods);
bfd_putl32 (0xffffffff, &h.version_signature);
bfd_putl32 (DBI_STREAM_VERSION_70, &h.version_header);
bfd_putl32 (1, &h.age);
@ -890,7 +1286,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
bfd_putl32 (mod_info_size, &h.mod_info_size);
bfd_putl32 (sc_size, &h.section_contribution_size);
bfd_putl32 (0, &h.section_map_size);
bfd_putl32 (0, &h.source_info_size);
bfd_putl32 (source_info_size, &h.source_info_size);
bfd_putl32 (0, &h.type_server_map_size);
bfd_putl32 (0, &h.mfc_type_server_index);
bfd_putl32 (sizeof (opt), &h.optional_dbg_header_size);
@ -901,6 +1297,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
{
free (source_info);
free (sc);
free (mod_info);
return false;
@ -908,6 +1305,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
if (bfd_bwrite (mod_info, mod_info_size, stream) != mod_info_size)
{
free (source_info);
free (sc);
free (mod_info);
return false;
@ -917,12 +1315,21 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
if (bfd_bwrite (sc, sc_size, stream) != sc_size)
{
free (source_info);
free (sc);
return false;
}
free (sc);
if (bfd_bwrite (source_info, source_info_size, stream) != source_info_size)
{
free (source_info);
return false;
}
free (source_info);
bfd_putl16 (0xffff, &opt.fpo_stream);
bfd_putl16 (0xffff, &opt.exception_stream);
bfd_putl16 (0xffff, &opt.fixup_stream);

View File

@ -156,6 +156,7 @@ struct optional_dbg_header
#define CV_SIGNATURE_C13 4
#define DEBUG_S_STRINGTABLE 0xf3
#define DEBUG_S_FILECHKSMS 0xf4
#define STRING_TABLE_SIGNATURE 0xeffeeffe
#define STRING_TABLE_VERSION 1
@ -200,6 +201,14 @@ struct module_info
uint32_t pdb_file_path_name_index;
};
/* filedata in dumpsym7.cpp */
struct file_checksum
{
uint32_t file_id;
uint8_t checksum_length;
uint8_t checksum_type;
} ATTRIBUTE_PACKED;
extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
#endif

View File

@ -824,6 +824,160 @@ proc test3 { } {
}
}
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 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"
}
}
test1
test2
test3
test4

View File

@ -0,0 +1,8 @@
*: file format binary
Contents of section .data:
0000 f4000000 30000000 02000000 10016745 ....0.........gE
0010 2301efcd ab8998ba dcfe1023 45670000 #..........#Eg..
0020 06000000 100198ba dcfe1023 45676745 ...........#EggE
0030 2301efcd ab890000 #.......

View File

@ -0,0 +1,8 @@
*: file format binary
Contents of section .data:
0000 f4000000 30000000 06000000 100198ba ....0...........
0010 dcfe1023 45676745 2301efcd ab890000 ...#EggE#.......
0020 0a000000 10013b2a 19087f6e 5d4c4c5d ......;*...n]LL]
0030 6e7f0819 2a3b0000 n...*;..

View File

@ -0,0 +1,7 @@
*: file format binary
Contents of section .data:
0000 03000300 00000100 02000200 02000000 ................
0010 00000000 04000000 04000000 08000000 ................
0020 666f6f00 62617200 62617a00 foo.bar.baz.

View File

@ -0,0 +1,52 @@
.equ CV_SIGNATURE_C13, 4
.equ DEBUG_S_STRINGTABLE, 0xf3
.equ DEBUG_S_FILECHKSMS, 0xf4
.equ CHKSUM_TYPE_MD5, 1
.equ NUM_MD5_BYTES, 16
.section ".debug$S", "rn"
.long CV_SIGNATURE_C13
.long DEBUG_S_STRINGTABLE
.long .strings_end - .strings_start
.strings_start:
.asciz ""
.src1:
.asciz "foo"
.src2:
.asciz "bar"
.strings_end:
.balign 4
.long DEBUG_S_FILECHKSMS
.long .chksms_end - .chksms_start
.chksms_start:
.long .src1 - .strings_start
.byte NUM_MD5_BYTES
.byte CHKSUM_TYPE_MD5
.long 0x01234567
.long 0x89abcdef
.long 0xfedcba98
.long 0x67452310
.short 0 # padding
.long .src2 - .strings_start
.byte NUM_MD5_BYTES
.byte CHKSUM_TYPE_MD5
.long 0xfedcba98
.long 0x67452310
.long 0x01234567
.long 0x89abcdef
.short 0 # padding
.chksms_end:
.balign 4

View File

@ -0,0 +1,52 @@
.equ CV_SIGNATURE_C13, 4
.equ DEBUG_S_STRINGTABLE, 0xf3
.equ DEBUG_S_FILECHKSMS, 0xf4
.equ CHKSUM_TYPE_MD5, 1
.equ NUM_MD5_BYTES, 16
.section ".debug$S", "rn"
.long CV_SIGNATURE_C13
.long DEBUG_S_STRINGTABLE
.long .strings_end - .strings_start
.strings_start:
.asciz ""
.src1:
.asciz "bar"
.src2:
.asciz "baz"
.strings_end:
.balign 4
.long DEBUG_S_FILECHKSMS
.long .chksms_end - .chksms_start
.chksms_start:
.long .src1 - .strings_start
.byte NUM_MD5_BYTES
.byte CHKSUM_TYPE_MD5
.long 0xfedcba98
.long 0x67452310
.long 0x01234567
.long 0x89abcdef
.short 0 # padding
.long .src2 - .strings_start
.byte NUM_MD5_BYTES
.byte CHKSUM_TYPE_MD5
.long 0x08192a3b
.long 0x4c5d6e7f
.long 0x7f6e5d4c
.long 0x3b2a1908
.short 0 # padding
.chksms_end:
.balign 4