我正在编写使用libao进行音频输出的应用程序。该部分
调用libao的程序中的一个驻留在共享对象中:

// playao.c
// compile with: gcc -shared -o libplayao.so playao.c -lao -lm
#include <ao/ao.h>
#include <stdio.h>
#include <math.h>

void playao(void) {
    int i;
    unsigned char samps[8000];
    ao_initialize();
    ao_sample_format sf;
    sf.bits = 8;
    sf.rate = 8000;
    sf.channels = 1;
    sf.byte_format = AO_FMT_NATIVE;
    sf.matrix = "M";
    ao_device *device = ao_open_live(ao_default_driver_id(), &sf, NULL);
    if(!device) {
        puts("ao_open_live error");
        ao_shutdown();
        return;
    }
    for(i = 0; i < 8000; ++i) {
        float time = (float)i / 8000;
        float freq = 440;
        float angle = time * freq * M_PI * 2;
        float value = sinf(angle);
        samps[i] = (unsigned char)(value * 127 + 127);
    }
    if(!ao_play(device, (char *)samps, 8000)) {
        puts("ao_play error");
    }
    ao_close(device);
    ao_shutdown();
}

如果我在程序中链接到此共享库,则可以正常工作:
// directlink.c
// compile with: gcc -o directlink directlink.c libplayao.so -Wl,-rpath,'$ORIGIN'
void playao(void);

int main(int argc, char **argv) {
    playao();
    return 0;
}

但是,如果我使用dlopen / dlsym调用它,则没有错误,但是
程序不会发出任何声音:
// usedl.c
// compile with: gcc -o usedl usedl.c -ldl
#include <dlfcn.h>
#include <stdio.h>

int main(int argc, char **argv) {
    void *handle = dlopen("./libplayao.so", RTLD_LAZY);
    if(!handle) {
        puts("dlopen failed");
        return 1;
    }
    void *playao = dlsym(handle, "playao");
    if(!playao) {
        puts("dlsym failed");
        dlclose(handle);
        return 1;
    }
    ((void (*)(void))playao)();
    dlclose(handle);
    return 0;
}

但是,使用usedl运行LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libao.so.4确实有效。因此,关于libao的一些事情要在
程序启动,并且不希望以后再加载。

为什么是这样?有什么办法可以解决这个问题,以便 libao 可以工作
即使稍后在程序执行中加载也可以正确地进行?

如果重要的话,我正在运行Debian 10“破坏者”。

最佳答案

我在Freenode的#xiph channel 上问了这个问题,xiphmont建议将turning on verbose mode设置为。一旦执行完此操作,失败的案例就会开始显示消息:

ERROR: Failed to load plugin /usr/lib/x86_64-linux-gnu/ao/plugins-4/libalsa.so => dlopen() failed

因此,libao本身正在尝试对某些内容进行dlopen,但失败了。它没有向我显示更多细节,因此我在GDB下运行了该程序,并在dlopen上设置了一个断点。在达到dlopenlibalsa断点并运行finish之后,我尝试使用print (const char *)dlerror()查找错误所在。有了这个,我得到了一个更详细的错误:
/usr/lib/x86_64-linux-gnu/ao/plugins-4/libalsa.so: undefined symbol: ao_is_big_endian

因此,ao的libalsa插件正尝试在libao中引用符号,但找不到它们。为什么会这样呢?引用dlopen文档,我看到:



因为我的dlopen调用仅使用RTLD_LAZY而不包含RTLD_GLOBALRTLD_LOCAL,所以它默认为RTLD_LOCAL,它不会将共享库中的符号(例如ao_is_big_endian)暴露给随后加载的共享库(例如libalsa.so)。

因此,我尝试更改以下代码:

void *handle = dlopen("./libplayao.so", RTLD_LAZY);

至:
void *handle = dlopen("./libplayao.so", RTLD_LAZY | RTLD_GLOBAL);

瞧,它起作用了!

关于c - 为什么使用dlopen加载libao时会保持静音?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57644228/

10-10 16:57