Ejemplo n.º 1
0
Archivo: ELF.py Proyecto: dagit/capdl
class ELF(object):
    def __init__(self, elf, name='', arch=None):
        """
        This constructor is overloaded and can accept either a string as the
        parameter 'elf', or a stream to ELF data. 'name' is only used when
        generating CapDL from the ELF file.
        """
        if isinstance(elf, six.string_types):
            f = open(elf, 'rb')
        else:
            f = elf
        self._elf = ELFFile(f)
        self.name = name
        self._symtab = None
        self.arch = arch or self.get_arch()

    def get_entry_point(self):
        return self._elf['e_entry']

    def _get_symbol(self, symbol):

        # If possible, let elftools do all the work.
        if hasattr(self._elf, 'get_symbol_by_name'):
            # From 46ae4bd this functionality is in elftools.
            sym = self._elf.get_symbol_by_name(symbol)
            if isinstance(sym, list):
                # From 9da4c45 get_symbol_by_name returns a list.
                return sym[0]
            return sym

        if self._symtab is None:
            table = self._elf.get_section_by_name('.symtab')
            if not table:
                # This ELF file has been stripped.
                raise Exception('No symbol table available')
            self._symtab = dict([(s.name, s) for s in table.iter_symbols()])

        return self._symtab.get(symbol)

    def get_symbol_vaddr(self, symbol):
        sym = self._get_symbol(symbol)
        if sym:
            return sym['st_value']
        return None

    def get_symbol_size(self, symbol):
        sym = self._get_symbol(symbol)
        if sym:
            return sym['st_size']
        return None

    def _safe_name(self):
        """
        Replace characters that the CapDL tools parse differently.
        """
        return re.sub(r'[^A-Za-z0-9]', '_', self.name)

    def get_arch(self):
        return self._elf.get_machine_arch()

    def get_pages(self, infer_asid=True, pd=None, use_large_frames=True):
        """
        Returns a dictionary of pages keyed on base virtual address, that are
        required to ELF load this file. Each dictionary entry is a dictionary
        containing booleans 'read', 'write' and 'execute' for the permissions
        of the page.
        """
        pages = PageCollection(self._safe_name(), self.arch, infer_asid, pd)

        # Various CAmkES output sections we are expecting to see in the ELF.
        TYPE = {"ignore": 1, "shared": 2, "persistent": 3, "guarded": 4}
        regex = re.compile("^(ignore_|shared_|persistent|guarded)");
        sections = [x for x in self._elf.iter_sections() if
            regex.match(_decode(x.name))]

        for seg in self._elf.iter_segments():
            if not seg['p_type'] == 'PT_LOAD':
                continue
            if seg['p_memsz'] == 0:
                continue

            regions = [{'addr': seg['p_vaddr'],
                        'size': seg['p_memsz'],
                        'type': 0}]
            relevant_sections = filter(seg.section_in_segment, sections)
            for sec in relevant_sections:
                region = [x for x in regions if
                    sec['sh_addr'] >= x['addr'] and sec['sh_addr'] < (x['addr'] + x['size'])]
                assert len(region) == 1
                region = region[0]
                orig_size = region['size']
                # Shrink the region to the range preceding this section.
                region['size'] = sec['sh_addr'] - region['addr']
                # Append a region for this section itself and that following
                # this section.
                regions += [{'addr': sec['sh_addr'],
                             'size': sec['sh_size'],
                             'type': TYPE[_decode(sec.name).split('_')[0]]},
                            {'addr': sec['sh_addr'] + sec['sh_size'],
                             'size': orig_size - region['size'] - sec['sh_size'],
                             'type': 0}]
            # Remove empty regions.
            regions[:] = [x for x in regions if x['size'] != 0]

            r = (seg['p_flags'] & P_FLAGS.PF_R) > 0
            w = (seg['p_flags'] & P_FLAGS.PF_W) > 0
            x = (seg['p_flags'] & P_FLAGS.PF_X) > 0

            # Allocate pages
            for reg in regions:
                if reg['type'] in [1, 2, 3, 4]:
                    # A range that must be backed by small pages.
                    vaddr = round_down(reg['addr'])
                    while vaddr < reg['addr'] + reg['size']:
                        pages.add_page(vaddr, r, w, x)
                        vaddr += PAGE_SIZE
                else:
                    # A range that is eligible for promotion.
                    possible_pages = list(reversed(page_sizes(self.arch)))
                    vaddr = round_down(reg['addr'])
                    remain = reg['addr'] + reg['size'] - vaddr
                    while vaddr < reg['addr'] + reg['size']:
                        size = PAGE_SIZE
                        if use_large_frames:
                            for p in possible_pages:
                                if remain >= p and vaddr % p == 0:
                                    size = p
                                    break
                        pages.add_page(vaddr, r, w, x, size)
                        vaddr += size
                        remain -= size

        return pages

    def get_spec(self, infer_tcb=True, infer_asid=True, pd=None,
            use_large_frames=True):
        """
        Return a CapDL spec with as much information as can be derived from the
        ELF file in isolation.
        """
        pages = self.get_pages(infer_asid, pd, use_large_frames)
        spec = pages.get_spec()

        if infer_tcb:
            # Create a single TCB.
            tcb = TCB('tcb_%s' % self._safe_name(), ip=self.get_entry_point(),
                elf=self.name)
            spec.add_object(tcb)
            tcb['vspace'] = pages.get_page_directory()[1]

        return spec

    def __repr__(self):
        return str(self._elf)
