From 265435fdf8b39b564fefda2f0dd40f2668c3f607 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 18 Feb 2023 11:50:44 +0800 Subject: [PATCH] Update Android port * configure.ac: Check for madvise. * lisp/international/fontset.el (script-representative-chars): Improve detection of CJK fonts. * src/pdumper.c (dump_discard_mem): Use madvise if possible. * src/sfnt.c (sfnt_map_glyf_table, sfnt_unmap_glyf_table): New functions. * src/sfnt.h (struct sfnt_glyf_table): New field. * src/sfntfont.c (struct sfnt_font_info, sfntfont_open) (sfntfont_close, sfntfont_detect_sigbus): Allow mmapping fonts if possible. * src/sfntfont.h: Update prototypes. * src/sysdep.c (handle_sigbus, init_sigbus, init_signals): Initialize SIGBUS correctly. --- configure.ac | 4 +- lisp/international/fontset.el | 5 +- src/pdumper.c | 4 +- src/sfnt.c | 90 +++++++++++++++++++++++++ src/sfnt.h | 9 +++ src/sfntfont.c | 121 ++++++++++++++++++++++++++++++++-- src/sfntfont.h | 9 +++ src/sysdep.c | 43 +++++++++++- 8 files changed, 276 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index a4a16ec8062..dc6a81d9f4a 100644 --- a/configure.ac +++ b/configure.ac @@ -5722,8 +5722,8 @@ if test "$with_unexec" = yes && test "$opsys" = "haiku"; then Please use the portable dumper instead.]) fi -# Dump loading -AC_CHECK_FUNCS([posix_madvise]) +# Dump loading. Android lacks posix_madvise. +AC_CHECK_FUNCS([posix_madvise madvise]) dnl Cannot use AC_CHECK_FUNCS AC_CACHE_CHECK([for __builtin_frame_address], diff --git a/lisp/international/fontset.el b/lisp/international/fontset.el index 8fa67d99477..a60ce4c4df6 100644 --- a/lisp/international/fontset.el +++ b/lisp/international/fontset.el @@ -200,7 +200,10 @@ (symbol . [#x201C #x2200 #x2500]) (braille #x2800) (ideographic-description #x2FF0) - (cjk-misc #x300E #xff0c) + ;; Noto Sans Phags Pa is broken and reuses the CJK misc code + ;; points for some of its own characters. Add one actual CJK + ;; character to prevent finding such broken fonts. + (cjk-misc #x300E #xff0c #x300a #xff09 #x5b50) (kana #x304B) (bopomofo #x3105) (kanbun #x319D) diff --git a/src/pdumper.c b/src/pdumper.c index baa33c4f647..645661060c1 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -4747,7 +4747,9 @@ dump_discard_mem (void *mem, size_t size) # ifdef HAVE_POSIX_MADVISE /* Discard COWed pages. */ (void) posix_madvise (mem, size, POSIX_MADV_DONTNEED); -# endif +# elif defined HAVE_MADVISE + (void) madvise (mem, size, MADV_DONTNEED); +#endif /* Release the commit charge for the mapping. */ (void) mprotect (mem, size, PROT_NONE); #endif diff --git a/src/sfnt.c b/src/sfnt.c index b6f4a48ea8b..06985c74a57 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -34,6 +34,11 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include +#include + +#ifdef HAVE_MMAP +#include +#endif #if defined __GNUC__ && !defined __clang__ #pragma GCC diagnostic ignored "-Wstringop-overflow" @@ -1584,6 +1589,91 @@ sfnt_read_glyf_table (int fd, struct sfnt_offset_subtable *subtable) return glyf; } +#if defined HAVE_MMAP && !defined TEST + +/* Map a glyph table from the given font FD. Use the table directory + specified in SUBTABLE. The glyph data is not byte-swapped. + + Value is the glyf table upon success, else NULL. + A mapped glyf table must be unmapped using `sfnt_unmap_glyf_table'. + The caller must correctly handle bus errors in between glyf->table + and glyf->size. */ + +struct sfnt_glyf_table * +sfnt_map_glyf_table (int fd, struct sfnt_offset_subtable *subtable) +{ + struct sfnt_table_directory *directory; + struct sfnt_glyf_table *glyf; + void *glyphs; + size_t offset, page, map_offset; + + /* Find the table in the directory. */ + + directory = sfnt_find_table (subtable, SFNT_TABLE_GLYF); + + if (!directory) + return NULL; + + /* Now try to map the glyph data. Make sure offset is a multiple of + the page size. */ + + page = getpagesize (); + offset = directory->offset & ~(page - 1); + + /* Figure out how much larger the mapping should be. */ + map_offset = directory->offset - offset; + + /* Do the mmap. */ + glyphs = mmap (NULL, directory->length + map_offset, + PROT_READ, MAP_PRIVATE, fd, offset); + + if (glyphs == MAP_FAILED) + { + fprintf (stderr, "sfnt_map_glyf_table: mmap: %s\n", + strerror (errno)); + + return NULL; + } + + /* An observation is that glyphs tend to be accessed in sequential + order and immediately after the font's glyph table is loaded. */ + +#ifdef HAVE_POSIX_MADVISE + posix_madvise (glyphs, directory->length, + POSIX_MADV_WILLNEED); +#elif defined HAVE_MADVISE + madvise (glyphs, directory->length, MADV_WILLNEED); +#endif + + /* Allocate the glyf table. */ + glyf = xmalloc (sizeof *glyf); + glyf->size = directory->length; + glyf->glyphs = (unsigned char *) glyphs + map_offset; + glyf->start = glyphs; + return glyf; +} + +/* Unmap the mmap'ed glyf table GLYF, then free its associated data. + Value is 0 upon success, else 1, in which case GLYF is still freed + all the same. */ + +int +sfnt_unmap_glyf_table (struct sfnt_glyf_table *glyf) +{ + int rc; + size_t size; + + /* Calculate the size of the mapping. */ + size = glyf->size + (glyf->glyphs - glyf->start); + + rc = munmap (glyf->start, size); + xfree (glyf); + + return rc != 0; +} + +#endif /* HAVE_MMAP */ + /* Read the simple glyph outline from the glyph GLYPH from the specified glyf table at the given offset. Set GLYPH->simple to a non-NULL value upon success, else set it to NULL. */ diff --git a/src/sfnt.h b/src/sfnt.h index 40c77ee123f..4bf46b62397 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -524,6 +524,10 @@ struct sfnt_glyf_table /* Pointer to possibly unaligned glyph data. */ unsigned char *glyphs; + + /* Pointer to the start of the mapping. + Only initialized if this table was mmapped. */ + unsigned char *start; }; struct sfnt_simple_glyph @@ -951,6 +955,11 @@ extern struct sfnt_loca_table_short *sfnt_read_loca_table_short (PROTOTYPE); extern struct sfnt_loca_table_long *sfnt_read_loca_table_long (PROTOTYPE); extern struct sfnt_maxp_table *sfnt_read_maxp_table (PROTOTYPE); extern struct sfnt_glyf_table *sfnt_read_glyf_table (PROTOTYPE); + +#ifdef HAVE_MMAP +extern struct sfnt_glyf_table *sfnt_map_glyf_table (PROTOTYPE); +extern int sfnt_unmap_glyf_table (struct sfnt_glyf_table *); +#endif /* HAVE_MMAP */ #undef PROTOTYPE extern struct sfnt_glyph *sfnt_read_glyph (sfnt_glyph, struct sfnt_glyf_table *, diff --git a/src/sfntfont.c b/src/sfntfont.c index 20c109f2401..bb816fabd3e 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -1805,6 +1805,11 @@ struct sfnt_font_info /* Parent font structure. */ struct font font; +#ifdef HAVE_MMAP + /* The next font in this chain. */ + struct sfnt_font_info *next; +#endif /* HAVE_MMAP */ + /* Various tables required to use the font. */ struct sfnt_cmap_table *cmap; struct sfnt_hhea_table *hhea; @@ -1842,8 +1847,21 @@ struct sfnt_font_info /* Graphics state after the execution of the font and control value programs. */ struct sfnt_graphics_state state; + +#ifdef HAVE_MMAP + /* Whether or not the glyph table has been mmapped. */ + bool glyf_table_mapped; +#endif /* HAVE_MMAP */ }; +#ifdef HAVE_MMAP + +/* List of all open fonts. */ + +static struct sfnt_font_info *open_fonts; + +#endif /* HAVE_MMAP */ + /* Look up the glyph corresponding to the character C in FONT. Return 0 upon failure, and the glyph otherwise. */ @@ -2040,6 +2058,9 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, struct sfnt_font_desc *desc; Lisp_Object font_object; int fd, i; +#ifdef HAVE_MMAP + int rc; +#endif /* HAVE_MMAP */ struct sfnt_offset_subtable *subtable; struct sfnt_cmap_encoding_subtable *subtables; struct sfnt_cmap_encoding_subtable_data **data; @@ -2096,6 +2117,9 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, font_info->raster_cache.last = &font_info->raster_cache; font_info->raster_cache_size = 0; font_info->interpreter = NULL; +#ifdef HAVE_MMAP + font_info->glyf_table_mapped = false; +#endif /* HAVE_MMAP */ /* Open the font. */ fd = emacs_open (desc->path, O_RDONLY, 0); @@ -2145,7 +2169,24 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, /* Read the hhea, maxp, glyf, and head tables. */ font_info->hhea = sfnt_read_hhea_table (fd, subtable); font_info->maxp = sfnt_read_maxp_table (fd, subtable); - font_info->glyf = sfnt_read_glyf_table (fd, subtable); + +#ifdef HAVE_MMAP + + /* First try to map the glyf table. If that fails, then read the + glyf table. */ + + font_info->glyf = sfnt_map_glyf_table (fd, subtable); + + /* Next, if this fails, read the glyf table. */ + + if (!font_info->glyf) +#endif /* HAVE_MMAP */ + font_info->glyf = sfnt_read_glyf_table (fd, subtable); +#ifdef HAVE_MMAP + else + font_info->glyf_table_mapped = true; +#endif /* HAVE_MMAP */ + font_info->head = sfnt_read_head_table (fd, subtable); /* If any of those tables couldn't be read, bail. */ @@ -2266,6 +2307,12 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, /* Free the offset subtable. */ xfree (subtable); +#ifdef HAVE_MMAP + /* Link the font onto the font table. */ + font_info->next = open_fonts; + open_fonts = font_info; +#endif /* HAVE_MMAP */ + /* All done. */ unblock_input (); return font_object; @@ -2281,7 +2328,19 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, bail4: xfree (font_info->hhea); xfree (font_info->maxp); - xfree (font_info->glyf); + +#ifdef HAVE_MMAP + if (font_info->glyf_table_mapped) + { + rc = sfnt_unmap_glyf_table (font_info->glyf); + + if (rc) + emacs_abort (); + } + else +#endif /* HAVE_MMAP */ + xfree (font_info->glyf); + xfree (font_info->head); font_info->hhea = NULL; font_info->maxp = NULL; @@ -2473,7 +2532,10 @@ sfntfont_text_extents (struct font *font, const unsigned int *code, void sfntfont_close (struct font *font) { - struct sfnt_font_info *info; + struct sfnt_font_info *info, **next; +#ifdef HAVE_MMAP + int rc; +#endif /* HAVE_MMAP */ info = (struct sfnt_font_info *) font; xfree (info->cmap); @@ -2481,7 +2543,20 @@ sfntfont_close (struct font *font) xfree (info->maxp); xfree (info->head); xfree (info->hmtx); - xfree (info->glyf); + +#ifdef HAVE_MMAP + + if (info->glyf_table_mapped) + { + rc = sfnt_unmap_glyf_table (info->glyf); + + if (rc) + emacs_abort (); + } + else +#endif /* HAVE_MMAP */ + xfree (info->glyf); + xfree (info->loca_short); xfree (info->loca_long); xfree (info->cmap_data); @@ -2490,6 +2565,16 @@ sfntfont_close (struct font *font) xfree (info->cvt); xfree (info->interpreter); + /* Unlink INFO. */ + + next = &open_fonts; + while (*next && (*next) != info) + next = &(*next)->next; + + if (*next) + *next = info->next; + info->next = NULL; + sfntfont_free_outline_cache (&info->outline_cache); sfntfont_free_raster_cache (&info->raster_cache); } @@ -2627,6 +2712,34 @@ sfntfont_list_family (struct frame *f) +/* mmap specific stuff. */ + +#ifdef HAVE_MMAP + +/* Return whether or not ADDR lies in a mapped glyph, and bus faults + should be ignored. */ + +bool +sfntfont_detect_sigbus (void *addr) +{ + struct sfnt_font_info *info; + + for (info = open_fonts; info; info = info->next) + { + if (info->glyf_table_mapped + && (unsigned char *) addr >= info->glyf->glyphs + && (unsigned char *) addr < (info->glyf->glyphs + + info->glyf->size)) + return true; + } + + return false; +} + +#endif + + + void syms_of_sfntfont (void) { diff --git a/src/sfntfont.h b/src/sfntfont.h index f49121438a5..dc37883b4a9 100644 --- a/src/sfntfont.h +++ b/src/sfntfont.h @@ -56,4 +56,13 @@ extern void mark_sfntfont (void); extern void init_sfntfont_vendor (Lisp_Object, const struct font_driver *, sfntfont_put_glyph_proc); + +/* mmap specific functions. */ + +#ifdef HAVE_MMAP + +extern bool sfntfont_detect_sigbus (void *); + +#endif /* HAVE_MMAP */ + #endif /* _SFNTFONT_H_ */ diff --git a/src/sysdep.c b/src/sysdep.c index ac26b12e9a5..cb63d8bba08 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -138,6 +138,10 @@ int _cdecl _spawnlp (int, const char *, const char *, ...); #include "android.h" #endif +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY +#include "sfntfont.h" +#endif + /* Declare here, including term.h is problematic on some systems. */ extern void tputs (const char *, int, int (*)(int)); @@ -1816,6 +1820,40 @@ handle_arith_signal (int sig) xsignal0 (Qarith_error); } +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && defined HAVE_MMAP + +static void +handle_sigbus (int sig, siginfo_t *siginfo, void *arg) +{ + /* If this arrives during sfntfont_open, then Emacs may be + screwed. */ + + if (sfntfont_detect_sigbus (siginfo->si_addr)) + return; + + handle_fatal_signal (sig); +} + +/* Try to set up SIGBUS handling for the sfnt font driver. + Value is 1 upon failure, 0 otherwise. */ + +static int +init_sigbus (void) +{ + struct sigaction sa; + + sigfillset (&sa.sa_mask); + sa.sa_sigaction = handle_sigbus; + sa.sa_flags = SA_SIGINFO; + + if (sigaction (SIGBUS, &sa, NULL)) + return 1; + + return 0; +} + +#endif + /* This does not work on Android and interferes with the system tombstone generation. */ @@ -2076,7 +2114,10 @@ init_signals (void) sigaction (SIGEMT, &thread_fatal_action, 0); #endif #ifdef SIGBUS - sigaction (SIGBUS, &thread_fatal_action, 0); +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && defined HAVE_MMAP + if (init_sigbus ()) +#endif + sigaction (SIGBUS, &thread_fatal_action, 0); #endif #if !defined HAVE_ANDROID || defined ANDROID_STUBIFY if (!init_sigsegv ()) -- 2.39.5