File: //usr/share/perl5/File/StripNondeterminism/handlers/bflt.pm
#
# Copyright 2016 Evgueni Souleimanov
#
# This file is part of strip-nondeterminism.
#
# strip-nondeterminism 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.
#
# strip-nondeterminism 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 strip-nondeterminism. If not, see <http://www.gnu.org/licenses/>.
#
package File::StripNondeterminism::handlers::bflt;
use strict;
use warnings;
use File::StripNondeterminism;
use Fcntl q/SEEK_SET/;
use constant bFLT => 0x62464C54;
use constant FLAT_HDRLEN => 64;
# Heuristic values
use constant MAX_OFFSET => 0xFFFFFFF;
use constant MAX_STACK_SIZE => 0xFFFFFF;
use constant MAX_COUNT => 0xFFFFFF;
use constant RESERVED_FLAGS => 0xFFFFFFC0;
=head1 DEPRECATION PLAN
bFLT format is used in uClibc/uCLinux systems. As of 2020-04-30 there are zero
.bflt files being shipped in Debian packages so this handler is a good
candidate to commence deprecation via first making it optional, etc., perhaps
surveying whether any embedded distributions could be relying on this.
Also see the uimage handler.
=cut
# From elf2flt flat.h
# /*
# * To make everything easier to port and manage cross platform
# * development, all fields are in network byte order.
# */
#
# struct flat_hdr {
# char magic[4];
# uint32_t rev; /* version (as above) */
# uint32_t entry; /* Offset of first executable instruction
# with text segment from beginning of file */
# uint32_t data_start; /* Offset of data segment from beginning of
# file */
# uint32_t data_end; /* Offset of end of data segment from beginning
# of file */
# uint32_t bss_end; /* Offset of end of bss segment from beginning
# of file */
#
# /* (It is assumed that data_end through bss_end forms the bss segment.) */
#
# uint32_t stack_size; /* Size of stack, in bytes */
# uint32_t reloc_start; /* Offset of relocation records from beginning
# of file */
# uint32_t reloc_count; /* Number of relocation records */
# uint32_t flags;
# uint32_t build_date; /* When the program/library was built */
# uint32_t filler[5]; /* Reservered, set to zero */
# };
sub is_bflt_header {
my ($hdr) = @_;
my ($f_magic, $f_rev, $f_entry, $f_data_start,
$f_data_end, $f_bss_end, $f_stack_size, $f_reloc_start,
$f_reloc_count, $f_flags, $f_build_date, $filler_11,
$filler_12, $filler_13, $filler_14, $filler_15) = unpack('NNNNNNNNNNNNNNNN', $hdr);
return 0 unless ($f_magic == bFLT);
return 0 unless ($f_rev == 4);
return 0 unless ($f_entry < MAX_OFFSET && $f_data_start < MAX_OFFSET
&& $f_data_end < MAX_OFFSET && $f_bss_end < MAX_OFFSET);
return 0 unless ($f_stack_size < MAX_STACK_SIZE);
return 0 unless ($f_reloc_start < MAX_OFFSET && $f_reloc_count < MAX_COUNT);
return 0 unless (($f_flags & RESERVED_FLAGS) == 0);
return 0 unless ($filler_11 == 0 && $filler_12 == 0
&& $filler_13 == 0 && $filler_14 == 0 && $filler_15 == 0);
return 1;
}
sub is_bflt_fh {
my ($fh) = @_;
my $hdr;
binmode($fh);
my $bytes_read = sysread($fh, $hdr, FLAT_HDRLEN);
return 0 unless $bytes_read == FLAT_HDRLEN;
return is_bflt_header($hdr);
}
sub is_bflt_file {
my ($filename) = @_;
my $fh;
return open($fh, '<', $filename) && is_bflt_fh($fh);
}
sub normalize {
my ($filename) = @_;
open(my $fh, '+<', $filename)
or die("failed to open $filename for read+write: $!");
binmode($fh);
my $hdr;
my $bytes_read = sysread($fh, $hdr, FLAT_HDRLEN);
return 0 unless $bytes_read == FLAT_HDRLEN;
my ($f_magic, $f_rev, $f_entry, $f_data_start,
$f_data_end, $f_bss_end, $f_stack_size, $f_reloc_start,
$f_reloc_count, $f_flags, $f_build_date, $filler_11,
$filler_12, $filler_13, $filler_14, $filler_15) = unpack('NNNNNNNNNNNNNNNN', $hdr);
return 0 unless $f_magic == bFLT;
return 0 unless $f_rev == 4;
return 0 unless (is_bflt_header($hdr));
my $f_build_date_orig = $f_build_date;
unless ($f_build_date == 0) { # Don't set a deterministic timestamp if there wasn't already a timestamp
if (defined $File::StripNondeterminism::canonical_time) {
if (!$File::StripNondeterminism::clamp_time || $f_build_date > $File::StripNondeterminism::canonical_time) {
$f_build_date = $File::StripNondeterminism::canonical_time;
}
} else {
$f_build_date = 0; # 0 is "no timestamp"
}
}
return 0 if $f_build_date == $f_build_date_orig;
my $hdr_new = pack('NNNNNNNNNNNNNNNN',
$f_magic, $f_rev, $f_entry, $f_data_start,
$f_data_end, $f_bss_end, $f_stack_size, $f_reloc_start,
$f_reloc_count, $f_flags, $f_build_date, $filler_11,
$filler_12, $filler_13, $filler_14, $filler_15);
seek $fh, 0, SEEK_SET;
syswrite ($fh, $hdr_new, 64);
return 1;
}
1;