Ejemplo n.º 2
0
class ELF(object):
    def __init__(self, elf, name='', arch=None):
        """
        This constructor is overloaded and can accept either a string as the
        parameter 'elf', or a stream to ELF data. 'name' is only used when
        generating CapDL from the ELF file.
        """
        if isinstance(elf, six.string_types):
            f = open(elf, 'rb')
        else:
            f = elf
        self._elf = ELFFile(f)
        self.name = name
        self._symtab = None
        self.arch = arch or self.get_arch()

    def get_entry_point(self):
        return self._elf['e_entry']

    def _get_symbol(self, symbol):

        # If possible, let elftools do all the work.
        if hasattr(self._elf, 'get_symbol_by_name'):
            # From 46ae4bd this functionality is in elftools.
            sym = self._elf.get_symbol_by_name(symbol)
            if isinstance(sym, list):
                # From 9da4c45 get_symbol_by_name returns a list.
                return sym[0]
            return sym

        if self._symtab is None:
            table = self._elf.get_section_by_name('.symtab')
            if not table:
                # This ELF file has been stripped.
                raise Exception('No symbol table available')
            self._symtab = dict([(s.name, s) for s in table.iter_symbols()])

        return self._symtab.get(symbol)

    def get_symbol_vaddr(self, symbol):
        sym = self._get_symbol(symbol)
        if sym:
            return sym['st_value']
        return None

    def get_symbol_size(self, symbol):
        sym = self._get_symbol(symbol)
        if sym:
            return sym['st_size']
        return None

    def _safe_name(self):
        """
        Replace characters that the CapDL tools parse differently.
        """
        return re.sub(r'[^A-Za-z0-9]', '_', self.name)

    def get_arch(self):
        return self._elf.get_machine_arch()

    def check_alignment(self, regions):
        for (vaddr, sizes, caps) in regions:
            for size in sizes:
                assert vaddr % size == 0, "vaddr: 0x%x is not aligned to frame_size: 0x%x" % (
                    vaddr, size)
                vaddr += size

    def regions_in_segment(self, segment, regions):
        seg_start = segment['p_vaddr']
        seg_size = segment['p_memsz']
        seg_end = seg_start + seg_size
        regions_return = []
        for (vaddr, sizes, caps) in regions:
            for size in sizes:
                if vaddr >= (seg_end) or (vaddr+size) < seg_start:
                    pass
                else:
                    assert vaddr >= seg_start and (
                        vaddr + size) <= seg_end, "Regions overlap segments which is not allowed"
                    regions_return.append((vaddr, size))
                vaddr += size
        return regions_return

    def compute_elf_fill_frame(self, vaddr, size, seg_p_vaddr, seg_p_filesz, seg_p_offset):
        dest_offset = 0 if vaddr >= seg_p_vaddr else seg_p_vaddr - vaddr
        assert dest_offset < size, "There is no section in this frame: %p" % vaddr

        target_vaddr_start = vaddr + dest_offset
        section_offset = target_vaddr_start - seg_p_vaddr
        if section_offset >= seg_p_filesz:
            # Past the end of the data to load
            return []
        # length to copy
        length = min(size-dest_offset, seg_p_filesz-section_offset)
        src_offset = seg_p_offset + section_offset

        return ["%d %d CDL_FrameFill_FileData \"%s\" %d" % (dest_offset, length, self.name, src_offset)]

    def get_pages(self, infer_asid=True, pd=None, use_large_frames=True, addr_space=None):
        """
        Returns a dictionary of pages keyed on base virtual address, that are
        required to ELF load this file. Each dictionary entry is a dictionary
        containing booleans 'read', 'write' and 'execute' for the permissions
        of the page.
        """
        pages = PageCollection(self._safe_name(), self.arch, infer_asid, pd)

        # We assume that this array contains aligned vaddrs and sizes that are frame sizes
        existing_pages = []
        if addr_space:
            # Update symbols with their vaddrs in the AddressSpaceAllocator if we were given one
            existing_pages = []
            for (symbol, (sizes, caps)) in iteritems(addr_space.get_symbols_and_clear()):
                assert self.get_symbol_size(symbol) >= sum(sizes), \
                    "Symbol (%s:%d) must have same or greater size than supplied cap range (%d)" % (
                        symbol, self.get_symbol_size(symbol), sum(sizes))
                existing_pages.append((self.get_symbol_vaddr(symbol), sizes, caps))

            existing_pages.sort(key=lambda phys_addr: phys_addr[0])
            self.check_alignment(existing_pages)
            for (vaddr, sizes, caps) in existing_pages:
                addr_space.add_region_with_caps(vaddr, sizes, caps)

        for seg in self._elf.iter_segments():
            if not seg['p_type'] == 'PT_LOAD':
                continue
            if seg['p_memsz'] == 0:
                continue

            seg_p_vaddr = seg['p_vaddr']
            seg_p_filesz = seg['p_filesz']
            seg_p_offset = seg['p_offset']
            regions = [{'addr': seg_p_vaddr,
                        'size': seg['p_memsz'],
                        'type': 0}]
            relevant_regions = self.regions_in_segment(seg, existing_pages)

            for reg_vaddr, reg_size in relevant_regions:
                region = None
                # Use binary search to find the correct region
                s, e = 0, len(regions)-1
                while s <= e:
                    m = s + (e - s) // 2
                    lower = regions[m]['addr']
                    upper = regions[m]['addr'] + regions[m]['size']
                    if lower <= reg_vaddr and reg_vaddr < upper:
                        region = regions[m]
                        break
                    elif reg_vaddr >= upper:
                        s = m+1
                    elif reg_vaddr < lower:
                        e = m-1

                assert region != None, "section is overlapping which is not allowed"
                orig_size = region['size']

                # Shrink the region to the range preceding this section.
                region['size'] = reg_vaddr - region['addr']
                # Append a region for this section itself and that following
                # this section.

                # only add if they are not empty
                if reg_size != 0:
                    regions += [{'addr': reg_vaddr,
                                 'size': reg_size,
                                 'type': 1}]
                if orig_size - region['size'] - reg_size != 0:
                    regions += [{'addr': reg_vaddr + reg_size,
                                 'size': orig_size - region['size'] - reg_size,
                                 'type': 0}]

            r = (seg['p_flags'] & P_FLAGS.PF_R) > 0
            w = (seg['p_flags'] & P_FLAGS.PF_W) > 0
            x = (seg['p_flags'] & P_FLAGS.PF_X) > 0

            # Allocate pages
            for reg in regions:
                if reg['type']:
                    vaddr = reg['addr']
                    pages.add_page(vaddr, r, w, x, reg['size'], self.compute_elf_fill_frame(
                        vaddr, reg['size'], seg_p_vaddr, seg_p_filesz, seg_p_offset))
                else:
                    # A range that is eligible for promotion.
                    possible_pages = list(reversed(page_sizes(self.arch)))
                    vaddr = round_down(reg['addr'])
                    remain = reg['addr'] + reg['size'] - vaddr
                    while vaddr < reg['addr'] + reg['size']:
                        size = PAGE_SIZE
                        if use_large_frames:
                            for p in possible_pages:
                                if remain >= p and vaddr % p == 0:
                                    size = p
                                    break
                        pages.add_page(vaddr, r, w, x, size, self.compute_elf_fill_frame(
                            vaddr, size, seg_p_vaddr, seg_p_filesz, seg_p_offset))
                        vaddr += size
                        remain -= size

        return pages

    def get_spec(self, infer_tcb=True, infer_asid=True, pd=None,
                 use_large_frames=True, addr_space=None):
        """
        Return a CapDL spec with as much information as can be derived from the
        ELF file in isolation.
        """
        pages = self.get_pages(infer_asid, pd, use_large_frames, addr_space=addr_space)
        spec = pages.get_spec(addr_space.get_regions_and_clear() if addr_space else {})

        if infer_tcb:
            # Create a single TCB.
            tcb = TCB('tcb_%s' % self._safe_name(), ip=self.get_entry_point())
            spec.add_object(tcb)
            tcb['vspace'] = pages.get_vspace_root()[1]

        return spec

    def __repr__(self):
        return str(self._elf)
