From 765d145ec4c7349bdc8c3c760ad98ae1f9b2079a Mon Sep 17 00:00:00 2001 From: John Bowler Date: Sat, 16 Feb 2013 07:43:48 -0600 Subject: [PATCH] [libpng16] Use approved/supported Android method to check for NEON, use Linux/POSIX 1003.1 API to check /proc/self/auxv avoiding buffer allocation and other library calls (ported from libpng15). --- ANNOUNCE | 3 + CHANGES | 3 + arm/arm_init.c | 149 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 135 insertions(+), 20 deletions(-) diff --git a/ANNOUNCE b/ANNOUNCE index a4346cf0a..83068a747 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -40,6 +40,9 @@ Version 1.6.1 [February 16, 2013] scripts/pnglibconf.dfa formatting improvements back ported from libpng17. Fixed a race condition in the creation of the build 'scripts' directory while building with a parallel make. + Use approved/supported Android method to check for NEON, use Linux/POSIX + 1003.1 API to check /proc/self/auxv avoiding buffer allocation and other + library calls (ported from libpng15). Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index 7ddf310bd..94d56f18d 100644 --- a/CHANGES +++ b/CHANGES @@ -4396,6 +4396,9 @@ Version 1.6.1 [February 16, 2013] scripts/pnglibconf.dfa formatting improvements back ported from libpng17. Fixed a race condition in the creation of the build 'scripts' directory while building with a parallel make. + Use approved/supported Android method to check for NEON, use Linux/POSIX + 1003.1 API to check /proc/self/auxv avoiding buffer allocation and other + library calls (ported from libpng15). Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/arm/arm_init.c b/arm/arm_init.c index 8925f352f..3a3e86e64 100644 --- a/arm/arm_init.c +++ b/arm/arm_init.c @@ -1,7 +1,7 @@ /* arm_init.c - NEON optimised filter functions * - * Copyright (c) 2012 Glenn Randers-Pehrson + * Copyright (c) 2013 Glenn Randers-Pehrson * Written by Mans Rullgard, 2011. * Last changed in libpng 1.5.14 [(PENDING RELEASE)] * @@ -9,45 +9,154 @@ * For conditions of distribution and use, see the disclaimer * and license in png.h */ +/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are + * called. + */ +#define _POSIX_SOURCE 1 + #include "../pngpriv.h" -/* __arm__ is defined by GCC, MSVC defines _M_ARM to the ARM version number */ +/* __arm__ is defined by GCC, MSVC defines _M_ARM to the ARM version number, + * Andoid intends to define __ANDROID__, however there are bugs in their + * toolchain; use -D__ANDROID__ to work round this. + */ #if defined __linux__ && defined __arm__ -#include +#define CHECK_NEON +#include /* for sig_atomic_t */ + +#ifdef __ANDROID__ +/* Linux provides access to information about CPU capabilites via + * /proc/self/auxv, however Android blocks this while still claiming to be + * Linux. The Andoid NDK, however, provides appropriate support. + * + * Documentation: http://www.kandroid.org/ndk/docs/CPU-ARM-NEON.html + */ +#include + +static int +png_have_neon(png_structp png_ptr) +{ + /* This is a whole lot easier than the mess below, however it is probably + * implemented as below, therefore it is better to cache the result (these + * function calls may be slow!) + */ + return andoid_getCpuFamily() == ANDROID_CPU_FAMILY_ARM && + (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; +} +#else +/* The generic __linux__ implementation requires reading /proc/self/auxv and + * looking at each element for one that records NEON capabilities. + */ +#include /* for POSIX 1003.1 */ +#include /* for EINTR */ + +#include +#include +#include #include #include -static int png_have_hwcap(unsigned cap) +/* A read call may be interupted, in which case it returns -1 and sets errno to + * EINTR if nothing was done, otherwise (if something was done) a partial read + * may result. + */ +static size_t +safe_read(png_structp png_ptr, int fd, void *buffer_in, size_t nbytes) { - FILE *f = fopen("/proc/self/auxv", "r"); - Elf32_auxv_t aux; - int have_cap = 0; + size_t ntotal = 0; + char *buffer = png_voidcast(char*, buffer_in); - if (!f) - return 0; - - while (fread(&aux, sizeof(aux), 1, f) > 0) + while (nbytes > 0) { - if (aux.a_type == AT_HWCAP && - aux.a_un.a_val & cap) + unsigned int nread; + int iread; + + /* Passing nread > INT_MAX to read is implementation defined in POSIX + * 1003.1, therefore despite the unsigned argument portable code must + * limit the value to INT_MAX! + */ + if (nbytes > INT_MAX) + nread = INT_MAX; + + else + nread = (unsigned int)/*SAFE*/nbytes; + + iread = read(fd, buffer, nread); + + if (iread == -1) { - have_cap = 1; - break; + /* This is the devil in the details, a read can terminate early with 0 + * bytes read because of EINTR, yet it still returns -1 otherwise end + * of file cannot be distinguished. + */ + if (errno != EINTR) + { + png_warning(png_ptr, "/proc read failed"); + return 0; /* I.e. a permanent failure */ + } + } + + else if (iread < 0) + { + /* Not a valid 'read' result: */ + png_warning(png_ptr, "OS /proc read bug"); + return 0; + } + + else if (iread > 0) + { + /* Continue reading until a permanent failure, or EOF */ + buffer += iread; + nbytes -= (unsigned int)/*SAFE*/iread; + ntotal += (unsigned int)/*SAFE*/iread; + } + + else + return ntotal; + } + + return ntotal; /* nbytes == 0 */ +} + +static int +png_have_neon(png_structp png_ptr) +{ + int fd = open("/proc/self/auxv", O_RDONLY); + Elf32_auxv_t aux; + + /* Failsafe: failure to open means no NEON */ + if (fd == -1) + { + png_warning(png_ptr, "/proc/self/auxv open failed"); + return 0; + } + + while (safe_read(png_ptr, fd, &aux, sizeof aux) == sizeof aux) + { + if (aux.a_type == AT_HWCAP && (aux.a_un.a_val & HWCAP_NEON) != 0) + { + close(fd); + return 1; } } - fclose(f); - - return have_cap; + close(fd); + return 0; } +#endif /* !__ANDROID__ */ #endif /* __linux__ && __arm__ */ void png_init_filter_functions_neon(png_structp pp, unsigned int bpp) { #ifdef __arm__ -#ifdef __linux__ - if (!png_have_hwcap(HWCAP_NEON)) +#ifdef CHECK_NEON + static volatile sig_atomic_t no_neon = -1; /* not checked */ + + if (no_neon < 0) + no_neon = !png_have_neon(pp); + + if (no_neon) return; #endif