Skip to content

Commit e0d8889

Browse files
authored
Merge pull request #3 from elicn/libumem-epoll-fixes
More fixes
2 parents 5ad49c0 + 31720cf commit e0d8889

2 files changed

Lines changed: 97 additions & 38 deletions

File tree

qiling/os/posix/syscall/epoll.py

Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,56 @@
11
from __future__ import annotations
22

3+
import ctypes
34
import select
45

5-
from typing import TYPE_CHECKING, Dict, KeysView
6+
from typing import TYPE_CHECKING, Dict, KeysView, NamedTuple
67

8+
from qiling.os import struct
79
from qiling.os.posix.const import *
810
from qiling.os.filestruct import PersistentQlFile, ql_file
911

1012

1113
if TYPE_CHECKING:
1214
from qiling import Qiling
15+
from qiling.arch.arch import QlArch
1316
from qiling.os.posix.posix import QlFileDes
1417

18+
19+
class QlEpollEntry(NamedTuple):
20+
"""A named tuple to represent an epoll entry.
21+
22+
This is used to store the events mask and the data for each entry in
23+
the epoll instance.
24+
"""
25+
26+
events: int
27+
data: int
28+
29+
30+
@struct.cache
31+
def __make_epoll_event(arch: QlArch):
32+
"""Create a structure to represent an epoll event.
33+
"""
34+
35+
Struct = struct.get_packed_struct(arch.endian)
36+
37+
class epoll_event(Struct):
38+
_fields_ = (
39+
('events', ctypes.c_uint32),
40+
('data', ctypes.c_uint64)
41+
)
42+
43+
return epoll_event
44+
45+
1546
class QlEpollObj:
1647
def __init__(self, epoll_object: select.epoll):
1748
self._epoll_object = epoll_object
1849

1950
# maps fd to eventmask
2051
# keep track of which fds have what eventmasks,
2152
# since this isn't directly supported in select.epoll
22-
self._fds: Dict[int, int] = {}
53+
self._fds: Dict[int, QlEpollEntry] = {}
2354

2455
@property
2556
def fds(self) -> KeysView[int]:
@@ -29,31 +60,30 @@ def fds(self) -> KeysView[int]:
2960
def epoll_instance(self) -> select.epoll:
3061
return self._epoll_object
3162

32-
def get_eventmask(self, fd: int) -> int:
63+
def close(self) -> None:
64+
self._epoll_object.close()
65+
66+
def __getitem__(self, fd: int) -> QlEpollEntry:
3367
return self._fds[fd]
3468

35-
def set_eventmask(self, fd: int, newmask: int):
36-
# the mask for an FD shouldn't ever be undefined
37-
# as it is set whenever an FD is added for a QlEpollObj instance
69+
def __setitem__(self, fd: int, entry: QlEpollEntry) -> None:
70+
# if fd is already being watched, modify its eventmask.
71+
if fd in self:
72+
self._epoll_object.modify(fd, entry.events)
3873

39-
newmask = self.get_eventmask(fd) | newmask
74+
# otherwise, register it with the epoll object
75+
else:
76+
self._epoll_object.register(fd, entry.events)
4077

41-
self._epoll_object.modify(fd, newmask)
42-
self._fds[fd] = newmask
78+
self._fds[fd] = entry
4379

44-
def monitor_fd(self, fd: int, eventmask: int) -> None:
45-
# tell the epoll object to watch the fd arg, looking for events matching the eventmask
46-
self._epoll_object.register(fd, eventmask)
47-
self._fds[fd] = eventmask
80+
def __delitem__(self, fd: int) -> None:
81+
"""Remove an fd from the epoll instance.
82+
"""
4883

49-
def delist_fd(self, fd: int) -> None:
5084
self._fds.pop(fd)
5185
self._epoll_object.unregister(fd)
5286

53-
def close(self) -> None:
54-
self._epoll_object.close()
55-
56-
5787
def __contains__(self, fd: int) -> bool:
5888
"""Test whether a specific fd is already being watched by this epoll instance.
5989
"""
@@ -153,23 +183,25 @@ def ql_syscall_epoll_ctl(ql: Qiling, epfd: int, op: int, fd: int, event: int):
153183
if not event:
154184
return -EINVAL
155185

156-
event_ptr = ql.mem.read_ptr(event)
157-
events = ql.mem.read_ptr(event_ptr, 4)
186+
# dereference the event pointer to get structure fields
187+
epoll_event_cls = __make_epoll_event(ql.arch)
188+
epoll_event = epoll_event_cls.load_from(ql.mem, event)
158189

