shim_parser.c 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  1. /* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
  2. /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
  3. /* Copyright (C) 2014 OSCAR lab, Stony Brook University
  4. This file is part of Graphene Library OS.
  5. Graphene Library OS is free software: you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation, either version 3 of the
  8. License, or (at your option) any later version.
  9. Graphene Library OS is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  15. /*
  16. * shim_parser.c
  17. *
  18. * This file contains codes for parsing system call arguements for debug
  19. * purpose.
  20. */
  21. #include <shim_internal.h>
  22. #include <shim_tls.h>
  23. #include <shim_table.h>
  24. #include <shim_thread.h>
  25. #include <shim_utils.h>
  26. #include <pal.h>
  27. #include <pal_error.h>
  28. #include <asm/unistd.h>
  29. #include <asm/prctl.h>
  30. #include <asm/mman.h>
  31. #include <asm/ioctls.h>
  32. #include <linux/sched.h>
  33. #include <linux/futex.h>
  34. #include <linux/wait.h>
  35. #include <errno.h>
  36. #include <fcntl.h>
  37. static void parse_open_flags (const char *, va_list);
  38. static void parse_open_mode (const char *, va_list);
  39. static void parse_access_mode (const char *, va_list);
  40. static void parse_clone_flags (const char *, va_list);
  41. static void parse_mmap_prot (const char *, va_list);
  42. static void parse_mmap_flags (const char *, va_list);
  43. static void parse_exec_args (const char *, va_list);
  44. static void parse_exec_envp (const char *, va_list);
  45. static void parse_pipe_fds (const char *, va_list);
  46. static void parse_signum (const char *, va_list);
  47. static void parse_sigmask (const char *, va_list);
  48. static void parse_sigprocmask_how (const char *, va_list);
  49. static void parse_timespec (const char *, va_list);
  50. static void parse_sockaddr (const char *, va_list);
  51. static void parse_futexop (const char *, va_list);
  52. static void parse_ioctlop (const char *, va_list);
  53. static void parse_seek (const char *, va_list);
  54. static void parse_at_fdcwd (const char *, va_list);
  55. static void parse_wait_option (const char *, va_list);
  56. struct parser_table {
  57. int slow;
  58. int stop;
  59. void (*parser[6]) (const char *, va_list);
  60. } syscall_parser_table[SHIM_NSYSCALLS] = {
  61. { .slow = 1, .parser = { NULL } }, /* read */
  62. { .slow = 1, .parser = { NULL } }, /* write */
  63. { .slow = 1, /* open */
  64. .parser = { NULL, &parse_open_flags, &parse_open_mode, } },
  65. { .slow = 0, .parser = { NULL } }, /* close */
  66. { .slow = 0, .parser = { NULL } }, /* stat */
  67. { .slow = 0, .parser = { NULL } }, /* fstat */
  68. { .slow = 0, .parser = { NULL } }, /* lstat */
  69. { .slow = 1, .parser = { NULL } }, /* poll */
  70. { .slow = 0, .parser = { NULL, NULL, &parse_seek } }, /* lseek */
  71. { .slow = 0, /* mmap */
  72. .parser = { NULL, NULL, &parse_mmap_prot, &parse_mmap_flags } },
  73. { .slow = 0, /* mprotect */
  74. .parser = { NULL, NULL, &parse_mmap_prot } },
  75. { .slow = 0, .parser = { NULL } }, /* munmap */
  76. { .slow = 0, .parser = { NULL } }, /* brk */
  77. { .slow = 0, .parser = { &parse_signum } }, /* rt_sigaction */
  78. { .slow = 0, /* rt_sigprocmask */
  79. .parser = { &parse_sigprocmask_how, &parse_sigmask, &parse_sigmask } },
  80. { .slow = 0, .parser = { NULL } }, /* rt_sigreturn */
  81. { .slow = 1, .parser = { NULL, &parse_ioctlop } }, /* ioctl */
  82. { .slow = 1, .parser = { NULL } }, /* pread64 */
  83. { .slow = 0, .parser = { NULL } }, /* pwrite64 */
  84. { .slow = 1, .parser = { NULL } }, /* readv */
  85. { .slow = 0, .parser = { NULL } }, /* writev */
  86. { .slow = 0, /* access */
  87. .parser = { NULL, &parse_access_mode } },
  88. { .slow = 0, /* pipe */
  89. .parser = { &parse_pipe_fds } },
  90. { .slow = 0, .parser = { NULL } }, /* select */
  91. { .slow = 0, .parser = { NULL } }, /* sched_yield */
  92. { .slow = 0, .parser = { NULL } }, /* mremap */
  93. { .slow = 0, .parser = { NULL } }, /* msync */
  94. { .slow = 0, .parser = { NULL } }, /* mincore */
  95. { .slow = 0, .parser = { NULL } }, /* madvise */
  96. { .slow = 0, .parser = { NULL } }, /* shmget */
  97. { .slow = 0, .parser = { NULL } }, /* shmat */
  98. { .slow = 0, .parser = { NULL } }, /* shmctl */
  99. { .slow = 0, .parser = { NULL } }, /* dup */
  100. { .slow = 0, .parser = { NULL } }, /* dup2 */
  101. { .slow = 0, .parser = { NULL } }, /* pause */
  102. { .slow = 1,
  103. .parser = { &parse_timespec } }, /* nanosleep */
  104. { .slow = 0, .parser = { NULL } }, /* getitimer */
  105. { .slow = 0, .parser = { NULL } }, /* alarm */
  106. { .slow = 0, .parser = { NULL } }, /* setitimer */
  107. { .slow = 0, .parser = { NULL } }, /* getpid */
  108. { .slow = 0, .parser = { NULL } }, /* sendfile */
  109. { .slow = 0, .parser = { NULL } }, /* socket */
  110. { .slow = 1, .parser = { NULL, &parse_sockaddr } }, /* connect */
  111. { .slow = 1, .parser = { NULL } }, /* accept */
  112. { .slow = 0, .parser = { NULL } }, /* sendto */
  113. { .slow = 0, .parser = { NULL } }, /* recvfrom */
  114. { .slow = 0, .parser = { NULL } }, /* sendmsg */
  115. { .slow = 1, .parser = { NULL } }, /* recvmsg */
  116. { .slow = 0, .parser = { NULL } }, /* shutdown */
  117. { .slow = 0, .parser = { NULL } }, /* bind */
  118. { .slow = 0, .parser = { NULL } }, /* listen */
  119. { .slow = 0, .parser = { NULL } }, /* getsockname */
  120. { .slow = 0, .parser = { NULL } }, /* getpeername */
  121. { .slow = 0, .stop = 3, /* socketpair */
  122. .parser = { NULL, NULL, NULL, &parse_pipe_fds } },
  123. { .slow = 0, .parser = { NULL } }, /* setsockopt */
  124. { .slow = 0, .parser = { NULL } }, /* getsockopt */
  125. { .slow = 1, .parser = { &parse_clone_flags } }, /* clone */
  126. { .slow = 1, .parser = { NULL } }, /* fork */
  127. { .slow = 1, .parser = { NULL } }, /* vfork */
  128. { .slow = 1, /* execve */
  129. .parser = { NULL, &parse_exec_args, &parse_exec_envp, } },
  130. { .slow = 0, .parser = { NULL } }, /* exit */
  131. { .slow = 1, .parser = { NULL, NULL,
  132. &parse_wait_option,
  133. NULL } }, /* wait4 */
  134. { .slow = 0, .parser = { NULL, &parse_signum, } }, /* kill */
  135. { .slow = 0, .parser = { NULL } }, /* uname */
  136. { .slow = 0, .parser = { NULL } }, /* semget */
  137. { .slow = 1, .parser = { NULL } }, /* semop */
  138. { .slow = 0, .parser = { NULL } }, /* semctl */
  139. { .slow = 0, .parser = { NULL } }, /* shmdt */
  140. { .slow = 1, .parser = { NULL } }, /* msgget */
  141. { .slow = 1, .parser = { NULL } }, /* msgsnd */
  142. { .slow = 1, .parser = { NULL } }, /* msgrcv */
  143. { .slow = 1, .parser = { NULL } }, /* msgctl */
  144. { .slow = 0, .parser = { NULL } }, /* fcntl */
  145. { .slow = 0, .parser = { NULL } }, /* flock */
  146. { .slow = 0, .parser = { NULL } }, /* fsync */
  147. { .slow = 0, .parser = { NULL } }, /* fdatasync */
  148. { .slow = 0, .parser = { NULL } }, /* truncate */
  149. { .slow = 0, .parser = { NULL } }, /* ftruncate */
  150. { .slow = 0, .parser = { NULL } }, /* getdents */
  151. { .slow = 0, .parser = { NULL } }, /* getcwd */
  152. { .slow = 0, .parser = { NULL } }, /* chdir */
  153. { .slow = 0, .parser = { NULL } }, /* fchdir */
  154. { .slow = 0, .parser = { NULL } }, /* rename */
  155. { .slow = 0, .parser = { NULL } }, /* mkdir */
  156. { .slow = 0, .parser = { NULL } }, /* rmdir */
  157. { .slow = 0, .parser = { NULL, &parse_open_mode } }, /* creat */
  158. { .slow = 0, .parser = { NULL } }, /* link */
  159. { .slow = 0, .parser = { NULL } }, /* unlink */
  160. { .slow = 0, .parser = { NULL } }, /* symlink */
  161. { .slow = 0, .parser = { NULL } }, /* readlink */
  162. { .slow = 0, .parser = { NULL } }, /* chmod */
  163. { .slow = 0, .parser = { NULL } }, /* fchmod */
  164. { .slow = 0, .parser = { NULL } }, /* chown */
  165. { .slow = 0, .parser = { NULL } }, /* fchown */
  166. { .slow = 0, .parser = { NULL } }, /* lchown */
  167. { .slow = 0, .parser = { NULL } }, /* umask */
  168. { .slow = 0, .parser = { NULL } }, /* gettimeofday */
  169. { .slow = 0, .parser = { NULL } }, /* getrlimit */
  170. { .slow = 0, .parser = { NULL } }, /* getrusage */
  171. { .slow = 0, .parser = { NULL } }, /* sysinfo */
  172. { .slow = 0, .parser = { NULL } }, /* times */
  173. { .slow = 0, .parser = { NULL } }, /* ptrace */
  174. { .slow = 0, .parser = { NULL } }, /* getuid */
  175. { .slow = 0, .parser = { NULL } }, /* syslog */
  176. { .slow = 0, .parser = { NULL } }, /* getgid */
  177. { .slow = 0, .parser = { NULL } }, /* setuid */
  178. { .slow = 0, .parser = { NULL } }, /* setgid */
  179. { .slow = 0, .parser = { NULL } }, /* geteuid */
  180. { .slow = 0, .parser = { NULL } }, /* getegid */
  181. { .slow = 0, .parser = { NULL } }, /* setpgid */
  182. { .slow = 0, .parser = { NULL } }, /* getppid */
  183. { .slow = 0, .parser = { NULL } }, /* getpgrp */
  184. { .slow = 0, .parser = { NULL } }, /* setsid */
  185. { .slow = 0, .parser = { NULL } }, /* setreuid */
  186. { .slow = 0, .parser = { NULL } }, /* setregid */
  187. { .slow = 0, .parser = { NULL } }, /* getgroups */
  188. { .slow = 0, .parser = { NULL } }, /* setgroups */
  189. { .slow = 0, .parser = { NULL } }, /* setresuid */
  190. { .slow = 0, .parser = { NULL } }, /* getresuid */
  191. { .slow = 0, .parser = { NULL } }, /* setresgid */
  192. { .slow = 0, .parser = { NULL } }, /* getresgid */
  193. { .slow = 0, .parser = { NULL } }, /* getpgid */
  194. { .slow = 0, .parser = { NULL } }, /* setfsuid */
  195. { .slow = 0, .parser = { NULL } }, /* setfsgid */
  196. { .slow = 0, .parser = { NULL } }, /* getsid */
  197. { .slow = 0, .parser = { NULL } }, /* capget */
  198. { .slow = 0, .parser = { NULL } }, /* capset */
  199. { .slow = 0, .parser = { NULL } }, /* rt_sigpending */
  200. { .slow = 0, .parser = { NULL } }, /* rt_sigtimedwait */
  201. { .slow = 0, .parser = { NULL } }, /* rt_sigqueueinfo */
  202. { .slow = 0, .parser = { NULL } }, /* rt_sigsuspend */
  203. { .slow = 0, .parser = { NULL } }, /* sigaltstack */
  204. { .slow = 0, .parser = { NULL } }, /* utime */
  205. { .slow = 0, .parser = { NULL } }, /* mknod */
  206. { .slow = 0, .parser = { NULL } }, /* uselib */
  207. { .slow = 0, .parser = { NULL } }, /* personality */
  208. { .slow = 0, .parser = { NULL } }, /* ustat */
  209. { .slow = 0, .parser = { NULL } }, /* statfs */
  210. { .slow = 0, .parser = { NULL } }, /* fstatfs */
  211. { .slow = 0, .parser = { NULL } }, /* sysfs */
  212. { .slow = 0, .parser = { NULL } }, /* getpriority */
  213. { .slow = 0, .parser = { NULL } }, /* setpriority */
  214. { .slow = 0, .parser = { NULL } }, /* sched_setparam */
  215. { .slow = 0, .parser = { NULL } }, /* sched_getparam */
  216. { .slow = 0, .parser = { NULL } }, /* sched_setscheduler */
  217. { .slow = 0, .parser = { NULL } }, /* sched_getscheduler */
  218. { .slow = 0, .parser = { NULL } }, /* sched_get_priority_max */
  219. { .slow = 0, .parser = { NULL } }, /* sched_get_priority_min */
  220. { .slow = 0, .parser = { NULL } }, /* sched_rr_get_interval */
  221. { .slow = 0, .parser = { NULL } }, /* mlock */
  222. { .slow = 0, .parser = { NULL } }, /* munlock */
  223. { .slow = 0, .parser = { NULL } }, /* mlockall */
  224. { .slow = 0, .parser = { NULL } }, /* munlockall */
  225. { .slow = 0, .parser = { NULL } }, /* vhangup */
  226. { .slow = 0, .parser = { NULL } }, /* modify_ldt */
  227. { .slow = 0, .parser = { NULL } }, /* pivot_root */
  228. { .slow = 0, .parser = { NULL } }, /* _sysctl */
  229. { .slow = 0, .parser = { NULL } }, /* prctl */
  230. { .slow = 0, .parser = { NULL } }, /* arch_prctl */
  231. { .slow = 0, .parser = { NULL } }, /* adjtimex */
  232. { .slow = 0, .parser = { NULL } }, /* setrlimit */
  233. { .slow = 0, .parser = { NULL } }, /* chroot */
  234. { .slow = 0, .parser = { NULL } }, /* sync */
  235. { .slow = 0, .parser = { NULL } }, /* acct */
  236. { .slow = 0, .parser = { NULL } }, /* settimeofday */
  237. { .slow = 0, .parser = { NULL } }, /* mount */
  238. { .slow = 0, .parser = { NULL } }, /* umount2 */
  239. { .slow = 0, .parser = { NULL } }, /* swapon */
  240. { .slow = 0, .parser = { NULL } }, /* swapoff */
  241. { .slow = 0, .parser = { NULL } }, /* reboot */
  242. { .slow = 0, .parser = { NULL } }, /* sethostname */
  243. { .slow = 0, .parser = { NULL } }, /* setdomainname */
  244. { .slow = 0, .parser = { NULL } }, /* iopl */
  245. { .slow = 0, .parser = { NULL } }, /* ioperm */
  246. { .slow = 0, .parser = { NULL } }, /* create_module */
  247. { .slow = 0, .parser = { NULL } }, /* init_module */
  248. { .slow = 0, .parser = { NULL } }, /* delete_module */
  249. { .slow = 0, .parser = { NULL } }, /* get_kernel_syms */
  250. { .slow = 0, .parser = { NULL } }, /* query_module */
  251. { .slow = 0, .parser = { NULL } }, /* quotactl */
  252. { .slow = 0, .parser = { NULL } }, /* nfsservctl */
  253. { .slow = 0, .parser = { NULL } }, /* getpmsg */
  254. { .slow = 0, .parser = { NULL } }, /* putpmsg */
  255. { .slow = 0, .parser = { NULL } }, /* afs_syscall */
  256. { .slow = 0, .parser = { NULL } }, /* tuxcall */
  257. { .slow = 0, .parser = { NULL } }, /* security */
  258. { .slow = 0, .parser = { NULL } }, /* gettid */
  259. { .slow = 0, .parser = { NULL } }, /* readahead */
  260. { .slow = 0, .parser = { NULL } }, /* setxattr */
  261. { .slow = 0, .parser = { NULL } }, /* lsetxattr */
  262. { .slow = 0, .parser = { NULL } }, /* fsetxattr */
  263. { .slow = 0, .parser = { NULL } }, /* getxattr */
  264. { .slow = 0, .parser = { NULL } }, /* lgetxattr */
  265. { .slow = 0, .parser = { NULL } }, /* fgetxattr */
  266. { .slow = 0, .parser = { NULL } }, /* listxattr */
  267. { .slow = 0, .parser = { NULL } }, /* llistxattr */
  268. { .slow = 0, .parser = { NULL } }, /* flistxattr */
  269. { .slow = 0, .parser = { NULL } }, /* removexattr */
  270. { .slow = 0, .parser = { NULL } }, /* lremovexattr */
  271. { .slow = 0, .parser = { NULL } }, /* fremovexattr */
  272. { .slow = 0, .parser = { NULL, &parse_signum } }, /* tkill */
  273. { .slow = 0, .parser = { NULL } }, /* time */
  274. { .slow = 1, .parser = { NULL, &parse_futexop } }, /* futex */
  275. { .slow = 0, .parser = { NULL } }, /* sched_setaffinity */
  276. { .slow = 0, .parser = { NULL } }, /* sched_getaffinity */
  277. { .slow = 0, .parser = { NULL } }, /* set_thread_area */
  278. { .slow = 0, .parser = { NULL } }, /* io_setup */
  279. { .slow = 0, .parser = { NULL } }, /* io_destroy */
  280. { .slow = 0, .parser = { NULL } }, /* io_getevents */
  281. { .slow = 0, .parser = { NULL } }, /* io_submit */
  282. { .slow = 0, .parser = { NULL } }, /* io_cancel */
  283. { .slow = 0, .parser = { NULL } }, /* get_thread_area */
  284. { .slow = 0, .parser = { NULL } }, /* lookup_dcookie */
  285. { .slow = 0, .parser = { NULL } }, /* epoll_create */
  286. { .slow = 0, .parser = { NULL } }, /* epoll_ctl_old */
  287. { .slow = 0, .parser = { NULL } }, /* epoll_wait_old */
  288. { .slow = 0, .parser = { NULL } }, /* remap_file_pages */
  289. { .slow = 0, .parser = { NULL } }, /* getdents64 */
  290. { .slow = 0, .parser = { NULL } }, /* set_tid_address */
  291. { .slow = 0, .parser = { NULL } }, /* restart_syscall */
  292. { .slow = 0, .parser = { NULL } }, /* semtimedop */
  293. { .slow = 0, .parser = { NULL } }, /* fadvise64 */
  294. { .slow = 0, .parser = { NULL } }, /* timer_create */
  295. { .slow = 0, .parser = { NULL } }, /* timer_settime */
  296. { .slow = 0, .parser = { NULL } }, /* timer_gettime */
  297. { .slow = 0, .parser = { NULL } }, /* timer_getoverrun */
  298. { .slow = 0, .parser = { NULL } }, /* timer_delete */
  299. { .slow = 0, .parser = { NULL } }, /* clock_settime */
  300. { .slow = 0, .parser = { NULL } }, /* clock_gettime */
  301. { .slow = 0, .parser = { NULL } }, /* clock_getres */
  302. { .slow = 0, .parser = { NULL } }, /* clock_nanosleep */
  303. { .slow = 0, .parser = { NULL } }, /* exit_group */
  304. { .slow = 1, .parser = { NULL } }, /* epoll_wait */
  305. { .slow = 0, .parser = { NULL } }, /* epoll_ctl */
  306. { .slow = 0,
  307. .parser = { NULL, NULL, &parse_signum } }, /* tgkill */
  308. { .slow = 0, .parser = { NULL } }, /* utimes */
  309. { .slow = 0, .parser = { NULL } }, /* vserver */
  310. { .slow = 0, .parser = { NULL } }, /* mbind */
  311. { .slow = 0, .parser = { NULL } }, /* set_mempolicy */
  312. { .slow = 0, .parser = { NULL } }, /* get_mempolicy */
  313. { .slow = 0, .parser = { NULL } }, /* mq_open */
  314. { .slow = 0, .parser = { NULL } }, /* mq_unlink */
  315. { .slow = 0, .parser = { NULL } }, /* mq_timedsend */
  316. { .slow = 0, .parser = { NULL } }, /* mq_timedreceive */
  317. { .slow = 0, .parser = { NULL } }, /* mq_notify */
  318. { .slow = 0, .parser = { NULL } }, /* mq_getsetattr */
  319. { .slow = 0, .parser = { NULL } }, /* kexec_load */
  320. { .slow = 1, .parser = { NULL } }, /* waitid */
  321. { .slow = 0, .parser = { NULL } }, /* add_key */
  322. { .slow = 0, .parser = { NULL } }, /* request_key */
  323. { .slow = 0, .parser = { NULL } }, /* keyctl */
  324. { .slow = 0, .parser = { NULL } }, /* ioprio_set */
  325. { .slow = 0, .parser = { NULL } }, /* ioprio_get */
  326. { .slow = 0, .parser = { NULL } }, /* inotify_init */
  327. { .slow = 0, .parser = { NULL } }, /* inotify_add_watch */
  328. { .slow = 0, .parser = { NULL } }, /* inotify_rm_watch */
  329. { .slow = 0, .parser = { NULL } }, /* migrate_pages */
  330. { .slow = 0, .parser = { &parse_at_fdcwd, NULL,
  331. &parse_open_flags,
  332. &parse_open_mode } }, /* openat */
  333. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* mkdirat */
  334. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* mknodat */
  335. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* fchownat */
  336. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* futimesat */
  337. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* newfstatat */
  338. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* unlinkat */
  339. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* renameat */
  340. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* linkat */
  341. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* symlinkat */
  342. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* readlinkat */
  343. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* fchmodat */
  344. { .slow = 0, .parser = { &parse_at_fdcwd, } }, /* faccessat */
  345. { .slow = 0, .parser = { NULL } }, /* pselect6 */
  346. { .slow = 1, .parser = { NULL } }, /* ppoll */
  347. { .slow = 0, .parser = { NULL } }, /* unshare */
  348. { .slow = 0, .parser = { NULL } }, /* set_robust_list */
  349. { .slow = 0, .parser = { NULL } }, /* get_robust_list */
  350. { .slow = 0, .parser = { NULL } }, /* splice */
  351. { .slow = 0, .parser = { NULL } }, /* tee */
  352. { .slow = 0, .parser = { NULL } }, /* sync_file_range */
  353. { .slow = 0, .parser = { NULL } }, /* vmsplice */
  354. { .slow = 0, .parser = { NULL } }, /* move_pages */
  355. { .slow = 0, .parser = { NULL } }, /* utimensat */
  356. { .slow = 1, .parser = { NULL } }, /* epoll_pwait */
  357. { .slow = 0, .parser = { NULL } }, /* signalfd */
  358. { .slow = 0, .parser = { NULL } }, /* timerfd_create */
  359. { .slow = 0, .parser = { NULL } }, /* eventfd */
  360. { .slow = 0, .parser = { NULL } }, /* fallocate */
  361. { .slow = 0, .parser = { NULL } }, /* timerfd_settime */
  362. { .slow = 0, .parser = { NULL } }, /* timerfd_gettime */
  363. { .slow = 1, .parser = { NULL } }, /* accept4 */
  364. { .slow = 0, .parser = { NULL } }, /* signalfd4 */
  365. { .slow = 0, .parser = { NULL } }, /* eventfd2 */
  366. { .slow = 0, .parser = { NULL } }, /* epoll_create1 */
  367. { .slow = 0, .parser = { NULL } }, /* dup3 */
  368. { .slow = 0, .parser = { NULL } }, /* pipe2 */
  369. { .slow = 0, .parser = { NULL } }, /* inotify_init1 */
  370. { .slow = 0, .parser = { NULL } }, /* preadv */
  371. { .slow = 0, .parser = { NULL } }, /* pwritev */
  372. { .slow = 0, .parser = { NULL } }, /* rt_tgsigqueueinfo */
  373. { .slow = 0, .parser = { NULL } }, /* perf_event_open */
  374. { .slow = 0, .parser = { NULL } }, /* recvmmsg */
  375. { .slow = 0, .parser = { NULL } },
  376. { .slow = 1, .parser = { NULL } }, /* checkpoint */
  377. { .slow = 1, .parser = { NULL } }, /* restore */
  378. { .slow = 1, .parser = { NULL } }, /* sandbox_create */
  379. { .slow = 0, .parser = { NULL } }, /* sandbox_attach */
  380. { .slow = 0, .parser = { NULL } }, /* sandbox_current */
  381. { .slow = 1, .parser = { NULL } }, /* msgpersist */
  382. { .slow = 1, .parser = { NULL } }, /* benchmark_ipc */
  383. { .slow = 1, .parser = { NULL } }, /* send_rpc */
  384. { .slow = 1, .parser = { NULL } }, /* recv_rpc */
  385. };
  386. static inline int is_pointer (const char * type)
  387. {
  388. return type[strlen(type) - 1] == '*' || !memcmp(type, "long", 5);
  389. }
  390. #define PRINTF(fmt, ...) \
  391. do { \
  392. debug_printf((fmt), __VA_ARGS__); \
  393. } while (0)
  394. #define PUTS(str) \
  395. do { \
  396. debug_puts((str)); \
  397. } while (0)
  398. #define PUTCH(ch) \
  399. do { \
  400. debug_putch((ch)); \
  401. } while (0)
  402. #define VPRINTF(fmt, ap) \
  403. do { \
  404. debug_vprintf((fmt), (ap)); \
  405. } while (0)
  406. static inline void parse_string_arg (va_list ap)
  407. {
  408. VPRINTF("\"%s\"", ap);
  409. }
  410. static inline void parse_pointer_arg (va_list ap)
  411. {
  412. VPRINTF("%p", ap);
  413. }
  414. static inline void parse_integer_arg (va_list ap)
  415. {
  416. VPRINTF("%d", ap);
  417. }
  418. static inline void parse_syscall_args (va_list ap)
  419. {
  420. const char * arg_type = va_arg(ap, const char *);
  421. if (memcmp(arg_type, "const char *", 13) == 0)
  422. parse_string_arg(ap);
  423. else if (is_pointer(arg_type))
  424. parse_pointer_arg(ap);
  425. else
  426. parse_integer_arg(ap);
  427. }
  428. static inline void skip_syscall_args (va_list ap)
  429. {
  430. const char * arg_type = va_arg (ap, const char *);
  431. if (memcmp(arg_type, "const char *", 13) == 0)
  432. va_arg(ap, const char *);
  433. else if (is_pointer(arg_type))
  434. va_arg(ap, void *);
  435. else
  436. va_arg(ap, int);
  437. }
  438. void sysparser_printf (const char * fmt, ...)
  439. {
  440. va_list ap;
  441. va_start(ap, fmt);
  442. VPRINTF(fmt, ap);
  443. va_end(ap);
  444. }
  445. void parse_syscall_before (int sysno, const char * name, int nr, ...)
  446. {
  447. if (!debug_handle)
  448. return;
  449. struct parser_table * parser = &syscall_parser_table[sysno];
  450. if (!parser->slow && !parser->stop)
  451. return;
  452. va_list ap;
  453. va_start(ap, nr);
  454. PRINTF("---- shim_%s(", name);
  455. for (int i = 0 ; i < nr ; i++) {
  456. if (parser->stop && parser->stop == i)
  457. goto dotdotdot;
  458. if (i)
  459. PUTCH(',');
  460. if (parser->parser[i]) {
  461. const char * type = va_arg(ap, const char *);
  462. (*parser->parser[i])(type, ap);
  463. } else
  464. parse_syscall_args(ap);
  465. }
  466. PUTCH(')');
  467. dotdotdot:
  468. PRINTF(" ...\n", name);
  469. va_end(ap);
  470. }
  471. void parse_syscall_after (int sysno, const char * name, int nr, ...)
  472. {
  473. if (!debug_handle)
  474. return;
  475. struct parser_table * parser = &syscall_parser_table[sysno];
  476. va_list ap;
  477. va_start (ap, nr);
  478. const char * ret_type = va_arg(ap, const char *);
  479. if (parser->slow || parser->stop)
  480. PRINTF("---- return from shim_%s(...", name);
  481. else
  482. PRINTF("---- shim_%s(", name);
  483. unsigned long ret_ptr;
  484. int ret_val;
  485. if (is_pointer(ret_type))
  486. ret_ptr = (unsigned long) va_arg(ap, void *);
  487. else
  488. ret_val = va_arg(ap, int);
  489. if (!parser->slow || parser->stop)
  490. for (int i = 0 ; i < nr ; i++) {
  491. if (parser->stop && i < parser->stop) {
  492. skip_syscall_args (ap);
  493. continue;
  494. }
  495. if (i)
  496. PUTCH(',');
  497. if (parser->parser[i]) {
  498. const char * type = va_arg(ap, const char *);
  499. (*parser->parser[i])(type, ap);
  500. } else
  501. parse_syscall_args(ap);
  502. }
  503. if (is_pointer(ret_type)) {
  504. if (ret_ptr < -4095L)
  505. PRINTF(") = %p\n", ret_ptr);
  506. else
  507. PRINTF(") = %ld\n", (long) ret_ptr);
  508. } else {
  509. if (ret_val >= 0)
  510. PRINTF(") = %d\n", ret_val);
  511. else
  512. PRINTF(") = %d\n", ret_val);
  513. }
  514. va_end (ap);
  515. }
  516. static void parse_open_flags (const char * type, va_list ap)
  517. {
  518. int flags = va_arg (ap, int);
  519. if (flags & O_WRONLY) {
  520. PUTS("O_WRONLY");
  521. flags &= ~O_WRONLY;
  522. } else if (flags & O_RDWR) {
  523. PUTS("O_RDWR");
  524. flags &= ~O_RDWR;
  525. } else
  526. PUTS("O_RDONLY");
  527. if (flags & O_APPEND) {
  528. PUTS("|O_APPEND");
  529. flags &= ~O_APPEND;
  530. }
  531. if (flags & O_CREAT) {
  532. PUTS("|O_CREAT");
  533. flags &= ~O_CREAT;
  534. }
  535. if (flags & O_TRUNC) {
  536. PUTS("|O_TRUNC");
  537. flags &= ~O_TRUNC;
  538. }
  539. if (flags & O_ASYNC) {
  540. PUTS("|O_ASYNC");
  541. flags &= ~O_ASYNC;
  542. }
  543. if (flags & O_EXCL) {
  544. PUTS("|O_EXCL");
  545. flags &= ~O_EXCL;
  546. }
  547. if (flags)
  548. PRINTF("|%o", flags);
  549. }
  550. static void parse_open_mode (const char * type, va_list ap)
  551. {
  552. VPRINTF("%04o", ap);
  553. }
  554. static void parse_access_mode (const char * type, va_list ap)
  555. {
  556. int mode = va_arg (ap, int);
  557. PUTS("F_OK");
  558. if (mode) {
  559. if (mode & R_OK)
  560. PUTS("|R_OK");
  561. if (mode & W_OK)
  562. PUTS("|W_OK");
  563. if (mode & X_OK)
  564. PUTS("|X_OK");
  565. }
  566. }
  567. static void parse_clone_flags (const char * type, va_list ap)
  568. {
  569. int flags = va_arg (ap, int);
  570. #define FLG(n) { "CLONE_" #n, CLONE_##n, }
  571. const struct {
  572. const char * name; int flag;
  573. } all_flags[] = {
  574. FLG(VM), FLG(FS), FLG(FILES), FLG(SIGHAND), FLG(PTRACE), FLG(VFORK),
  575. FLG(PARENT), FLG(THREAD), FLG(NEWNS), FLG(SYSVSEM), FLG(SETTLS),
  576. FLG(PARENT_SETTID), FLG(CHILD_CLEARTID), FLG(DETACHED), FLG(UNTRACED),
  577. FLG(CHILD_SETTID), FLG(NEWUTS), FLG(NEWIPC), FLG(NEWUSER),
  578. FLG(NEWPID), FLG(NEWNET), FLG(IO),
  579. };
  580. #undef FLG
  581. bool printed = false;
  582. for (int i = 0 ; i < sizeof(all_flags) / sizeof(all_flags[0]) ; i++)
  583. if (flags & all_flags[i].flag) {
  584. if (printed)
  585. PUTCH('|');
  586. else
  587. printed = true;
  588. PUTS(all_flags[i].name);
  589. flags &= ~all_flags[i].flag;
  590. }
  591. if (flags)
  592. PRINTF("|0x%x", flags);
  593. }
  594. static void parse_mmap_prot (const char * type, va_list ap)
  595. {
  596. int prot = va_arg (ap, int);
  597. int nflags = 0;
  598. if (prot == PROT_NONE) {
  599. PUTS("PROT_NONE");
  600. return;
  601. }
  602. if (prot & PROT_READ) {
  603. if (nflags++)
  604. PUTS("|");
  605. PUTS("PROT_READ");
  606. }
  607. if (prot & PROT_WRITE) {
  608. if (nflags++)
  609. PUTS("|");
  610. PUTS("PROT_WRITE");
  611. }
  612. if (prot & PROT_EXEC) {
  613. if (nflags++)
  614. PUTS("|");
  615. PUTS("PROT_EXEC");
  616. }
  617. }
  618. static void parse_mmap_flags (const char * type, va_list ap)
  619. {
  620. int flags = va_arg (ap, int);
  621. if (flags & MAP_SHARED) {
  622. PUTS("MAP_SHARED");
  623. flags &= ~MAP_SHARED;
  624. }
  625. if (flags & MAP_PRIVATE) {
  626. PUTS("MAP_PRIVATE");
  627. flags &= ~MAP_PRIVATE;
  628. }
  629. if (flags & MAP_ANONYMOUS) {
  630. PUTS("|MAP_ANON");
  631. flags &= ~MAP_ANONYMOUS;
  632. }
  633. if (flags & MAP_FILE) {
  634. PUTS("|MAP_FILE");
  635. flags &= ~MAP_FILE;
  636. }
  637. if (flags & MAP_FIXED) {
  638. PUTS("|MAP_FIXED");
  639. flags &= ~MAP_FIXED;
  640. }
  641. #ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED
  642. if (flags & MAP_UNINITIALIZED) {
  643. PUTS("|MAP_UNINITIALIZED");
  644. flags &= ~MAP_UNINITIALIZED;
  645. }
  646. #endif
  647. if (flags)
  648. PRINTF("|0x%x", flags);
  649. }
  650. static void parse_exec_args (const char * type, va_list ap)
  651. {
  652. const char ** args = va_arg (ap, const char **);
  653. PUTS("[");
  654. for (; *args ; args++) {
  655. PUTS(*args);
  656. PUTS(",");
  657. }
  658. PUTS("]");
  659. }
  660. static void parse_exec_envp (const char * type, va_list ap)
  661. {
  662. const char ** envp = va_arg (ap, const char **);
  663. if (!envp) {
  664. PUTS("NULL");
  665. return;
  666. }
  667. int cnt = 0;
  668. PUTS("[");
  669. for (; *envp ; envp++)
  670. if (cnt++ < 2) {
  671. PUTS(*envp);
  672. PUTS(",");
  673. }
  674. if (cnt > 2)
  675. PRINTF("(%d more)", cnt);
  676. PUTS("]");
  677. }
  678. static void parse_pipe_fds (const char * type, va_list ap)
  679. {
  680. int * fds = va_arg (ap, int *);
  681. PRINTF("[%d, %d]", fds[0], fds[1]);
  682. }
  683. #define S(sig) #sig
  684. const char *const siglist[NUM_KNOWN_SIGS + 1] =
  685. { NULL,
  686. S(SIGHUP),
  687. S(SIGINT),
  688. S(SIGQUIT),
  689. S(SIGILL),
  690. S(SIGTRAP),
  691. S(SIGABRT),
  692. S(SIGBUS),
  693. S(SIGFPE),
  694. S(SIGKILL),
  695. S(SIGUSR1),
  696. S(SIGSEGV),
  697. S(SIGUSR2),
  698. S(SIGPIPE),
  699. S(SIGALRM),
  700. S(SIGTERM),
  701. S(SIGSTKFLT),
  702. S(SIGCHLD),
  703. S(SIGCONT),
  704. S(SIGSTOP),
  705. S(SIGTSTP),
  706. S(SIGTTIN),
  707. S(SIGTTOU), };
  708. static void parse_signum (const char * type, va_list ap)
  709. {
  710. unsigned int signum = va_arg (ap, unsigned int);
  711. if (signum > 0 && signum <= NUM_KNOWN_SIGS)
  712. PUTS(siglist[signum]);
  713. else
  714. PRINTF("Signal %u", signum);
  715. }
  716. static void parse_sigmask (const char * type, va_list ap)
  717. {
  718. sigset_t * sigset = va_arg (ap, sigset_t *);
  719. if (!sigset) {
  720. PUTS("NULL");
  721. return;
  722. }
  723. PUTS("[");
  724. for (int signum = 1 ; signum <= sizeof(sigset) * 8 ; signum++)
  725. if (__sigismember(sigset, signum)) {
  726. if (signum <= NUM_KNOWN_SIGS) {
  727. PUTS(siglist[signum]);
  728. PUTS(",");
  729. } else
  730. PRINTF("Signal %u,", signum);
  731. }
  732. PUTS("]");
  733. }
  734. static void parse_sigprocmask_how (const char * type, va_list ap)
  735. {
  736. int how = va_arg (ap, int);
  737. switch (how) {
  738. case SIG_BLOCK:
  739. PUTS("BLOCK");
  740. break;
  741. case SIG_UNBLOCK:
  742. PUTS("UNBLOCK");
  743. break;
  744. case SIG_SETMASK:
  745. PUTS("SETMASK");
  746. break;
  747. default:
  748. PUTS("<unknown>");
  749. break;
  750. }
  751. }
  752. static void parse_timespec (const char * type, va_list ap)
  753. {
  754. const struct timespec *tv = va_arg (ap, const struct timespec *);
  755. if (!tv) {
  756. PUTS("NULL");
  757. return;
  758. }
  759. PRINTF("[%ld,%lld]", tv->tv_sec, tv->tv_nsec);
  760. }
  761. static void parse_sockaddr (const char * type, va_list ap)
  762. {
  763. const struct sockaddr *addr = va_arg (ap, const struct sockaddr *);
  764. if (!addr) {
  765. PUTS("NULL");
  766. return;
  767. }
  768. switch (addr->sa_family) {
  769. case AF_INET: {
  770. struct sockaddr_in * a = (void *) addr;
  771. unsigned char * ip = (void *) &a->sin_addr.s_addr;
  772. PRINTF("{family=INET,ip=%u.%u.%u.%u,port=htons(%u)}",
  773. ip[0], ip[1], ip[2], ip[3], __ntohs(a->sin_port));
  774. break;
  775. }
  776. case AF_INET6: {
  777. struct sockaddr_in6 * a = (void *) addr;
  778. unsigned short * ip = (void *) &a->sin6_addr.s6_addr;
  779. PRINTF("{family=INET,ip=[%x:%x:%x:%x:%x:%x:%x:%x],"
  780. "port=htons(%u)}",
  781. ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6],
  782. ip[7], __ntohs(a->sin6_port));
  783. break;
  784. }
  785. case AF_UNIX: {
  786. struct sockaddr_un * a = (void *) addr;
  787. PRINTF("{family=UNIX,path=%s}", a->sun_path);
  788. break;
  789. }
  790. default:
  791. PUTS("UNKNOWN");
  792. break;
  793. }
  794. }
  795. static void parse_futexop (const char * type, va_list ap)
  796. {
  797. int op = va_arg (ap, int);
  798. #ifdef FUTEX_PRIVATE_FLAG
  799. if (op & FUTEX_PRIVATE_FLAG) {
  800. PUTS("FUTEX_PRIVATE|");
  801. op &= ~FUTEX_PRIVATE_FLAG;
  802. }
  803. #endif
  804. #ifdef FUTEX_CLOCK_REALTIME
  805. if (op & FUTEX_CLOCK_REALTIME) {
  806. PUTS("FUTEX_CLOCK_REALTIME|");
  807. op &= ~FUTEX_CLOCK_REALTIME;
  808. }
  809. #endif
  810. op &= FUTEX_CMD_MASK;
  811. switch (op) {
  812. case FUTEX_WAIT:
  813. PUTS("FUTEX_WAIT");
  814. break;
  815. case FUTEX_WAKE:
  816. PUTS("FUTEX_WAKE");
  817. break;
  818. case FUTEX_FD:
  819. PUTS("FUTEX_FD");
  820. break;
  821. case FUTEX_REQUEUE:
  822. PUTS("FUTEX_REQUEUE");
  823. break;
  824. case FUTEX_CMP_REQUEUE:
  825. PUTS("FUTEX_CMP_REQUEUE");
  826. break;
  827. case FUTEX_WAKE_OP:
  828. PUTS("FUTEX_WAKE_OP");
  829. break;
  830. default:
  831. PRINTF("OP %d", op);
  832. break;
  833. }
  834. }
  835. static void parse_ioctlop (const char * type, va_list ap)
  836. {
  837. int op = va_arg (ap, int);
  838. if (op >= TCGETS && op <= TIOCVHANGUP) {
  839. const char * opnames[] = {
  840. "TCGETS", /* 0x5401 */ "TCSETS", /* 0x5402 */
  841. "TCSETSW", /* 0x5403 */ "TCSETSF", /* 0x5404 */
  842. "TCGETA", /* 0x5405 */ "TCSETA", /* 0x5406 */
  843. "TCSETAW", /* 0x5407 */ "TCSETAF", /* 0x5408 */
  844. "TCSBRK", /* 0x5409 */ "TCXONC", /* 0x540A */
  845. "TCFLSH", /* 0x540B */ "TIOCEXCL", /* 0x540C */
  846. "TIOCNXCL", /* 0x540D */ "TIOCSCTTY", /* 0x540E */
  847. "TIOCGPGRP", /* 0x540F */ "TIOCSPGRP", /* 0x5410 */
  848. "TIOCOUTQ", /* 0x5411 */ "TIOCSTI", /* 0x5412 */
  849. "TIOCGWINSZ", /* 0x5413 */ "TIOCSWINSZ", /* 0x5414 */
  850. "TIOCMGET", /* 0x5415 */ "TIOCMBIS", /* 0x5416 */
  851. "TIOCMBIC", /* 0x5417 */ "TIOCMSET", /* 0x5418 */
  852. "TIOCGSOFTCAR", /* 0x5419 */ "TIOCSSOFTCAR", /* 0x541A */
  853. "FIONREAD", /* 0x541B */ "TIOCLINUX", /* 0x541C */
  854. "TIOCCONS", /* 0x541D */ "TIOCGSERIAL", /* 0x541E */
  855. "TIOCSSERIAL", /* 0x541F */ "TIOCPKT", /* 0x5420 */
  856. "FIONBIO", /* 0x5421 */ "TIOCNOTTY", /* 0x5422 */
  857. "TIOCSETD", /* 0x5423 */ "TIOCGETD", /* 0x5424 */
  858. "TCSBRKP", /* 0x5425 */ "",
  859. "TIOCSBRK", /* 0x5427 */ "TIOCCBRK", /* 0x5428 */
  860. "TIOCGSID", /* 0x5429 */ "TCGETS2", /* 0x542A */
  861. "TCSETS2", /* 0x542B */ "TCSETSW2", /* 0x542C */
  862. "TCSETSF2", /* 0x542D */ "TIOCGRS485", /* 0x542E */
  863. "TIOCSRS485", /* 0x542F */ "TIOCGPTN" /* 0x5430 */
  864. "TIOCSPTLCK", /* 0x5431 */ "TCGETX", /* 0x5432 */
  865. "TCSETX", /* 0x5433 */ "TCSETXF", /* 0x5434 */
  866. "TCSETXW", /* 0x5435 */ "TIOCSIG", /* 0x5436 */
  867. "TIOCVHANGUP", /* 0x5437 */
  868. };
  869. PUTS(opnames[op - TCGETS]);
  870. return;
  871. }
  872. PRINTF("OP 0x%04u", op);
  873. }
  874. static void parse_seek (const char * type, va_list ap)
  875. {
  876. int seek = va_arg (ap, int);
  877. switch(seek) {
  878. case SEEK_CUR:
  879. PUTS("SEEK_CUR");
  880. break;
  881. case SEEK_SET:
  882. PUTS("SEEK_SET");
  883. break;
  884. case SEEK_END:
  885. PUTS("SEEK_END");
  886. break;
  887. default:
  888. PRINTF("%d", seek);
  889. break;
  890. }
  891. }
  892. static void parse_at_fdcwd (const char * type, va_list ap)
  893. {
  894. int fd = va_arg (ap, int);
  895. switch(fd) {
  896. case AT_FDCWD:
  897. PUTS("AT_FDCWD");
  898. break;
  899. default:
  900. PRINTF("%d", fd);
  901. break;
  902. }
  903. }
  904. static void parse_wait_option (const char * type, va_list ap)
  905. {
  906. int option = va_arg (ap, int);
  907. if (option & WNOHANG)
  908. PUTS("WNOHANG");
  909. }