File Coverage

lib/ELF/Extract/Sections.pm
Criterion Covered Total %
statement 88 112 78.5
branch 5 10 50.0
condition n/a
subroutine 21 24 87.5
pod 1 2 50.0
total 115 148 77.7


line stmt bran cond sub pod time code
1 2     2   103121 use 5.006;
  2         6  
2 2     2   11 use strict;
  2         2  
  2         43  
3 2     2   9 use warnings;
  2         3  
  2         139  
4              
5             package ELF::Extract::Sections;
6              
7             # ABSTRACT: Extract Raw Chunks of data from identifiable ELF Sections
8              
9             our $VERSION = '1.001002'; # TRIAL
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 2     2   1485 use Moose qw( with has );
  2         875116  
  2         16  
14 2     2   10989 use Carp qw( croak );
  2         7  
  2         166  
15             with 'MooseX::Log::Log4perl';
16              
17 2     2   1951 use MooseX::Has::Sugar 0.0300;
  2         1321  
  2         10  
18 2     2   1518 use MooseX::Types::Moose ( ':all', );
  2         120706  
  2         20  
19 2     2   17897 use MooseX::Types::Path::Tiny ( 'File', );
  2         281139  
  2         19  
20 2     2   6832 use ELF::Extract::Sections::Meta::Types ( ':all', );
  2         6  
  2         11  
21 2     2   2247 use Module::Runtime ( 'require_module', );
  2         6  
  2         19  
22 2     2   1950 use MooseX::Params::Validate (qw( validated_list pos_validated_list ));
  2         147740  
  2         17  
23              
24             require ELF::Extract::Sections::Section;
25              
26              
27              
28              
29              
30              
31              
32             has 'file' => ( isa => File, ro, required, coerce, );
33              
34              
35              
36              
37              
38              
39              
40             has 'sections' => ( isa => HashRef [ElfSection], ro, lazy_build, );
41              
42              
43              
44              
45              
46              
47              
48             has 'scanner' => ( isa => Str, ro, default => 'Objdump', );
49              
50              
51              
52              
53              
54              
55              
56              
57              
58              
59              
60              
61              
62              
63              
64              
65              
66             sub BUILD {
67 4     4 0 10 my ( $self, ) = @_;
68 4 50       149 if ( not $self->file->stat ) {
69 0         0 $self->log->logconfess(q{File Specifed could not be found.});
70             }
71 4         14822 return;
72             }
73              
74              
75              
76              
77              
78              
79              
80              
81              
82              
83              
84              
85              
86              
87              
88              
89              
90              
91              
92              
93              
94              
95              
96              
97              
98              
99              
100              
101              
102              
103              
104              
105              
106             sub sorted_sections {
107 0     0 1 0 my ( $self, $field, $descending ) = validated_list(
108             \@_,
109             'field' => { isa => FilterField, optional => 1 },
110             'descending' => { isa => Bool, optional => 1 },
111             );
112 0         0 my $m = 1;
113 0 0       0 $m = 0 - 1 if ($descending);
114 0         0 return [ sort { $m * ( $a->compare( other => $b, field => $field ) ) } values %{ $self->sections } ];
  0         0  
  0         0  
115             }
116              
117             sub _build_sections {
118 4     4   10 my ($self) = @_;
119 4         36 $self->log->debug('Building Section List');
120 4 50       1564 if ( $self->_scanner_instance->can_compute_size ) {
121 0         0 return $self->_scan_with_size;
122             }
123             else {
124 4         19 return $self->_scan_guess_size;
125             }
126             }
127              
128             has '_scanner_package' => ( isa => ClassName, ro, lazy_build, );
129              
130             has '_scanner_instance' => ( isa => Object, ro, lazy_build, );
131              
132             __PACKAGE__->meta->make_immutable;
133 2     2   1345 no Moose;
  2         4  
  2         22  
