shim_init.c 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244
  1. /* Copyright (C) 2014 Stony Brook University
  2. This file is part of Graphene Library OS.
  3. Graphene Library OS is free software: you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public License
  5. as published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. Graphene Library OS is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. /*!
  14. * \file shim_init.c
  15. *
  16. * This file contains entry and exit functions of library OS.
  17. */
  18. #include <shim_internal.h>
  19. #include <shim_table.h>
  20. #include <shim_tls.h>
  21. #include <shim_thread.h>
  22. #include <shim_handle.h>
  23. #include <shim_vma.h>
  24. #include <shim_checkpoint.h>
  25. #include <shim_fs.h>
  26. #include <shim_ipc.h>
  27. #include <shim_profile.h>
  28. #include <shim_vdso.h>
  29. #include <pal.h>
  30. #include <pal_debug.h>
  31. #include <pal_error.h>
  32. #include <sys/mman.h>
  33. #include <asm/unistd.h>
  34. #include <asm/fcntl.h>
  35. unsigned long allocsize;
  36. unsigned long allocshift;
  37. unsigned long allocmask;
  38. /* The following constants will help matching glibc version with compatible
  39. SHIM libraries */
  40. #include "glibc-version.h"
  41. const unsigned int glibc_version = GLIBC_VERSION;
  42. static void handle_failure (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
  43. {
  44. __UNUSED(event);
  45. __UNUSED(context);
  46. if ((arg <= PAL_ERROR_NATIVE_COUNT) || (arg >= PAL_ERROR_CRYPTO_START &&
  47. arg <= PAL_ERROR_CRYPTO_END))
  48. shim_get_tls()->pal_errno = arg;
  49. else
  50. shim_get_tls()->pal_errno = PAL_ERROR_DENIED;
  51. }
  52. noreturn void __abort(void) {
  53. PAUSE();
  54. shim_terminate(-ENOTRECOVERABLE);
  55. }
  56. void warn (const char *format, ...)
  57. {
  58. va_list args;
  59. va_start (args, format);
  60. __SYS_VPRINTF(format, args);
  61. va_end (args);
  62. }
  63. void __stack_chk_fail (void)
  64. {
  65. }
  66. static int pal_errno_to_unix_errno [PAL_ERROR_NATIVE_COUNT + 1] = {
  67. /* reserved */ 0,
  68. /* PAL_ERROR_NOTIMPLEMENTED */ ENOSYS,
  69. /* PAL_ERROR_NOTDEFINED */ ENOSYS,
  70. /* PAL_ERROR_NOTSUPPORT */ EACCES,
  71. /* PAL_ERROR_INVAL */ EINVAL,
  72. /* PAL_ERROR_TOOLONG */ ENAMETOOLONG,
  73. /* PAL_ERROR_DENIED */ EACCES,
  74. /* PAL_ERROR_BADHANDLE */ EFAULT,
  75. /* PAL_ERROR_STREAMEXIST */ EEXIST,
  76. /* PAL_ERROR_STREAMNOTEXIST */ ENOENT,
  77. /* PAL_ERROR_STREAMISFILE */ ENOTDIR,
  78. /* PAL_ERROR_STREAMISDIR */ EISDIR,
  79. /* PAL_ERROR_STREAMISDEVICE */ ESPIPE,
  80. /* PAL_ERROR_INTERRUPTED */ EINTR,
  81. /* PAL_ERROR_OVERFLOW */ EFAULT,
  82. /* PAL_ERROR_BADADDR */ EFAULT,
  83. /* PAL_ERROR_NOMEM */ ENOMEM,
  84. /* PAL_ERROR_NOTKILLABLE */ EACCES,
  85. /* PAL_ERROR_INCONSIST */ EFAULT,
  86. /* PAL_ERROR_TRYAGAIN */ EAGAIN,
  87. /* PAL_ERROR_ENDOFSTREAM */ 0,
  88. /* PAL_ERROR_NOTSERVER */ EINVAL,
  89. /* PAL_ERROR_NOTCONNECTION */ ENOTCONN,
  90. /* PAL_ERROR_ZEROSIZE */ 0,
  91. /* PAL_ERROR_CONNFAILED */ ECONNRESET,
  92. /* PAL_ERROR_ADDRNOTEXIST */ EADDRNOTAVAIL,
  93. };
  94. long convert_pal_errno (long err)
  95. {
  96. return (err >= 0 && err <= PAL_ERROR_NATIVE_COUNT) ?
  97. pal_errno_to_unix_errno[err] : EACCES;
  98. }
  99. /*!
  100. * \brief Parse a number into an unsigned long.
  101. *
  102. * \param str A string containing a non-negative number.
  103. *
  104. * By default the number should be decimal, but if it starts with 0x it is
  105. * parsed as hexadecimal and if it otherwise starts with 0, it is parsed as
  106. * octal.
  107. */
  108. unsigned long parse_int (const char * str)
  109. {
  110. unsigned long num = 0;
  111. int radix = 10;
  112. char c;
  113. if (str[0] == '0') {
  114. str++;
  115. radix = 8;
  116. if (str[0] == 'x') {
  117. str++;
  118. radix = 16;
  119. }
  120. }
  121. while ((c = *(str++))) {
  122. int val;
  123. if (c >= 'A' && c <= 'F')
  124. val = c - 'A' + 10;
  125. else if (c >= 'a' && c <= 'f')
  126. val = c - 'a' + 10;
  127. else if (c >= '0' && c <= '9')
  128. val = c - '0';
  129. else
  130. break;
  131. if (val >= radix)
  132. break;
  133. num = num * radix + val;
  134. }
  135. if (c == 'G' || c == 'g')
  136. num *= 1024 * 1024 * 1024;
  137. else if (c == 'M' || c == 'm')
  138. num *= 1024 * 1024;
  139. else if (c == 'K' || c == 'k')
  140. num *= 1024;
  141. return num;
  142. }
  143. long int glibc_option (const char * opt)
  144. {
  145. char cfg[CONFIG_MAX];
  146. if (!strcmp_static(opt, "heap_size")) {
  147. ssize_t ret = get_config(root_config, "glibc.heap_size", cfg, CONFIG_MAX);
  148. if (ret <= 0) {
  149. debug("no glibc option: %s (err=%ld)\n", opt, ret);
  150. return -ENOENT;
  151. }
  152. long int heap_size = parse_int(cfg);
  153. debug("glibc option: heap_size = %ld\n", heap_size);
  154. return (long int) heap_size;
  155. }
  156. return -EINVAL;
  157. }
  158. void * migrated_memory_start;
  159. void * migrated_memory_end;
  160. const char ** initial_envp __attribute_migratable;
  161. /* library_paths is populated with LD_PRELOAD entries once during LibOS
  162. * initialization and is used in __load_interp_object() to search for ELF
  163. * program interpreter in specific paths. Once allocated, its memory is
  164. * never freed or updated. */
  165. char ** library_paths = NULL;
  166. struct shim_lock __master_lock;
  167. bool lock_enabled;
  168. void init_tcb (shim_tcb_t * tcb)
  169. {
  170. tcb->canary = SHIM_TLS_CANARY;
  171. tcb->self = tcb;
  172. }
  173. void copy_tcb (shim_tcb_t * new_tcb, const shim_tcb_t * old_tcb)
  174. {
  175. memset(new_tcb, 0, sizeof(shim_tcb_t));
  176. new_tcb->canary = SHIM_TLS_CANARY;
  177. new_tcb->self = new_tcb;
  178. new_tcb->tp = old_tcb->tp;
  179. memcpy(&new_tcb->context, &old_tcb->context, sizeof(struct shim_context));
  180. new_tcb->tid = old_tcb->tid;
  181. new_tcb->debug_buf = old_tcb->debug_buf;
  182. }
  183. /* This function is used to allocate tls before interpreter start running */
  184. void allocate_tls (__libc_tcb_t * tcb, bool user, struct shim_thread * thread)
  185. {
  186. assert(tcb);
  187. tcb->tcb = tcb;
  188. init_tcb(&tcb->shim_tcb);
  189. if (thread) {
  190. thread->tcb = tcb;
  191. thread->user_tcb = user;
  192. tcb->shim_tcb.tp = thread;
  193. tcb->shim_tcb.tid = thread->tid;
  194. } else {
  195. tcb->shim_tcb.tp = NULL;
  196. tcb->shim_tcb.tid = 0;
  197. }
  198. DkSegmentRegister(PAL_SEGMENT_FS, tcb);
  199. assert(shim_tls_check_canary());
  200. }
  201. void populate_tls (__libc_tcb_t * tcb, bool user)
  202. {
  203. assert(tcb);
  204. tcb->tcb = tcb;
  205. copy_tcb(&tcb->shim_tcb, shim_get_tls());
  206. struct shim_thread * thread = (struct shim_thread *) tcb->shim_tcb.tp;
  207. if (thread) {
  208. thread->tcb = tcb;
  209. thread->user_tcb = user;
  210. }
  211. DkSegmentRegister(PAL_SEGMENT_FS, tcb);
  212. assert(shim_tls_check_canary());
  213. }
  214. DEFINE_PROFILE_OCCURENCE(alloc_stack, memory);
  215. DEFINE_PROFILE_OCCURENCE(alloc_stack_count, memory);
  216. #define STACK_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS)
  217. void * allocate_stack (size_t size, size_t protect_size, bool user)
  218. {
  219. size = ALIGN_UP(size);
  220. protect_size = ALIGN_UP(protect_size);
  221. /* preserve a non-readable, non-writable page below the user
  222. stack to stop user program to clobber other vmas */
  223. void * stack = NULL;
  224. int flags = STACK_FLAGS|(user ? 0 : VMA_INTERNAL);
  225. if (user) {
  226. stack = bkeep_unmapped_heap(size + protect_size, PROT_NONE,
  227. flags, NULL, 0, "stack");
  228. if (!stack)
  229. return NULL;
  230. stack = (void *)
  231. DkVirtualMemoryAlloc(stack, size + protect_size,
  232. 0, PAL_PROT_NONE);
  233. } else {
  234. stack = system_malloc(size + protect_size);
  235. }
  236. if (!stack)
  237. return NULL;
  238. ADD_PROFILE_OCCURENCE(alloc_stack, size + protect_size);
  239. INC_PROFILE_OCCURENCE(alloc_stack_count);
  240. stack += protect_size;
  241. // Ensure proper alignment for process' initial stack pointer value.
  242. stack += (16 - (uintptr_t)stack % 16) % 16;
  243. DkVirtualMemoryProtect(stack, size, PAL_PROT_READ|PAL_PROT_WRITE);
  244. if (bkeep_mprotect(stack, size, PROT_READ|PROT_WRITE, flags) < 0)
  245. return NULL;
  246. debug("allocated stack at %p (size = %ld)\n", stack, size);
  247. return stack;
  248. }
  249. static int populate_user_stack (void * stack, size_t stack_size,
  250. elf_auxv_t ** auxpp, int ** argcpp,
  251. const char *** argvp, const char *** envpp)
  252. {
  253. const int argc = **argcpp;
  254. const char ** argv = *argvp, ** envp = *envpp;
  255. const char ** new_argv = NULL, ** new_envp = NULL;
  256. elf_auxv_t *new_auxp = NULL;
  257. void * stack_bottom = stack;
  258. void * stack_top = stack + stack_size;
  259. #define ALLOCATE_TOP(size) \
  260. ({ if ((stack_top -= (size)) < stack_bottom) return -ENOMEM; \
  261. stack_top; })
  262. #define ALLOCATE_BOTTOM(size) \
  263. ({ if ((stack_bottom += (size)) > stack_top) return -ENOMEM; \
  264. stack_bottom - (size); })
  265. /* ld.so expects argc as long on stack, not int. */
  266. long * argcp = ALLOCATE_BOTTOM(sizeof(long));
  267. *argcp = **argcpp;
  268. if (!argv) {
  269. *(const char **) ALLOCATE_BOTTOM(sizeof(const char *)) = NULL;
  270. goto copy_envp;
  271. }
  272. new_argv = stack_bottom;
  273. while (argv) {
  274. for (const char ** a = argv ; *a ; a++) {
  275. const char ** t = ALLOCATE_BOTTOM(sizeof(const char *));
  276. int len = strlen(*a) + 1;
  277. char * abuf = ALLOCATE_TOP(len);
  278. memcpy(abuf, *a, len);
  279. *t = abuf;
  280. }
  281. *((const char **) ALLOCATE_BOTTOM(sizeof(const char *))) = NULL;
  282. copy_envp:
  283. if (!envp)
  284. break;
  285. new_envp = stack_bottom;
  286. argv = envp;
  287. envp = NULL;
  288. }
  289. if (!new_envp)
  290. *(const char **) ALLOCATE_BOTTOM(sizeof(const char *)) = NULL;
  291. /* reserve space for ELF aux vectors, populated later by LibOS */
  292. new_auxp = ALLOCATE_BOTTOM(REQUIRED_ELF_AUXV * sizeof(elf_auxv_t) +
  293. REQUIRED_ELF_AUXV_SPACE);
  294. /* x86_64 ABI requires 16 bytes alignment on stack on every function
  295. call. */
  296. size_t move_size = stack_bottom - stack;
  297. *argcpp = stack_top - move_size;
  298. *argcpp = ALIGN_DOWN_PTR(*argcpp, 16UL);
  299. **argcpp = argc;
  300. size_t shift = (void*)(*argcpp) - stack;
  301. memmove(*argcpp, stack, move_size);
  302. *argvp = new_argv ? (void *) new_argv + shift : NULL;
  303. *envpp = new_envp ? (void *) new_envp + shift : NULL;
  304. *auxpp = new_auxp ? (void *) new_auxp + shift : NULL;
  305. /* clear working area at the bottom */
  306. memset(stack, 0, shift);
  307. return 0;
  308. }
  309. int init_stack (const char ** argv, const char ** envp,
  310. int ** argcpp, const char *** argpp,
  311. elf_auxv_t ** auxpp, size_t reserve)
  312. {
  313. uint64_t stack_size = get_rlimit_cur(RLIMIT_STACK);
  314. if (root_config) {
  315. char stack_cfg[CONFIG_MAX];
  316. if (get_config(root_config, "sys.stack.size", stack_cfg, CONFIG_MAX) > 0) {
  317. stack_size = ALIGN_UP(parse_int(stack_cfg));
  318. set_rlimit_cur(RLIMIT_STACK, stack_size);
  319. }
  320. }
  321. struct shim_thread * cur_thread = get_cur_thread();
  322. if (!cur_thread || cur_thread->stack)
  323. return 0;
  324. void * stack = allocate_stack(stack_size, allocsize, true);
  325. if (!stack)
  326. return -ENOMEM;
  327. if (initial_envp)
  328. envp = initial_envp;
  329. int ret = populate_user_stack(stack, stack_size - reserve,
  330. auxpp, argcpp, &argv, &envp);
  331. if (ret < 0)
  332. return ret;
  333. *argpp = argv;
  334. initial_envp = envp;
  335. cur_thread->stack_top = stack + stack_size;
  336. cur_thread->stack = stack;
  337. cur_thread->stack_red = stack - allocsize;
  338. return 0;
  339. }
  340. int read_environs (const char ** envp)
  341. {
  342. for (const char ** e = envp ; *e ; e++) {
  343. if (strstartswith_static(*e, "LD_LIBRARY_PATH=")) {
  344. /* populate library_paths with entries from LD_LIBRARY_PATH envvar */
  345. const char * s = *e + static_strlen("LD_LIBRARY_PATH=");
  346. size_t npaths = 2; // One for the first entry, one for the last
  347. // NULL.
  348. for (const char * tmp = s ; *tmp ; tmp++)
  349. if (*tmp == ':')
  350. npaths++;
  351. char** paths = malloc(sizeof(const char *) *
  352. npaths);
  353. if (!paths)
  354. return -ENOMEM;
  355. size_t cnt = 0;
  356. while (*s) {
  357. const char * next;
  358. for (next = s ; *next && *next != ':' ; next++);
  359. size_t len = next - s;
  360. char * str = malloc(len + 1);
  361. if (!str) {
  362. for (size_t i = 0; i < cnt; i++)
  363. free(paths[i]);
  364. free(paths);
  365. return -ENOMEM;
  366. }
  367. memcpy(str, s, len);
  368. str[len] = 0;
  369. paths[cnt++] = str;
  370. s = *next ? next + 1 : next;
  371. }
  372. paths[cnt] = NULL;
  373. assert(!library_paths);
  374. library_paths = paths;
  375. return 0;
  376. }
  377. }
  378. return 0;
  379. }
  380. struct config_store * root_config = NULL;
  381. static void * __malloc (size_t size)
  382. {
  383. return malloc(size);
  384. }
  385. static void __free (void * mem)
  386. {
  387. free(mem);
  388. }
  389. int init_manifest (PAL_HANDLE manifest_handle)
  390. {
  391. int ret = 0;
  392. void * addr = NULL;
  393. size_t size = 0, map_size = 0;
  394. #define MAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS|VMA_INTERNAL)
  395. if (PAL_CB(manifest_preload.start)) {
  396. addr = PAL_CB(manifest_preload.start);
  397. size = PAL_CB(manifest_preload.end) - PAL_CB(manifest_preload.start);
  398. } else {
  399. PAL_STREAM_ATTR attr;
  400. if (!DkStreamAttributesQueryByHandle(manifest_handle, &attr))
  401. return -PAL_ERRNO;
  402. size = attr.pending_size;
  403. map_size = ALIGN_UP(size);
  404. addr = bkeep_unmapped_any(map_size, PROT_READ, MAP_FLAGS,
  405. 0, "manifest");
  406. if (!addr)
  407. return -ENOMEM;
  408. void * ret_addr = DkStreamMap(manifest_handle, addr,
  409. PAL_PROT_READ, 0,
  410. ALIGN_UP(size));
  411. if (!ret_addr) {
  412. bkeep_munmap(addr, map_size, MAP_FLAGS);
  413. return -ENOMEM;
  414. } else {
  415. assert(addr == ret_addr);
  416. }
  417. }
  418. struct config_store * new_root_config = malloc(sizeof(struct config_store));
  419. if (!new_root_config) {
  420. ret = -ENOMEM;
  421. goto fail;
  422. }
  423. new_root_config->raw_data = addr;
  424. new_root_config->raw_size = size;
  425. new_root_config->malloc = __malloc;
  426. new_root_config->free = __free;
  427. const char * errstring = "Unexpected error";
  428. if ((ret = read_config(new_root_config, NULL, &errstring)) < 0) {
  429. SYS_PRINTF("Unable to read manifest file: %s\n", errstring);
  430. goto fail;
  431. }
  432. root_config = new_root_config;
  433. return 0;
  434. fail:
  435. if (map_size) {
  436. DkStreamUnmap(addr, map_size);
  437. if (bkeep_munmap(addr, map_size, MAP_FLAGS) < 0)
  438. BUG();
  439. }
  440. free(new_root_config);
  441. return ret;
  442. }
  443. #ifdef PROFILE
  444. struct shim_profile profile_root;
  445. #endif
  446. # define FIND_ARG_COMPONENTS(cookie, argc, argv, envp, auxp) \
  447. do { \
  448. void *_tmp = (cookie); \
  449. (argv) = _tmp; \
  450. _tmp += sizeof(char *) * ((argc) + 1); \
  451. (envp) = _tmp; \
  452. for ( ; *(char **) _tmp; _tmp += sizeof(char *)); \
  453. (auxp) = _tmp + sizeof(char *); \
  454. } while (0)
  455. #ifdef PROFILE
  456. static void set_profile_enabled (const char ** envp)
  457. {
  458. const char ** p;
  459. for (p = envp ; (*p) ; p++)
  460. if (strstartswith_static(*p, "PROFILE_ENABLED="))
  461. break;
  462. if (!(*p))
  463. return;
  464. for (size_t i = 0 ; i < N_PROFILE ; i++)
  465. PROFILES[i].disabled = true;
  466. const char * str = (*p) + 16;
  467. bool enabled = false;
  468. while (*str) {
  469. const char * next = str;
  470. for ( ; (*next) && (*next) != ',' ; next++);
  471. if (next > str) {
  472. size_t len = next - str;
  473. for (size_t i = 0 ; i < N_PROFILE ; i++) {
  474. struct shim_profile * profile = &PROFILES[i];
  475. if (!memcmp(profile->name, str, len) && !profile->name[len]) {
  476. profile->disabled = false;
  477. if (profile->type == CATEGORY)
  478. enabled = true;
  479. }
  480. }
  481. }
  482. str = (*next) ? next + 1 : next;
  483. }
  484. while (enabled) {
  485. enabled = false;
  486. for (size_t i = 0 ; i < N_PROFILE ; i++) {
  487. struct shim_profile * profile = &PROFILES[i];
  488. if (!profile->disabled || profile->root == &profile_)
  489. continue;
  490. if (!profile->root->disabled) {
  491. profile->disabled = false;
  492. if (profile->type == CATEGORY)
  493. enabled = true;
  494. }
  495. }
  496. }
  497. for (size_t i = 0 ; i < N_PROFILE ; i++) {
  498. struct shim_profile * profile = &PROFILES[i];
  499. if (profile->type == CATEGORY || profile->disabled)
  500. continue;
  501. for (profile = profile->root ;
  502. profile != &profile_ && profile->disabled ;
  503. profile = profile->root)
  504. profile->disabled = false;
  505. }
  506. }
  507. #endif
  508. static int init_newproc (struct newproc_header * hdr)
  509. {
  510. BEGIN_PROFILE_INTERVAL();
  511. int bytes = DkStreamRead(PAL_CB(parent_process), 0,
  512. sizeof(struct newproc_header), hdr,
  513. NULL, 0);
  514. if (!bytes)
  515. return -PAL_ERRNO;
  516. SAVE_PROFILE_INTERVAL(child_wait_header);
  517. SAVE_PROFILE_INTERVAL_SINCE(child_receive_header, hdr->write_proc_time);
  518. return hdr->failure;
  519. }
  520. DEFINE_PROFILE_CATEGORY(pal, );
  521. DEFINE_PROFILE_INTERVAL(pal_startup_time, pal);
  522. DEFINE_PROFILE_INTERVAL(pal_host_specific_startup_time, pal);
  523. DEFINE_PROFILE_INTERVAL(pal_relocation_time, pal);
  524. DEFINE_PROFILE_INTERVAL(pal_linking_time, pal);
  525. DEFINE_PROFILE_INTERVAL(pal_manifest_loading_time, pal);
  526. DEFINE_PROFILE_INTERVAL(pal_allocation_time, pal);
  527. DEFINE_PROFILE_INTERVAL(pal_tail_startup_time, pal);
  528. DEFINE_PROFILE_INTERVAL(pal_child_creation_time, pal);
  529. DEFINE_PROFILE_CATEGORY(init, );
  530. DEFINE_PROFILE_INTERVAL(init_vma, init);
  531. DEFINE_PROFILE_INTERVAL(init_slab, init);
  532. DEFINE_PROFILE_INTERVAL(init_str_mgr, init);
  533. DEFINE_PROFILE_INTERVAL(init_internal_map, init);
  534. DEFINE_PROFILE_INTERVAL(init_rlimit, init);
  535. DEFINE_PROFILE_INTERVAL(init_fs, init);
  536. DEFINE_PROFILE_INTERVAL(init_dcache, init);
  537. DEFINE_PROFILE_INTERVAL(init_handle, init);
  538. DEFINE_PROFILE_INTERVAL(read_from_checkpoint, init);
  539. DEFINE_PROFILE_INTERVAL(read_from_file, init);
  540. DEFINE_PROFILE_INTERVAL(init_newproc, init);
  541. DEFINE_PROFILE_INTERVAL(init_mount_root, init);
  542. DEFINE_PROFILE_INTERVAL(init_from_checkpoint_file, init);
  543. DEFINE_PROFILE_INTERVAL(restore_from_file, init);
  544. DEFINE_PROFILE_INTERVAL(init_manifest, init);
  545. DEFINE_PROFILE_INTERVAL(init_ipc, init);
  546. DEFINE_PROFILE_INTERVAL(init_thread, init);
  547. DEFINE_PROFILE_INTERVAL(init_important_handles, init);
  548. DEFINE_PROFILE_INTERVAL(init_mount, init);
  549. DEFINE_PROFILE_INTERVAL(init_async, init);
  550. DEFINE_PROFILE_INTERVAL(init_stack, init);
  551. DEFINE_PROFILE_INTERVAL(read_environs, init);
  552. DEFINE_PROFILE_INTERVAL(init_loader, init);
  553. DEFINE_PROFILE_INTERVAL(init_ipc_helper, init);
  554. DEFINE_PROFILE_INTERVAL(init_signal, init);
  555. #define CALL_INIT(func, args ...) func(args)
  556. #define RUN_INIT(func, ...) \
  557. do { \
  558. int _err = CALL_INIT(func, ##__VA_ARGS__); \
  559. if (_err < 0) { \
  560. SYS_PRINTF("shim_init() in " #func " (%d)\n", _err); \
  561. shim_terminate(_err); \
  562. } \
  563. SAVE_PROFILE_INTERVAL(func); \
  564. } while (0)
  565. extern PAL_HANDLE thread_start_event;
  566. noreturn void* shim_init (int argc, void * args)
  567. {
  568. debug_handle = PAL_CB(debug_stream);
  569. cur_process.vmid = (IDTYPE) PAL_CB(process_id);
  570. /* create the initial TCB, shim can not be run without a tcb */
  571. __libc_tcb_t tcb;
  572. memset(&tcb, 0, sizeof(__libc_tcb_t));
  573. allocate_tls(&tcb, false, NULL);
  574. __disable_preempt(&tcb.shim_tcb); // Temporarily disable preemption for delaying any signal
  575. // that arrives during initialization
  576. debug_setbuf(&tcb.shim_tcb, true);
  577. debug("set tcb to %p\n", &tcb);
  578. #ifdef PROFILE
  579. unsigned long begin_time = GET_PROFILE_INTERVAL();
  580. #endif
  581. debug("host: %s\n", PAL_CB(host_type));
  582. DkSetExceptionHandler(&handle_failure, PAL_EVENT_FAILURE);
  583. allocsize = PAL_CB(alloc_align);
  584. allocshift = allocsize - 1;
  585. allocmask = ~allocshift;
  586. create_lock(&__master_lock);
  587. int * argcp = &argc;
  588. const char ** argv, ** envp, ** argp = NULL;
  589. elf_auxv_t * auxp;
  590. /* call to figure out where the arguments are */
  591. FIND_ARG_COMPONENTS(args, argc, argv, envp, auxp);
  592. #ifdef PROFILE
  593. set_profile_enabled(envp);
  594. #endif
  595. struct newproc_header hdr;
  596. void * cpaddr = NULL;
  597. #ifdef PROFILE
  598. unsigned long begin_create_time = 0;
  599. #endif
  600. BEGIN_PROFILE_INTERVAL();
  601. RUN_INIT(init_vma);
  602. RUN_INIT(init_slab);
  603. RUN_INIT(read_environs, envp);
  604. RUN_INIT(init_str_mgr);
  605. RUN_INIT(init_internal_map);
  606. RUN_INIT(init_rlimit);
  607. RUN_INIT(init_fs);
  608. RUN_INIT(init_dcache);
  609. RUN_INIT(init_handle);
  610. debug("shim loaded at %p, ready to initialize\n", &__load_address);
  611. if (argc && argv[0][0] == '-') {
  612. if (!strcmp_static(argv[0], "-resume") && argc >= 2) {
  613. const char * filename = *(argv + 1);
  614. argc -= 2;
  615. argv += 2;
  616. RUN_INIT(init_mount_root);
  617. RUN_INIT(init_from_checkpoint_file, filename, &hdr.checkpoint,
  618. &cpaddr);
  619. }
  620. }
  621. if (!cpaddr && PAL_CB(parent_process)) {
  622. RUN_INIT(init_newproc, &hdr);
  623. SAVE_PROFILE_INTERVAL_SET(child_created_in_new_process,
  624. hdr.create_time, begin_time);
  625. #ifdef PROFILE
  626. begin_create_time = hdr.begin_create_time;
  627. #endif
  628. if (hdr.checkpoint.hdr.size)
  629. RUN_INIT(do_migration, &hdr.checkpoint, &cpaddr);
  630. }
  631. if (cpaddr) {
  632. thread_start_event = DkNotificationEventCreate(PAL_FALSE);
  633. RUN_INIT(restore_checkpoint,
  634. &hdr.checkpoint.hdr, &hdr.checkpoint.mem,
  635. (ptr_t) cpaddr, 0);
  636. }
  637. if (PAL_CB(manifest_handle))
  638. RUN_INIT(init_manifest, PAL_CB(manifest_handle));
  639. RUN_INIT(init_mount_root);
  640. RUN_INIT(init_ipc);
  641. RUN_INIT(init_thread);
  642. RUN_INIT(init_mount);
  643. RUN_INIT(init_important_handles);
  644. RUN_INIT(init_async);
  645. RUN_INIT(init_stack, argv, envp, &argcp, &argp, &auxp, 0);
  646. RUN_INIT(init_loader);
  647. RUN_INIT(init_ipc_helper);
  648. RUN_INIT(init_signal);
  649. if (PAL_CB(parent_process)) {
  650. /* Notify the parent process */
  651. struct newproc_response res;
  652. res.child_vmid = cur_process.vmid;
  653. res.failure = 0;
  654. if (!DkStreamWrite(PAL_CB(parent_process), 0,
  655. sizeof(struct newproc_response),
  656. &res, NULL))
  657. shim_do_exit(-PAL_ERRNO);
  658. }
  659. debug("shim process initialized\n");
  660. #ifdef PROFILE
  661. if (begin_create_time)
  662. SAVE_PROFILE_INTERVAL_SINCE(child_total_migration_time,
  663. begin_create_time);
  664. #endif
  665. SAVE_PROFILE_INTERVAL_SET(pal_startup_time, 0, pal_control.startup_time);
  666. SAVE_PROFILE_INTERVAL_SET(pal_host_specific_startup_time, 0,
  667. pal_control.host_specific_startup_time);
  668. SAVE_PROFILE_INTERVAL_SET(pal_relocation_time, 0,
  669. pal_control.relocation_time);
  670. SAVE_PROFILE_INTERVAL_SET(pal_linking_time, 0, pal_control.linking_time);
  671. SAVE_PROFILE_INTERVAL_SET(pal_manifest_loading_time, 0,
  672. pal_control.manifest_loading_time);
  673. SAVE_PROFILE_INTERVAL_SET(pal_allocation_time, 0,
  674. pal_control.allocation_time);
  675. SAVE_PROFILE_INTERVAL_SET(pal_tail_startup_time, 0,
  676. pal_control.tail_startup_time);
  677. SAVE_PROFILE_INTERVAL_SET(pal_child_creation_time, 0,
  678. pal_control.child_creation_time);
  679. if (thread_start_event)
  680. DkEventSet(thread_start_event);
  681. shim_tcb_t * cur_tcb = shim_get_tls();
  682. struct shim_thread * cur_thread = (struct shim_thread *) cur_tcb->tp;
  683. if (cur_tcb->context.regs && cur_tcb->context.regs->rsp) {
  684. vdso_map_migrate();
  685. restore_context(&cur_tcb->context);
  686. }
  687. if (cur_thread->exec)
  688. execute_elf_object(cur_thread->exec, argcp, argp, auxp);
  689. shim_do_exit(0);
  690. }
  691. static int create_unique (int (*mkname) (char *, size_t, void *),
  692. int (*create) (const char *, void *),
  693. int (*output) (char *, size_t, const void *,
  694. struct shim_qstr *),
  695. char * name, size_t size, void * id, void * obj,
  696. struct shim_qstr * qstr)
  697. {
  698. int ret, len;
  699. while (1) {
  700. len = mkname(name, size, id);
  701. if (len < 0)
  702. return len;
  703. if ((ret = create(name, obj)) < 0)
  704. return ret;
  705. if (ret)
  706. continue;
  707. if (output)
  708. return output(name, size, id, qstr);
  709. if (qstr)
  710. qstrsetstr(qstr, name, len);
  711. return len;
  712. }
  713. }
  714. static int name_pipe_rand (char * uri, size_t size, void * id)
  715. {
  716. IDTYPE pipeid;
  717. size_t len;
  718. int ret = DkRandomBitsRead(&pipeid, sizeof(pipeid));
  719. if (ret < 0)
  720. return -convert_pal_errno(-ret);
  721. debug("creating pipe: pipe.srv:%u\n", pipeid);
  722. if ((len = snprintf(uri, size, "pipe.srv:%u", pipeid)) >= size)
  723. return -ERANGE;
  724. *((IDTYPE *)id) = pipeid;
  725. return len;
  726. }
  727. static int name_pipe_vmid (char * uri, size_t size, void * id)
  728. {
  729. IDTYPE pipeid = cur_process.vmid;
  730. size_t len;
  731. debug("creating pipe: pipe.srv:%u\n", pipeid);
  732. if ((len = snprintf(uri, size, "pipe.srv:%u", pipeid)) >= size)
  733. return -ERANGE;
  734. *((IDTYPE *)id) = pipeid;
  735. return len;
  736. }
  737. static int open_pipe (const char * uri, void * obj)
  738. {
  739. PAL_HANDLE pipe = DkStreamOpen(uri, 0, 0, 0, 0);
  740. if (!pipe)
  741. return PAL_NATIVE_ERRNO == PAL_ERROR_STREAMEXIST ? 1 :
  742. -PAL_ERRNO;
  743. if (obj)
  744. *((PAL_HANDLE *) obj) = pipe;
  745. else
  746. DkObjectClose(pipe);
  747. return 0;
  748. }
  749. static int pipe_addr (char * uri, size_t size, const void * id,
  750. struct shim_qstr * qstr)
  751. {
  752. IDTYPE pipeid = *((IDTYPE *) id);
  753. size_t len;
  754. if ((len = snprintf(uri, size, "pipe:%u", pipeid)) == size)
  755. return -ERANGE;
  756. if (qstr)
  757. qstrsetstr(qstr, uri, len);
  758. return len;
  759. }
  760. int create_pipe (IDTYPE * id, char * uri, size_t size, PAL_HANDLE * hdl,
  761. struct shim_qstr * qstr, bool use_vmid_for_name)
  762. {
  763. IDTYPE pipeid;
  764. int ret;
  765. if (use_vmid_for_name)
  766. ret = create_unique(&name_pipe_vmid, &open_pipe, &pipe_addr,
  767. uri, size, &pipeid, hdl, qstr);
  768. else
  769. ret = create_unique(&name_pipe_rand, &open_pipe, &pipe_addr,
  770. uri, size, &pipeid, hdl, qstr);
  771. if (ret > 0 && id)
  772. *id = pipeid;
  773. return ret;
  774. }
  775. static int name_path (char * path, size_t size, void * id)
  776. {
  777. unsigned int suffix;
  778. int prefix_len = strlen(path);
  779. size_t len;
  780. int ret = DkRandomBitsRead(&suffix, sizeof(suffix));
  781. if (ret < 0)
  782. return -convert_pal_errno(-ret);
  783. len = snprintf(path + prefix_len, size - prefix_len, "%08x", suffix);
  784. if (len == size)
  785. return -ERANGE;
  786. *((unsigned int *) id) = suffix;
  787. return prefix_len + len;
  788. }
  789. static int open_dir (const char * path, void * obj)
  790. {
  791. struct shim_handle * dir = NULL;
  792. if (obj) {
  793. dir = get_new_handle();
  794. if (!dir)
  795. return -ENOMEM;
  796. }
  797. int ret = open_namei(dir, NULL, path, O_CREAT|O_EXCL|O_DIRECTORY, 0700,
  798. NULL);
  799. if (ret < 0)
  800. return ret = -EEXIST ? 1 : ret;
  801. if (obj)
  802. *((struct shim_handle **) obj) = dir;
  803. return 0;
  804. }
  805. static int open_file (const char * path, void * obj)
  806. {
  807. struct shim_handle * file = NULL;
  808. if (obj) {
  809. file = get_new_handle();
  810. if (!file)
  811. return -ENOMEM;
  812. }
  813. int ret = open_namei(file, NULL, path, O_CREAT|O_EXCL|O_RDWR, 0600,
  814. NULL);
  815. if (ret < 0)
  816. return ret = -EEXIST ? 1 : ret;
  817. if (obj)
  818. *((struct shim_handle **) obj) = file;
  819. return 0;
  820. }
  821. static int open_pal_handle (const char * uri, void * obj)
  822. {
  823. PAL_HANDLE hdl;
  824. if (strstartswith_static(uri, "dev:"))
  825. hdl = DkStreamOpen(uri, 0,
  826. PAL_SHARE_OWNER_X|PAL_SHARE_OWNER_W|
  827. PAL_SHARE_OWNER_R,
  828. PAL_CREATE_TRY|PAL_CREATE_ALWAYS,
  829. 0);
  830. else
  831. hdl = DkStreamOpen(uri, PAL_ACCESS_RDWR,
  832. PAL_SHARE_OWNER_W|PAL_SHARE_OWNER_R,
  833. PAL_CREATE_TRY|PAL_CREATE_ALWAYS,
  834. 0);
  835. if (!hdl) {
  836. if (PAL_NATIVE_ERRNO == PAL_ERROR_STREAMEXIST)
  837. return 0;
  838. else
  839. return -PAL_ERRNO;
  840. }
  841. if (obj) {
  842. *((PAL_HANDLE *) obj) = hdl;
  843. } else {
  844. DkObjectClose(hdl);
  845. }
  846. return 0;
  847. }
  848. static int output_path (char * path, size_t size, const void * id,
  849. struct shim_qstr * qstr)
  850. {
  851. size_t len = strlen(path);
  852. // API compatibility
  853. __UNUSED(size);
  854. __UNUSED(id);
  855. if (qstr)
  856. qstrsetstr(qstr, path, len);
  857. return len;
  858. }
  859. int create_dir (const char * prefix, char * path, size_t size,
  860. struct shim_handle ** hdl)
  861. {
  862. unsigned int suffix;
  863. if (prefix) {
  864. size_t len = strlen(prefix);
  865. if (len >= size)
  866. return -ERANGE;
  867. memcpy(path, prefix, len + 1);
  868. }
  869. return create_unique(&name_path, &open_dir, &output_path, path, size,
  870. &suffix, hdl, NULL);
  871. }
  872. int create_file (const char * prefix, char * path, size_t size,
  873. struct shim_handle ** hdl)
  874. {
  875. unsigned int suffix;
  876. if (prefix) {
  877. size_t len = strlen(prefix);
  878. if (len >= size)
  879. return -ERANGE;
  880. memcpy(path, prefix, len + 1);
  881. }
  882. return create_unique(&name_path, &open_file, &output_path, path, size,
  883. &suffix, hdl, NULL);
  884. }
  885. int create_handle (const char * prefix, char * uri, size_t size,
  886. PAL_HANDLE * hdl, unsigned int * id)
  887. {
  888. unsigned int suffix;
  889. if (prefix) {
  890. size_t len = strlen(prefix);
  891. if (len >= size)
  892. return -ERANGE;
  893. memcpy(uri, prefix, len + 1);
  894. }
  895. return create_unique(&name_path, &open_pal_handle, &output_path, uri, size,
  896. id ? : &suffix, hdl, NULL);
  897. }
  898. void check_stack_hook (void)
  899. {
  900. struct shim_thread * cur_thread = get_cur_thread();
  901. void * rsp;
  902. __asm__ volatile ("movq %%rsp, %0" : "=r"(rsp) :: "memory");
  903. if (rsp <= cur_thread->stack_top && rsp > cur_thread->stack) {
  904. if ((uintptr_t) rsp - (uintptr_t) cur_thread->stack < PAL_CB(pagesize))
  905. SYS_PRINTF("*** stack is almost drained (RSP = %p, stack = %p-%p) ***\n",
  906. rsp, cur_thread->stack, cur_thread->stack_top);
  907. } else {
  908. SYS_PRINTF("*** context dismatched with thread stack (RSP = %p, stack = %p-%p) ***\n",
  909. rsp, cur_thread->stack, cur_thread->stack_top);
  910. }
  911. }
  912. #ifdef PROFILE
  913. static void print_profile_result (PAL_HANDLE hdl, struct shim_profile * root,
  914. int level)
  915. {
  916. unsigned long total_interval_time = 0;
  917. unsigned long total_interval_count = 0;
  918. for (size_t i = 0 ; i < N_PROFILE ; i++) {
  919. struct shim_profile * profile = &PROFILES[i];
  920. if (profile->root != root || profile->disabled)
  921. continue;
  922. switch (profile->type) {
  923. case OCCURENCE: {
  924. unsigned int count =
  925. atomic_read(&profile->val.occurence.count);
  926. if (count) {
  927. for (int j = 0 ; j < level ; j++)
  928. __SYS_FPRINTF(hdl, " ");
  929. __SYS_FPRINTF(hdl, "- %s: %u times\n", profile->name, count);
  930. }
  931. break;
  932. }
  933. case INTERVAL: {
  934. unsigned int count =
  935. atomic_read(&profile->val.interval.count);
  936. if (count) {
  937. unsigned long time =
  938. atomic_read(&profile->val.interval.time);
  939. unsigned long ind_time = time / count;
  940. total_interval_time += time;
  941. total_interval_count += count;
  942. for (int j = 0 ; j < level ; j++)
  943. __SYS_FPRINTF(hdl, " ");
  944. __SYS_FPRINTF(hdl, "- (%11.11lu) %s: %u times, %lu msec\n",
  945. time, profile->name, count, ind_time);
  946. }
  947. break;
  948. }
  949. case CATEGORY:
  950. for (int j = 0 ; j < level ; j++)
  951. __SYS_FPRINTF(hdl, " ");
  952. __SYS_FPRINTF(hdl, "- %s:\n", profile->name);
  953. print_profile_result(hdl, profile, level + 1);
  954. break;
  955. }
  956. }
  957. if (total_interval_count) {
  958. __SYS_FPRINTF(hdl, " - (%11.11lu) total: %lu times, %lu msec\n",
  959. total_interval_time, total_interval_count,
  960. total_interval_time / total_interval_count);
  961. }
  962. }
  963. #endif /* PROFILE */
  964. static struct atomic_int in_terminate = { .counter = 0, };
  965. noreturn void shim_terminate (int err)
  966. {
  967. debug("teminating the whole process (%d)\n", err);
  968. /* do last clean-up of the process */
  969. shim_clean(err);
  970. DkProcessExit(err);
  971. }
  972. /* cleanup and terminate process, preserve exit code if err == 0 */
  973. int shim_clean (int err)
  974. {
  975. /* preventing multiple cleanup, this is mostly caused by
  976. assertion in shim_clean */
  977. if (atomic_inc_return(&in_terminate) > 1)
  978. return 0;
  979. if (err != 0)
  980. cur_process.exit_code = err;
  981. store_all_msg_persist();
  982. #ifdef PROFILE
  983. if (ENTER_TIME) {
  984. switch (shim_get_tls()->context.orig_rax) {
  985. case __NR_exit_group:
  986. SAVE_PROFILE_INTERVAL_SINCE(syscall_exit_group, ENTER_TIME);
  987. break;
  988. case __NR_exit:
  989. SAVE_PROFILE_INTERVAL_SINCE(syscall_exit, ENTER_TIME);
  990. break;
  991. }
  992. }
  993. if (ipc_cld_profile_send()) {
  994. MASTER_LOCK();
  995. PAL_HANDLE hdl = __open_shim_stdio();
  996. if (hdl) {
  997. __SYS_FPRINTF(hdl, "******************************\n");
  998. __SYS_FPRINTF(hdl, "profiling:\n");
  999. print_profile_result(hdl, &profile_root, 0);
  1000. __SYS_FPRINTF(hdl, "******************************\n");
  1001. }
  1002. MASTER_UNLOCK();
  1003. DkObjectClose(hdl);
  1004. }
  1005. #endif
  1006. del_all_ipc_ports();
  1007. if (shim_stdio && shim_stdio != (PAL_HANDLE) -1)
  1008. DkObjectClose(shim_stdio);
  1009. shim_stdio = NULL;
  1010. debug("process %u exited with status %d\n", cur_process.vmid & 0xFFFF, cur_process.exit_code);
  1011. MASTER_LOCK();
  1012. DkProcessExit(cur_process.exit_code);
  1013. return 0;
  1014. }
  1015. int message_confirm (const char * message, const char * options)
  1016. {
  1017. char answer;
  1018. int noptions = strlen(options);
  1019. char * option_str = __alloca(noptions * 2 + 3), * str = option_str;
  1020. int ret = 0;
  1021. *(str++) = ' ';
  1022. *(str++) = '[';
  1023. for (int i = 0 ; i < noptions ; i++) {
  1024. *(str++) = options[i];
  1025. *(str++) = '/';
  1026. }
  1027. str--;
  1028. *(str++) = ']';
  1029. *(str++) = ' ';
  1030. MASTER_LOCK();
  1031. PAL_HANDLE hdl = __open_shim_stdio();
  1032. if (!hdl) {
  1033. MASTER_UNLOCK();
  1034. return -EACCES;
  1035. }
  1036. #define WRITE(buf, len) \
  1037. ({ int _ret = DkStreamWrite(hdl, 0, len, (void*)(buf), NULL); \
  1038. _ret ? : -PAL_ERRNO; })
  1039. #define READ(buf, len) \
  1040. ({ int _ret = DkStreamRead(hdl, 0, len, buf, NULL, 0); \
  1041. _ret ? : -PAL_ERRNO; })
  1042. if ((ret = WRITE(message, strlen(message))) < 0)
  1043. goto out;
  1044. if ((ret = WRITE(option_str, noptions * 2 + 3)) < 0)
  1045. goto out;
  1046. if ((ret = READ(&answer, 1)) < 0)
  1047. goto out;
  1048. out:
  1049. DkObjectClose(hdl);
  1050. MASTER_UNLOCK();
  1051. return (ret < 0) ? ret : answer;
  1052. }