/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- mtcp_strtol
- mtcp_strlen
- mtcp_strncpy
- mtcp_strcpy
- mtcp_strncat
- mtcp_strncmp
- mtcp_strcmp
- mtcp_strstr
- mtcp_strchr
- mtcp_strstartswith
- mtcp_strendswith
- mtcp_memset
- mtcp_memcpy
- mtcp_read_all
- mtcp_mkdir
- mtcp_readfile
- mtcp_skipfile
- mtcp_write_all
- mtcp_readchar
- mtcp_readdec
- mtcp_readhex
- mtcp_readmapsline
- mtcp_get_memory_region_of_this_library
- rwrite
- mtcp_printf
1 /*****************************************************************************
2 * Copyright (C) 2006-2009 Michael Rieker <mrieker@nii.net> *
3 * Copyright (C) 2010-2014 Kapil Arya <kapil@ccs.neu.edu> *
4 * Copyright (C) 2010-2014 Gene Cooperman <gene@ccs.neu.edu> *
5 * *
6 * DMTCP is free software: you can redistribute it and/or *
7 * modify it under the terms of the GNU Lesser General Public License as *
8 * published by the Free Software Foundation, either version 3 of the *
9 * License, or (at your option) any later version. *
10 * *
11 * DMTCP is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU Lesser General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with DMTCP. If not, see <http://www.gnu.org/licenses/>. *
18 *****************************************************************************/
19
20 /*****************************************************************************
21 *
22 * Read from file without using any external memory routines (like malloc,
23 * fget, etc)
24 *
25 *
26 *****************************************************************************/
27
28 #include <unistd.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <sys/sysmacros.h>
32 #include <limits.h>
33
34 #include "mtcp_util.h"
35 #include "../membarrier.h"
36
37 unsigned long mtcp_strtol (char *str)
38 {
39 int mtcp_sys_errno=0;
40 unsigned long int v = 0;
41 int base = 10;
42 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
43 str += 2;
44 base = 16;
45 } else if (str[0] == '0') {
46 str += 1;
47 base = 8;
48 } else {
49 base = 10;
50 }
51
52 while (*str != '\0') {
53 int c;
54 if ((*str >= '0') && (*str <= '9')) c = *str - '0';
55 else if ((*str >= 'a') && (*str <= 'f')) c = *str + 10 - 'a';
56 else if ((*str >= 'A') && (*str <= 'F')) c = *str + 10 - 'A';
57 else {
58 MTCP_PRINTF("Error converting str to int\n");
59 mtcp_abort();
60 }
61 MTCP_ASSERT(c < base);
62 v = v * base + c;
63 str++;
64 }
65 return v;
66 }
67
68 size_t mtcp_strlen(const char *s)
69 {
70 size_t len = 0;
71 while (*s++ != '\0') {
72 len++;
73 }
74 return len;
75 }
76
77 void mtcp_strncpy(char *dest, const char *src, size_t n)
78 {
79 size_t i;
80
81 for (i = 0; i < n && src[i] != '\0'; i++)
82 dest[i] = src[i];
83 if (i < n) {
84 dest[i] = '\0';
85 }
86
87 //return dest;
88 }
89
90 void mtcp_strcpy(char *dest, const char *src)
91 {
92 while (*src != '\0') {
93 *dest++ = *src++;
94 }
95 }
96
97 void mtcp_strncat(char *dest, const char *src, size_t n)
98 {
99 mtcp_strncpy(dest + mtcp_strlen(dest), src, n);
100 //return dest;
101 }
102
103 int mtcp_strncmp (const char *s1, const char *s2, size_t n)
104 {
105 unsigned char c1 = '\0';
106 unsigned char c2 = '\0';
107
108 while (n > 0) {
109 c1 = (unsigned char) *s1++;
110 c2 = (unsigned char) *s2++;
111 if (c1 == '\0' || c1 != c2)
112 return c1 - c2;
113 n--;
114 }
115 return c1 - c2;
116 }
117
118 int mtcp_strcmp (const char *s1, const char *s2)
119 {
120 size_t n = mtcp_strlen(s2);
121 unsigned char c1 = '\0';
122 unsigned char c2 = '\0';
123
124 while (n > 0) {
125 c1 = (unsigned char) *s1++;
126 c2 = (unsigned char) *s2++;
127 if (c1 == '\0' || c1 != c2)
128 return c1 - c2;
129 n--;
130 }
131 return c1 - c2;
132 }
133
134 const void *mtcp_strstr(const char *string, const char *substring)
135 {
136 for ( ; *string != '\0' ; string++) {
137 const char *ptr1, *ptr2;
138 for (ptr1 = string, ptr2 = substring;
139 *ptr1 == *ptr2 && *ptr2 != '\0';
140 ptr1++, ptr2++) ;
141 if (*ptr2 == '\0')
142 return string;
143 }
144 return NULL;
145 }
146
147 // The strchr() function from earlier C library returns a ptr to the first
148 // occurrence of c (converted to a char) in string s, or a
149 // null pointer if c does not occur in the string.
150 char *mtcp_strchr(const char *s, int c) {
151 for (; *s != (char)'\0'; s++)
152 if (*s == (char)c)
153 return (char *)s;
154 return NULL;
155 }
156
157 int mtcp_strstartswith (const char *s1, const char *s2)
158 {
159 if (mtcp_strlen(s1) >= mtcp_strlen(s2)) {
160 return mtcp_strncmp(s1, s2, mtcp_strlen(s2)) == 0;
161 }
162 return 0;
163 }
164
165 int mtcp_strendswith (const char *s1, const char *s2)
166 {
167 size_t len1 = mtcp_strlen(s1);
168 size_t len2 = mtcp_strlen(s2);
169
170 if (len1 < len2)
171 return 0;
172
173 s1 += (len1 - len2);
174
175 return mtcp_strncmp(s1, s2, len2) == 0;
176 }
177
178 void *mtcp_memset(void *s, int c, size_t n)
179 {
180 char *p = s;
181 while (n-- > 0) {
182 *p++ = (char)c;
183 }
184 return s;
185 }
186
187 void *mtcp_memcpy(void *dstpp, const void *srcpp, size_t len)
188 {
189 char *dst = (char*) dstpp;
190 const char *src = (const char*) srcpp;
191 while (len > 0) {
192 *dst++ = *src++;
193 len--;
194 }
195 return dstpp;
196 }
197
198 ssize_t mtcp_read_all(int fd, void *buf, size_t count)
199 {
200 int mtcp_sys_errno;
201 int rc;
202 char *ptr = (char *) buf;
203 size_t num_read = 0;
204 for (num_read = 0; num_read < count;) {
205 rc = mtcp_sys_read (fd, ptr + num_read, count - num_read);
206 if (rc == -1) {
207 if (mtcp_sys_errno == EINTR || mtcp_sys_errno == EAGAIN)
208 continue;
209 else
210 return -1;
211 }
212 else if (rc == 0)
213 break;
214 else // else rc > 0
215 num_read += rc;
216 }
217 return num_read;
218 }
219
220 void mtcp_mkdir(const char *dir)
221 {
222 int mtcp_sys_errno;
223 char tmp[PATH_MAX];
224 size_t i;
225 size_t len = mtcp_strlen(dir);
226 MTCP_ASSERT(len < PATH_MAX);
227
228 mtcp_memset(tmp, 0, sizeof(tmp));
229 mtcp_strcpy(tmp, dir);
230
231 while (tmp[len - 1] == '/') {
232 tmp[len - 1] = 0;
233 len--;
234 }
235
236 for(i = 1; i < len; i++) {
237 if(tmp[i] == '/') {
238 tmp[i] = '\0';
239 mtcp_sys_mkdir(tmp, S_IRWXU);
240 tmp[i] = '/';
241 }
242 }
243 mtcp_sys_mkdir(tmp, S_IRWXU);
244 }
245
246 int mtcp_readfile(int fd, void *buf, size_t size)
247 {
248 int mtcp_sys_errno;
249 ssize_t rc;
250 size_t ar = 0;
251 int tries = 0;
252
253 #if __arm__ || __aarch64__
254 /* ARM requires DMB instruction to ensure that any store to memory
255 * by a prior kernel mmap call has completed.
256 * SEE ARM Information Center article:
257 * "In what siutations might I need to insert memory barrier instructions?"
258 * (and especially section on "Memory Remapping"
259 */
260 WMB;
261 #endif
262
263 while(ar != size) {
264 rc = mtcp_sys_read(fd, buf + ar, size - ar);
265 if (rc < 0 && rc > -4096) { /* kernel could return large unsigned int */
266 if (rc == -1 && (mtcp_sys_errno == EAGAIN || mtcp_sys_errno == EINTR)) {
267 tries++;
268 if (tries++ >= 10) {
269 MTCP_PRINTF(" failed to read after 10 tries in a row.\n");
270 mtcp_abort();
271 }
272 continue;
273 } else { /* else error: rc < 0 and not EAGAIN and not EINTR */
274 MTCP_PRINTF("error %d reading checkpoint\n", mtcp_sys_errno);
275 MTCP_PRINTF("only read %u bytes instead of %u from checkpoint file\n",
276 (unsigned)ar, (unsigned)size);
277 mtcp_abort();
278 }
279 }
280 else if (rc == 0) { /* if end of file */
281 return 0; /* success: end of file */
282 }
283 ar += rc;
284 }
285 #if __arm__ || __aarch64__
286 /* ARM requires DSB and ISB instructions to ensure that prior read
287 * instructions complete, and prevent instructions being fetched prior to this.
288 * SEE ARM Information Center article:
289 * "In what siutations might I need to insert memory barrier instructions?"
290 * (and especially section on "Memory Remapping"
291 */
292 WMB;
293 IMB;
294 #endif
295 return ar; /* read ar characters */
296 }
297
298 // FIXME: Equiv. to mtcp_sys_lseek(fd, size, SEEK_CUR), but doesn't work
299 // on sockets. Why not just call mtcp_readfile(fd, tmp_addr, size)?
300 void mtcp_skipfile(int fd, size_t size)
301 {
302 int mtcp_sys_errno;
303 VA tmp_addr = mtcp_sys_mmap(0, size, PROT_WRITE | PROT_READ,
304 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
305 if (tmp_addr == MAP_FAILED) {
306 MTCP_PRINTF("mtcp_sys_mmap() failed with error: %d", mtcp_sys_errno);
307 mtcp_abort();
308 }
309 mtcp_readfile(fd, tmp_addr, size);
310 if (mtcp_sys_munmap(tmp_addr, size) == -1) {
311 MTCP_PRINTF("mtcp_sys_munmap() failed with error: %d", mtcp_sys_errno);
312 mtcp_abort();
313 }
314 }
315
316 // NOTE: This functions is called by mtcp_printf() so do not invoke
317 // mtcp_printf() from within this function.
318 ssize_t mtcp_write_all(int fd, const void *buf, size_t count)
319 {
320 int mtcp_sys_errno;
321 const char *ptr = (const char *) buf;
322 size_t num_written = 0;
323
324 do {
325 ssize_t rc = mtcp_sys_write (fd, ptr + num_written, count - num_written);
326 if (rc == -1) {
327 if (mtcp_sys_errno == EINTR || mtcp_sys_errno == EAGAIN)
328 continue;
329 else
330 return rc;
331 }
332 else if (rc == 0)
333 break;
334 else // else rc > 0
335 num_written += rc;
336 } while (num_written < count);
337 return num_written;
338 }
339
340 /* Read non-null character, return null if EOF */
341 char mtcp_readchar (int fd)
342 {
343 int mtcp_sys_errno;
344 char c;
345 int rc;
346
347 do {
348 rc = mtcp_sys_read (fd, &c, 1);
349 } while ( rc == -1 && mtcp_sys_errno == EINTR );
350 if (rc <= 0) return (0);
351 return (c);
352 }
353
354 /* Read decimal number, return value and terminating character */
355 char mtcp_readdec (int fd, VA *value)
356 {
357 char c;
358 unsigned long int v;
359
360 v = 0;
361 while (1) {
362 c = mtcp_readchar (fd);
363 if ((c >= '0') && (c <= '9')) c -= '0';
364 else break;
365 v = v * 10 + c;
366 }
367 *value = (VA)v;
368 return (c);
369 }
370
371 /* Read decimal number, return value and terminating character */
372 char mtcp_readhex (int fd, VA *value)
373 {
374 char c;
375 unsigned long int v;
376
377 v = 0;
378 while (1) {
379 c = mtcp_readchar (fd);
380 if ((c >= '0') && (c <= '9')) c -= '0';
381 else if ((c >= 'a') && (c <= 'f')) c -= 'a' - 10;
382 else if ((c >= 'A') && (c <= 'F')) c -= 'A' - 10;
383 else break;
384 v = v * 16 + c;
385 }
386 *value = (VA)v;
387 return (c);
388 }
389
390 /*****************************************************************************
391 *
392 * Read /proc/self/maps line, converting it to an Area descriptor struct
393 * Input:
394 * mapsfd = /proc/self/maps file, positioned to beginning of a line
395 * Output:
396 * mtcp_readmapsline = 0 : was at end-of-file, nothing read
397 * *area = filled in
398 * Note:
399 * Line from /procs/self/maps is in form:
400 * <startaddr>-<endaddrexclusive> rwxs <fileoffset> <devmaj>:<devmin>
401 * <inode> <filename>\n
402 * all numbers in hexadecimal except inode is in decimal
403 * anonymous will be shown with offset=devmaj=devmin=inode=0 and
404 * no ' filename'
405 *
406 *****************************************************************************/
407
408 int mtcp_readmapsline (int mapsfd, Area *area)
409 {
410 int mtcp_sys_errno __attribute__((unused));
411 char c, rflag, sflag, wflag, xflag;
412 int i;
413 off_t offset;
414 unsigned int long devmajor, devminor, inodenum;
415 VA startaddr, endaddr;
416
417 c = mtcp_readhex (mapsfd, &startaddr);
418 if (c != '-') {
419 if ((c == 0) && (startaddr == 0)) return (0);
420 goto skipeol;
421 }
422 c = mtcp_readhex (mapsfd, &endaddr);
423 if (c != ' ') goto skipeol;
424 if (endaddr < startaddr) goto skipeol;
425
426 rflag = c = mtcp_readchar (mapsfd);
427 if ((c != 'r') && (c != '-')) goto skipeol;
428 wflag = c = mtcp_readchar (mapsfd);
429 if ((c != 'w') && (c != '-')) goto skipeol;
430 xflag = c = mtcp_readchar (mapsfd);
431 if ((c != 'x') && (c != '-')) goto skipeol;
432 sflag = c = mtcp_readchar (mapsfd);
433 if ((c != 's') && (c != 'p')) goto skipeol;
434
435 c = mtcp_readchar (mapsfd);
436 if (c != ' ') goto skipeol;
437
438 c = mtcp_readhex (mapsfd, (VA *)&offset);
439 if (c != ' ') goto skipeol;
440 area -> offset = offset;
441
442 c = mtcp_readhex (mapsfd, (VA *)&devmajor);
443 if (c != ':') goto skipeol;
444 c = mtcp_readhex (mapsfd, (VA *)&devminor);
445 if (c != ' ') goto skipeol;
446 c = mtcp_readdec (mapsfd, (VA *)&inodenum);
447 area -> name[0] = '\0';
448 while (c == ' ') c = mtcp_readchar (mapsfd);
449 if (c == '/' || c == '[') { /* absolute pathname, or [stack], [vdso], etc. */
450 i = 0;
451 do {
452 area -> name[i++] = c;
453 if (i == sizeof area -> name) goto skipeol;
454 c = mtcp_readchar (mapsfd);
455 } while (c != '\n');
456 area -> name[i] = '\0';
457 }
458
459 if (c != '\n') goto skipeol;
460
461 area -> addr = startaddr;
462 area -> endAddr = endaddr;
463 area -> size = endaddr - startaddr;
464 area -> prot = 0;
465 if (rflag == 'r') area -> prot |= PROT_READ;
466 if (wflag == 'w') area -> prot |= PROT_WRITE;
467 if (xflag == 'x') area -> prot |= PROT_EXEC;
468 area -> flags = MAP_FIXED;
469 if (sflag == 's') area -> flags |= MAP_SHARED;
470 if (sflag == 'p') area -> flags |= MAP_PRIVATE;
471 if (area -> name[0] == '\0') area -> flags |= MAP_ANONYMOUS;
472
473 area->devmajor = devmajor;
474 area->devminor = devminor;
475 area->inodenum = inodenum;
476 return (1);
477
478 skipeol:
479 DPRINTF("ERROR: mtcp readmapsline*: bad maps line <%c", c);
480 while ((c != '\n') && (c != '\0')) {
481 c = mtcp_readchar (mapsfd);
482 mtcp_printf ("%c", c);
483 }
484 mtcp_printf (">\n");
485 mtcp_abort ();
486 return (0); /* NOTREACHED : stop compiler warning */
487 }
488
489 /*****************************************************************************
490 * Discover the memory occupied by this library (libmtcp.so)
491 *
492 * This is used to find: mtcp_shareable_begin mtcp_shareable_end
493 * The standard way is to modifiy the linker script (mtcp.t in Makefile).
494 * The method here works by looking at /proc/PID/maps
495 * However, this is error-prone. It assumes that the kernel labels
496 * all memory regions of this library with the library filename,
497 * except for a single memory region for static vars in lib. The
498 * latter case is handled by assuming a single region adjacent to
499 * to the labelled regions, and occuring after the labelled regions.
500 * This assumes that all of these memory regions form a contiguous region.
501 * We optionally call this only because Fedora uses eu-strip in rpmlint,
502 * and eu-strip modifies libmtcp.so in a way that libmtcp.so no longer works.
503 * This is arguably a bug in eu-strip.
504 *****************************************************************************/
505 // static int dummy_uninitialized_static_var;
506 void mtcp_get_memory_region_of_this_library(VA *startaddr, VA *endaddr)
507 {
508 int mtcp_sys_errno;
509 ino_t lib_inode;
510 struct {
511 VA start_addr;
512 VA end_addr;
513 } text, guard, rodata, rwdata, bssdata;
514
515 Area area;
516 VA thislib_fnc = (void*) &mtcp_get_memory_region_of_this_library;
517 // VA thislib_static_var = (VA) &dummy_uninitialized_static_var;
518 char filename[PATH_MAX] = {0};
519 text.start_addr = guard.start_addr = rodata.start_addr = NULL;
520 rwdata.start_addr = bssdata.start_addr = bssdata.end_addr = NULL;
521 int mapsfd = mtcp_sys_open("/proc/self/maps", O_RDONLY, 0);
522 MTCP_ASSERT(mapsfd != -1);
523
524 while (mtcp_readmapsline (mapsfd, &area)) {
525 VA start_addr = area.addr;
526 VA end_addr = area.addr + area.size;
527
528 if (thislib_fnc >= start_addr && thislib_fnc < end_addr) {
529 MTCP_ASSERT(text.start_addr == NULL);
530 text.start_addr = start_addr; text.end_addr = end_addr;
531 mtcp_strcpy(filename, area.name);
532 lib_inode = area.inodenum;
533 continue;
534 }
535
536 if (text.start_addr != NULL && guard.start_addr == NULL &&
537 area.inodenum == lib_inode) {
538 MTCP_ASSERT(mtcp_strcmp(filename, area.name) == 0);
539 MTCP_ASSERT(area.addr == text.end_addr);
540 if (area.prot == 0) {
541 /* The guard pages are unreadable due to the "---p" protection. Even if
542 * the protection is changed to "r--p", a read will result in a SIGSEGV
543 * as the pages are not backed by the kernel. A better way to handle
544 * this is to remap these pages with anonymous memory.
545 */
546 MTCP_ASSERT(mtcp_sys_mmap(start_addr, area.size, PROT_READ,
547 MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED,
548 -1, 0) == start_addr);
549 guard.start_addr = start_addr; guard.end_addr = end_addr;
550 continue;
551 } else {
552 // No guard pages found. This is probably the ROData section.
553 guard.start_addr = start_addr; guard.end_addr = start_addr;
554 }
555 }
556
557 if (guard.start_addr != NULL && rodata.start_addr == NULL &&
558 area.inodenum == lib_inode) {
559 MTCP_ASSERT(mtcp_strcmp(filename, area.name) == 0);
560 MTCP_ASSERT(area.addr == guard.end_addr);
561 if (area.prot == PROT_READ ||
562 // On some systems, all sections of the library have exec
563 // permissions.
564 area.prot == (PROT_READ|PROT_EXEC)) {
565 rodata.start_addr = start_addr; rodata.end_addr = end_addr;
566 continue;
567 } else {
568 // No ROData section. This is probably the RWData section.
569 rodata.start_addr = start_addr; rodata.end_addr = start_addr;
570 }
571 }
572
573 if (rodata.start_addr != NULL && rwdata.start_addr == NULL &&
574 area.inodenum == lib_inode) {
575 MTCP_ASSERT(mtcp_strcmp(filename, area.name) == 0);
576 MTCP_ASSERT(area.addr == rodata.end_addr);
577 MTCP_ASSERT(area.prot == (PROT_READ|PROT_WRITE) ||
578 // On some systems, all sections of the library have exec
579 // permissions.
580 area.prot == (PROT_READ|PROT_WRITE|PROT_EXEC));
581 rwdata.start_addr = start_addr; rwdata.end_addr = end_addr;
582 continue;
583 }
584
585 if (rwdata.start_addr != NULL && bssdata.start_addr == NULL &&
586 area.name[0] == '\0') {
587 /* /proc/PID/maps does not label the filename for memory region holding
588 * static variables in a library. But that is also part of this
589 * library (libmtcp.so).
590 * So, find the meory region for static memory variables and add it.
591 */
592 MTCP_ASSERT(area.addr == rwdata.end_addr);
593 MTCP_ASSERT(area.prot == (PROT_READ|PROT_WRITE) ||
594 // On some systems, all sections of the library have exec
595 // permissions.
596 area.prot == (PROT_READ|PROT_WRITE|PROT_EXEC));
597 //MTCP_ASSERT(thislib_static_var >= start_addr &&
598 //thislib_static_var < end_addr);
599 bssdata.start_addr = start_addr; bssdata.end_addr = end_addr;
600 break;
601 }
602 }
603 mtcp_sys_close(mapsfd);
604
605 MTCP_ASSERT(text.start_addr != NULL);
606 *startaddr = text.start_addr;
607
608 if (bssdata.end_addr != NULL) {
609 *endaddr = bssdata.end_addr;
610 } else if (rwdata.end_addr != NULL) {
611 *endaddr = rwdata.end_addr;
612 } else if (rodata.end_addr != NULL) {
613 *endaddr = rodata.end_addr;
614 } else {
615 MTCP_PRINTF("Not implemented.\n");
616 mtcp_abort();
617 }
618 }
619
620 /*****************************************************************************
621 * Print on stderr without using any malloc stuff
622 *
623 * We can't use vsnprintf or anything like that as it calls malloc.
624 * This routine supports only simple %c, %d, %o, %p, %s, %u, %x (or %X)
625 *****************************************************************************/
626
627 static void rwrite (char const *buff, int size) {
628 mtcp_write_all(2, buff, size);
629 }
630
631 void mtcp_printf (char const *format, ...)
632 {
633 char hexdigits[] = "0123456789abcdef";
634 char const *p, *q;
635 va_list ap;
636
637 va_start (ap, format);
638
639 /* Scan along until we find a % */
640
641 for (p = format; (q = mtcp_strchr (p, '%')) != NULL; p = ++ q) {
642
643 /* Print all before the % as is */
644
645 if (q > p) rwrite (p, q - p);
646
647 /* Process based on character following the % */
648
649 gofish:
650 switch (*(++ q)) {
651
652 /* Ignore digits (field width) */
653
654 case '0' ... '9': {
655 goto gofish;
656 }
657
658 /* Single character */
659
660 case 'c': {
661 char buff[4];
662
663 buff[0] = va_arg (ap, int); // va_arg (ap, char);
664 rwrite (buff, 1);
665 break;
666 }
667
668 /* Signed decimal integer */
669
670 case 'd': {
671 // On 64-bit machines the largest unsigned is 20 digits.
672 char buff[20];
673 int i, n, neg;
674
675 i = sizeof buff;
676 n = va_arg (ap, int);
677 neg = (n < 0);
678 if (neg) n = - n;
679 do {
680 buff[--i] = (n % 10) + '0';
681 n /= 10;
682 } while (n > 0);
683 if (neg) buff[--i] = '-';
684 rwrite (buff + i, sizeof buff - i);
685 break;
686 }
687
688 /* Unsigned octal number */
689
690 case 'o': {
691 // On 64-bit machines the largest unsigned is 22 digits.
692 char buff[24];
693 int i;
694 unsigned int n;
695
696 i = sizeof buff;
697 n = va_arg (ap, unsigned int);
698 do {
699 buff[--i] = (n & 7) + '0';
700 n /= 8;
701 } while (n > 0);
702 rwrite (buff + i, sizeof buff - i);
703 break;
704 }
705
706 /* Address in hexadecimal */
707
708 case 'p': {
709 // On 64-bit machines the largest unsigned is 16 digits.
710 char buff[18];
711 int i;
712 unsigned long int n;
713
714 i = sizeof buff;
715 n = (unsigned long int) va_arg (ap, void *);
716 do {
717 buff[--i] = hexdigits[n%16];
718 n /= 16;
719 } while (n > 0);
720 buff[--i] = 'x';
721 buff[--i] = '0';
722 rwrite (buff + i, sizeof buff - i);
723 break;
724 }
725
726 /* Null terminated string */
727
728 case 's': {
729 p = va_arg (ap, char *);
730 rwrite (p, mtcp_strlen (p));
731 break;
732 }
733
734 /* Unsigned decimal integer */
735
736 case 'u': {
737 // On 64-bit machines the largest unsigned is 20 digits.
738 char buff[18];
739 int i;
740 unsigned int n;
741
742 i = sizeof buff;
743 n = va_arg (ap, unsigned int);
744 do {
745 buff[--i] = (n % 10) + '0';
746 n /= 10;
747 } while (n > 0);
748 rwrite (buff + i, sizeof buff - i);
749 break;
750 }
751
752 /* Unsigned hexadecimal number */
753
754 case 'X':
755 case 'x': {
756 // On 64-bit machines the largest unsigned is 16 digits.
757 char buff[18];
758 int i;
759 unsigned int n;
760
761 i = sizeof buff;
762 n = va_arg (ap, unsigned int);
763 do {
764 buff[--i] = hexdigits[n%16];
765 n /= 16;
766 } while (n > 0);
767 rwrite (buff + i, sizeof buff - i);
768 break;
769 }
770
771 /* Anything else, print the character as is */
772
773 default: {
774 rwrite (q, 1);
775 break;
776 }
777 }
778 }
779
780 va_end (ap);
781
782 /* Print whatever comes after the last format spec */
783
784 rwrite (p, mtcp_strlen (p));
785 }