diff --git a/perlmod-bin/genpackage.pl b/perlmod-bin/genpackage.pl index 6328ad4..45b435a 100755 --- a/perlmod-bin/genpackage.pl +++ b/perlmod-bin/genpackage.pl @@ -34,6 +34,10 @@ my $opts = { 'PATH', "Path to additional perl code to include in the package after the 'use' statements", ], + 'from-notes' => [ + undef, + "Read the package list from ELF notes sections", + ], }; sub help { @@ -43,7 +47,7 @@ sub help { print {$fd} "mandatory OPTIONS are:\n"; for my $o (sort keys %$opts) { my ($arg, $desc) = $opts->{$o}->@*; - my $p = "--$o=$arg"; + my $p = defined($arg) ? "--$o=$arg" : "--$o"; printf {$fd} " %20s %s\n", $p, $desc; } } @@ -74,6 +78,13 @@ ARGPARSE: while (@ARGV) { } else { next; }; + + if (!defined($opts->{$o}->[0])) { + unshift @ARGV, $arg; + $params->{$o} = 1; + next ARGPARSE; + } + die "--$o requires an argument\n" if !defined($arg); if (ref($params->{$o}) eq 'ARRAY') { push $params->{$o}->@*, $arg; @@ -99,7 +110,8 @@ my $lib_prefix = $params->{'lib-prefix'} or die "missing --lib-prefix parameter\n"; my $lib = $params->{'lib'} or die "missing --lib parameter\n"; -my $lib_tag = $params->{'lib-tag'}; +my $lib_tag = $params->{'lib-tag'} + or die "missing --lib-tag parameter\n"; my $debug_libpath = $params->{'debug-libpath'} // ''; my $extra_code = ''; for my $file ($params->{'include-file'}->@*) { @@ -108,6 +120,7 @@ for my $file ($params->{'include-file'}->@*) { die "error reading '$file': $!\n" if !defined($more); $extra_code .= $more; } +my $from_notes = $params->{'from-notes'}; sub pkg2file { return ($_[0] =~ s@::@/@gr) . ".pm"; @@ -225,6 +238,29 @@ if ($lib ne '-') { close($fh); } +if ($from_notes) { + die "missing library file to read packages from\n" if !@ARGV; + die "--from-notes requires exactly one library\n" if @ARGV > 1; + + open my $fh, '<', $ARGV[0] or die "failed to open $ARGV[0] for reading: $!\n"; + my $fd = fileno($fh); + my $data = `objcopy -O binary --only-section .note.perlmod.package $ARGV[0] /dev/stdout`; + close($fh); + + my @packages; + while (length($data)) { + my ($name_size, $desc_size, $ty) = unpack('LLL', substr($data, 0, 3*4, '')); + die "unexpected description in package note - incompatible perlmod version?\n" + if $desc_size; + my $name = substr($data, 0, $name_size, ''); + my $desc = substr($data, 0, $desc_size, ''); + print("Found package '$name'\n"); + push @packages, $name; + } + die "trailing data in notes section\n" if length($data); + @ARGV = @packages; +} + for my $package (@ARGV) { my $path = ($package =~ s@::@/@gr) . ".pm"; diff --git a/perlmod-macro/src/package.rs b/perlmod-macro/src/package.rs index c442920..4a06d62 100644 --- a/perlmod-macro/src/package.rs +++ b/perlmod-macro/src/package.rs @@ -107,6 +107,9 @@ impl Package { }); } + let package_name_bytes = + syn::LitByteStr::new(self.attrs.package_name.as_bytes(), Span::call_site()); + let package_name_len = self.attrs.package_name.len(); let bootstrap_name = format!("boot_{}", self.attrs.package_name).replace("::", "__"); let bootstrap_ident = Ident::new(&bootstrap_name, Span::call_site()); @@ -120,7 +123,13 @@ impl Package { pub extern "C" fn #bootstrap_ident( _cv: Option<&::perlmod::ffi::CV>, ) { + #[used] + #[link_section = ".note.perlmod.package"] + static PACKAGE_ENTRY: ::perlmod::__private__::ElfNote<{#package_name_len}> = + ::perlmod::__private__::ElfNote::new_package(*#package_name_bytes); + static ONCE: ::std::sync::Once = ::std::sync::Once::new(); + ONCE.call_once(|| { unsafe { use ::perlmod::ffi::RSPL_newXS_flags; diff --git a/perlmod/src/elf_notes.rs b/perlmod/src/elf_notes.rs new file mode 100644 index 0000000..dee6056 --- /dev/null +++ b/perlmod/src/elf_notes.rs @@ -0,0 +1,32 @@ +//! The `#[package]` macro stores package lists in ELF file note sections. +//! This is the implementation for this. +//! +//! This is private and not meant to be a public API. + +/* +#[repr(C, packed)] +pub struct Info { + extra stuff +} +*/ + +#[repr(C, packed)] +pub struct ElfNote { + pub name_size: u32, + pub desc_size: u32, + pub ty: u32, + pub name: [u8; N], + //pub desc: Info, +} + +impl ElfNote<{ N }> { + pub const fn new_package(name: [u8; N]) -> Self { + Self { + name_size: N as u32, + desc_size: 0, // size_of::() + ty: 0, + name, + //desc: Info::new(), + } + } +} diff --git a/perlmod/src/lib.rs b/perlmod/src/lib.rs index c360835..1ee3513 100644 --- a/perlmod/src/lib.rs +++ b/perlmod/src/lib.rs @@ -113,3 +113,12 @@ pub use perlmod_macro::package; /// /// For an example on making blessed objects, see [`Value::bless_box`](Value::bless_box()). pub use perlmod_macro::export; + +mod elf_notes; + +#[doc(hidden)] +pub mod __private__ { + //! This is private and not meant to be a public API and thus semver exempt. + + pub use super::elf_notes::ElfNote; +}