Ejemplo n.º 3
0
Archivo: ELF.py Proyecto: hyhugh/capdl
class ELF(object):
    def __init__(self, elf, name='', arch=None):
        """
        This constructor is overloaded and can accept either a string as the
        parameter 'elf', or a stream to ELF data. 'name' is only used when
        generating CapDL from the ELF file.
        """

        if isinstance(elf, six.string_types):
            f = open(elf, 'rb')
        else:
            f = elf
        self._elf = ELFFile(f)
        self.name = name
        # symbol table
        self._symtab = None
        self.arch = arch or self.get_arch()

    def get_entry_point(self):
        return self._elf['e_entry']

    def _get_symbol(self, symbol):

        # If possible, let elftools do all the work.
        if hasattr(self._elf, 'get_symbol_by_name'):
            # From 46ae4bd this functionality is in elftools.
            sym = self._elf.get_symbol_by_name(symbol)
            if isinstance(sym, list):
                # From 9da4c45 get_symbol_by_name returns a list.
                return sym[0]
            return sym

        if self._symtab is None:
            table = self._elf.get_section_by_name('.symtab')
            if not table:
                # This ELF file has been stripped.
                raise Exception('No symbol table available')
            self._symtab = dict([(s.name, s) for s in table.iter_symbols()])

        return self._symtab.get(symbol)

    def get_symbol_vaddr(self, symbol):
        sym = self._get_symbol(symbol)
        if sym:
            return sym['st_value']
        return None

    def get_symbol_size(self, symbol):
        sym = self._get_symbol(symbol)
        if sym:
            return sym['st_size']
        return None

    def _safe_name(self):
        """
        Replace characters that the CapDL tools parse differently.
        """
        return re.sub(r'[^A-Za-z0-9]', '_', self.name)

    def get_arch(self):
        return self._elf.get_machine_arch()

    def check_alignment(self, regions):
        for (vaddr, sizes, caps) in regions:
            for size in sizes:
                assert vaddr % size == 0, "vaddr: 0x%x is not aligned to frame_size: 0x%x" %(vaddr, size)
                vaddr += size

    def regions_in_segment(self, segment, regions):
        seg_start = segment['p_vaddr']
        seg_size = segment['p_memsz']
        seg_end = seg_start + seg_size
        regions_return = []
        for (vaddr, sizes, caps) in regions:
            for size in sizes:
                if vaddr >= (seg_end) or (vaddr+size) < seg_start:
                    pass
                else:
                    assert vaddr >= seg_start and (vaddr + size) <= seg_end, "Regions overlap segments which is not allowed"
                    regions_return.append((vaddr, size))
                vaddr += size
        return regions_return

    def get_pages(self, infer_asid=True, pd=None, use_large_frames=True, addr_space=None):
        """
        Returns a dictionary of pages keyed on base virtual address, that are
        required to ELF load this file. Each dictionary entry is a dictionary
        containing booleans 'read', 'write' and 'execute' for the permissions
        of the page.
        """
        pages = PageCollection(self._safe_name(), self.arch, infer_asid, pd)

        # We assume that this array contains aligned vaddrs and sizes that are frame sizes
        existing_pages = []
        if addr_space:

            for (symbol, (sizes, caps)) in addr_space.get_symbols().iteritems():
                print(symbol)
                print(sizes)
                print(caps)
            # Update symbols with their vaddrs in the AddressSpaceAllocator if we were given one
            existing_pages = [(self.get_symbol_vaddr(symbol), sizes, caps)
                 for (symbol, (sizes, caps)) in addr_space.get_symbols_and_clear().iteritems()]

            print('existing pages are : ' + str(existing_pages))
            self.check_alignment(existing_pages)
            for (vaddr, sizes, caps) in existing_pages:
                addr_space.add_region_with_caps(vaddr, sizes, caps)

        for seg in self._elf.iter_segments():
            if not seg['p_type'] == 'PT_LOAD':
                continue
            if seg['p_memsz'] == 0:
                continue

            regions = [{'addr': seg['p_vaddr'],
                        'size': seg['p_memsz'],
                        'type': 0}]
            relevant_regions = self.regions_in_segment(seg, existing_pages)
            for (reg_vaddr, reg_size) in relevant_regions:
                region = [x for x in regions if
                    reg_vaddr >= x['addr'] and reg_vaddr < (x['addr'] + x['size'])]
                assert len(region) == 1, "section is overlapping which is not allowed"
                region = region[0]
                orig_size = region['size']
                # Shrink the region to the range preceding this section.
                region['size'] = reg_vaddr - region['addr']
                # Append a region for this section itself and that following
                # this section.
                regions += [{'addr': reg_vaddr,
                             'size': reg_size,
                             'type': 1},
                            {'addr': reg_vaddr + reg_size,
                             'size': orig_size - region['size'] - reg_size,
                             'type': 0}]
            # Remove empty regions.
            regions[:] = [x for x in regions if x['size'] != 0]

            r = (seg['p_flags'] & P_FLAGS.PF_R) > 0
            w = (seg['p_flags'] & P_FLAGS.PF_W) > 0
            x = (seg['p_flags'] & P_FLAGS.PF_X) > 0

            # Allocate pages
            for reg in regions:
                if reg['type']:
                    vaddr = reg['addr']
                    pages.add_page(vaddr, r, w, x, reg['size'])
                else:
                    # A range that is eligible for promotion.
                    possible_pages = list(reversed(page_sizes(self.arch)))
                    vaddr = round_down(reg['addr'])
                    remain = reg['addr'] + reg['size'] - vaddr
                    while vaddr < reg['addr'] + reg['size']:
                        size = PAGE_SIZE
                        if use_large_frames:
                            for p in possible_pages:
                                if remain >= p and vaddr % p == 0:
                                    size = p
                                    break
                        pages.add_page(vaddr, r, w, x, size)
                        vaddr += size
                        remain -= size

        return pages

    def get_spec(self, infer_tcb=True, infer_asid=True, pd=None,
            use_large_frames=True, addr_space=None):
        """
        Return a CapDL spec with as much information as can be derived from the
        ELF file in isolation.
        """
        pages = self.get_pages(infer_asid, pd, use_large_frames, addr_space=addr_space)
        spec = pages.get_spec(addr_space.get_regions_and_clear() if addr_space else {})

        if infer_tcb:
            # Create a single TCB.
            tcb = TCB('tcb_%s' % self._safe_name(), ip=self.get_entry_point(),
                elf=self.name)
            spec.add_object(tcb)
            tcb['vspace'] = pages.get_vspace_root()[1]

        return spec

    def __repr__(self):
        return str(self._elf)
