1212from math import floor
1313import ctypes
1414import os
15+ from qiling .os .posix .const import *
1516
1617def __get_timespec_struct (archbits : int ):
17- long = getattr (ctypes , f' c_int{ archbits } ' )
18- ulong = getattr (ctypes , f' c_uint{ archbits } ' )
18+ long = getattr (ctypes , f" c_int{ archbits } " )
19+ ulong = getattr (ctypes , f" c_uint{ archbits } " )
1920
2021 class timespec (ctypes .Structure ):
2122 _pack_ = archbits // 8
2223
23- _fields_ = (
24- ('tv_sec' , ulong ),
25- ('tv_nsec' , long )
26- )
24+ _fields_ = (("tv_sec" , ulong ), ("tv_nsec" , long ))
2725
2826 return timespec
2927
28+
3029def __get_timespec_obj (archbits : int ):
3130 now = datetime .now ().timestamp ()
3231
@@ -40,17 +39,24 @@ def __get_timespec_obj(archbits: int):
4039def ql_syscall_set_thread_area (ql : Qiling , u_info_addr : int ):
4140 if ql .arch .type == QL_ARCH .X86 :
4241 u_info = ql .mem .read (u_info_addr , 4 * 4 )
43- index = ql .unpack32s (u_info [0 : 4 ])
44- base = ql .unpack32 (u_info [4 : 8 ])
45- limit = ql .unpack32 (u_info [8 : 12 ])
42+ index = ql .unpack32s (u_info [0 : 4 ])
43+ base = ql .unpack32 (u_info [4 : 8 ])
44+ limit = ql .unpack32 (u_info [8 : 12 ])
4645
4746 ql .log .debug ("set_thread_area base : 0x%x limit is : 0x%x" % (base , limit ))
4847
4948 if index == - 1 :
5049 index = ql .os .gdtm .get_free_idx (12 )
5150
5251 if index in (12 , 13 , 14 ):
53- access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W
52+ access = (
53+ QL_X86_A_PRESENT
54+ | QL_X86_A_PRIV_3
55+ | QL_X86_A_DESC_DATA
56+ | QL_X86_A_DATA
57+ | QL_X86_A_DATA_E
58+ | QL_X86_A_DATA_W
59+ )
5460
5561 ql .os .gdtm .register_gdt_segment (index , base , limit , access )
5662 ql .mem .write_ptr (u_info_addr , index , 4 )
@@ -59,12 +65,12 @@ def ql_syscall_set_thread_area(ql: Qiling, u_info_addr: int):
5965 return - 1
6066
6167 elif ql .arch .type == QL_ARCH .MIPS :
62- CONFIG3_ULR = ( 1 << 13 )
68+ CONFIG3_ULR = 1 << 13
6369 ql .arch .regs .cp0_config3 = CONFIG3_ULR
6470 ql .arch .regs .cp0_userlocal = u_info_addr
6571 ql .arch .regs .v0 = 0
6672 ql .arch .regs .a3 = 0
67- ql .log .debug ("set_thread_area(0x%x)" % u_info_addr )
73+ ql .log .debug ("set_thread_area(0x%x)" % u_info_addr )
6874
6975 return 0
7076
@@ -77,83 +83,167 @@ def ql_syscall_set_tls(ql: Qiling, address: int):
7783
7884 ql .log .debug ("settls(%#x)" , address )
7985
86+
8087def ql_syscall_clock_gettime (ql : Qiling , clock_id : int , tp : int ):
8188 ts_obj = __get_timespec_obj (ql .arch .bits )
8289 ql .mem .write (tp , bytes (ts_obj ))
8390
8491 return 0
8592
93+
8694def ql_syscall_gettimeofday (ql : Qiling , tv : int , tz : int ):
8795 if tv :
8896 ts_obj = __get_timespec_obj (ql .arch .bits )
8997 ql .mem .write (tv , bytes (ts_obj ))
9098
9199 if tz :
92- ql .mem .write (tz , b' \x00 ' * 8 )
100+ ql .mem .write (tz , b" \x00 " * 8 )
93101
94102 return 0
95103
96- '''
97- TODO
98- int futimens(int fd, const struct timespec times[_Nullable 2]);
99104
100- '''
105+ """
106+ TODO: This is considered deprecated,
107+ https://www.man7.org/linux/man-pages/man2/futimesat.2.html
108+ but should there be a wrapper added for legacy code?
109+ int futimesat(int dirfd, const char *pathname,
110+ const struct timeval times[2]);
111+
112+ """
113+
114+
115+ # Handle seconds conversions 'in house'
116+ def microseconds_to_nanoseconds (s ):
117+ return s * 1000
118+
119+
120+ def seconds_to_nanoseconds (s ):
121+ return s * 1000000000
101122
102- '''
103- TODO
123+
124+ """
125+ Actual implmentation of utime(s)
126+ Rather than repeat work based on different
127+ precision requirements, just convert seconds/microseconds
128+ to ns and pass to os.utime()
129+ """
130+
131+
132+ def do_utime (ql : Qiling , filename : ctypes .POINTER , times : ctypes .POINTER , s ):
133+ real_file = ""
134+ try :
135+ # get path inside of qiling rootfs
136+ real_file = ql .os .path .transform_to_real_path (ql .mem .string (filename ))
137+ except Exception as ex : # return errors appropriately, don't try to handle
138+ # everything ourselves
139+ return - ex .errno
140+ actime = modtime = 0
141+ """
142+ times[0] specifies the new access time, and times[1] specifies the new modification time.
143+ If times is NULL, then analogously to utime(), the access and modification times of the file are set to the
144+ current time.
145+ """
146+ if s : # utimes, times[0] == new access time, times[1] == modification
147+ data = make_timeval_buf (ql .arch .bits , ql .arch .endian )
148+ with data .ref (ql .mem , times ) as ref_atime : # times[0]
149+ actime = seconds_to_nanoseconds (ref_atime .tv_sec )
150+ actime += microseconds_to_nanoseconds (ref_atime .tv_usec )
151+ with data .ref (ql .mem , times + ctypes .sizeof (data )) as ref_mtime : # increment by ctypes.sizeof() to get times[1]
152+ modtime = seconds_to_nanoseconds (ref_mtime .tv_sec )
153+ modtime += microseconds_to_nanoseconds (ref_mtime .tv_usec )
154+
155+ else :
156+ # utime uses utimbuf, so different data handling needs to be done
157+ data = make_utimbuf (ql .arch .bits , ql .arch .endian )
158+ with data .ref (ql .mem , times ) as ref :
159+ actime = seconds_to_nanoseconds (ref .actime )
160+ modtime = seconds_to_nanoseconds (ref .modtime )
161+ try :
162+ os .utime (real_file , ns = (actime , modtime ))
163+ except Exception as ex :
164+ return - ex .errno
165+ return 0
166+
167+
168+ """
169+ https://www.man7.org/linux/man-pages/man2/utimes.2.html
104170 int utime(const char *filename,
105171 const struct utimbuf *_Nullable times);
172+ """
106173
107- '''
108174
109- '''
110- TODO: This is considered deprecated,
111- but should there be a wrapper added for legacy code?
112- int futimesat(int dirfd, const char *pathname,
113- const struct timeval times[2]);
175+ def ql_syscall_utime (ql : Qiling , filename : ctypes .POINTER , times : ctypes .POINTER ):
176+ return do_utime (ql , filename , times , False ) # False for 's' means
177+ # do plain utime
114178
115- '''
116179
117- '''
118- TODO: int utimes(const char *filename,
180+ """
181+ https://www.man7.org/linux/man-pages/man2/utimes.2.html
182+ int utimes(const char *filename,
119183 const struct timeval times[_Nullable 2]);
184+ """
120185
121- '''
122186
123- '''
124- sys_utimensat int dfd const char *filename struct timespec *utimes int flags
125- '''
126- def ql_syscall_utimensat (ql :qiling .Qiling , dfd : int , filename :POINTER , utimes : POINTER , flags : int ):
127- path = ''
128- if dfd == AT_FDCWD :
129- ql .emu_stop ()
130- ql .log .debug ("inside hook" )
131- if filename == 0 :
132- return EACCES
133- if utimes == 0 :
134- return EACCES
135- ql .log .debug (hex (filename ))
136- unpacked_filename = ql .mem .string (filename )
137- if unpacked_filename .find ("/" ) == 0 : # starts with /, assumed to be absolute path
138- dfd = None
187+ def ql_syscall_utimes (ql : Qiling , filename : ctypes .POINTER , times : ctypes .POINTER ):
188+ return do_utime (ql , filename , times , True ) # True for 's' means the
189+ # we want 'utimes', which has a different prototype, and consequently,
190+ # struct unpacking requirements, then utime
191+
192+
193+ """
194+ Not re-using the do_utime implementation so we can handle
195+ the dfd and timespec unpacking here
196+ """
197+
198+
199+ def do_utime_fd_ns (
200+ ql : Qiling , dfd : int , filename : ctypes .POINTER , utimes : ctypes .POINTER , flags : int , symlinks
201+ ):
202+ # transform to real path, which ensures that we are
203+ # operating inside of the qiling root
204+ unpacked_filename = ql .os .path .transform_to_real_path (ql .mem .string (filename ))
139205 timespec_struct = make_timespec_buf (ql .arch .bits , ql .arch .endian )
140- atime_nsec = mtime_nsec = atime_sec = mtime_sec = 0
206+ atime_nsec = mtime_nsec = 0
207+ if dfd is not None :
208+ dfd = ql .os .fd [dfd ].fileno
141209 with timespec_struct .ref (ql .mem , utimes ) as atime_ref :
142210 atime_nsec = atime_ref .tv_nsec
143- atime_sec = atime_ref .tv_sec
144- with timespec_struct .ref (ql .mem , utimes + ctypes .sizeof (timespec_struct )) as mtime_ref :
211+ atime_nsec += seconds_to_nanoseconds (atime_ref .tv_sec )
212+ with timespec_struct .ref (
213+ ql .mem , utimes + ctypes .sizeof (timespec_struct )
214+ ) as mtime_ref :
145215 mtime_nsec = mtime_ref .tv_nsec
146- mtime_sec = mtime_ref .tv_sec
147- ql .log .debug (f"Got filename { unpacked_filename } for utime syscall " )
216+ mtime_nsec += seconds_to_nanoseconds ( mtime_ref .tv_sec )
217+ ql .log .debug (f"Got filename { unpacked_filename } for utimensat syscall " )
148218 try :
149- follow_symlink = True
150- if flags == AT_SYMLINK_NOFOLLOW :
151- follow_symlink = False
152- os .utime (unpacked_filename , ns = (atime_nsec , mtime_nsec ), dir_fd = dfd , follow_symlinks = follow_symlink )
153- os .utime (unpacked_filename , (atime_sec , mtime_sec ), dir_fd = dfd , follow_symlinks = follow_symlink )
219+ os .utime (
220+ unpacked_filename ,
221+ ns = (atime_nsec , mtime_nsec ),
222+ dir_fd = dfd ,
223+ follow_symlinks = symlinks ,
224+ )
154225 except Exception as ex :
155226 return - ex .errno
156227 return 0
157-
158228
159229
230+ """
231+ https://www.man7.org/linux/man-pages/man2/utimensat.2.html
232+ sys_utimensat int dfd const char *filename struct timespec *utimes int flags
233+ """
234+
235+
236+ def ql_syscall_utimensat (
237+ ql : Qiling , dfd : int , filename : ctypes .POINTER , utimes : ctypes .POINTER , flags : int
238+ ):
239+ if filename == 0 :
240+ return EACCES
241+ if utimes == 0 :
242+ return EACCES
243+ if dfd == AT_FDCWD :
244+ dfd = None
245+ if flags == AT_SYMLINK_NOFOLLOW :
246+ follow_symlink = False
247+ else :
248+ follow_symlink = True
249+ return do_utime_fd_ns (ql , dfd , filename , utimes , flags , follow_symlink )
0 commit comments