| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package ELF::Writer; | 
| 2 | 1 |  |  | 1 |  | 33058 | use Moo 2; | 
|  | 1 |  |  |  |  | 9538 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 3 | 1 |  |  | 1 |  | 1019 | use Carp; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 44 |  | 
| 4 | 1 |  |  | 1 |  | 376 | use IO::File; | 
|  | 1 |  |  |  |  | 6186 |  | 
|  | 1 |  |  |  |  | 96 |  | 
| 5 | 1 |  |  | 1 |  | 407 | use namespace::clean; | 
|  | 1 |  |  |  |  | 6840 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $VERSION= '0.000_004'; | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | # ABSTRACT: Encode elf files with pure-perl | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | sub _init_enum { | 
| 13 | 7 |  |  | 7 |  | 18 | my ($to_sym, $from_sym, @name_to_num)= @_; | 
| 14 | 7 |  |  |  |  | 40 | %$from_sym= @name_to_num; | 
| 15 | 7 |  |  |  |  | 38 | %$to_sym= reverse @name_to_num; | 
| 16 |  |  |  |  |  |  | } | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | our (%class_to_sym, %class_from_sym); | 
| 20 |  |  |  |  |  |  | _init_enum(\%class_to_sym, \%class_from_sym, | 
| 21 |  |  |  |  |  |  | '32bit' => 1, | 
| 22 |  |  |  |  |  |  | '64bit' => 2, | 
| 23 |  |  |  |  |  |  | ); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | has class => ( is => 'rw', coerce => sub { | 
| 26 |  |  |  |  |  |  | my $x= $class_from_sym{$_[0]}; | 
| 27 |  |  |  |  |  |  | defined $x? $x | 
| 28 |  |  |  |  |  |  | : (int($_[0]) == $_[0])? $_[0] | 
| 29 |  |  |  |  |  |  | : croak "$_[0] is not a valid 'class'" | 
| 30 |  |  |  |  |  |  | }); | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | sub class_sym { | 
| 33 | 2 |  |  | 2 | 0 | 12 | my $self= shift; | 
| 34 | 2 | 50 |  |  |  | 5 | $self->class($_[0]) if @_; | 
| 35 | 2 |  |  |  |  | 16 | my $v= $self->class; | 
| 36 | 2 | 50 |  |  |  | 413 | $class_to_sym{$v} || $v | 
| 37 |  |  |  |  |  |  | } | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | our (%data_to_sym, %data_from_sym); | 
| 41 |  |  |  |  |  |  | _init_enum(\%data_to_sym, \%data_from_sym, | 
| 42 |  |  |  |  |  |  | '2LSB' => 1, | 
| 43 |  |  |  |  |  |  | '2MSB' => 2, | 
| 44 |  |  |  |  |  |  | ); | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | has data => ( is => 'rw', coerce => sub { | 
| 47 |  |  |  |  |  |  | my $x= $data_from_sym{$_[0]}; | 
| 48 |  |  |  |  |  |  | defined $x? $x | 
| 49 |  |  |  |  |  |  | : (int($_[0]) == $_[0])? $_[0] | 
| 50 |  |  |  |  |  |  | : croak "$_[0] is not a valid 'data'" | 
| 51 |  |  |  |  |  |  | }); | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | sub data_sym { | 
| 54 | 2 |  |  | 2 | 0 | 11 | my $self= shift; | 
| 55 | 2 | 50 |  |  |  | 5 | $self->data($_[0]) if @_; | 
| 56 | 2 |  |  |  |  | 16 | my $v= $self->data; | 
| 57 | 2 | 50 |  |  |  | 392 | $data_to_sym{$v} || $v | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | has header_version  => ( is => 'rw', default => sub { 1 } ); | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | our (%osabi_to_sym, %osabi_from_sym); | 
| 65 |  |  |  |  |  |  | _init_enum(\%osabi_to_sym, \%osabi_from_sym, | 
| 66 |  |  |  |  |  |  | 'SystemV'  => 0, | 
| 67 |  |  |  |  |  |  | 'HP-UX'    => 1, | 
| 68 |  |  |  |  |  |  | 'NetBSD'   => 2, | 
| 69 |  |  |  |  |  |  | 'Linux'    => 3, | 
| 70 |  |  |  |  |  |  | 'Solaris'  => 6, | 
| 71 |  |  |  |  |  |  | 'AIX'      => 7, | 
| 72 |  |  |  |  |  |  | 'IRIX'     => 8, | 
| 73 |  |  |  |  |  |  | 'FreeBSD'  => 9, | 
| 74 |  |  |  |  |  |  | 'OpenBSD'  => 0x0C, | 
| 75 |  |  |  |  |  |  | 'OpenVMS'  => 0x0D, | 
| 76 |  |  |  |  |  |  | ); | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | has osabi => ( is => 'rw', coerce => sub { | 
| 79 |  |  |  |  |  |  | my $x= $osabi_from_sym{$_[0]}; | 
| 80 |  |  |  |  |  |  | defined $x? $x | 
| 81 |  |  |  |  |  |  | : (int($_[0]) == $_[0])? $_[0] | 
| 82 |  |  |  |  |  |  | : croak "$_[0] is not a valid 'osabi'" | 
| 83 |  |  |  |  |  |  | }); | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | sub osabi_sym { | 
| 86 | 2 |  |  | 2 | 0 | 12 | my $self= shift; | 
| 87 | 2 | 50 |  |  |  | 6 | $self->osabi($_[0]) if @_; | 
| 88 | 2 |  |  |  |  | 15 | my $v= $self->osabi; | 
| 89 | 2 | 50 |  |  |  | 408 | $osabi_to_sym{$v} || $v | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | has osabi_version   => ( is => 'rw', default => sub { 0 } ); | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | our (%type_to_sym, %type_from_sym); | 
| 96 |  |  |  |  |  |  | _init_enum(\%type_to_sym, \%type_from_sym, | 
| 97 |  |  |  |  |  |  | 'none'        => 0, | 
| 98 |  |  |  |  |  |  | 'relocatable' => 1, | 
| 99 |  |  |  |  |  |  | 'executable'  => 2, | 
| 100 |  |  |  |  |  |  | 'shared'      => 3, | 
| 101 |  |  |  |  |  |  | 'core'        => 4, | 
| 102 |  |  |  |  |  |  | ); | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | has type => ( is => 'rw', coerce => sub { | 
| 105 |  |  |  |  |  |  | my $x= $type_from_sym{$_[0]}; | 
| 106 |  |  |  |  |  |  | defined $x? $x | 
| 107 |  |  |  |  |  |  | : (int($_[0]) == $_[0])? $_[0] | 
| 108 |  |  |  |  |  |  | : croak "$_[0] is not a valid 'type'" | 
| 109 |  |  |  |  |  |  | }); | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | sub type_sym { | 
| 112 | 5 |  |  | 5 | 0 | 297 | my $self= shift; | 
| 113 | 5 | 50 |  |  |  | 8 | $self->type($_[0]) if @_; | 
| 114 | 5 |  |  |  |  | 60 | my $v= $self->type; | 
| 115 | 5 | 100 |  |  |  | 445 | $type_to_sym{$v} || $v | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | our (%machine_to_sym, %machine_from_sym); | 
| 120 |  |  |  |  |  |  | _init_enum(\%machine_to_sym, \%machine_from_sym, | 
| 121 |  |  |  |  |  |  | 'SPARC'       => 0x02, | 
| 122 |  |  |  |  |  |  | 'i386'        => 0x03, | 
| 123 |  |  |  |  |  |  | 'Motorola68K' => 0x04, | 
| 124 |  |  |  |  |  |  | 'Motorola88K' => 0x05, | 
| 125 |  |  |  |  |  |  | 'i860'        => 0x07, | 
| 126 |  |  |  |  |  |  | 'MIPS-RS3000' => 0x08, | 
| 127 |  |  |  |  |  |  | 'MIPS-RS4000' => 0xA0, | 
| 128 |  |  |  |  |  |  | 'PowerPC'     => 0x14, | 
| 129 |  |  |  |  |  |  | 'ARM'         => 0x28, | 
| 130 |  |  |  |  |  |  | 'SuperH'      => 0x2A, | 
| 131 |  |  |  |  |  |  | 'IA-64'       => 0x32, | 
| 132 |  |  |  |  |  |  | 'x86-64'      => 0x3E, | 
| 133 |  |  |  |  |  |  | 'AArch64'     => 0xB7, | 
| 134 |  |  |  |  |  |  | ); | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | has machine => ( is => 'rw', coerce => sub { | 
| 137 |  |  |  |  |  |  | my $x= $machine_from_sym{$_[0]}; | 
| 138 |  |  |  |  |  |  | defined $x? $x | 
| 139 |  |  |  |  |  |  | : (int($_[0]) == $_[0])? $_[0] | 
| 140 |  |  |  |  |  |  | : croak "$_[0] is not a valid 'machine'" | 
| 141 |  |  |  |  |  |  | }); | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | sub machine_sym { | 
| 144 | 1 |  |  | 1 | 0 | 7 | my $self= shift; | 
| 145 | 1 | 50 |  |  |  | 3 | $self->machine($_[0]) if @_; | 
| 146 | 1 |  |  |  |  | 3 | my $v= $self->machine; | 
| 147 | 1 | 50 |  |  |  | 390 | $machine_to_sym{$v} || $v | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | has version         => ( is => 'rw', default => sub { 1 } ); | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | has flags           => ( is => 'rw', default => sub { 0 } ); | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | has entry_point     => ( is => 'rw' ); | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | our $Magic= "\x7fELF"; | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | sub elf_header_len { | 
| 161 | 5 |  |  | 5 | 0 | 138 | my $class= shift->class; | 
| 162 | 5 | 50 |  |  |  | 422 | return $class == 1? 52 | 
|  |  | 50 |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | : $class == 2? 64 | 
| 164 |  |  |  |  |  |  | : croak "Don't know structs for elf class $class"; | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  | our @Elf_Header_Pack= ( | 
| 167 |  |  |  |  |  |  | 'a4 C C C C C a7 S< S< L< L< L< L< L< S< S< S< S< S< S<', # 32-bit LE | 
| 168 |  |  |  |  |  |  | 'a4 C C C C C a7 S> S> L> L> L> L> L> S> S> S> S> S> S>', # 32-bit BE | 
| 169 |  |  |  |  |  |  | 'a4 C C C C C a7 S< S< L< Q< Q< Q< L< S< S< S< S< S< S<', # 64-bit LE | 
| 170 |  |  |  |  |  |  | 'a4 C C C C C a7 S> S> L> Q> Q> Q> L> S> S> S> S> S> S>', # 64-bit BE | 
| 171 |  |  |  |  |  |  | ); | 
| 172 |  |  |  |  |  |  | sub _elf_header_packstr { | 
| 173 | 1 |  |  | 1 |  | 2 | my ($self, $encoding)= @_; | 
| 174 | 1 | 50 |  |  |  | 4 | $encoding= $self->_encoding unless defined $encoding; | 
| 175 | 1 |  |  |  |  | 17 | $Elf_Header_Pack[ $encoding ]; | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | sub segment_header_elem_len { | 
| 179 | 2 |  |  | 2 | 0 | 27 | my $class= shift->class; | 
| 180 | 2 | 50 |  |  |  | 15 | return $class == 1? 32 | 
|  |  | 50 |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | : $class == 2? 56 | 
| 182 |  |  |  |  |  |  | : croak "Don't know structs for elf class $class"; | 
| 183 |  |  |  |  |  |  | } | 
| 184 |  |  |  |  |  |  | # Note! there is also a field swap between 32bit and 64bit | 
| 185 |  |  |  |  |  |  | our @Segment_Header_Pack= ( | 
| 186 |  |  |  |  |  |  | 'L< L< L< L< L< L< L< L<', | 
| 187 |  |  |  |  |  |  | 'L> L> L> L> L> L> L> L>', | 
| 188 |  |  |  |  |  |  | 'L< L< Q< Q< Q< Q< Q< Q<', | 
| 189 |  |  |  |  |  |  | 'L> L> Q> Q> Q> Q> Q> Q>', | 
| 190 |  |  |  |  |  |  | ); | 
| 191 |  |  |  |  |  |  | sub _segment_header_packstr { | 
| 192 | 1 |  |  | 1 |  | 1 | my ($self, $encoding)= @_; | 
| 193 | 1 | 50 |  |  |  | 3 | $encoding= $self->_encoding unless defined $encoding; | 
| 194 | 1 |  |  |  |  | 14 | $Segment_Header_Pack[ $encoding ]; | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | sub section_header_elem_len { | 
| 198 | 1 |  |  | 1 | 0 | 18 | my $class= shift->class; | 
| 199 | 1 | 50 |  |  |  | 10 | return $class == 1? 40 | 
|  |  | 50 |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | : $class == 2? 64 | 
| 201 |  |  |  |  |  |  | : croak "Don't know structs for elf class $class"; | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  | our @Section_Header_Pack= ( | 
| 204 |  |  |  |  |  |  | 'L< L< L< L< L< L< L< L< L< L<', | 
| 205 |  |  |  |  |  |  | 'L> L> L> L> L> L> L> L> L> L>', | 
| 206 |  |  |  |  |  |  | 'L< L< Q< Q< Q< Q< L< L< Q< Q<', | 
| 207 |  |  |  |  |  |  | 'L> L> Q> Q> Q> Q> L> L> Q> Q>', | 
| 208 |  |  |  |  |  |  | ); | 
| 209 |  |  |  |  |  |  | sub _section_header_packstr { | 
| 210 | 0 |  |  | 0 |  | 0 | my ($self, $encoding)= @_; | 
| 211 | 0 | 0 |  |  |  | 0 | $encoding= $self->_encoding unless defined $encoding; | 
| 212 | 0 |  |  |  |  | 0 | $Section_Header_Pack[ $encoding ]; | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | # Returns a number 0..3 used by the various routines when packing binary data | 
| 216 |  |  |  |  |  |  | sub _encoding { | 
| 217 | 3 |  |  | 3 |  | 3 | my $self= shift; | 
| 218 | 3 |  |  |  |  | 44 | my $endian= $self->data; | 
| 219 | 3 |  |  |  |  | 43 | my $bits=   $self->class; | 
| 220 | 3 | 50 | 33 |  |  | 24 | defined $endian && $endian > 0 && $endian < 3 or croak "Can't encode for data=$endian"; | 
|  |  |  | 33 |  |  |  |  | 
| 221 | 3 | 50 | 33 |  |  | 17 | defined $bits && $bits > 0 && $bits < 3 or croak "Can't encode for class=$bits"; | 
|  |  |  | 33 |  |  |  |  | 
| 222 | 3 |  |  |  |  | 12 | return ($bits-1)*2 + ($endian-1); | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | has segments => ( is => 'rw', coerce => \&_coerce_segments, default => sub { [] } ); | 
| 227 | 1 |  |  | 1 | 0 | 11 | sub segment_count { scalar @{ shift->segments } } | 
|  | 1 |  |  |  |  | 13 |  | 
| 228 | 1 |  |  | 1 | 0 | 2 | sub segment_list { @{ shift->segments } } | 
|  | 1 |  |  |  |  | 17 |  | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | has sections => ( is => 'rw', coerce => \&_coerce_sections, default => sub { [] } ); | 
| 232 | 1 |  |  | 1 | 0 | 1 | sub section_count { scalar @{ shift->sections } } | 
|  | 1 |  |  |  |  | 19 |  | 
| 233 | 1 |  |  | 1 | 0 | 1 | sub section_list { @{ shift->sections } } | 
|  | 1 |  |  |  |  | 5 |  | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | has section_name_string_table_idx => ( is => 'rw' ); | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | sub serialize { | 
| 239 | 1 |  |  | 1 | 0 | 460 | my $self= shift; | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | # Faster than checking bit lengths on every field ourself | 
| 242 | 1 |  |  | 1 |  | 1544 | use warnings FATAL => 'pack'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 720 |  | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | # Make sure all required attributes are defined | 
| 245 |  |  |  |  |  |  | defined($self->$_) || croak "Attribute $_ is not defined" | 
| 246 | 1 |  | 50 |  |  | 17 | for qw( class data osabi type machine header_version osabi_version version entry_point ); | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | # Clone the segments and sections so that our changes don't affect the | 
| 249 |  |  |  |  |  |  | # configuration the user built. | 
| 250 | 1 |  |  |  |  | 1185 | my @segments= map { $_->clone } $self->segment_list; | 
|  | 1 |  |  |  |  | 7 |  | 
| 251 | 1 |  |  |  |  | 5 | my @sections= map { $_->clone } $self->section_list; | 
|  | 0 |  |  |  |  | 0 |  | 
| 252 | 1 |  |  |  |  | 358 | my $segment_table; | 
| 253 |  |  |  |  |  |  | my $section_table; | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | # Now apply defaults and set numbering for diagostics of errors | 
| 256 | 1 |  |  |  |  | 2 | my $i= 0; | 
| 257 | 1 |  |  |  |  | 3 | for (@segments) { | 
| 258 | 1 |  |  |  |  | 3 | $_->_index($i++); | 
| 259 | 1 |  |  |  |  | 4 | $self->_apply_segment_defaults($_); | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | # There can be one segment which loads the segment table itself | 
| 262 |  |  |  |  |  |  | # into the program's address space.  If used, we track the pointer | 
| 263 |  |  |  |  |  |  | # to that segment.  We also clear it's 'data' and set it's 'size' | 
| 264 |  |  |  |  |  |  | # to keep from confusing the code below. | 
| 265 | 1 | 50 |  |  |  | 14 | if ($_->type == 6) { | 
| 266 | 0 | 0 |  |  |  | 0 | croak "There can be only one segment of type 'phdr'" | 
| 267 |  |  |  |  |  |  | if defined $segment_table; | 
| 268 | 0 |  |  |  |  | 0 | $segment_table= $_; | 
| 269 | 0 |  |  |  |  | 0 | $segment_table->data(undef); | 
| 270 | 0 |  |  |  |  | 0 | $segment_table->size($self->segment_header_len * @segments); | 
| 271 |  |  |  |  |  |  | } | 
| 272 |  |  |  |  |  |  | } | 
| 273 | 1 |  |  |  |  | 7 | $i= 0; | 
| 274 | 1 |  |  |  |  | 2 | for (@sections) { | 
| 275 | 0 |  |  |  |  | 0 | $_->_index($i++); | 
| 276 | 0 |  |  |  |  | 0 | $self->_apply_section_defaults($_); | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | # Build a list of every defined range of data in the file, | 
| 280 |  |  |  |  |  |  | # and a list of every segment/section which needs automatically placed. | 
| 281 | 1 |  |  |  |  | 1 | my @defined_ranges; | 
| 282 |  |  |  |  |  |  | my @auto_offset; | 
| 283 | 1 |  |  |  |  | 2 | for (@segments, @sections) { | 
| 284 |  |  |  |  |  |  | # size is guaranteed to be defined by "_apply...defaults()" | 
| 285 |  |  |  |  |  |  | # Data might not be defined if the user just wanted to point the | 
| 286 |  |  |  |  |  |  | # segment at something, and offset might not be defined if the user | 
| 287 |  |  |  |  |  |  | # just wants it appended wherever. | 
| 288 | 1 | 50 |  |  |  | 3 | if (!defined $_->offset) { | 
| 289 | 0 |  |  |  |  | 0 | push @auto_offset, $_; | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  | else { | 
| 292 | 1 | 50 |  |  |  | 10 | $_->offset >= 0 or croak $_->_name." offset cannot be negative"; | 
| 293 | 1 | 50 | 33 |  |  | 8 | push @defined_ranges, $_ | 
| 294 |  |  |  |  |  |  | if defined $_->data && length $_->data; | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  | } | 
| 297 |  |  |  |  |  |  |  | 
| 298 | 1 | 50 |  |  |  | 5 | if (@sections) { | 
| 299 |  |  |  |  |  |  | # First section must always be the NULL section.  If the user forgot this | 
| 300 |  |  |  |  |  |  | # then their indicies might be off. | 
| 301 | 0 | 0 |  |  |  | 0 | $sections[0]->type == 0 | 
| 302 |  |  |  |  |  |  | or croak "Section 0 must be type NULL"; | 
| 303 |  |  |  |  |  |  | # Sections may not overlap, regardless of whether the user attached data to them | 
| 304 | 0 |  |  |  |  | 0 | my $prev_end= 0; | 
| 305 | 0 |  |  |  |  | 0 | my $prev; | 
| 306 | 0 |  |  |  |  | 0 | for (sort { $a->offset <=> $b->offset } $self->section_list) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 307 | 0 | 0 |  |  |  | 0 | croak 'Section overlap between '.$_->_name.' and '.$prev->_name | 
| 308 |  |  |  |  |  |  | if $_->offset < $prev_end; | 
| 309 | 0 |  |  |  |  | 0 | $prev_end= $_->offset + $_->size; | 
| 310 |  |  |  |  |  |  | } | 
| 311 |  |  |  |  |  |  | } | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | # Each segment and section can define data to be written to the file, | 
| 314 |  |  |  |  |  |  | # but segments can overlap sections.  Make sure their defined data doesn't | 
| 315 |  |  |  |  |  |  | # conflict, or we wouldn't know which to write. | 
| 316 | 1 |  |  |  |  | 2 | my $prev; | 
| 317 | 1 |  |  |  |  | 4 | my $prev_end= $self->elf_header_len; | 
| 318 | 1 |  |  |  |  | 2 | my $first_data; | 
| 319 | 1 |  |  |  |  | 2 | @defined_ranges= sort { $a->data_offset <=> $b->data_offset } @defined_ranges; | 
|  | 0 |  |  |  |  | 0 |  | 
| 320 | 1 |  |  |  |  | 2 | for (@defined_ranges) { | 
| 321 | 1 | 0 |  |  |  | 4 | croak 'Data overlap between '.$_->_name.' and '.($prev? $prev->_name : 'ELF header') | 
|  |  | 50 |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | if $_->data_offset < $prev_end; | 
| 323 | 1 |  |  |  |  | 1 | $prev= $_; | 
| 324 | 1 |  |  |  |  | 2 | $prev_end= $_->data_offset + $_->size; | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | # For each segment or section that needs an offset assigned, append to | 
| 328 |  |  |  |  |  |  | # end of file. | 
| 329 | 1 |  |  |  |  | 1 | for (@auto_offset) { | 
| 330 | 0 |  |  |  |  | 0 | my $align= $_->_required_file_alignment; | 
| 331 | 0 |  |  |  |  | 0 | $prev_end= int(($prev_end + $align - 1) / $align) * $align; | 
| 332 | 0 |  |  |  |  | 0 | $_->offset($prev_end); | 
| 333 | 0 | 0 | 0 |  |  | 0 | push @defined_ranges, $_ if defined $_->data && length $_->data; | 
| 334 | 0 |  |  |  |  | 0 | $prev_end += $_->size; | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | # Now, every segment and section have an offset and a length. | 
| 338 |  |  |  |  |  |  | # We can now encode the tables. | 
| 339 | 1 |  |  |  |  | 2 | my @insert; | 
| 340 | 1 | 50 |  |  |  | 2 | if (@segments) { | 
| 341 | 1 |  |  |  |  | 1 | my $segment_table_data= ''; | 
| 342 |  |  |  |  |  |  | $segment_table_data .= $self->_serialize_segment_header($_) | 
| 343 | 1 |  |  |  |  | 6 | for @segments; | 
| 344 |  |  |  |  |  |  | # The user might have defined this segment on their own. | 
| 345 |  |  |  |  |  |  | # Otherwise we just create a dummy to use below. | 
| 346 | 1 | 50 |  |  |  | 27 | if (!defined $segment_table) { | 
| 347 | 1 |  |  |  |  | 19 | $segment_table= ELF::Writer::Segment->new( | 
| 348 |  |  |  |  |  |  | align => 8, | 
| 349 |  |  |  |  |  |  | filesize => length($segment_table_data), | 
| 350 |  |  |  |  |  |  | data => $segment_table_data, | 
| 351 |  |  |  |  |  |  | ); | 
| 352 | 1 |  |  |  |  | 4 | push @insert, $segment_table; | 
| 353 |  |  |  |  |  |  | } else { | 
| 354 | 0 |  |  |  |  | 0 | $segment_table->data($segment_table_data); | 
| 355 |  |  |  |  |  |  | } | 
| 356 |  |  |  |  |  |  | } | 
| 357 | 1 | 50 |  |  |  | 4 | if (@sections) { | 
| 358 | 0 |  |  |  |  | 0 | my $section_table_data= ''; | 
| 359 |  |  |  |  |  |  | $section_table_data .= $self->_serialize_section_header($_) | 
| 360 | 0 |  |  |  |  | 0 | for @sections; | 
| 361 |  |  |  |  |  |  |  | 
| 362 | 0 |  |  |  |  | 0 | $section_table= ELF::Writer::Segment->new( | 
| 363 |  |  |  |  |  |  | align => 8, | 
| 364 |  |  |  |  |  |  | filesize => length($section_table_data), | 
| 365 |  |  |  |  |  |  | data => $section_table_data, | 
| 366 |  |  |  |  |  |  | ); | 
| 367 | 0 |  |  |  |  | 0 | push @insert, $section_table; | 
| 368 |  |  |  |  |  |  | } | 
| 369 |  |  |  |  |  |  |  | 
| 370 |  |  |  |  |  |  | # Find a spot for the segment and/or section tables. | 
| 371 |  |  |  |  |  |  | # Due to alignment, there is probably room to squeeze the table(s) inbetween | 
| 372 |  |  |  |  |  |  | # other defined ranges.  Else, put them at the end. | 
| 373 | 1 |  |  |  |  | 2 | $prev_end= $self->elf_header_len; | 
| 374 | 1 |  | 66 |  |  | 6 | for (my $i= 0; @insert and $i <= @defined_ranges; ++$i) { | 
| 375 | 1 |  |  |  |  | 4 | my $align= $insert[0]->_required_file_alignment; | 
| 376 | 1 |  |  |  |  | 4 | $prev_end= int(($prev_end + $align-1) / $align) * $align; | 
| 377 | 1 | 50 | 33 |  |  | 6 | if ($i == @defined_ranges | 
| 378 |  |  |  |  |  |  | or $prev_end + $insert[0]->size <= $defined_ranges[$i]->data_offset | 
| 379 |  |  |  |  |  |  | ) { | 
| 380 | 1 |  |  |  |  | 6 | $insert[0]->offset($prev_end); | 
| 381 | 1 |  |  |  |  | 4 | splice @defined_ranges, $i, 0, shift @insert; | 
| 382 |  |  |  |  |  |  | } | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | # Now, we can finally encode the ELF header. | 
| 386 | 1 | 50 | 50 |  |  | 7 | my $header= pack($self->_elf_header_packstr, | 
|  |  | 50 |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | $Magic, $self->class, $self->data, $self->header_version, | 
| 388 |  |  |  |  |  |  | $self->osabi, $self->osabi_version, '', | 
| 389 |  |  |  |  |  |  | $self->type, $self->machine, $self->version, $self->entry_point, | 
| 390 |  |  |  |  |  |  | ($segment_table? $segment_table->offset : 0), | 
| 391 |  |  |  |  |  |  | ($section_table? $section_table->offset : 0), | 
| 392 |  |  |  |  |  |  | $self->flags, $self->elf_header_len, | 
| 393 |  |  |  |  |  |  | $self->segment_header_elem_len, $self->segment_count, | 
| 394 |  |  |  |  |  |  | $self->section_header_elem_len, $self->section_count, | 
| 395 |  |  |  |  |  |  | $self->section_name_string_table_idx || 0, | 
| 396 |  |  |  |  |  |  | ); | 
| 397 |  |  |  |  |  |  | # sanity check | 
| 398 | 1 | 50 |  |  |  | 21 | length($header) == $self->elf_header_len | 
| 399 |  |  |  |  |  |  | or croak "Elf header len mismatch"; | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | # Write out the header and each range of defined bytes, padded with NULs as needed. | 
| 402 | 1 |  |  |  |  | 2 | my $data= $header; | 
| 403 | 1 |  |  |  |  | 2 | for (@defined_ranges) { | 
| 404 | 2 |  |  |  |  | 5 | my $pad= $_->data_offset - length($data); | 
| 405 | 2 | 50 |  |  |  | 4 | $data .= "\0" x $pad if $pad; | 
| 406 | 2 |  |  |  |  | 7 | $data .= $_->data; | 
| 407 |  |  |  |  |  |  | } | 
| 408 | 1 |  |  |  |  | 6 | return $data; | 
| 409 |  |  |  |  |  |  | } | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | sub _serialize_segment_header { | 
| 412 | 1 |  |  | 1 |  | 1 | my ($self, $seg)= @_; | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | # Faster than checking bit lengths on every field ourself | 
| 415 | 1 |  |  | 1 |  | 4 | use warnings FATAL => 'pack'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 185 |  | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | # Make sure all required attributes are defined | 
| 418 |  |  |  |  |  |  | defined $seg->$_ or croak "Attribute $_ is not defined" | 
| 419 | 1 |  | 50 |  |  | 17 | for qw( type offset virt_addr align ); | 
| 420 |  |  |  |  |  |  |  | 
| 421 | 1 |  |  |  |  | 20 | my $filesize= $seg->filesize; | 
| 422 | 1 | 50 |  |  |  | 2 | $filesize= length($seg->data) + $seg->data_offset | 
| 423 |  |  |  |  |  |  | unless defined $filesize; | 
| 424 |  |  |  |  |  |  |  | 
| 425 | 1 |  |  |  |  | 3 | my $align= $seg->align; | 
| 426 | 1 |  |  |  |  | 2 | my $memsize= $seg->memsize; | 
| 427 | 1 | 50 |  |  |  | 5 | $memsize= int(($filesize + $align - 1) / $align) * $align | 
| 428 |  |  |  |  |  |  | unless defined $memsize; | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | # 'flags' moves depending on 32 vs 64 bit, so changing the pack string isn't enough | 
| 431 | 1 | 50 | 0 |  |  | 4 | return $self->_encoding < 2? | 
|  |  |  | 50 |  |  |  |  | 
| 432 |  |  |  |  |  |  | pack($self->_segment_header_packstr, | 
| 433 |  |  |  |  |  |  | $seg->type, $seg->offset, $seg->virt_addr, $seg->phys_addr || 0, | 
| 434 |  |  |  |  |  |  | $filesize, $memsize, $seg->flags, $seg->align | 
| 435 |  |  |  |  |  |  | ) | 
| 436 |  |  |  |  |  |  | : pack($self->_segment_header_packstr, | 
| 437 |  |  |  |  |  |  | $seg->type, $seg->flags, $seg->offset, $seg->virt_addr, | 
| 438 |  |  |  |  |  |  | $seg->phys_addr || 0, $filesize, $memsize, $seg->align | 
| 439 |  |  |  |  |  |  | ); | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | sub _serialize_section_header { | 
| 443 | 0 |  |  | 0 |  | 0 | my ($self, $sec)= @_; | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | # Make sure all required attributes are defined | 
| 446 |  |  |  |  |  |  | defined $sec->$_ or croak "Attribute $_ is not defined" | 
| 447 | 0 |  | 0 |  |  | 0 | for qw( type name flags addr offset size link info addralign entsize ); | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | # Faster than checking bit lengths on every field ourself | 
| 450 | 1 |  |  | 1 |  | 4 | use warnings FATAL => 'pack'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 468 |  | 
| 451 |  |  |  |  |  |  |  | 
| 452 | 0 |  |  |  |  | 0 | return pack($self->_section_header_packstr, | 
| 453 |  |  |  |  |  |  | $sec->name, $sec->type, $sec->flags, $sec->addr, $sec->offset, | 
| 454 |  |  |  |  |  |  | $sec->size, $sec->link, $sec->info, $sec->align, $sec->entry_size | 
| 455 |  |  |  |  |  |  | ); | 
| 456 |  |  |  |  |  |  | } | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | sub write_file { | 
| 460 | 0 |  |  | 0 | 0 | 0 | my ($self, $filename, $mode)= @_; | 
| 461 | 0 | 0 |  |  |  | 0 | $mode= 0755 unless defined $mode; | 
| 462 | 0 |  |  |  |  | 0 | require File::Temp; | 
| 463 | 0 |  |  |  |  | 0 | my ($fh, $tmpname)= File::Temp::tempfile( $filename.'-XXXXXX' ); | 
| 464 | 0 | 0 |  |  |  | 0 | print $fh $self->serialize or croak "write: $!"; | 
| 465 | 0 | 0 |  |  |  | 0 | close $fh or croak "close: $!"; | 
| 466 | 0 | 0 |  |  |  | 0 | chmod($mode, $tmpname) or croak "chmod: $!"; | 
| 467 | 0 | 0 |  |  |  | 0 | rename($tmpname, $filename) or croak "rename: $!"; | 
| 468 |  |  |  |  |  |  | } | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | # coerce arrayref of hashrefs into arrayref of objects | 
| 471 |  |  |  |  |  |  | sub _coerce_segments { | 
| 472 | 13 |  |  | 13 |  | 17 | my $spec= shift; | 
| 473 | 13 |  |  |  |  | 149 | return [ map { (__PACKAGE__.'::Segment')->coerce($_) } @$spec ]; | 
|  | 1 |  |  |  |  | 4 |  | 
| 474 |  |  |  |  |  |  | } | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | # coerce arrayref of hashrefs into arrayref of objects | 
| 477 |  |  |  |  |  |  | sub _coerce_sections { | 
| 478 | 13 |  |  | 13 |  | 11 | my $spec= shift; | 
| 479 | 13 |  |  |  |  | 158 | return [ map { (__PACKAGE__.'::Section')->coerce($_) } @$spec ]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 480 |  |  |  |  |  |  | } | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | # Overridden by subclasses for machine-specific defaults | 
| 483 |  |  |  |  |  |  | sub _apply_section_defaults { | 
| 484 | 0 |  |  | 0 |  | 0 | my ($self, $sec)= @_; | 
| 485 |  |  |  |  |  |  | # Undef type is "null" type 0 | 
| 486 | 0 |  |  |  |  | 0 | my $type= $sec->type; | 
| 487 | 0 | 0 |  |  |  | 0 | defined $type | 
| 488 |  |  |  |  |  |  | or $sec->type($type= 0); | 
| 489 | 0 |  |  |  |  | 0 | my $offset= $sec->offset; | 
| 490 | 0 |  |  |  |  | 0 | my $size= $sec->size; | 
| 491 |  |  |  |  |  |  |  | 
| 492 | 0 | 0 |  |  |  | 0 | if ($type == 0) { # 'null' | 
|  |  | 0 |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | # Ensure length and offset are zero | 
| 494 | 0 | 0 |  |  |  | 0 | $size= $sec->size(0) unless defined $size; | 
| 495 | 0 | 0 |  |  |  | 0 | $offset= $sec->offset(0) unless defined $offset; | 
| 496 | 0 | 0 | 0 |  |  | 0 | croak "null section should have offset=0 and size=0" | 
| 497 |  |  |  |  |  |  | if $offset || $size; | 
| 498 |  |  |  |  |  |  | } | 
| 499 |  |  |  |  |  |  | elsif ($type == 8) { # 'nobits' | 
| 500 |  |  |  |  |  |  | # Offset can be set but ensure size is zero | 
| 501 | 0 | 0 |  |  |  | 0 | $size= $sec->size(0) unless defined $size; | 
| 502 | 0 | 0 |  |  |  | 0 | croak "nobits section should have size=0" | 
| 503 |  |  |  |  |  |  | if $size; | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | } | 
| 506 |  |  |  |  |  |  | else { | 
| 507 |  |  |  |  |  |  | # 'size' is required, but can be computed from 'data' and 'data_offset'. | 
| 508 | 0 | 0 |  |  |  | 0 | if (!defined $size) { | 
| 509 | 0 | 0 |  |  |  | 0 | defined $sec->data or croak "Section must define 'size' or 'data'"; | 
| 510 | 0 |  |  |  |  | 0 | $sec->size($sec->data_start + length($sec->data)); | 
| 511 |  |  |  |  |  |  | } | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  | } | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | # Overridden by subclasses for machine-specific defaults | 
| 516 |  |  |  |  |  |  | sub _apply_segment_defaults { | 
| 517 | 1 |  |  | 1 |  | 2 | my ($self, $seg)= @_; | 
| 518 |  |  |  |  |  |  | # Undef type is "null" type 0 | 
| 519 | 1 |  |  |  |  | 15 | my $type= $seg->type; | 
| 520 | 1 | 50 |  |  |  | 7 | defined $type | 
| 521 |  |  |  |  |  |  | or $seg->type($type= 0); | 
| 522 | 1 |  |  |  |  | 8 | my $offset= $seg->offset; | 
| 523 | 1 |  |  |  |  | 2 | my $filesize= $seg->filesize; | 
| 524 |  |  |  |  |  |  |  | 
| 525 | 1 | 50 |  |  |  | 3 | if ($type == 0) { # 'null' | 
| 526 |  |  |  |  |  |  | # Ensure length and offset are zero | 
| 527 | 0 | 0 |  |  |  | 0 | $filesize= $seg->filesize(0) unless defined $filesize; | 
| 528 | 0 | 0 |  |  |  | 0 | $offset= $seg->offset(0) unless defined $offset; | 
| 529 | 0 | 0 | 0 |  |  | 0 | croak "null segment should have offset=0 and filesize=0" | 
| 530 |  |  |  |  |  |  | if $offset || $filesize; | 
| 531 |  |  |  |  |  |  | } | 
| 532 |  |  |  |  |  |  | else { | 
| 533 |  |  |  |  |  |  | # 'filesize' is required, but can be computed from 'data' and 'data_offset' | 
| 534 | 1 | 50 |  |  |  | 2 | if (!defined $filesize) { | 
| 535 | 1 | 50 |  |  |  | 3 | defined $seg->data or croak "Segment must define 'filesize' or 'data'"; | 
| 536 | 1 |  |  |  |  | 5 | $filesize= $seg->filesize($seg->data_start + length($seg->data)); | 
| 537 |  |  |  |  |  |  | } | 
| 538 |  |  |  |  |  |  | # Default memsize to filesize | 
| 539 | 1 | 50 |  |  |  | 5 | $seg->memsize($filesize) unless defined $seg->memsize; | 
| 540 |  |  |  |  |  |  | } | 
| 541 |  |  |  |  |  |  | } | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | # Load last so make sure data is initialized | 
| 544 |  |  |  |  |  |  | require ELF::Writer::Segment; | 
| 545 |  |  |  |  |  |  | require ELF::Writer::Section; | 
| 546 |  |  |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | 1; | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | __END__ |