Ejemplo n.º 4
0
class ELF(object):
    def __init__(self, elf, name='', arch=None):
        """
        This constructor is overloaded and can accept either a string as the
        parameter 'elf', or a stream to ELF data. 'name' is only used when
        generating CapDL from the ELF file.
        """
        if isinstance(elf, six.string_types):
            f = open(elf, 'rb')
        else:
            f = elf
        self._elf = ELFFile(f)
        self.name = name
        self._symtab = None
        self.arch = arch or self.get_arch()

    def get_entry_point(self):
        return self._elf['e_entry']

    def _get_symbol(self, symbol):

        # If possible, let elftools do all the work.
        if hasattr(self._elf, 'get_symbol_by_name'):
            # From 46ae4bd this functionality is in elftools.
            sym = self._elf.get_symbol_by_name(symbol)
            if isinstance(sym, list):
                # From 9da4c45 get_symbol_by_name returns a list.
                return sym[0]
            return sym

        if self._symtab is None:
            table = self._elf.get_section_by_name('.symtab')
            if not table:
                # This ELF file has been stripped.
                raise Exception('No symbol table available')
            self._symtab = dict([(s.name, s) for s in table.iter_symbols()])

        return self._symtab.get(symbol)

    def get_symbol_vaddr(self, symbol):
        sym = self._get_symbol(symbol)
        if sym:
            return sym['st_value']
        return None

    def get_symbol_size(self, symbol):
        sym = self._get_symbol(symbol)
        if sym:
            return sym['st_size']
        return None

    def _safe_name(self):
        """
        Replace characters that the CapDL tools parse differently.
        """
        return re.sub(r'[^A-Za-z0-9]', '_', self.name)

    def get_arch(self):
        return self._elf.get_machine_arch()

    def get_pages(self, infer_asid=True, pd=None, use_large_frames=True):
        """
        Returns a dictionary of pages keyed on base virtual address, that are
        required to ELF load this file. Each dictionary entry is a dictionary
        containing booleans 'read', 'write' and 'execute' for the permissions
        of the page.
        """
        pages = PageCollection(self._safe_name(), self.arch, infer_asid, pd)

        # Various CAmkES output sections we are expecting to see in the ELF.
        TYPE = {"ignore": 1, "shared": 2, "persistent": 3, "guarded": 4}
        regex = re.compile("^(ignore_|shared_|persistent|guarded)");
        sections = [x for x in self._elf.iter_sections() if
            regex.match(_decode(x.name))]

        for seg in self._elf.iter_segments():
            if not seg['p_type'] == 'PT_LOAD':
                continue
            if seg['p_memsz'] == 0:
                continue

            regions = [{'addr': seg['p_vaddr'],
                        'size': seg['p_memsz'],
                        'type': 0}]
            relevant_sections = filter(seg.section_in_segment, sections)
            for sec in relevant_sections:
                region = [x for x in regions if
                    sec['sh_addr'] >= x['addr'] and sec['sh_addr'] < (x['addr'] + x['size'])]
                assert len(region) == 1
                region = region[0]
                orig_size = region['size']
                # Shrink the region to the range preceding this section.
                region['size'] = sec['sh_addr'] - region['addr']
                # Append a region for this section itself and that following
                # this section.
                regions += [{'addr': sec['sh_addr'],
                             'size': sec['sh_size'],
                             'type': TYPE[_decode(sec.name).split('_')[0]]},
                            {'addr': sec['sh_addr'] + sec['sh_size'],
                             'size': orig_size - region['size'] - sec['sh_size'],
                             'type': 0}]
            # Remove empty regions.
            regions[:] = [x for x in regions if x['size'] != 0]

            r = (seg['p_flags'] & P_FLAGS.PF_R) > 0
            w = (seg['p_flags'] & P_FLAGS.PF_W) > 0
            x = (seg['p_flags'] & P_FLAGS.PF_X) > 0

            # Allocate pages
            for reg in regions:
                if reg['type'] in [1, 2, 3, 4]:
                    # A range that must be backed by small pages.
                    vaddr = round_down(reg['addr'])
                    while vaddr < reg['addr'] + reg['size']:
                        pages.add_page(vaddr, r, w, x)
                        vaddr += PAGE_SIZE
                else:
                    # A range that is eligible for promotion.
                    possible_pages = list(reversed(page_sizes(self.arch)))
                    vaddr = round_down(reg['addr'])
                    remain = reg['addr'] + reg['size'] - vaddr
                    while vaddr < reg['addr'] + reg['size']:
                        size = PAGE_SIZE
                        if use_large_frames:
                            for p in possible_pages:
                                if remain >= p and vaddr % p == 0:
                                    size = p
                                    break
                        pages.add_page(vaddr, r, w, x, size)
                        vaddr += size
                        remain -= size

        return pages

    def get_spec(self, infer_tcb=True, infer_asid=True, pd=None,
            use_large_frames=True):
        """
        Return a CapDL spec with as much information as can be derived from the
        ELF file in isolation.
        """
        pages = self.get_pages(infer_asid, pd, use_large_frames)
        spec = pages.get_spec()

        if infer_tcb:
            # Create a single TCB.
            tcb = TCB('tcb_%s' % self._safe_name(), ip=self.get_entry_point(),
                elf=self.name)
            spec.add_object(tcb)
            tcb['vspace'] = pages.get_vspace_root()[1]

        return spec

    def __repr__(self):
        return str(self._elf)