Merge https://gitlab.denx.de/u-boot/custodians/u-boot-sh
[platform/kernel/u-boot.git] / tools / binman / entry.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 #
4 # Base class for all entries
5 #
6
7 from collections import namedtuple
8 import importlib
9 import os
10 import sys
11
12 from dtoc import fdt_util
13 from patman import tools
14 from patman.tools import ToHex, ToHexSize
15 from patman import tout
16
17 modules = {}
18
19
20 # An argument which can be passed to entries on the command line, in lieu of
21 # device-tree properties.
22 EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
23
24 # Information about an entry for use when displaying summaries
25 EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
26                                      'image_pos', 'uncomp_size', 'offset',
27                                      'entry'])
28
29 class Entry(object):
30     """An Entry in the section
31
32     An entry corresponds to a single node in the device-tree description
33     of the section. Each entry ends up being a part of the final section.
34     Entries can be placed either right next to each other, or with padding
35     between them. The type of the entry determines the data that is in it.
36
37     This class is not used by itself. All entry objects are subclasses of
38     Entry.
39
40     Attributes:
41         section: Section object containing this entry
42         node: The node that created this entry
43         offset: Offset of entry within the section, None if not known yet (in
44             which case it will be calculated by Pack())
45         size: Entry size in bytes, None if not known
46         pre_reset_size: size as it was before ResetForPack(). This allows us to
47             keep track of the size we started with and detect size changes
48         uncomp_size: Size of uncompressed data in bytes, if the entry is
49             compressed, else None
50         contents_size: Size of contents in bytes, 0 by default
51         align: Entry start offset alignment relative to the start of the
52             containing section, or None
53         align_size: Entry size alignment, or None
54         align_end: Entry end offset alignment relative to the start of the
55             containing section, or None
56         pad_before: Number of pad bytes before the contents when it is placed
57             in the containing section, 0 if none. The pad bytes become part of
58             the entry.
59         pad_after: Number of pad bytes after the contents when it is placed in
60             the containing section, 0 if none. The pad bytes become part of
61             the entry.
62         data: Contents of entry (string of bytes). This does not include
63             padding created by pad_before or pad_after. If the entry is
64             compressed, this contains the compressed data.
65         uncomp_data: Original uncompressed data, if this entry is compressed,
66             else None
67         compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
68         orig_offset: Original offset value read from node
69         orig_size: Original size value read from node
70         missing: True if this entry is missing its contents
71         allow_missing: Allow children of this entry to be missing (used by
72             subclasses such as Entry_section)
73         external: True if this entry contains an external binary blob
74     """
75     def __init__(self, section, etype, node, name_prefix=''):
76         # Put this here to allow entry-docs and help to work without libfdt
77         global state
78         from binman import state
79
80         self.section = section
81         self.etype = etype
82         self._node = node
83         self.name = node and (name_prefix + node.name) or 'none'
84         self.offset = None
85         self.size = None
86         self.pre_reset_size = None
87         self.uncomp_size = None
88         self.data = None
89         self.uncomp_data = None
90         self.contents_size = 0
91         self.align = None
92         self.align_size = None
93         self.align_end = None
94         self.pad_before = 0
95         self.pad_after = 0
96         self.offset_unset = False
97         self.image_pos = None
98         self._expand_size = False
99         self.compress = 'none'
100         self.missing = False
101         self.external = False
102         self.allow_missing = False
103
104     @staticmethod
105     def Lookup(node_path, etype):
106         """Look up the entry class for a node.
107
108         Args:
109             node_node: Path name of Node object containing information about
110                        the entry to create (used for errors)
111             etype:   Entry type to use
112
113         Returns:
114             The entry class object if found, else None
115         """
116         # Convert something like 'u-boot@0' to 'u_boot' since we are only
117         # interested in the type.
118         module_name = etype.replace('-', '_')
119         if '@' in module_name:
120             module_name = module_name.split('@')[0]
121         module = modules.get(module_name)
122
123         # Also allow entry-type modules to be brought in from the etype directory.
124
125         # Import the module if we have not already done so.
126         if not module:
127             try:
128                 module = importlib.import_module('binman.etype.' + module_name)
129             except ImportError as e:
130                 raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
131                                  (etype, node_path, module_name, e))
132             modules[module_name] = module
133
134         # Look up the expected class name
135         return getattr(module, 'Entry_%s' % module_name)
136
137     @staticmethod
138     def Create(section, node, etype=None):
139         """Create a new entry for a node.
140
141         Args:
142             section: Section object containing this node
143             node:    Node object containing information about the entry to
144                      create
145             etype:   Entry type to use, or None to work it out (used for tests)
146
147         Returns:
148             A new Entry object of the correct type (a subclass of Entry)
149         """
150         if not etype:
151             etype = fdt_util.GetString(node, 'type', node.name)
152         obj = Entry.Lookup(node.path, etype)
153
154         # Call its constructor to get the object we want.
155         return obj(section, etype, node)
156
157     def ReadNode(self):
158         """Read entry information from the node
159
160         This must be called as the first thing after the Entry is created.
161
162         This reads all the fields we recognise from the node, ready for use.
163         """
164         if 'pos' in self._node.props:
165             self.Raise("Please use 'offset' instead of 'pos'")
166         self.offset = fdt_util.GetInt(self._node, 'offset')
167         self.size = fdt_util.GetInt(self._node, 'size')
168         self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset')
169         self.orig_size = fdt_util.GetInt(self._node, 'orig-size')
170         if self.GetImage().copy_to_orig:
171             self.orig_offset = self.offset
172             self.orig_size = self.size
173
174         # These should not be set in input files, but are set in an FDT map,
175         # which is also read by this code.
176         self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
177         self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
178
179         self.align = fdt_util.GetInt(self._node, 'align')
180         if tools.NotPowerOfTwo(self.align):
181             raise ValueError("Node '%s': Alignment %s must be a power of two" %
182                              (self._node.path, self.align))
183         self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
184         self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
185         self.align_size = fdt_util.GetInt(self._node, 'align-size')
186         if tools.NotPowerOfTwo(self.align_size):
187             self.Raise("Alignment size %s must be a power of two" %
188                        self.align_size)
189         self.align_end = fdt_util.GetInt(self._node, 'align-end')
190         self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
191         self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
192         self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
193
194         # This is only supported by blobs and sections at present
195         self.compress = fdt_util.GetString(self._node, 'compress', 'none')
196
197     def GetDefaultFilename(self):
198         return None
199
200     def GetFdts(self):
201         """Get the device trees used by this entry
202
203         Returns:
204             Empty dict, if this entry is not a .dtb, otherwise:
205             Dict:
206                 key: Filename from this entry (without the path)
207                 value: Tuple:
208                     Fdt object for this dtb, or None if not available
209                     Filename of file containing this dtb
210         """
211         return {}
212
213     def ExpandEntries(self):
214         pass
215
216     def AddMissingProperties(self, have_image_pos):
217         """Add new properties to the device tree as needed for this entry
218
219         Args:
220             have_image_pos: True if this entry has an image position. This can
221                 be False if its parent section is compressed, since compression
222                 groups all entries together into a compressed block of data,
223                 obscuring the start of each individual child entry
224         """
225         for prop in ['offset', 'size']:
226             if not prop in self._node.props:
227                 state.AddZeroProp(self._node, prop)
228         if have_image_pos and 'image-pos' not in self._node.props:
229             state.AddZeroProp(self._node, 'image-pos')
230         if self.GetImage().allow_repack:
231             if self.orig_offset is not None:
232                 state.AddZeroProp(self._node, 'orig-offset', True)
233             if self.orig_size is not None:
234                 state.AddZeroProp(self._node, 'orig-size', True)
235
236         if self.compress != 'none':
237             state.AddZeroProp(self._node, 'uncomp-size')
238         err = state.CheckAddHashProp(self._node)
239         if err:
240             self.Raise(err)
241
242     def SetCalculatedProperties(self):
243         """Set the value of device-tree properties calculated by binman"""
244         state.SetInt(self._node, 'offset', self.offset)
245         state.SetInt(self._node, 'size', self.size)
246         base = self.section.GetRootSkipAtStart() if self.section else 0
247         if self.image_pos is not None:
248             state.SetInt(self._node, 'image-pos', self.image_pos - base)
249         if self.GetImage().allow_repack:
250             if self.orig_offset is not None:
251                 state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
252             if self.orig_size is not None:
253                 state.SetInt(self._node, 'orig-size', self.orig_size, True)
254         if self.uncomp_size is not None:
255             state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
256         state.CheckSetHashValue(self._node, self.GetData)
257
258     def ProcessFdt(self, fdt):
259         """Allow entries to adjust the device tree
260
261         Some entries need to adjust the device tree for their purposes. This
262         may involve adding or deleting properties.
263
264         Returns:
265             True if processing is complete
266             False if processing could not be completed due to a dependency.
267                 This will cause the entry to be retried after others have been
268                 called
269         """
270         return True
271
272     def SetPrefix(self, prefix):
273         """Set the name prefix for a node
274
275         Args:
276             prefix: Prefix to set, or '' to not use a prefix
277         """
278         if prefix:
279             self.name = prefix + self.name
280
281     def SetContents(self, data):
282         """Set the contents of an entry
283
284         This sets both the data and content_size properties
285
286         Args:
287             data: Data to set to the contents (bytes)
288         """
289         self.data = data
290         self.contents_size = len(self.data)
291
292     def ProcessContentsUpdate(self, data):
293         """Update the contents of an entry, after the size is fixed
294
295         This checks that the new data is the same size as the old. If the size
296         has changed, this triggers a re-run of the packing algorithm.
297
298         Args:
299             data: Data to set to the contents (bytes)
300
301         Raises:
302             ValueError if the new data size is not the same as the old
303         """
304         size_ok = True
305         new_size = len(data)
306         if state.AllowEntryExpansion() and new_size > self.contents_size:
307             # self.data will indicate the new size needed
308             size_ok = False
309         elif state.AllowEntryContraction() and new_size < self.contents_size:
310             size_ok = False
311
312         # If not allowed to change, try to deal with it or give up
313         if size_ok:
314             if new_size > self.contents_size:
315                 self.Raise('Cannot update entry size from %d to %d' %
316                         (self.contents_size, new_size))
317
318             # Don't let the data shrink. Pad it if necessary
319             if size_ok and new_size < self.contents_size:
320                 data += tools.GetBytes(0, self.contents_size - new_size)
321
322         if not size_ok:
323             tout.Debug("Entry '%s' size change from %s to %s" % (
324                 self._node.path, ToHex(self.contents_size),
325                 ToHex(new_size)))
326         self.SetContents(data)
327         return size_ok
328
329     def ObtainContents(self):
330         """Figure out the contents of an entry.
331
332         Returns:
333             True if the contents were found, False if another call is needed
334             after the other entries are processed.
335         """
336         # No contents by default: subclasses can implement this
337         return True
338
339     def ResetForPack(self):
340         """Reset offset/size fields so that packing can be done again"""
341         self.Detail('ResetForPack: offset %s->%s, size %s->%s' %
342                     (ToHex(self.offset), ToHex(self.orig_offset),
343                      ToHex(self.size), ToHex(self.orig_size)))
344         self.pre_reset_size = self.size
345         self.offset = self.orig_offset
346         self.size = self.orig_size
347
348     def Pack(self, offset):
349         """Figure out how to pack the entry into the section
350
351         Most of the time the entries are not fully specified. There may be
352         an alignment but no size. In that case we take the size from the
353         contents of the entry.
354
355         If an entry has no hard-coded offset, it will be placed at @offset.
356
357         Once this function is complete, both the offset and size of the
358         entry will be know.
359
360         Args:
361             Current section offset pointer
362
363         Returns:
364             New section offset pointer (after this entry)
365         """
366         self.Detail('Packing: offset=%s, size=%s, content_size=%x' %
367                     (ToHex(self.offset), ToHex(self.size),
368                      self.contents_size))
369         if self.offset is None:
370             if self.offset_unset:
371                 self.Raise('No offset set with offset-unset: should another '
372                            'entry provide this correct offset?')
373             self.offset = tools.Align(offset, self.align)
374         needed = self.pad_before + self.contents_size + self.pad_after
375         needed = tools.Align(needed, self.align_size)
376         size = self.size
377         if not size:
378             size = needed
379         new_offset = self.offset + size
380         aligned_offset = tools.Align(new_offset, self.align_end)
381         if aligned_offset != new_offset:
382             size = aligned_offset - self.offset
383             new_offset = aligned_offset
384
385         if not self.size:
386             self.size = size
387
388         if self.size < needed:
389             self.Raise("Entry contents size is %#x (%d) but entry size is "
390                        "%#x (%d)" % (needed, needed, self.size, self.size))
391         # Check that the alignment is correct. It could be wrong if the
392         # and offset or size values were provided (i.e. not calculated), but
393         # conflict with the provided alignment values
394         if self.size != tools.Align(self.size, self.align_size):
395             self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
396                   (self.size, self.size, self.align_size, self.align_size))
397         if self.offset != tools.Align(self.offset, self.align):
398             self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
399                   (self.offset, self.offset, self.align, self.align))
400         self.Detail('   - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' %
401                     (self.offset, self.size, self.contents_size, new_offset))
402
403         return new_offset
404
405     def Raise(self, msg):
406         """Convenience function to raise an error referencing a node"""
407         raise ValueError("Node '%s': %s" % (self._node.path, msg))
408
409     def Detail(self, msg):
410         """Convenience function to log detail referencing a node"""
411         tag = "Node '%s'" % self._node.path
412         tout.Detail('%30s: %s' % (tag, msg))
413
414     def GetEntryArgsOrProps(self, props, required=False):
415         """Return the values of a set of properties
416
417         Args:
418             props: List of EntryArg objects
419
420         Raises:
421             ValueError if a property is not found
422         """
423         values = []
424         missing = []
425         for prop in props:
426             python_prop = prop.name.replace('-', '_')
427             if hasattr(self, python_prop):
428                 value = getattr(self, python_prop)
429             else:
430                 value = None
431             if value is None:
432                 value = self.GetArg(prop.name, prop.datatype)
433             if value is None and required:
434                 missing.append(prop.name)
435             values.append(value)
436         if missing:
437             self.GetImage().MissingArgs(self, missing)
438         return values
439
440     def GetPath(self):
441         """Get the path of a node
442
443         Returns:
444             Full path of the node for this entry
445         """
446         return self._node.path
447
448     def GetData(self):
449         """Get the contents of an entry
450
451         Returns:
452             bytes content of the entry, excluding any padding. If the entry is
453                 compressed, the compressed data is returned
454         """
455         self.Detail('GetData: size %s' % ToHexSize(self.data))
456         return self.data
457
458     def GetPaddedData(self, data=None):
459         """Get the data for an entry including any padding
460
461         Gets the entry data and uses its section's pad-byte value to add padding
462         before and after as defined by the pad-before and pad-after properties.
463
464         This does not consider alignment.
465
466         Returns:
467             Contents of the entry along with any pad bytes before and
468             after it (bytes)
469         """
470         if data is None:
471             data = self.GetData()
472         return self.section.GetPaddedDataForEntry(self, data)
473
474     def GetOffsets(self):
475         """Get the offsets for siblings
476
477         Some entry types can contain information about the position or size of
478         other entries. An example of this is the Intel Flash Descriptor, which
479         knows where the Intel Management Engine section should go.
480
481         If this entry knows about the position of other entries, it can specify
482         this by returning values here
483
484         Returns:
485             Dict:
486                 key: Entry type
487                 value: List containing position and size of the given entry
488                     type. Either can be None if not known
489         """
490         return {}
491
492     def SetOffsetSize(self, offset, size):
493         """Set the offset and/or size of an entry
494
495         Args:
496             offset: New offset, or None to leave alone
497             size: New size, or None to leave alone
498         """
499         if offset is not None:
500             self.offset = offset
501         if size is not None:
502             self.size = size
503
504     def SetImagePos(self, image_pos):
505         """Set the position in the image
506
507         Args:
508             image_pos: Position of this entry in the image
509         """
510         self.image_pos = image_pos + self.offset
511
512     def ProcessContents(self):
513         """Do any post-packing updates of entry contents
514
515         This function should call ProcessContentsUpdate() to update the entry
516         contents, if necessary, returning its return value here.
517
518         Args:
519             data: Data to set to the contents (bytes)
520
521         Returns:
522             True if the new data size is OK, False if expansion is needed
523
524         Raises:
525             ValueError if the new data size is not the same as the old and
526                 state.AllowEntryExpansion() is False
527         """
528         return True
529
530     def WriteSymbols(self, section):
531         """Write symbol values into binary files for access at run time
532
533         Args:
534           section: Section containing the entry
535         """
536         pass
537
538     def CheckEntries(self):
539         """Check that the entry offsets are correct
540
541         This is used for entries which have extra offset requirements (other
542         than having to be fully inside their section). Sub-classes can implement
543         this function and raise if there is a problem.
544         """
545         pass
546
547     @staticmethod
548     def GetStr(value):
549         if value is None:
550             return '<none>  '
551         return '%08x' % value
552
553     @staticmethod
554     def WriteMapLine(fd, indent, name, offset, size, image_pos):
555         print('%s  %s%s  %s  %s' % (Entry.GetStr(image_pos), ' ' * indent,
556                                     Entry.GetStr(offset), Entry.GetStr(size),
557                                     name), file=fd)
558
559     def WriteMap(self, fd, indent):
560         """Write a map of the entry to a .map file
561
562         Args:
563             fd: File to write the map to
564             indent: Curent indent level of map (0=none, 1=one level, etc.)
565         """
566         self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
567                           self.image_pos)
568
569     def GetEntries(self):
570         """Return a list of entries contained by this entry
571
572         Returns:
573             List of entries, or None if none. A normal entry has no entries
574                 within it so will return None
575         """
576         return None
577
578     def GetArg(self, name, datatype=str):
579         """Get the value of an entry argument or device-tree-node property
580
581         Some node properties can be provided as arguments to binman. First check
582         the entry arguments, and fall back to the device tree if not found
583
584         Args:
585             name: Argument name
586             datatype: Data type (str or int)
587
588         Returns:
589             Value of argument as a string or int, or None if no value
590
591         Raises:
592             ValueError if the argument cannot be converted to in
593         """
594         value = state.GetEntryArg(name)
595         if value is not None:
596             if datatype == int:
597                 try:
598                     value = int(value)
599                 except ValueError:
600                     self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
601                                (name, value))
602             elif datatype == str:
603                 pass
604             else:
605                 raise ValueError("GetArg() internal error: Unknown data type '%s'" %
606                                  datatype)
607         else:
608             value = fdt_util.GetDatatype(self._node, name, datatype)
609         return value
610
611     @staticmethod
612     def WriteDocs(modules, test_missing=None):
613         """Write out documentation about the various entry types to stdout
614
615         Args:
616             modules: List of modules to include
617             test_missing: Used for testing. This is a module to report
618                 as missing
619         """
620         print('''Binman Entry Documentation
621 ===========================
622
623 This file describes the entry types supported by binman. These entry types can
624 be placed in an image one by one to build up a final firmware image. It is
625 fairly easy to create new entry types. Just add a new file to the 'etype'
626 directory. You can use the existing entries as examples.
627
628 Note that some entries are subclasses of others, using and extending their
629 features to produce new behaviours.
630
631
632 ''')
633         modules = sorted(modules)
634
635         # Don't show the test entry
636         if '_testing' in modules:
637             modules.remove('_testing')
638         missing = []
639         for name in modules:
640             module = Entry.Lookup('WriteDocs', name)
641             docs = getattr(module, '__doc__')
642             if test_missing == name:
643                 docs = None
644             if docs:
645                 lines = docs.splitlines()
646                 first_line = lines[0]
647                 rest = [line[4:] for line in lines[1:]]
648                 hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
649                 print(hdr)
650                 print('-' * len(hdr))
651                 print('\n'.join(rest))
652                 print()
653                 print()
654             else:
655                 missing.append(name)
656
657         if missing:
658             raise ValueError('Documentation is missing for modules: %s' %
659                              ', '.join(missing))
660
661     def GetUniqueName(self):
662         """Get a unique name for a node
663
664         Returns:
665             String containing a unique name for a node, consisting of the name
666             of all ancestors (starting from within the 'binman' node) separated
667             by a dot ('.'). This can be useful for generating unique filesnames
668             in the output directory.
669         """
670         name = self.name
671         node = self._node
672         while node.parent:
673             node = node.parent
674             if node.name == 'binman':
675                 break
676             name = '%s.%s' % (node.name, name)
677         return name
678
679     def ExpandToLimit(self, limit):
680         """Expand an entry so that it ends at the given offset limit"""
681         if self.offset + self.size < limit:
682             self.size = limit - self.offset
683             # Request the contents again, since changing the size requires that
684             # the data grows. This should not fail, but check it to be sure.
685             if not self.ObtainContents():
686                 self.Raise('Cannot obtain contents when expanding entry')
687
688     def HasSibling(self, name):
689         """Check if there is a sibling of a given name
690
691         Returns:
692             True if there is an entry with this name in the the same section,
693                 else False
694         """
695         return name in self.section.GetEntries()
696
697     def GetSiblingImagePos(self, name):
698         """Return the image position of the given sibling
699
700         Returns:
701             Image position of sibling, or None if the sibling has no position,
702                 or False if there is no such sibling
703         """
704         if not self.HasSibling(name):
705             return False
706         return self.section.GetEntries()[name].image_pos
707
708     @staticmethod
709     def AddEntryInfo(entries, indent, name, etype, size, image_pos,
710                      uncomp_size, offset, entry):
711         """Add a new entry to the entries list
712
713         Args:
714             entries: List (of EntryInfo objects) to add to
715             indent: Current indent level to add to list
716             name: Entry name (string)
717             etype: Entry type (string)
718             size: Entry size in bytes (int)
719             image_pos: Position within image in bytes (int)
720             uncomp_size: Uncompressed size if the entry uses compression, else
721                 None
722             offset: Entry offset within parent in bytes (int)
723             entry: Entry object
724         """
725         entries.append(EntryInfo(indent, name, etype, size, image_pos,
726                                  uncomp_size, offset, entry))
727
728     def ListEntries(self, entries, indent):
729         """Add files in this entry to the list of entries
730
731         This can be overridden by subclasses which need different behaviour.
732
733         Args:
734             entries: List (of EntryInfo objects) to add to
735             indent: Current indent level to add to list
736         """
737         self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
738                           self.image_pos, self.uncomp_size, self.offset, self)
739
740     def ReadData(self, decomp=True):
741         """Read the data for an entry from the image
742
743         This is used when the image has been read in and we want to extract the
744         data for a particular entry from that image.
745
746         Args:
747             decomp: True to decompress any compressed data before returning it;
748                 False to return the raw, uncompressed data
749
750         Returns:
751             Entry data (bytes)
752         """
753         # Use True here so that we get an uncompressed section to work from,
754         # although compressed sections are currently not supported
755         tout.Debug("ReadChildData section '%s', entry '%s'" %
756                    (self.section.GetPath(), self.GetPath()))
757         data = self.section.ReadChildData(self, decomp)
758         return data
759
760     def ReadChildData(self, child, decomp=True):
761         """Read the data for a particular child entry
762
763         This reads data from the parent and extracts the piece that relates to
764         the given child.
765
766         Args:
767             child: Child entry to read data for (must be valid)
768             decomp: True to decompress any compressed data before returning it;
769                 False to return the raw, uncompressed data
770
771         Returns:
772             Data for the child (bytes)
773         """
774         pass
775
776     def LoadData(self, decomp=True):
777         data = self.ReadData(decomp)
778         self.contents_size = len(data)
779         self.ProcessContentsUpdate(data)
780         self.Detail('Loaded data size %x' % len(data))
781
782     def GetImage(self):
783         """Get the image containing this entry
784
785         Returns:
786             Image object containing this entry
787         """
788         return self.section.GetImage()
789
790     def WriteData(self, data, decomp=True):
791         """Write the data to an entry in the image
792
793         This is used when the image has been read in and we want to replace the
794         data for a particular entry in that image.
795
796         The image must be re-packed and written out afterwards.
797
798         Args:
799             data: Data to replace it with
800             decomp: True to compress the data if needed, False if data is
801                 already compressed so should be used as is
802
803         Returns:
804             True if the data did not result in a resize of this entry, False if
805                  the entry must be resized
806         """
807         if self.size is not None:
808             self.contents_size = self.size
809         else:
810             self.contents_size = self.pre_reset_size
811         ok = self.ProcessContentsUpdate(data)
812         self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok))
813         section_ok = self.section.WriteChildData(self)
814         return ok and section_ok
815
816     def WriteChildData(self, child):
817         """Handle writing the data in a child entry
818
819         This should be called on the child's parent section after the child's
820         data has been updated. It
821
822         This base-class implementation does nothing, since the base Entry object
823         does not have any children.
824
825         Args:
826             child: Child Entry that was written
827
828         Returns:
829             True if the section could be updated successfully, False if the
830                 data is such that the section could not updat
831         """
832         return True
833
834     def GetSiblingOrder(self):
835         """Get the relative order of an entry amoung its siblings
836
837         Returns:
838             'start' if this entry is first among siblings, 'end' if last,
839                 otherwise None
840         """
841         entries = list(self.section.GetEntries().values())
842         if entries:
843             if self == entries[0]:
844                 return 'start'
845             elif self == entries[-1]:
846                 return 'end'
847         return 'middle'
848
849     def SetAllowMissing(self, allow_missing):
850         """Set whether a section allows missing external blobs
851
852         Args:
853             allow_missing: True if allowed, False if not allowed
854         """
855         # This is meaningless for anything other than sections
856         pass
857
858     def CheckMissing(self, missing_list):
859         """Check if any entries in this section have missing external blobs
860
861         If there are missing blobs, the entries are added to the list
862
863         Args:
864             missing_list: List of Entry objects to be added to
865         """
866         if self.missing:
867             missing_list.append(self)
868
869     def GetAllowMissing(self):
870         """Get whether a section allows missing external blobs
871
872         Returns:
873             True if allowed, False if not allowed
874         """
875         return self.allow_missing
876
877     def GetHelpTags(self):
878         """Get the tags use for missing-blob help
879
880         Returns:
881             list of possible tags, most desirable first
882         """
883         return list(filter(None, [self.missing_msg, self.name, self.etype]))
884
885     def CompressData(self, indata):
886         """Compress data according to the entry's compression method
887
888         Args:
889             indata: Data to compress
890
891         Returns:
892             Compressed data (first word is the compressed size)
893         """
894         self.uncomp_data = indata
895         if self.compress != 'none':
896             self.uncomp_size = len(indata)
897         data = tools.Compress(indata, self.compress)
898         return data