| 149 | def load_with_ld(self, elffile: ELFFile, stack_addr: int, load_address: int, argv: Sequence[str] = [], env: Mapping[AnyStr, AnyStr] = {}): |
| 150 | |
| 151 | def load_elf_segments(elffile: ELFFile, load_address: int, info: str): |
| 152 | # get list of loadable segments; these segments will be loaded to memory |
| 153 | load_segments = sorted(elffile.iter_segments(type='PT_LOAD'), key=lambda s: s['p_vaddr']) |
| 154 | |
| 155 | # determine the memory regions that need to be mapped in order to load the segments. |
| 156 | # note that region boundaries are aligned to page, which means they may be larger than |
| 157 | # the segment they contain. to reduce mapping clutter, adjacent regions with the same |
| 158 | # perms are consolidated into one contigous memory region |
| 159 | load_regions: Sequence[Tuple[int, int, int]] = [] |
| 160 | |
| 161 | # iterate over loadable segments |
| 162 | for seg in load_segments: |
| 163 | lbound = self.ql.mem.align(load_address + seg['p_vaddr']) |
| 164 | ubound = self.ql.mem.align_up(load_address + seg['p_vaddr'] + seg['p_memsz']) |
| 165 | perms = QlLoaderELF.seg_perm_to_uc_prot(seg['p_flags']) |
| 166 | |
| 167 | if load_regions: |
| 168 | prev_lbound, prev_ubound, prev_perms = load_regions[-1] |
| 169 | |
| 170 | # new region starts where the previous one ended |
| 171 | if lbound == prev_ubound: |
| 172 | # same perms? extend previous memory region |
| 173 | if perms == prev_perms: |
| 174 | load_regions[-1] = (prev_lbound, ubound, prev_perms) |
| 175 | |
| 176 | # different perms? start a new one |
| 177 | else: |
| 178 | load_regions.append((lbound, ubound, perms)) |
| 179 | |
| 180 | # start a new memory region |
| 181 | elif lbound > prev_ubound: |
| 182 | load_regions.append((lbound, ubound, perms)) |
| 183 | |
| 184 | # overlapping segments? something probably went wrong |
| 185 | elif lbound < prev_ubound: |
| 186 | # EDL ELF files use 0x400 bytes pages, which might make some segments look as if they |
| 187 | # start at the same segment as their predecessor. though that is fixable, unicorn |
| 188 | # supports only 0x1000 bytes pages; this becomes problematic when using mem.protect |
| 189 | # |
| 190 | # this workaround unifies such "overlapping" segments, which may apply more permissive |
| 191 | # protection flags to that memory region. |
| 192 | if self.ql.arch.type == QL_ARCH.ARM64: |
| 193 | load_regions[-1] = (prev_lbound, ubound, prev_perms | perms) |
| 194 | continue |
| 195 | |
| 196 | raise RuntimeError |
| 197 | |
| 198 | else: |
| 199 | load_regions.append((lbound, ubound, perms)) |
| 200 | |
| 201 | # map the memory regions |
| 202 | for lbound, ubound, perms in load_regions: |
| 203 | size = ubound - lbound |
| 204 | |
| 205 | # there might be a region with zero size. in this case, do not mmap it |
| 206 | if size: |
| 207 | try: |
| 208 | self.ql.mem.map(lbound, size, perms, os.path.basename(info)) |