From 206f4d267899b25437d34ffd4d5ab3a066b69c50 Mon Sep 17 00:00:00 2001 From: John Bowler Date: Sat, 16 Feb 2013 07:53:19 -0600 Subject: [PATCH] [libpng17] 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 | 153 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 137 insertions(+), 22 deletions(-) diff --git a/ANNOUNCE b/ANNOUNCE index b3a57e9f9..ade31471d 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -171,6 +171,9 @@ Version 1.7.0beta01 [February 15, 2013] Version 1.7.0beta02 [February 16, 2013] 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 526423ba3..4411366d9 100644 --- a/CHANGES +++ b/CHANGES @@ -4457,6 +4457,9 @@ Version 1.7.0beta01 [February 15, 2013] Version 1.7.0beta02 [February 16, 2013] 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 e455940cb..82c06de1a 100644 --- a/arm/arm_init.c +++ b/arm/arm_init.c @@ -1,53 +1,162 @@ /* 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.6.0 [(PENDING RELEASE)] + * Last changed in libpng 1.5.14 [(PENDING RELEASE)] * * This code is released under the libpng license. * 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 interrupted, 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_structrp pp, unsigned int bpp) +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