We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
pwn checksec: do not require libunicorn to be loaded
It seems that `pwn checksec` requires libunicorn. Is that really required? The Unicorn Engine mmaps a 1GB+ memory page which aborts the program on failures. This can happen on low-memory systems such as cheap VPSes. This is actually a bug in libunicorn that I reported in https://github.com/unicorn-engine/unicorn/issues/1766 but it still hasn't been fixed.

Given all this, it would be great if we could not require libunicorn in `checksec`. But if we need, it would be nice to get it documented here for users who stumble upon the same issue and try to search it here.

Here is an example issue of this:

```
root@pwndbg:~# free -h
               total        used        free      shared  buff/cache   available
Mem:           957Mi       265Mi       153Mi       4.0Mi       538Mi       532Mi
Swap:             0B          0B          0B

root@pwndbg:~# strace -e openat,mmap pwn checksec /root/x/bin/main 2>&1 | tail -n 10
openat(AT_FDCWD, "/usr/lib/python3/dist-packages/mpmath-0.0.0.egg-info/PKG-INFO", O_RDONLY|O_CLOEXEC) = 7
openat(AT_FDCWD, "/usr/local/lib/python3.10/dist-packages/unicorn/lib/libunicorn.so.2", O_RDONLY|O_CLOEXEC) = 7
mmap(NULL, 22447520, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 7, 0) = 0x7f2604f9d000
mmap(0x7f2605339000, 13496320, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x39c000) = 0x7f2605339000
mmap(0x7f2606018000, 3039232, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x107b000) = 0x7f2606018000
mmap(0x7f26062fe000, 1601536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x1360000) = 0x7f26062fe000
mmap(0x7f2606485000, 525728, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2606485000
mmap(NULL, 1073741824, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
Could not allocate dynamic translator buffer
+++ exited with 1 +++
```
pwn checksec: do not require libunicorn to be loaded
It seems that `pwn checksec` requires libunicorn. Is that really required? The Unicorn Engine mmaps a 1GB+ memory page which aborts the program on failures. This can happen on low-memory systems such as cheap VPSes. This is actually a bug in libunicorn that I reported in https://github.com/unicorn-engine/unicorn/issues/1766 but it still hasn't been fixed.

Given all this, it would be great if we could not require libunicorn in `checksec`. But if we need, it would be nice to get it documented here for users who stumble upon the same issue and try to search it here.

Here is an example issue of this:

```
root@pwndbg:~# free -h
               total        used        free      shared  buff/cache   available
Mem:           957Mi       265Mi       153Mi       4.0Mi       538Mi       532Mi
Swap:             0B          0B          0B

root@pwndbg:~# strace -e openat,mmap pwn checksec /root/x/bin/main 2>&1 | tail -n 10
openat(AT_FDCWD, "/usr/lib/python3/dist-packages/mpmath-0.0.0.egg-info/PKG-INFO", O_RDONLY|O_CLOEXEC) = 7
openat(AT_FDCWD, "/usr/local/lib/python3.10/dist-packages/unicorn/lib/libunicorn.so.2", O_RDONLY|O_CLOEXEC) = 7
mmap(NULL, 22447520, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 7, 0) = 0x7f2604f9d000
mmap(0x7f2605339000, 13496320, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x39c000) = 0x7f2605339000
mmap(0x7f2606018000, 3039232, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x107b000) = 0x7f2606018000
mmap(0x7f26062fe000, 1601536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x1360000) = 0x7f26062fe000
mmap(0x7f2606485000, 525728, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2606485000
mmap(NULL, 1073741824, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
Could not allocate dynamic translator buffer
+++ exited with 1 +++
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pwnlib/elf/plt.py`
Content:
```
1 """Emulates instructions in the PLT to locate symbols more accurately.
2 """
3 from __future__ import division
4 import logging
5 
6 from pwnlib.args import args
7 from pwnlib.log import getLogger
8 from pwnlib.util import fiddling
9 from pwnlib.util import packing
10 
11 log = getLogger(__name__)
12 
13 
14 def emulate_plt_instructions(elf, got, address, data, targets):
15     """Emulates instructions in ``data``
16 
17     Arguments:
18         elf(ELF): ELF that we are emulating
19         got(int): Address of the GOT, as expected in e.g. EBX
20         address(int): Address of ``data`` for emulation
21         data(str): Array of bytes to emulate
22         targets(list): List of target addresses
23 
24     Returns:
25         :class:`dict`: Map of ``{address: target}`` for each address which
26             reaches one of the selected targets.
27     """
28     rv = {}
29 
30     if not args.PLT_DEBUG:
31         log.setLevel(logging.DEBUG + 1)
32 
33     # Unicorn doesn't support big-endian for everything yet.
34     if elf.endian == 'big' and elf.arch == 'mips':
35         data = packing.unpack_many(data, bits=32, endian='little')
36         data = packing.flat(data, bits=32, endian='big')
37 
38     uc, ctx = prepare_unicorn_and_context(elf, got, address, data)
39 
40     # Brute force addresses, assume that PLT entry points are at 4-byte aligned
41     # Do not emulate more than a handful of instructions.
42     for i, pc in enumerate(range(address, address + len(data), 4)):
43         if log.isEnabledFor(logging.DEBUG):
44             log.debug('%s %#x', fiddling.enhex(data[i*4:(i+1) * 4]), pc)
45             log.debug(elf.disasm(pc, 4))
46 
47         uc.context_restore(ctx)
48         target = emulate_plt_instructions_inner(uc, elf, got, pc, data[i*4:])
49 
50         if target in targets:
51             log.debug("%#x -> %#x", pc, target)
52             rv[pc] = target
53 
54     return rv
55 
56 def prepare_unicorn_and_context(elf, got, address, data):
57     import unicorn as U
58 
59     # Instantiate the emulator with the correct arguments for the current
60     # architecutre.
61     arch = {
62         'aarch64': U.UC_ARCH_ARM64,
63         'amd64': U.UC_ARCH_X86,
64         'arm': U.UC_ARCH_ARM,
65         'i386': U.UC_ARCH_X86,
66         'mips': U.UC_ARCH_MIPS,
67         'mips64': U.UC_ARCH_MIPS,
68         # 'powerpc': U.UC_ARCH_PPC, <-- Not actually supported
69         'thumb': U.UC_ARCH_ARM,
70         'riscv32': U.UC_ARCH_RISCV,
71         'riscv64': U.UC_ARCH_RISCV,
72     }.get(elf.arch, None)
73 
74     if arch is None:
75         log.warn("Could not emulate PLT instructions for %r" % elf)
76         return {}
77 
78     emulation_bits = elf.bits
79 
80     # x32 uses 64-bit instructions, just restricts itself to a 32-bit
81     # address space.
82     if elf.arch == 'amd64' and elf.bits == 32:
83         emulation_bits = 64
84 
85     mode = {
86         32: U.UC_MODE_32,
87         64: U.UC_MODE_64
88     }.get(emulation_bits)
89 
90     if elf.arch in ('arm', 'aarch64'):
91         mode = U.UC_MODE_ARM
92 
93     uc = U.Uc(arch, mode)
94 
95     # Map the page of memory, and fill it with the contents
96     start = address & (~0xfff)
97     stop  = (address + len(data) + 0xfff) & (~0xfff)
98 
99     if not (0 <= start <= stop <= (1 << elf.bits)):
100         return None
101 
102     uc.mem_map(start, stop-start)
103     uc.mem_write(address, data)
104     assert uc.mem_read(address, len(data)) == data
105 
106     # MIPS is unique in that it relies entirely on _DYNAMIC, at the beginning
107     # of the GOT.  Each PLT stub loads an address stored here.
108     # Because of this, we have to support loading memory from this location.
109     #
110     # https://www.cr0.org/paper/mips.elf.external.resolution.txt
111     magic_addr = 0x7c7c7c7c
112 
113     if elf.arch == 'mips':
114         # Map the GOT so that MIPS can access it
115         p_magic = packing.p32(magic_addr)
116         start = got & (~0xfff)
117         try:
118             uc.mem_map(start, 0x1000)
119         except Exception:
120             # Ignore double-mapping
121             pass
122 
123         uc.mem_write(got, p_magic)
124 
125     return uc, uc.context_save()
126 
127 
128 def emulate_plt_instructions_inner(uc, elf, got, pc, data):
129     import unicorn as U
130 
131     # Hook invalid addresses and any accesses out of the specified address range
132     stopped_addr = []
133 
134     # For MIPS. Explanation at prepare_unicorn_and_context.
135     magic_addr = 0x7c7c7c7c
136 
137     def hook_mem(uc, access, address, size, value, user_data):
138         # Special case to allow MIPS to dereference the _DYNAMIC pointer
139         # in the GOT.
140         if elf.arch == 'mips' and address == got:
141             return True
142 
143         user_data.append(address)
144         uc.emu_stop()
145         return False
146 
147     hooks = [
148         uc.hook_add(U.UC_HOOK_MEM_READ | U.UC_HOOK_MEM_READ_UNMAPPED,
149                     hook_mem, stopped_addr),
150     ]
151 
152     # callback for tracing instructions
153     # def hook_code(uc, address, size, user_data):
154     #     print(">>> Tracing instruction at %#x, instr size = %#x, data=%r"
155     #           % (address, size, uc.mem_read(address, size)))
156     # hooks.append(uc.hook_add(U.UC_HOOK_CODE, hook_code))
157 
158     # For Intel, set the value of EBX
159     if elf.arch == 'i386':
160         uc.reg_write(U.x86_const.UC_X86_REG_EBX, got)
161 
162     # Special case for MIPS, which is the most silly architecture
163     # https://sourceware.org/ml/binutils/2004-11/msg00116.html
164     if elf.arch == 'mips' and elf.bits == 32:
165         OFFSET_GP_GOT = 0x7ff0
166         uc.reg_write(U.mips_const.UC_MIPS_REG_GP, got + 0x7ff0)
167 
168     try:
169         uc.emu_start(pc, until=-1, count=5)
170     except U.UcError as error:
171         UC_ERR = next(k for k,v in \
172                     U.unicorn_const.__dict__.items()
173                     if error.errno == v and k.startswith('UC_ERR_'))
174         log.debug("%#x: %s (%s)", pc, error, UC_ERR)
175 
176     if elf.arch == 'mips':
177         pc = uc.reg_read(U.mips_const.UC_MIPS_REG_PC)
178         if pc == magic_addr:
179             t8 = uc.reg_read(U.mips_const.UC_MIPS_REG_T8)
180             stopped_addr.append(elf._mips_got.get(t8, 0))
181 
182     retval = 0
183     if stopped_addr:
184         retval = stopped_addr.pop()
185 
186     for hook in hooks:
187         uc.hook_del(hook)
188 
189     return retval
190
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pwnlib/elf/plt.py b/pwnlib/elf/plt.py
--- a/pwnlib/elf/plt.py
+++ b/pwnlib/elf/plt.py
@@ -53,9 +53,30 @@
 
     return rv
 
+
+def __ensure_memory_to_run_unicorn():
+    """
+    Check if there is enough memory to run Unicorn Engine.
+    Unicorn Engine requires 1GB of memory to run, if there isn't enough memory it calls exit(1).
+
+    This is a bug in Unicorn Engine, see: https://github.com/unicorn-engine/unicorn/issues/1766
+    """
+    try:
+        from mmap import mmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE
+
+        mm = mmap(
+            -1, 1024 * 1024 * 1024, MAP_PRIVATE | MAP_ANON, PROT_WRITE | PROT_READ | PROT_EXEC
+        )
+        mm.close()
+    except OSError:
+        raise OSError("Cannot allocate 1GB memory to run Unicorn Engine")
+
+
 def prepare_unicorn_and_context(elf, got, address, data):
     import unicorn as U
 
+    __ensure_memory_to_run_unicorn()
+
     # Instantiate the emulator with the correct arguments for the current
     # architecutre.
     arch = {

{"golden_diff": "diff --git a/pwnlib/elf/plt.py b/pwnlib/elf/plt.py\n--- a/pwnlib/elf/plt.py\n+++ b/pwnlib/elf/plt.py\n@@ -53,9 +53,30 @@\n \n     return rv\n \n+\n+def __ensure_memory_to_run_unicorn():\n+    \"\"\"\n+    Check if there is enough memory to run Unicorn Engine.\n+    Unicorn Engine requires 1GB of memory to run, if there isn't enough memory it calls exit(1).\n+\n+    This is a bug in Unicorn Engine, see: https://github.com/unicorn-engine/unicorn/issues/1766\n+    \"\"\"\n+    try:\n+        from mmap import mmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE\n+\n+        mm = mmap(\n+            -1, 1024 * 1024 * 1024, MAP_PRIVATE | MAP_ANON, PROT_WRITE | PROT_READ | PROT_EXEC\n+        )\n+        mm.close()\n+    except OSError:\n+        raise OSError(\"Cannot allocate 1GB memory to run Unicorn Engine\")\n+\n+\n def prepare_unicorn_and_context(elf, got, address, data):\n     import unicorn as U\n \n+    __ensure_memory_to_run_unicorn()\n+\n     # Instantiate the emulator with the correct arguments for the current\n     # architecutre.\n     arch = {\n", "issue": "pwn checksec: do not require libunicorn to be loaded\nIt seems that `pwn checksec` requires libunicorn. Is that really required? The Unicorn Engine mmaps a 1GB+ memory page which aborts the program on failures. This can happen on low-memory systems such as cheap VPSes. This is actually a bug in libunicorn that I reported in https://github.com/unicorn-engine/unicorn/issues/1766 but it still hasn't been fixed.\r\n\r\nGiven all this, it would be great if we could not require libunicorn in `checksec`. But if we need, it would be nice to get it documented here for users who stumble upon the same issue and try to search it here.\r\n\r\nHere is an example issue of this:\r\n\r\n```\r\nroot@pwndbg:~# free -h\r\n               total        used        free      shared  buff/cache   available\r\nMem:           957Mi       265Mi       153Mi       4.0Mi       538Mi       532Mi\r\nSwap:             0B          0B          0B\r\n\r\nroot@pwndbg:~# strace -e openat,mmap pwn checksec /root/x/bin/main 2>&1 | tail -n 10\r\nopenat(AT_FDCWD, \"/usr/lib/python3/dist-packages/mpmath-0.0.0.egg-info/PKG-INFO\", O_RDONLY|O_CLOEXEC) = 7\r\nopenat(AT_FDCWD, \"/usr/local/lib/python3.10/dist-packages/unicorn/lib/libunicorn.so.2\", O_RDONLY|O_CLOEXEC) = 7\r\nmmap(NULL, 22447520, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 7, 0) = 0x7f2604f9d000\r\nmmap(0x7f2605339000, 13496320, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x39c000) = 0x7f2605339000\r\nmmap(0x7f2606018000, 3039232, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x107b000) = 0x7f2606018000\r\nmmap(0x7f26062fe000, 1601536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x1360000) = 0x7f26062fe000\r\nmmap(0x7f2606485000, 525728, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2606485000\r\nmmap(NULL, 1073741824, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)\r\nCould not allocate dynamic translator buffer\r\n+++ exited with 1 +++\r\n```\npwn checksec: do not require libunicorn to be loaded\nIt seems that `pwn checksec` requires libunicorn. Is that really required? The Unicorn Engine mmaps a 1GB+ memory page which aborts the program on failures. This can happen on low-memory systems such as cheap VPSes. This is actually a bug in libunicorn that I reported in https://github.com/unicorn-engine/unicorn/issues/1766 but it still hasn't been fixed.\r\n\r\nGiven all this, it would be great if we could not require libunicorn in `checksec`. But if we need, it would be nice to get it documented here for users who stumble upon the same issue and try to search it here.\r\n\r\nHere is an example issue of this:\r\n\r\n```\r\nroot@pwndbg:~# free -h\r\n               total        used        free      shared  buff/cache   available\r\nMem:           957Mi       265Mi       153Mi       4.0Mi       538Mi       532Mi\r\nSwap:             0B          0B          0B\r\n\r\nroot@pwndbg:~# strace -e openat,mmap pwn checksec /root/x/bin/main 2>&1 | tail -n 10\r\nopenat(AT_FDCWD, \"/usr/lib/python3/dist-packages/mpmath-0.0.0.egg-info/PKG-INFO\", O_RDONLY|O_CLOEXEC) = 7\r\nopenat(AT_FDCWD, \"/usr/local/lib/python3.10/dist-packages/unicorn/lib/libunicorn.so.2\", O_RDONLY|O_CLOEXEC) = 7\r\nmmap(NULL, 22447520, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 7, 0) = 0x7f2604f9d000\r\nmmap(0x7f2605339000, 13496320, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x39c000) = 0x7f2605339000\r\nmmap(0x7f2606018000, 3039232, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x107b000) = 0x7f2606018000\r\nmmap(0x7f26062fe000, 1601536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 7, 0x1360000) = 0x7f26062fe000\r\nmmap(0x7f2606485000, 525728, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2606485000\r\nmmap(NULL, 1073741824, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)\r\nCould not allocate dynamic translator buffer\r\n+++ exited with 1 +++\r\n```\n", "before_files": [{"content": "\"\"\"Emulates instructions in the PLT to locate symbols more accurately.\n\"\"\"\nfrom __future__ import division\nimport logging\n\nfrom pwnlib.args import args\nfrom pwnlib.log import getLogger\nfrom pwnlib.util import fiddling\nfrom pwnlib.util import packing\n\nlog = getLogger(__name__)\n\n\ndef emulate_plt_instructions(elf, got, address, data, targets):\n    \"\"\"Emulates instructions in ``data``\n\n    Arguments:\n        elf(ELF): ELF that we are emulating\n        got(int): Address of the GOT, as expected in e.g. EBX\n        address(int): Address of ``data`` for emulation\n        data(str): Array of bytes to emulate\n        targets(list): List of target addresses\n\n    Returns:\n        :class:`dict`: Map of ``{address: target}`` for each address which\n            reaches one of the selected targets.\n    \"\"\"\n    rv = {}\n\n    if not args.PLT_DEBUG:\n        log.setLevel(logging.DEBUG + 1)\n\n    # Unicorn doesn't support big-endian for everything yet.\n    if elf.endian == 'big' and elf.arch == 'mips':\n        data = packing.unpack_many(data, bits=32, endian='little')\n        data = packing.flat(data, bits=32, endian='big')\n\n    uc, ctx = prepare_unicorn_and_context(elf, got, address, data)\n\n    # Brute force addresses, assume that PLT entry points are at 4-byte aligned\n    # Do not emulate more than a handful of instructions.\n    for i, pc in enumerate(range(address, address + len(data), 4)):\n        if log.isEnabledFor(logging.DEBUG):\n            log.debug('%s %#x', fiddling.enhex(data[i*4:(i+1) * 4]), pc)\n            log.debug(elf.disasm(pc, 4))\n\n        uc.context_restore(ctx)\n        target = emulate_plt_instructions_inner(uc, elf, got, pc, data[i*4:])\n\n        if target in targets:\n            log.debug(\"%#x -> %#x\", pc, target)\n            rv[pc] = target\n\n    return rv\n\ndef prepare_unicorn_and_context(elf, got, address, data):\n    import unicorn as U\n\n    # Instantiate the emulator with the correct arguments for the current\n    # architecutre.\n    arch = {\n        'aarch64': U.UC_ARCH_ARM64,\n        'amd64': U.UC_ARCH_X86,\n        'arm': U.UC_ARCH_ARM,\n        'i386': U.UC_ARCH_X86,\n        'mips': U.UC_ARCH_MIPS,\n        'mips64': U.UC_ARCH_MIPS,\n        # 'powerpc': U.UC_ARCH_PPC, <-- Not actually supported\n        'thumb': U.UC_ARCH_ARM,\n        'riscv32': U.UC_ARCH_RISCV,\n        'riscv64': U.UC_ARCH_RISCV,\n    }.get(elf.arch, None)\n\n    if arch is None:\n        log.warn(\"Could not emulate PLT instructions for %r\" % elf)\n        return {}\n\n    emulation_bits = elf.bits\n\n    # x32 uses 64-bit instructions, just restricts itself to a 32-bit\n    # address space.\n    if elf.arch == 'amd64' and elf.bits == 32:\n        emulation_bits = 64\n\n    mode = {\n        32: U.UC_MODE_32,\n        64: U.UC_MODE_64\n    }.get(emulation_bits)\n\n    if elf.arch in ('arm', 'aarch64'):\n        mode = U.UC_MODE_ARM\n\n    uc = U.Uc(arch, mode)\n\n    # Map the page of memory, and fill it with the contents\n    start = address & (~0xfff)\n    stop  = (address + len(data) + 0xfff) & (~0xfff)\n\n    if not (0 <= start <= stop <= (1 << elf.bits)):\n        return None\n\n    uc.mem_map(start, stop-start)\n    uc.mem_write(address, data)\n    assert uc.mem_read(address, len(data)) == data\n\n    # MIPS is unique in that it relies entirely on _DYNAMIC, at the beginning\n    # of the GOT.  Each PLT stub loads an address stored here.\n    # Because of this, we have to support loading memory from this location.\n    #\n    # https://www.cr0.org/paper/mips.elf.external.resolution.txt\n    magic_addr = 0x7c7c7c7c\n\n    if elf.arch == 'mips':\n        # Map the GOT so that MIPS can access it\n        p_magic = packing.p32(magic_addr)\n        start = got & (~0xfff)\n        try:\n            uc.mem_map(start, 0x1000)\n        except Exception:\n            # Ignore double-mapping\n            pass\n\n        uc.mem_write(got, p_magic)\n\n    return uc, uc.context_save()\n\n\ndef emulate_plt_instructions_inner(uc, elf, got, pc, data):\n    import unicorn as U\n\n    # Hook invalid addresses and any accesses out of the specified address range\n    stopped_addr = []\n\n    # For MIPS. Explanation at prepare_unicorn_and_context.\n    magic_addr = 0x7c7c7c7c\n\n    def hook_mem(uc, access, address, size, value, user_data):\n        # Special case to allow MIPS to dereference the _DYNAMIC pointer\n        # in the GOT.\n        if elf.arch == 'mips' and address == got:\n            return True\n\n        user_data.append(address)\n        uc.emu_stop()\n        return False\n\n    hooks = [\n        uc.hook_add(U.UC_HOOK_MEM_READ | U.UC_HOOK_MEM_READ_UNMAPPED,\n                    hook_mem, stopped_addr),\n    ]\n\n    # callback for tracing instructions\n    # def hook_code(uc, address, size, user_data):\n    #     print(\">>> Tracing instruction at %#x, instr size = %#x, data=%r\"\n    #           % (address, size, uc.mem_read(address, size)))\n    # hooks.append(uc.hook_add(U.UC_HOOK_CODE, hook_code))\n\n    # For Intel, set the value of EBX\n    if elf.arch == 'i386':\n        uc.reg_write(U.x86_const.UC_X86_REG_EBX, got)\n\n    # Special case for MIPS, which is the most silly architecture\n    # https://sourceware.org/ml/binutils/2004-11/msg00116.html\n    if elf.arch == 'mips' and elf.bits == 32:\n        OFFSET_GP_GOT = 0x7ff0\n        uc.reg_write(U.mips_const.UC_MIPS_REG_GP, got + 0x7ff0)\n\n    try:\n        uc.emu_start(pc, until=-1, count=5)\n    except U.UcError as error:\n        UC_ERR = next(k for k,v in \\\n                    U.unicorn_const.__dict__.items()\n                    if error.errno == v and k.startswith('UC_ERR_'))\n        log.debug(\"%#x: %s (%s)\", pc, error, UC_ERR)\n\n    if elf.arch == 'mips':\n        pc = uc.reg_read(U.mips_const.UC_MIPS_REG_PC)\n        if pc == magic_addr:\n            t8 = uc.reg_read(U.mips_const.UC_MIPS_REG_T8)\n            stopped_addr.append(elf._mips_got.get(t8, 0))\n\n    retval = 0\n    if stopped_addr:\n        retval = stopped_addr.pop()\n\n    for hook in hooks:\n        uc.hook_del(hook)\n\n    return retval\n", "path": "pwnlib/elf/plt.py"}], "after_files": [{"content": "\"\"\"Emulates instructions in the PLT to locate symbols more accurately.\n\"\"\"\nfrom __future__ import division\nimport logging\n\nfrom pwnlib.args import args\nfrom pwnlib.log import getLogger\nfrom pwnlib.util import fiddling\nfrom pwnlib.util import packing\n\nlog = getLogger(__name__)\n\n\ndef emulate_plt_instructions(elf, got, address, data, targets):\n    \"\"\"Emulates instructions in ``data``\n\n    Arguments:\n        elf(ELF): ELF that we are emulating\n        got(int): Address of the GOT, as expected in e.g. EBX\n        address(int): Address of ``data`` for emulation\n        data(str): Array of bytes to emulate\n        targets(list): List of target addresses\n\n    Returns:\n        :class:`dict`: Map of ``{address: target}`` for each address which\n            reaches one of the selected targets.\n    \"\"\"\n    rv = {}\n\n    if not args.PLT_DEBUG:\n        log.setLevel(logging.DEBUG + 1)\n\n    # Unicorn doesn't support big-endian for everything yet.\n    if elf.endian == 'big' and elf.arch == 'mips':\n        data = packing.unpack_many(data, bits=32, endian='little')\n        data = packing.flat(data, bits=32, endian='big')\n\n    uc, ctx = prepare_unicorn_and_context(elf, got, address, data)\n\n    # Brute force addresses, assume that PLT entry points are at 4-byte aligned\n    # Do not emulate more than a handful of instructions.\n    for i, pc in enumerate(range(address, address + len(data), 4)):\n        if log.isEnabledFor(logging.DEBUG):\n            log.debug('%s %#x', fiddling.enhex(data[i*4:(i+1) * 4]), pc)\n            log.debug(elf.disasm(pc, 4))\n\n        uc.context_restore(ctx)\n        target = emulate_plt_instructions_inner(uc, elf, got, pc, data[i*4:])\n\n        if target in targets:\n            log.debug(\"%#x -> %#x\", pc, target)\n            rv[pc] = target\n\n    return rv\n\n\ndef __ensure_memory_to_run_unicorn():\n    \"\"\"\n    Check if there is enough memory to run Unicorn Engine.\n    Unicorn Engine requires 1GB of memory to run, if there isn't enough memory it calls exit(1).\n\n    This is a bug in Unicorn Engine, see: https://github.com/unicorn-engine/unicorn/issues/1766\n    \"\"\"\n    try:\n        from mmap import mmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE\n\n        mm = mmap(\n            -1, 1024 * 1024 * 1024, MAP_PRIVATE | MAP_ANON, PROT_WRITE | PROT_READ | PROT_EXEC\n        )\n        mm.close()\n    except OSError:\n        raise OSError(\"Cannot allocate 1GB memory to run Unicorn Engine\")\n\n\ndef prepare_unicorn_and_context(elf, got, address, data):\n    import unicorn as U\n\n    __ensure_memory_to_run_unicorn()\n\n    # Instantiate the emulator with the correct arguments for the current\n    # architecutre.\n    arch = {\n        'aarch64': U.UC_ARCH_ARM64,\n        'amd64': U.UC_ARCH_X86,\n        'arm': U.UC_ARCH_ARM,\n        'i386': U.UC_ARCH_X86,\n        'mips': U.UC_ARCH_MIPS,\n        'mips64': U.UC_ARCH_MIPS,\n        # 'powerpc': U.UC_ARCH_PPC, <-- Not actually supported\n        'thumb': U.UC_ARCH_ARM,\n        'riscv32': U.UC_ARCH_RISCV,\n        'riscv64': U.UC_ARCH_RISCV,\n    }.get(elf.arch, None)\n\n    if arch is None:\n        log.warn(\"Could not emulate PLT instructions for %r\" % elf)\n        return {}\n\n    emulation_bits = elf.bits\n\n    # x32 uses 64-bit instructions, just restricts itself to a 32-bit\n    # address space.\n    if elf.arch == 'amd64' and elf.bits == 32:\n        emulation_bits = 64\n\n    mode = {\n        32: U.UC_MODE_32,\n        64: U.UC_MODE_64\n    }.get(emulation_bits)\n\n    if elf.arch in ('arm', 'aarch64'):\n        mode = U.UC_MODE_ARM\n\n    uc = U.Uc(arch, mode)\n\n    # Map the page of memory, and fill it with the contents\n    start = address & (~0xfff)\n    stop  = (address + len(data) + 0xfff) & (~0xfff)\n\n    if not (0 <= start <= stop <= (1 << elf.bits)):\n        return None\n\n    uc.mem_map(start, stop-start)\n    uc.mem_write(address, data)\n    assert uc.mem_read(address, len(data)) == data\n\n    # MIPS is unique in that it relies entirely on _DYNAMIC, at the beginning\n    # of the GOT.  Each PLT stub loads an address stored here.\n    # Because of this, we have to support loading memory from this location.\n    #\n    # https://www.cr0.org/paper/mips.elf.external.resolution.txt\n    magic_addr = 0x7c7c7c7c\n\n    if elf.arch == 'mips':\n        # Map the GOT so that MIPS can access it\n        p_magic = packing.p32(magic_addr)\n        start = got & (~0xfff)\n        try:\n            uc.mem_map(start, 0x1000)\n        except Exception:\n            # Ignore double-mapping\n            pass\n\n        uc.mem_write(got, p_magic)\n\n    return uc, uc.context_save()\n\n\ndef emulate_plt_instructions_inner(uc, elf, got, pc, data):\n    import unicorn as U\n\n    # Hook invalid addresses and any accesses out of the specified address range\n    stopped_addr = []\n\n    # For MIPS. Explanation at prepare_unicorn_and_context.\n    magic_addr = 0x7c7c7c7c\n\n    def hook_mem(uc, access, address, size, value, user_data):\n        # Special case to allow MIPS to dereference the _DYNAMIC pointer\n        # in the GOT.\n        if elf.arch == 'mips' and address == got:\n            return True\n\n        user_data.append(address)\n        uc.emu_stop()\n        return False\n\n    hooks = [\n        uc.hook_add(U.UC_HOOK_MEM_READ | U.UC_HOOK_MEM_READ_UNMAPPED,\n                    hook_mem, stopped_addr),\n    ]\n\n    # callback for tracing instructions\n    # def hook_code(uc, address, size, user_data):\n    #     print(\">>> Tracing instruction at %#x, instr size = %#x, data=%r\"\n    #           % (address, size, uc.mem_read(address, size)))\n    # hooks.append(uc.hook_add(U.UC_HOOK_CODE, hook_code))\n\n    # For Intel, set the value of EBX\n    if elf.arch == 'i386':\n        uc.reg_write(U.x86_const.UC_X86_REG_EBX, got)\n\n    # Special case for MIPS, which is the most silly architecture\n    # https://sourceware.org/ml/binutils/2004-11/msg00116.html\n    if elf.arch == 'mips' and elf.bits == 32:\n        OFFSET_GP_GOT = 0x7ff0\n        uc.reg_write(U.mips_const.UC_MIPS_REG_GP, got + 0x7ff0)\n\n    try:\n        uc.emu_start(pc, until=-1, count=5)\n    except U.UcError as error:\n        UC_ERR = next(k for k,v in \\\n                    U.unicorn_const.__dict__.items()\n                    if error.errno == v and k.startswith('UC_ERR_'))\n        log.debug(\"%#x: %s (%s)\", pc, error, UC_ERR)\n\n    if elf.arch == 'mips':\n        pc = uc.reg_read(U.mips_const.UC_MIPS_REG_PC)\n        if pc == magic_addr:\n            t8 = uc.reg_read(U.mips_const.UC_MIPS_REG_T8)\n            stopped_addr.append(elf._mips_got.get(t8, 0))\n\n    retval = 0\n    if stopped_addr:\n        retval = stopped_addr.pop()\n\n    for hook in hooks:\n        uc.hook_del(hook)\n\n    return retval\n", "path": "pwnlib/elf/plt.py"}]}