Commit e4fa7cc3 authored by Matthias Kraft's avatar Matthias Kraft Committed by Richard Levitte
Browse files

Custome built dladdr() for AIX.



Implemented a stripped down dladdr()-implementation using AIX' own
loadquery()-function. Following the SGI example in the same code, the
DL_info only has the dli_fname member. As the scope of
dlfcn_pathbyaddr() is the filename, this implementation does not
consider archive members, which can be dlopen()ed in AIX.
Added DATA segment checking to catch ptrgl virtual addresses.

Added test case for DSO_dsobyaddr(), but only for DSO_DLFCN.
Added PIC-flag to aix*-cc build targets.

Signed-off-by: default avatarMatthias Kraft <makr@gmx.eu>

Reviewed-by: default avatarAndy Polyakov <appro@openssl.org>
Reviewed-by: default avatarRich Salz <rsalz@openssl.org>
Reviewed-by: default avatarRichard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/5626)
parent ec46830f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1210,6 +1210,7 @@ sub vms_info {
        perlasm_scheme   => "aix32",
        dso_scheme       => "dlfcn",
        shared_target    => "aix-shared",
        shared_cflag     => "-qpic",
        shared_ldflag    => "-q32 -G",
        shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
        arflags          => "-X 32",
@@ -1228,6 +1229,7 @@ sub vms_info {
        perlasm_scheme   => "aix64",
        dso_scheme       => "dlfcn",
        shared_target    => "aix-shared",
        shared_cflag     => "-qpic",
        shared_ldflag    => "-q64 -G",
        shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)",
        arflags          => "-X 64",
+80 −3
Original line number Diff line number Diff line
/*
 * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
 * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the OpenSSL license (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
@@ -26,7 +26,7 @@
#  endif
#  include <dlfcn.h>
#  define HAVE_DLINFO 1
#  if defined(_AIX) || defined(__CYGWIN__) || \
#  if defined(__CYGWIN__) || \
     defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
     (defined(__osf__) && !defined(RTLD_NEXT))     || \
     (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
@@ -308,6 +308,76 @@ static int dladdr(void *address, Dl_info *dl)
}
# endif                         /* __sgi */

# ifdef _AIX
/*-
 * See IBM's AIX Version 7.2, Technical Reference:
 *  Base Operating System and Extensions, Volume 1 and 2
 *  https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm
 */
#  include <sys/ldr.h>
#  include <errno.h>
/* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */
#  define DLFCN_LDINFO_SIZE 86976
typedef struct Dl_info {
    const char *dli_fname;
} Dl_info;
/*
 * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual
 * address of a function, which is just located in the DATA segment instead of
 * the TEXT segment.
 */
static int dladdr(void *ptr, Dl_info *dl)
{
    uintptr_t addr = (uintptr_t)ptr;
    unsigned int found = 0;
    struct ld_info *ldinfos, *next_ldi, *this_ldi;

    if ((ldinfos = (struct ld_info *)OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
        errno = ENOMEM;
        dl->dli_fname = NULL;
        return 0;
    }

    if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
        /*-
         * Error handling is done through errno and dlerror() reading errno:
         *  ENOMEM (ldinfos buffer is too small),
         *  EINVAL (invalid flags),
         *  EFAULT (invalid ldinfos ptr)
         */
        OPENSSL_free((void *)ldinfos);
        dl->dli_fname = NULL;
        return 0;
    }
    next_ldi = ldinfos;

    do {
        this_ldi = next_ldi;
        if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg)
             && (addr < ((uintptr_t)this_ldi->ldinfo_textorg +
                         this_ldi->ldinfo_textsize)))
            || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg)
                && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg +
                            this_ldi->ldinfo_datasize)))) {
            found = 1;
            /*
             * Ignoring the possibility of a member name and just returning
             * the path name. See docs: sys/ldr.h, loadquery() and
             * dlopen()/RTLD_MEMBER.
             */
            if ((dl->dli_fname =
                 OPENSSL_strdup(this_ldi->ldinfo_filename)) == NULL)
                errno = ENOMEM;
        } else {
            next_ldi =
                (struct ld_info *)((uintptr_t)this_ldi + this_ldi->ldinfo_next);
        }
    } while (this_ldi->ldinfo_next && !found);
    OPENSSL_free((void *)ldinfos);
    return (found && dl->dli_fname != NULL);
}
# endif                         /* _AIX */

