为什么 Rust 找不到 wl_display_get_registry?

Why is Rust unable to find wl_display_get_registry?

在编写代码以使用 Wayland Client API 时,Rust 似乎无法找到 wl_display_get_registry 符号。该文件的 objdump 也无法找到该符号。但是,用 GCC 编译的 C 程序能够找到该符号。

为什么C程序能找到符号,而Rust却不能?如何修复 Rust 程序以找到符号?

OS:Ubuntu 18.04 LTS

生锈

建造

rustc -o wayland main.rs

输出

error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-L" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "wayland.main0.rcgu.o" "wayland.main1.rcgu.o" "wayland.main2.rcgu.o" "wayland.main3.rcgu.o" "wayland.main4.rcgu.o" "-o" "wayland" "wayland.crate.allocator.rcgu.o" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "-L" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-l" "wayland-client" "-Wl,--start-group" "-Wl,-Bstatic" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-2da986ecbb2c5327.rlib" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-57f46841c9a9f4ee.rlib" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc_jemalloc-23263fe5893322f6.rlib" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-6ed5262c9a0a3e5a.rlib" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc_system-99c162b689d43349.rlib" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-cd415b85dd267875.rlib" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-3876ac10aa96a1e3.rlib" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd_unicode-598b0e9aca382e9a.rlib" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-4eabb2b1c31071b8.rlib" "-Wl,--end-group" "/home/myrddin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-d7a656735ceeae9e.rlib" "-Wl,-Bdynamic" "-l" "dl" "-l" "rt" "-l" "pthread" "-l" "pthread" "-l" "gcc_s" "-l" "c" "-l" "m" "-l" "rt" "-l" "pthread" "-l" "util" "-l" "util"
  = note: wayland.main3.rcgu.o: In function `main::main':
          main3-317d481089b8c8fe83113de504472633.rs:(.text._ZN4main4main17h52709beaa06fe157E+0x109): undefined reference to `wl_display_get_registry'
          collect2: error: ld returned 1 exit status

代码

pub enum WLDisplay {}
pub enum WLRegistry {}

#[link(name = "wayland-client")]
extern "C" {
    pub fn wl_display_connect(name: *const ::std::os::raw::c_char) -> *mut WLDisplay;
    pub fn wl_display_disconnect(display: *mut WLDisplay) -> ();

    pub fn wl_display_get_registry(display: *mut WLDisplay) -> *mut WLRegistry;
}

fn main() {
    let display: *mut WLDisplay;
    let registry: *mut WLRegistry;

    unsafe {
        display = wl_display_connect(::std::ptr::null());
        if display.is_null() == true {
            eprintln!("Unable to connect to the Wayland display server.");
            return;
        }
        println!("Connected to Wayland display server.");

        println!("Retrieving the registry from the Wayland display.");
        registry = wl_display_get_registry(display);
        if registry.is_null() == true {
            eprintln!("Unable to retrieve registry.");
        } else {
            println!("Registry retrieved.");
        }

        wl_display_disconnect(display);
        println!("Disconnected from Wayland display server.");
    }
}

C

建造

gcc -o wayland -lwayland-client main.c

输出

Connected to Wayland display server.
Retrieving the registry from the Wayland display.
Registry retrieved.
Disconnected from Wayland display server.

代码

#include <stdio.h>
#include <wayland-client.h>


int main()
{
   struct wl_display *display;
   struct wl_registry *registry;

   display = wl_display_connect(NULL);
   if (display == NULL)
   {
      fprintf(stderr, "Unable to connect to the Wayland display server.\n");
      return 1;
   }
   fprintf(stdout, "Connected to Wayland display server.\n");

   fprintf(stdout, "Retrieving the registry from the Wayland display.\n");
   registry = wl_display_get_registry(display);
   if (registry == NULL)
   {
      fprintf(stderr, "Unable to retrieve registry.\n");
   }
   else
   {
      fprintf(stdout, "Registry retrieved.\n");
   }

   wl_display_disconnect(display);
   fprintf(stdout, "Disconnected from Wayland display server.\n");

   return 0;
}

objdump

命令

objdump -TC /usr/lib/x86_64-linux-gnu/libwayland-client.so | grep "wl_display*"

输出