134              
135             sub _error_scanner_missing {
136 0     0   0 my ( $self, @args ) = @_;
137 0         0 my ( $scanner, $package, $error ) = pos_validated_list(
138             \@args,
139             { isa => Str, }, #
140             { isa => Str, }, #
141             { isa => Str, }, #
142             );
143 0         0 my $message = sprintf qq[The Scanner %s could not be found as %s\n.], $scanner, $package;
144 0         0 $message .= '>' . $error;
145 0         0 $self->log->logconfess($message);
146 0         0 return;
147             }
148              
149             sub _build__scanner_package {
150 4     4   9 my ($self) = @_;
151 4         217 my $pkg = 'ELF::Extract::Sections::Scanner::' . $self->scanner;
152 4         11 local $@ = undef;
153 4 50       11 if ( not eval { require_module($pkg); 1 } ) {
  4         44  
  4         156  
154 0         0 return $self->_error_scanner_missing( $self->scanner, $pkg, $@ );
155             }
156 4         143 return $pkg;
157             }
158              
159             sub _build__scanner_instance {
160 4     4   12 my ($self) = @_;
161 4         214 my $instance = $self->_scanner_package->new();
162 4         137 return $instance;
163             }
164              
165             sub _warn_stash_collision {
166 71     71   219 my ( $self, @args ) = @_;
167 71         202 my ( $stashname, $header, $offset ) = pos_validated_list(
168             \@args,
169             { isa => Str, }, #
170             { isa => Str, },
171             { isa => Str, },
172             );
173 71         137727 my $message = q[Warning, duplicate file offset reported by scanner.];
174 71         266 $message .= sprintf q[<%s> and <%s> collide at <%s>.], $stashname, $header, $offset;
175 71         135 $message .= sprintf q[Assuming <%s> is empty and replacing it.], $stashname;
176 71         220 $self->log->warn($message);
177 71         3427 return;
178             }
179              
180             sub _stash_record {
181 5668     5668   12013 my ( $self, @args ) = @_;
182 5668         16561 my ( $stash, $header, $offset ) = pos_validated_list(
183             \@args,
184             { isa => HashRef, }, #
185             { isa => Str, },
186             { isa => Str, },
187             );
188 5668 100       10879322 if ( exists $stash->{$offset} ) {
189 71         235 $self->_warn_stash_collision( $stash->{$offset}, $header, $offset );
190             }
191 5668         16518 $stash->{$offset} = $header;
192 5668         214057 return;
193             }
194              
195             sub _build_section_section {
196 5593     5593   14507 my ( $self, @args ) = @_;
197 5593         14715 my ( $stashName, $start, $stop, $file ) = pos_validated_list(
198             \@args,
199             { isa => Str, required => 1 },
200             { isa => Int, required => 1 },
201             { isa => Int, required => 1 },
202             { isa => File, required => 1 },
203             );
204 5593         16221459 $self->log->info(" Section ${stashName} , ${start} -> ${stop} ");
205 5593         462708 return ELF::Extract::Sections::Section->new(
206             offset => $start,
207             size => $stop - $start,
208             name => $stashName,
209             source => $file,
210             );
211             }
212              
213             sub _build_section_table {
214 4     4   21 my ( $self, @args ) = @_;
215 4         23 my ($ob) = pos_validated_list(
216             \@args, #
217             { isa => HashRef },
218             );
219 4         4170 my %datastash = ();
220 4         9 my @k = sort { $a <=> $b } keys %{$ob};
  57670         61813  
  4         1701  
221 4         250 my $i = 0;
222 4         22 while ( $i < $#k ) {
223 5593         191222 $datastash{ $ob->{ $k[$i] } } = $self->_build_section_section( $ob->{ $k[$i] }, $k[$i], $k[ $i + 1 ], $self->file );
224 5593         31334 $i++;
225             }
226 4         973 return \%datastash;
227             }
228              
229             sub _scan_guess_size {
230 4     4   7 my ($self) = @_;
231              
232             # HACK: Temporary hack around rt#67210
233 4         134 scalar $self->_scanner_instance->open_file( file => $self->file );
234 4         32 my %offsets = ();
235 4         236 while ( $self->_scanner_instance->next_section() ) {
236 5668         199566 my $name = $self->_scanner_instance->section_name;
237 5668         196099 my $offset = $self->_scanner_instance->section_offset;
238 5668         14599 $self->_stash_record( \%offsets, $name, $offset );
239             }
240 4         27 return $self->_build_section_table( \%offsets );
241             }
242              
243             sub _scan_with_size {
244 0     0     my ($self) = @_;
245 0           my %datastash = ();
246 0           $self->_scanner_instance->open_file( file => $self->file );
247 0           while ( $self->_scanner_instance->next_section() ) {
248 0           my $name = $self->_scanner_instance->section_name;
249 0           my $offset = $self->_scanner_instance->section_offset;
250 0           my $size = $self->_scanner_instance->section_size;
251 0           $datastash{$name} = $self->_build_section_section( $name, $offset, $offset + $size, $self->file );
252             }
253 0           return \%datastash;
254             }
255              
256             1;
257              
258             __END__
259              
260             =pod
261              
262             =encoding UTF-8
263              
264             =head1 NAME
265              
266             ELF::Extract::Sections - Extract Raw Chunks of data from identifiable ELF Sections
267              
268             =head1 VERSION
269              
270             version 1.001002
271              
272             =head1 SYNOPSIS
273              
274             use ELF::Extract::Sections;
275              
276             # Create an extractor object for foo.so
277             my $extractor = ELF::Extract::Sections->new( file => '/path/to/foo.so' );
278              
279             # Scan file for section data, returns a hash
280             my %sections = ${ $extractor->sections };
281              
282             # Retreive the section object for the comment section
283             my $data = $sections{.comment};
284              
285             # Print the stringified explanation of the section
286             print "$data";
287              
288             # Get the raw bytes out of the section.
289             print $data->contents # returns bytes
290              
291             =head1 METHODS
292              
293             =head2 C<new>
294              
295             my $object = ELF::Extract::Sections->new( file => FILENAME );
296              
297             Creates A new Section Extractor object with the default scanner
298              
299             my $object = ELF::Extract::Sections->new( file => FILENAME , scanner => 'Objdump' )
300              
301             Creates A new Section Extractor object with the specified scanner
302              
303             =head2 C<sorted_sections>
304              
305             my $sections = $object->sorted_sections( field => SORT_BY )
306              
307             Returns an ArrayRef sorted by the SORT_BY field, in the default order.
308              
309             my $sections = $object->sorted_sections( field => SORT_BY, descending => DESCENDING );
310              
311             Returns an ArrayRef sorted by the SORT_BY field. May be Ascending or Descending depending on requirements.
312              
313             =head3 DESCENDING
314              
315             Optional parameters. True for descending, False or absent for ascending.
316              
317             =head3 SORT_BY
318              
319             A String of the field to sort by. Valid options at present are
320              
321             =head4 name
322              
323             The Section Name
324              
325             =head4 offset
326              
327             The Sections offset relative to the start of the file.
328              
329             =head4 size
330              
331             The Size of the section.
332              
333             =head1 ATTRIBUTES
334              
335             =head2 C<file>
336              
337             Returns the file the section data is being created for.
338              
339             =head2 C<sections>
340              
341             Returns a HashRef of the available sections.
342              
343             =head2 C<scanner>
344              
345             Returns the name of the default scanner plug-in
346              
347             =for Pod::Coverage BUILD
348              
349             =head1 CAVEATS
350              
351             =over 4
352              
353             =item 1. Beta Software
354              
355             This code is relatively new. It exists only as a best attempt at present until further notice. It
356             has proved as practical for at least one application, and this is why the module exists. However, it can't be
357             guaranteed it will work for whatever you want it to in all cases. Please report any bugs you find.
358              
359             =item 2. Feature Incomplete
360              
361             This only presently has a very bare-bones functionality, which should however prove practical for most purposes.
362             If you have any suggestions, please tell me via "report bugs". If you never seek, you'll never find.
363              
364             =item 3. Humans
365              
366             This code is written by a human, and like all human code, it sucks. There will be bugs. Please report them.
367              
368             =back
369              
370             =head1 DEBUGGING
371              
372             This library uses L<Log::Log4perl>. To see more verbose processing notices, do this:
373              
374             use Log::Log4perl qw( :easy );
375             Log::Log4perl->easy_init($DEBUG);
376              
377             For convenience to make sure you don't happen to miss this fact, we never initialize Log4perl ourselves, so it will
378             spit the following message if you have not set it up:
379              
380             Log4perl: Seems like no initialization happened. Forgot to call init()?
381              
382             To suppress this, just do
383              
384             use Log::Log4perl qw( :easy );
385              
386             I request however you B<don't> do that for modules intended to be consumed by others without good cause.
387              
388             =head1 AUTHOR
389              
390             Kent Fredric <kentnl@cpan.org>
391              
392             =head1 COPYRIGHT AND LICENSE
393              
394             This software is copyright (c) 2015 by Kent Fredric <kentfredric@gmail.com>.
395              
396             This is free software; you can redistribute it and/or modify it under
397             the same terms as the Perl 5 programming language system itself.
398              
399             =cut