LINUX.ORG.RU

Собрать бинарник + cdylib

 


1

3

Всем привет.

Осваиваю Rust. Хочу собрать программу, которая использует свою cdylib. Идея именно в том, чтобы держать код cdylib в одном дереве с кодом самой программы.

Задача такая:
cdylib («mylib») использует xml-rs с помощью extern crate xml;. Сама по себе (с отдельным Cargo.toml) компилится и работает отлично.

Проблема: Не могу написать Cargo.toml так, чтобы из src/main.rs делающий extern crate mylib; получить исполняемый файл, а из src/mylib/lib.rs получить solib (с вкомпиленной в него xml-rs).

P. S.: Мануал по секциям [lib] и [dependencies] для Cargo.toml смотрел и экспериментировал. Похоже, я готовлю их как-то не так.

P. P. S.: Возможно, описал задачу плохо. Пишите, какие данные нужны для диагностики проблемы.


Ответ на: комментарий от Kilte

Раздел «Specifying path dependencies» читал. Просто указать зависимость я могу.

Живой пример полезен. Много нового узнал для себя про возможности Cargo.toml.

Я правильно понимаю, что «grep» в примере строится как раз в виде cdylib и потом подключается к main.rs?

NIR
() автор топика
Ответ на: комментарий от NIR

Раздел «Specifying path dependencies» читал. Просто указать зависимость я могу.

Так а что не получилось? В %libname%/Cargo.toml указываешь нужные crate-type и всё.

Я правильно понимаю, что «grep» в примере строится как раз в виде cdylib и потом подключается к main.rs?

Не, там rlib вроде по дефолту.

Kilte ★★★★★
()
Ответ на: комментарий от Kilte

Это в Cargo.toml для mylib:

[package]
name = "mylib"
version = "0.0.1"
authors = ["NIR"]
build = "build.rs"

[dependencies]
xml-rs = "0.3"

[lib]
name = "mylib"
crate_type = ["cdylib"]

[profile.dev]
rpath = true

[profile.release]
lto = true
#panic = 'abort'
rpath = true


Это в Cargo.toml для main.rs:
[package]
name = "myapp"
version = "0.0.1"
authors = ["NIR"]
build = "build.rs"

[dependencies]
fwod = {path = "src/mylib", version = "0.0.1", crate_type = "cdylib"}

[profile.dev]
rpath = true

[profile.release]
lto = true
#panic = 'abort'
rpath = true


Пишет, что не может найти mylib. При сборке release. При debug почему-то находит.

NIR
() автор топика
Ответ на: комментарий от Kilte

Окей, перечитал внимательно и понял, что я делаю что-то не так. Нужно вписать флаг prefer-dynamic и добавить crate_type типа rlib. Не могу понять в каком виде оно должно быть вписано и куда.

Подскажи, если не сложно. Туплю-торможу.

NIR
() автор топика
Ответ на: комментарий от Kilte

Теперь Cargo всё находит. Большое спасибо!

Желаю тебе лучей добра и котиков.

NIR
() автор топика
Ответ на: комментарий от Kilte

Облом. ldd говорит, что никакую лишнюю solib он не использует. Удаление соответствующей solib из каталога deps/ так же не помогает программе завалиться.

NIR
() автор топика

Из того, что я выяснил:
- Необходимо добавить:

#![crate_type = "cdylib"]
extern crate libc;
use libc::{нужные типы данных};

...в lib.rs библиотеки. Возможно libc необязательна, но можно будет сделать принимаемые функциями параметры вполне себе соответствующими сишным.
- В Cargo.toml прописываем:
[dependencies]
libc = "0.2"
- В main.rs прописываем:
#[link(name = "mylib")]
extern {
        fn mylib_function(<сишные параметры>);
}

fn main () {
        unsafe {
                mylib_function(<передаваемые параметры>);
        }
}

Руководствовался этим:
https://doc.rust-lang.org/book/ffi.html
Там, правда, рекомендуют для всех объявленных прототипов функций ещё написать обёртки, но это мне пока рано делать.

Вообще, оно у меня всё равно падает с ошибкой линковки, но это скорее потому, что я неправильно описал принимаемые типы libc. И ещё не может обнаружить путь к самой библиотеке. В процессе решения проблемы.

NIR
() автор топика
Последнее исправление: NIR (всего исправлений: 1)
Ответ на: комментарий от NIR

Когда в crate-type добавили rlib, она слинковалась статически. cdylib нужен для того, чтобы её можно было использовать в других языках.

Если хочешь подгружать её в своём коде на rust, то наверное лучше воспользоваться dylib, чем мучаться с cdylib. Могу посоветовать сюда ещё посмотреть: https://damienradtke.com/post/rusty-dynamic-loading/

Дальше я вряд-ли чем-то смогу помочь, так как сам ещё плаваю в этой теме.

Kilte ★★★★★
()

Короче, всё получилось.

Структура файлов:
./doc
./src
./Cargo.lock
./Cargo.toml
./ChangeLog.md
./AUTHORS.md
./LICENSE.md
./README.md
./src/main.rs
./src/fwod/Cargo.lock
./src/fwod/Cargo.toml
./src/fwod/src/lib.rs
./src/fwod/src/fwod.rs