0000000000005f60 g DF .text 0000000000000275  Base wl_display_connect
0000000000006960 g DF .text 0000000000000078  Base wl_display_flush
0000000000006a90 g DF .text 000000000000000c  Base wl_display_dispatch
00000000000067b0 g DF .text 0000000000000042  Base wl_display_cancel_read
00000000000068c0 g DF .text 000000000000000c  Base wl_display_dispatch_pending
0000000000006800 g DF .text 00000000000000bf  Base wl_display_dispatch_queue_pending
00000000000061e0 g DF .text 0000000000000057  Base wl_display_disconnect
0000000000006be0 g DF .text 00000000000000fc  Base wl_display_roundtrip_queue
00000000000068d0 g DF .text 000000000000002c  Base wl_display_get_error
0000000000005610 g DF .text 0000000000000031  Base wl_display_create_queue
0000000000006740 g DF .text 0000000000000052  Base wl_display_prepare_read_queue
0000000000006900 g DF .text 0000000000000051  Base wl_display_get_protocol_error
0000000000005dc0 g DF .text 0000000000000194  Base wl_display_connect_to_fd
00000000000069e0 g DF .text 00000000000000ac  Base wl_display_dispatch_queue
0000000000006ce0 g DF .text 000000000000000c  Base wl_display_roundtrip
0000000000006250 g DF .text 00000000000004f0  Base wl_display_read_events
0000000000006240 g DF .text 0000000000000004  Base wl_display_get_fd
000000000020dce0 g DO .data.rel.ro 0000000000000028 Base wl_display_interface
00000000000067a0 g DF .text 000000000000000c  Base wl_display_prepare_read

wl_display_get_registry 函数和其他几个函数在头文件 wayland-client-protocols.h 中标记为 static inlineFunctions marked static inline will not have their symbols exported。这解释了 C 如何能够看到和使用该函数,而 Rust 却不能。

我们必须将 C 宏和静态内联函数重新实现为 Rust 函数:

pub enum WLDisplay {}
pub enum WLProxy {}
pub enum WLRegistry {}

const WL_DISPLAY_GET_REGISTRY: ::std::os::raw::c_uint = 1;

#[link(name = "wayland-client")]
extern "C" {
    #[no_mangle]
    static wl_registry_interface: WLInterface;

    pub fn wl_display_connect(name: *const ::std::os::raw::c_char) -> *mut WLDisplay;
    pub fn wl_display_disconnect(display: *mut WLDisplay) -> ();

    pub fn wl_proxy_marshal_constructor(
        proxy: *mut WLProxy,
        opcode: ::std::os::raw::c_uint,
        interface: *const WLInterface,
    ) -> *mut WLProxy;
}

#[repr(C)]
pub struct WLMessage {
    pub name: *const ::std::os::raw::c_char,
    pub signature: *const ::std::os::raw::c_char,
    pub types: *const *const WLInterface,
}

#[repr(C)]
pub struct WLInterface {
    pub name: *const ::std::os::raw::c_char,
    pub version: ::std::os::raw::c_int,
    pub request_count: ::std::os::raw::c_int,
    pub requests: *const WLMessage,
    pub event_count: ::std::os::raw::c_int,
    pub events: *const WLMessage,
}

fn wl_display_get_registry(display: *mut WLDisplay) -> *mut WLRegistry {
    let proxy: *mut WLProxy;

    unsafe {
        proxy = wl_proxy_marshal_constructor(
            display as *mut _ as *mut WLProxy,
            WL_DISPLAY_GET_REGISTRY,
            &wl_registry_interface,
        );
        proxy as *mut _ as *mut WLRegistry
    }
}

fn main() {
    let display: *mut WLDisplay;
    let registry: *mut WLRegistry;

    unsafe {
        display = wl_display_connect(::std::ptr::null());
        if display.is_null() == true {
            eprintln!("Unable to connect to the Wayland display server.");
            return;
        }
        println!("Connected to Wayland display server.");

        println!("Retrieving the registry from the Wayland display.");
        registry = wl_display_get_registry(display);
        if registry.is_null() == true {
            eprintln!("Unable to retrieve registry.");
        } else {
            println!("Registry retrieved.");
        }

        wl_display_disconnect(display);
        println!("Disconnected from Wayland display server.");
    }
}