159190
# EPOLLEXCLUSIVE was specified in event and fd refers to an epoll instance
160-
if isinstance(fd_obj, QlEpollObj) and (op & EPOLLEXCLUSIVE):
191+
if isinstance(fd_obj, QlEpollObj) and (epoll_event.events & EPOLLEXCLUSIVE):
161192
return -EINVAL
162193

163-
# add to list of fds to be monitored with per-fd eventmask register will actual epoll
164-
# instance and add eventmask accordingly
165-
epoll_parent_obj.monitor_fd(fd, events)
194+
epoll_parent_obj[fd] = QlEpollEntry(
195+
epoll_event.events,
196+
epoll_event.data
197+
)
166198

167199
elif op == EPOLL_CTL_DEL:
168200
if fd not in epoll_parent_obj:
169201
return -ENOENT
170202

171203
# remove from fds list and do so in the underlying epoll instance
172-
epoll_parent_obj.delist_fd(fd)
204+
del epoll_parent_obj[fd]
173205

174206
elif op == EPOLL_CTL_MOD:
175207
if fd not in epoll_parent_obj:
@@ -178,14 +210,18 @@ def ql_syscall_epoll_ctl(ql: Qiling, epfd: int, op: int, fd: int, event: int):
178210
if not event:
179211
return -EINVAL
180212

181-
event_ptr = ql.mem.read_ptr(event)
182-
events = ql.mem.read_ptr(event_ptr, 4)
213+
# dereference the event pointer to get structure fields
214+
epoll_event_cls = __make_epoll_event(ql.arch)
215+
epoll_event = epoll_event_cls.load_from(ql.mem, event)
183216

184217
# EPOLLEXCLUSIVE cannot be set on MOD operation, only on ADD
185-
if events & EPOLLEXCLUSIVE:
218+
if epoll_event.events & EPOLLEXCLUSIVE:
186219
return -EINVAL
187220

188-
epoll_parent_obj.set_eventmask(fd, events)
221+
epoll_parent_obj[fd] = QlEpollEntry(
222+
epoll_event.events,
223+
epoll_event.data
224+
)
189225

190226
return 0
191227

@@ -216,20 +252,22 @@ def ql_syscall_epoll_wait(ql: Qiling, epfd: int, epoll_events: int, maxevents: i
216252

217253
ready_fds = epoll_obj.poll(timeout, maxevents)
218254

255+
epoll_event_cls = __make_epoll_event(ql.arch)
256+
219257
# Each tuple in ready_fds consists of (file descriptor, eventmask) so we iterate
220258
# through these to indicate which fds are ready and 'why'
221-
259+
#
260+
# FIXME: emulated system fds are not the same as hosted system fds
222261
for i, (fd, events) in enumerate(ready_fds):
223-
# if no longer interested in this fd, remove from list
224-
if events & EPOLLONESHOT:
225-
epoll_parent_obj.delist_fd(fd)
262+
entry = epoll_parent_obj[fd]
263+
epoll_event = epoll_event_cls(events, entry.data)
226264

227-
# FIXME: the data packed after events should be the one passed on epoll_ctl
228-
# for that specific fd. currently this does not align with the spec
229-
data = ql.pack32(events) + ql.pack64(fd)
230-
offset = len(data) * i
265+
offset = epoll_event_cls.sizeof() * i
266+
ql.mem.write(epoll_events + offset, bytes(epoll_event))
231267

232-
ql.mem.write(epoll_events + offset, data)
268+
# if no longer interested in this fd, remove from list
269+
if events & EPOLLONESHOT:
270+
del epoll_parent_obj[fd]
233271

234272
return len(ready_fds)
235273

qiling/os/struct.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,34 @@ class BaseStructEB(BaseStruct, ctypes.BigEndianStructure):
222222
pass
223223

224224

225+
@cache
226+
def get_packed_struct(endian: QL_ENDIAN = QL_ENDIAN.EL) -> Type[BaseStruct]:
227+
"""Provide a packed version of BaseStruct based on the emulated
228+
architecture endianess.
229+
230+
Args:
231+
archbits: required alignment in bits
232+
"""
233+
234+
Struct = {
235+
QL_ENDIAN.EL: BaseStructEL,
236+
QL_ENDIAN.EB: BaseStructEB
237+
}[endian]
238+
239+
class PackedStruct(Struct):
240+
_pack_ = 1
241+
242+
return PackedStruct
243+
244+
225245
@cache
226246
def get_aligned_struct(archbits: int, endian: QL_ENDIAN = QL_ENDIAN.EL) -> Type[BaseStruct]:
227247
"""Provide an aligned version of BaseStruct based on the emulated
228248
architecture properties.
229249
230250
Args:
231251
archbits: required alignment in bits
252+
endian: required endianness
232253
"""
233254

234255
Struct = {

0 commit comments

Comments
 (0)