static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
{
# ifdef HAVE_DLINFO
@@ -326,12 +396,19 @@ static int dlfcn_pathbyaddr(void *addr, char *path, int sz)

    if (dladdr(addr, &dli)) {
        len = (int)strlen(dli.dli_fname);
        if (sz <= 0)
        if (sz <= 0) {
#  ifdef _AIX
            OPENSSL_free((void *)dli.dli_fname);
#  endif
            return len + 1;
        }
        if (len >= sz)
            len = sz - 1;
        memcpy(path, dli.dli_fname, len);
        path[len++] = 0;
#  ifdef _AIX
        OPENSSL_free((void *)dli.dli_fname);
#  endif
        return len;
    }

+16 −1
Original line number Diff line number Diff line
/*
 * Copyright 2016-2017 The OpenSSL Project Authors. All Rights Reserved.
 * Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the OpenSSL license (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
@@ -114,6 +114,15 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_base)

        ERR_set_mark();
        dso = DSO_dsobyaddr(&base_inited, DSO_FLAG_NO_UNLOAD_ON_FREE);
#  ifdef OPENSSL_INIT_DEBUG
        fprintf(stderr, "OPENSSL_INIT: obtained DSO reference? %s\n",
                (dso == NULL ? "No!" : "Yes."));
        /*
         * In case of No!, it is uncertain our exit()-handlers can still be
         * called. After dlclose() the whole library might have been unloaded
         * already.
         */
#  endif
        DSO_free(dso);
        ERR_pop_to_mark();
    }
@@ -657,6 +666,12 @@ int OPENSSL_atexit(void (*handler)(void))

            ERR_set_mark();
            dso = DSO_dsobyaddr(handlersym.sym, DSO_FLAG_NO_UNLOAD_ON_FREE);
#  ifdef OPENSSL_INIT_DEBUG
            fprintf(stderr,
                    "OPENSSL_INIT: OPENSSL_atexit: obtained DSO reference? %s\n",
                    (dso == NULL ? "No!" : "Yes."));
            /* See same code above in ossl_init_base() for an explanation. */
#  endif
            DSO_free(dso);
            ERR_pop_to_mark();
        }
+4 −2
Original line number Diff line number Diff line
#! /usr/bin/env perl
# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
# Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License").  You may not use
# this file except in compliance with the License.  You can obtain a copy
@@ -20,7 +20,7 @@ use configdata;

plan skip_all => "Test only supported in a shared build" if disabled("shared");

plan tests => 3;
plan tests => 4;

my $libcrypto_idx = $unified_info{rename}->{libcrypto} // "libcrypto";
my $libssl_idx = $unified_info{rename}->{libssl} // "libssl";
@@ -35,4 +35,6 @@ ok(run(test(["shlibloadtest", "-ssl_first", $libcrypto, $libssl])),
   "running shlibloadtest -ssl_first");
ok(run(test(["shlibloadtest", "-just_crypto", $libcrypto, $libssl])),
   "running shlibloadtest -just_crypto");
ok(run(test(["shlibloadtest", "-dso_ref", $libcrypto, $libssl])),
   "running shlibloadtest -dso_ref");
+53 −5
Original line number Diff line number Diff line
@@ -40,6 +40,16 @@ static OpenSSL_version_num_t OpenSSL_version_num;

#ifdef DSO_DLFCN

# define DSO_DSOBYADDR "DSO_dsobyaddr"
# define DSO_FREE "DSO_free"

typedef void DSO;
typedef DSO * (*DSO_dsobyaddr_t)(void (*addr)(), int flags);
typedef int (*DSO_free_t)(DSO *dso);

static DSO_dsobyaddr_t DSO_dsobyaddr;
static DSO_free_t DSO_free;

# include <dlfcn.h>

typedef void * SHLIB;
@@ -108,11 +118,13 @@ static int shlib_close(SHLIB lib)
# define CRYPTO_FIRST_OPT    "-crypto_first"
# define SSL_FIRST_OPT       "-ssl_first"
# define JUST_CRYPTO_OPT     "-just_crypto"
# define DSO_REFTEST_OPT     "-dso_ref"