./Cargo.toml:

[package]
name = "fww"
version = "0.0.1"
authors = ["NIR"]
description = """
Library and a program to work with FWObjectDatabase.
"""
documentation = ""
homepage = ""
repository = ""
readme = "README.md"
keywords = ["firewall"]
categories = ["command-line-utilities"]
license = "AGPLv3"
license-file = "LICENSE.md"
#build = "build.rs"

[dependencies]
fwod = {path = "src/fwod", version = "0.0.1", crate_type = "cdylib"}

[profile.dev]
opt-level = 0
rpath = true
prefer-dynamic = true
debug = true
panic = "unwind"

[profile.release]
opt-level = 3
lto = true
prefer-dynamic = true
panic = 'abort'
rpath = true
debug = false


Указание на файлы build.rs у меня закомментировано и сами файлы отсутствуют, так как я пока не умею их писать.

./src/main.rs:
#[link(name = "fwod")]
extern {
	fn fwod_from_file(fwod_file: &str);
}

fn main () {
	println!("Starting fwbuilder-nftables rule compiler.");
	let path: &'static str = "FirewallBuilder.fwb";
	unsafe {
		fwod_from_file(path);
	}
}



./src/fwod/Cargo.toml:
[package]
name = "fwod"
version = "0.0.1"
authors = ["NIR"]
description = """
Highlevel interface to FWObjectDatabase from Firewall Builder.
"""
documentation = ""
homepage = ""
repository = ""
readme = "README.md"
keywords = ["firewall"]
categories = ["command-line-utilities"]
license = "AGPLv3"
license-file = "LICENSE.md"
#build = "build.rs"

[dependencies]
xml-rs = "0.3"

[lib]
name = "fwod"
crate_type = ["cdylib"]

[profile.dev]
opt-level = 0
rpath = true
prefer-dynamic = true
debug = true
panic = "unwind"

[profile.release]
opt-level = 3
lto = true
prefer-dynamic = true
panic = "abort"
rpath = true
debug = false



./src/fwod/src/lib.rs:
#![crate_type = "cdylib"]
pub mod fwod;


Обратите внимание на восклицательный знак в первой строке. Cargo грит (на данный момент), что глобальная для модуля директива должна идти с восклицательным знаком.

./src/fwod/src/fwod.rs:
extern crate xml;

// This is needed to read XML file.
use std::fs::File;
use std::io::BufReader;
use self::xml::reader::{EventReader, XmlEvent};

// Function to get FWObjectDatabase from file on file system.
//#[repr(C)]
#[no_mangle]
pub extern "C" fn fwod_from_file(fwod_file: &str) {
	// The next two lines is and interesting solution regarding
	// variable reuse but I don't sure it has to be like this.
	let file = File::open(fwod_file).unwrap();
	let file = BufReader::new(file);
	let parser = EventReader::new(file);
	// Iterate over XML elements.
	for element in parser {
		match element {
			Ok(XmlEvent::StartElement { name, .. }) => {
				println!("+{}", name);
			}
			Ok(XmlEvent::EndElement { name }) => {
				println!("-{}", name);
			}
			Err(e) => {
				println!("Error: {}", e);
				break;
			}
			_ => {}
		}
	}
}


Код cdylib - упрощённый пример для библиотеки xml-rs.

В процессе выяснилось, что для обеспечения возможности экспортирования структур данных в C перед ними необходимо добавлять директиву:
#[repr(C)]


Так-то я решил переписать свой недо-XML-парсер файлов FirewallBuilder (FWObjectDatabase) с Go (только начал, тоже осваивал язык) на Rust, так как Rust больше нравится. Ну, и, в целом, возможность относительно лёгкой сборки solibs с экспортом C-совместимых символов радует, так как с библиотекой смогут работать из других программ, а не только тех, что написаны на крестах.

Разработчик FirewallBuilder направил меня в куски кода для Juniper, чтобы я смог пользоваться этим драйвером как примером. К сожалению, я абсолютно отвык от C++. Понимаю конструкции с трудом.

Алсо, если допишу это недо-поделие, то постараюсь отправить код в FirewallBuilder. Надеюсь, что программа от наличия лишней обвязки только выиграет (но придётся поддерживать два парсера, да).

Касательно сборки:
Собираю через:
cargo build --release --verbose

и оно собирается, компиляет мою cdylib и отваливается на этапе компиляции самой программы ругаясь на невозможность найти мои библиотеку.

Пока решаю проблему копированием собраной solib в /usr/lib . Вообще, надо глубже копнуть в сторону build.rs и научиться генерить путь поиска библиотек для линкера.

В итоге, при повторном запуске:
cargo build --release --verbose

оно находит библиотеку и:
[quote] ldd fww [br][/quote]	linux-vdso.so.1 (0x00007ffcfcd4a000)
	libfwod.so => <Бинго!>
	librt.so.1 => /usr/lib/librt.so.1 (0x00007f213f9c0000)
	libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f213f7a0000)
	libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f213f588000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007f213f1e8000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f213fbc8000)
[quote][br][/quote]

NIR
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.