I recently uncovered what appears to be a subtle bug with
libsane-hpaio.so.  I did some digging, but felt that I wasn't
familiar enough with either sane or the hplip project to
propose a patch.

I'm hoping that someone here will step up and resolve the issue.

Specifically, libsane-hpaio.so is built with a weak symbolic
reference to sanei_init_debug; all 'normal' sane backends
include their own private copy of that function and so have
no need for libsane.so.  This breaks applications in rare situations.

Now, in normal operation, libsane is dlopen'd by an application
with RTLD_GLOBAL, so that all of it's symbols are available
for further resolution.

When libsane initializes, it dlopen's libsane-hpaio with local|lazy,
and then dlsyms to the various functions.  Then, when those
functions are invoked, ld fixes them up, uses the global libsane
to resolve sanei_init_debug, and life is good.

However, if you have an application which dlopen's libsane.so
*without* RTLD_GLOBAL, life is much worse.  Because when ld goes
to fix up the dlsym'd functions, it realizes - ZOMG - that
sanei_debug_init isn't available, and crashes the whole application.

This results in Wine applications crashing any time they attempt
to initialize libsane, which is quite awful, as you can imagine.

Now, I've patched Wine to explicitly load libsane with RTLD_GLOBAL:
  
http://source.winehq.org/git/wine.git/?a=commit;h=16f33d8b436aabf3b9e3ba810b68b596d9ddd687

But I felt honor bound to report this problem 'upstream', in the hopes
that someone here could fix it properly.  At the least, I've now
done my duty :-/.

I've attached a test program I used in probing this, in case it's handy,
along with output runs (on a Fedora Core 6 box, where I first noticed this)
that illustrate the problem.

Cheers,

Jeremy

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sane/sane.h>
#include <dlfcn.h>

SANE_Status (* p_sane_init) (SANE_Int * version_code,
			      SANE_Auth_Callback authorize);
void (* p_sane_exit) (void);
void (* p_sane_close) (SANE_Handle handle);
SANE_Status (* p_sane_get_devices) (const SANE_Device *** device_list,
				     SANE_Bool local_only);
SANE_Status (* p_sane_open) (SANE_String_Const devicename,
			      SANE_Handle * handle);
const SANE_Option_Descriptor *
  (* p_sane_get_option_descriptor) (SANE_Handle handle, SANE_Int option);
SANE_Status (* p_sane_control_option) (SANE_Handle handle, SANE_Int option,
					SANE_Action action, void *value,
					SANE_Int * info);
SANE_String_Const (* p_sane_strstatus) (SANE_Status status);

int dlopts = RTLD_LAZY;

void init_dl(void)
{
    void *h;

    h = dlopen("libsane.so.1", dlopts);
    if (!h)
    {
        fprintf(stderr, "Error dlopening libsane.so");
        exit (1);
    }

    p_sane_init = dlsym(h, "sane_init");
    p_sane_exit = dlsym(h, "sane_exit");
    p_sane_close = dlsym(h, "sane_close");
    p_sane_get_devices = dlsym(h, "sane_get_devices");
    p_sane_open = dlsym(h, "sane_open");
    p_sane_get_option_descriptor = dlsym(h, "sane_get_option_descriptor");
    p_sane_control_option = dlsym(h, "sane_control_option");
    p_sane_strstatus = dlsym(h, "sane_strstatus");

    //dlclose(h);

}



void display_device(SANE_String_Const name)
{
    SANE_Status rc;
    SANE_Handle h;
    const SANE_Option_Descriptor *opt;
    SANE_Int optcount;
    int i;

    rc = (*p_sane_open)(name, &h);
    if (rc != SANE_STATUS_GOOD)
    {
        fprintf(stderr, "Error '%s' opening '%s'\n", (*p_sane_strstatus) (rc), name);
        return;
    }

    rc = (*p_sane_control_option)(h, 0, SANE_ACTION_GET_VALUE, &optcount, NULL);
    if (rc != SANE_STATUS_GOOD)
    {
        fprintf(stderr, "Error '%s' getting first control option\n", (*p_sane_strstatus) (rc));
    }
    else
    {
        for (i = 0; i < optcount; i++)
        {
            opt = (*p_sane_get_option_descriptor)(h, i);
            if (opt)
            {
                printf("[%d|%s|%s|%s]\n", i, opt->name, opt->title, opt->desc);
            }
        }
    }


    (*p_sane_close)(h);


}