enum test_types_en {
    CRYPTO_FIRST,
    SSL_FIRST,
    JUST_CRYPTO
    JUST_CRYPTO,
    DSO_REFTEST
};

int main(int argc, char **argv)
@@ -123,7 +135,7 @@ int main(int argc, char **argv)
        void (*func) (void);
        SHLIB_SYM sym;
    } tls_method_sym, ssl_ctx_new_sym, ssl_ctx_free_sym, err_get_error_sym,
    openssl_version_num_sym;
    openssl_version_num_sym, dso_dsobyaddr_sym, dso_free_sym;
    enum test_types_en test_type;
    int i;

@@ -138,6 +150,8 @@ int main(int argc, char **argv)
            test_type = SSL_FIRST;
    } else if (strcmp(argv[1], JUST_CRYPTO_OPT) == 0) {
            test_type = JUST_CRYPTO;
    } else if (strcmp(argv[1], DSO_REFTEST_OPT) == 0) {
            test_type = DSO_REFTEST;
    } else {
        printf("Unrecognised argument\n");
        return 1;
@@ -145,7 +159,8 @@ int main(int argc, char **argv)

    for (i = 0; i < 2; i++) {
        if ((i == 0 && (test_type == CRYPTO_FIRST
                       || test_type == JUST_CRYPTO))
                       || test_type == JUST_CRYPTO
                       || test_type == DSO_REFTEST))
               || (i == 1 && test_type == SSL_FIRST)) {
            if (!shlib_load(argv[2], &cryptolib)) {
                printf("Unable to load libcrypto\n");
@@ -161,7 +176,7 @@ int main(int argc, char **argv)
        }
    }

    if (test_type != JUST_CRYPTO) {
    if (test_type != JUST_CRYPTO && test_type != DSO_REFTEST) {
        if (!shlib_sym(ssllib, TLS_METHOD, &tls_method_sym.sym)
                || !shlib_sym(ssllib, SSL_CTX_NEW, &ssl_ctx_new_sym.sym)
                || !shlib_sym(ssllib, SSL_CTX_FREE, &ssl_ctx_free_sym.sym)) {
@@ -215,6 +230,38 @@ int main(int argc, char **argv)
        return 1;
    }

    if (test_type == DSO_REFTEST) {
# ifdef DSO_DLFCN
        /*
         * This is resembling the code used in ossl_init_base() and
         * OPENSSL_atexit() to block unloading the library after dlclose().
         * We are not testing this on Windows, because it is done there in a
         * completely different way. Especially as a call to DSO_dsobyaddr()
         * will always return an error, because DSO_pathbyaddr() is not
         * implemented there.
         */
        if (!shlib_sym(cryptolib, DSO_DSOBYADDR, &dso_dsobyaddr_sym.sym)
            || !shlib_sym(cryptolib, DSO_FREE, &dso_free_sym.sym)) {
            printf("Unable to load crypto dso symbols\n");
            return 1;
        }

        DSO_dsobyaddr = (DSO_dsobyaddr_t)dso_dsobyaddr_sym.func;
        DSO_free = (DSO_free_t)dso_free_sym.func;

        {
            DSO *hndl;
            /* use known symbol from crypto module */
            if ((hndl = DSO_dsobyaddr((void (*)())ERR_get_error, 0)) != NULL) {
                DSO_free(hndl);
            } else {
                printf("Unable to obtain DSO reference from crypto symbol\n");
                return 1;
            }
        }
# endif /* DSO_DLFCN */
    }

    for (i = 0; i < 2; i++) {
        if ((i == 0 && test_type == CRYPTO_FIRST)
                || (i == 1 && test_type == SSL_FIRST)) {
@@ -224,7 +271,8 @@ int main(int argc, char **argv)
            }
        }
        if ((i == 0 && (test_type == SSL_FIRST
                       || test_type == JUST_CRYPTO))
                       || test_type == JUST_CRYPTO
                       || test_type == DSO_REFTEST))
                || (i == 1 && test_type == CRYPTO_FIRST)) {
            if (!shlib_close(cryptolib)) {
                printf("Unable to close libcrypto\n");