int main(int argc, char *argv[])
{
    SANE_Status rc;
    const SANE_Device **devices;

    SANE_Int ver;
    int i;

    for (i = 1; i < argc; i++)
    {
        if (strstr(argv[i], "now") != 0)
        {
            dlopts &= ~RTLD_LAZY; 
            dlopts |= RTLD_NOW;
        }
        else if (strstr(argv[i], "global") != 0)
        {
            dlopts &= ~RTLD_LOCAL; 
            dlopts |= RTLD_GLOBAL;
        }
        else
        {
            fprintf(stderr, "Error:  '%s' not valid dlopen flag\n", argv[i]);
            fprintf(stderr, "Usage\n    %s: [now|global]\n", argv[0]);
            exit(2);
        }
    }

    printf("dlopts 0x%x [", dlopts);
    if ((dlopts & RTLD_LAZY) == RTLD_LAZY)
        printf("lazy|");
    if ((dlopts & RTLD_NOW) == RTLD_NOW)
        printf("now|");
    if ((dlopts & RTLD_NOLOAD) == RTLD_NOLOAD)
        printf("noload|");
    if ((dlopts & RTLD_GLOBAL) == RTLD_GLOBAL)
        printf("global");
    else
        printf("local");
    printf("]\n");


    init_dl();

    rc = (*p_sane_init)(&ver, NULL);
    if (rc != SANE_STATUS_GOOD)
    {
        fprintf(stderr, "Error initializing '%s'\n", (*p_sane_strstatus) (rc));
        return (1);
    }

    rc = (*p_sane_get_devices)(&devices, SANE_FALSE);
    if (rc != SANE_STATUS_GOOD || !devices)
    {
        fprintf(stderr, "Error getting devices '%s'\n", (*p_sane_strstatus) (rc));
        goto EXIT;
    }

    for (i = 0; devices[i]; i++)
    {
        display_device(devices[i]->name);
    }


EXIT:
    (*p_sane_exit)();

    return 0;
}

--- dlopen of libsane causes a crash ---

[EMAIL PROTECTED] ~]$ /home/public/xfer/mysane 
dlopts 0x1 [lazy|local]
dlopen of /usr/lib/sane/libsane-hpaio.so succeeded
dlsym of sane_hpaio_init ok
Segmentation fault

--- dlopen of libsane with RTLD_GLOBAL is A-OK ---

[EMAIL PROTECTED] ~]$ /home/public/xfer/mysane global
dlopts 0x101 [lazy|global]
dlopen of /usr/lib/sane/libsane-hpaio.so succeeded
dlsym of sane_hpaio_init ok
[0||Number of options|Read-only option that specifies how many options a 
specific devices supports.]
[1|(null)|Scan mode|(null)]
[2|mode|Scan mode|Selects the scan mode (e.g., lineart,monochrome, or color).]
[3|resolution|Scan resolution|Sets the resolution of the scanned image.]
[4|(null)|Advanced|(null)]
[5|contrast|Contrast|Controls the contrast of the acquired image.]
[6|compression|Compression|Selects the scanner compression method for faster 
scans, possibly at the expense of image quality.]
[7|jpeg-compression-factor|JPEG compression factor|Sets the scanner JPEG 
compression factor.  Larger numbers mean better compression, and smaller 
numbers mean better image quality.]
[8|batch-scan|Batch scan|Guarantees that a "no documents" condition will be 
returned after the last scanned page, to prevent endless flatbed scans after a 
batch scan.  For some models, option changes in the middle of a batch scan 
don't take effect until after the last page.]
[9|source|Source|Selects the desired scan source for models with both flatbed 
and automatic document feeder (ADF) capabilities.  The "Auto" setting means 
that the ADF will be used if it's loaded, and the flatbed (if present) will be 
used otherwise.]
[10|duplex|Duplex|Enables scanning on both sides of the page for models with 
duplex-capable document feeders.  For pages printed in "book"-style duplex 
mode, one side will be scanned upside-down.  This feature is experimental.]
[11|(null)|Geometry|(null)]
[12|length-measurement|Length measurement|Selects how the scanned image length 
is measured and reported, which is impossible to know in advance for scrollfed 
scans.]
[13|tl-x|Top-left x|Top-left x position of scan area.]
[14|tl-y|Top-left y|Top-left y position of scan area.]
[15|br-x|Bottom-right x|Bottom-right x position of scan area.]
[16|br-y|Bottom-right y|Bottom-right y position of scan area.]

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
HPLIP-Devel mailing list
HPLIP-Devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/hplip-devel

Reply via email to