• Re: Package for development of out-of-tree kernel modules written in Ru

    From NoisyCoil@21:1/5 to Miguel Ojeda on Wed Feb 26 20:20:01 2025
    On 26/02/25 19:47, Miguel Ojeda wrote:
    On Wed, Feb 26, 2025 at 7:24 PM NoisyCoil<noisycoil@disroot.org> wrote:
    It does indeed depend on the configuration, because at least some
    abstractions are enabled via the configuration and different metadata
    will be produced depending on whether they are or not (Miguel should
    confirm this, but I'm pretty sure about it). The first example that
    comes to my mind is the firmware abstractions, which are already in
    stable. So it can't go in linux-kbuild, and if it can't go in
    linux-headers either then it needs a new package.
    I was referring to the `.so`, not the `.rmeta`s, i.e. I thought Ben
    was referring to the last paragraph quoted.

    The `.rmeta`s definitely depend on the kernel config (and always
    will). The `.so` do not get the config passed right now, but we may
    end up passing it if we need it. So I would just assume they do.


    Ah, I assumed he was talking about both. Ben, do I understand correctly
    that the .so files shouldn't go in linux-headers because you want to use *linux-headers* for cross-compiling, meaning everything in linux-headers
    should be usable by the build arch instead of target arch? If this is
    the case, then I would also ask Miguel if the .rmeta files can be used
    from another architecture for cross-compiling. I think the .rmeta files
    can only be used with the same rustc that compiled the kernel crates?
    Don't know exactly which checks are performed on the compiler, are they
    only version checks?

    Cheers,
    Miguel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From NoisyCoil@21:1/5 to Miguel Ojeda on Wed Feb 26 22:00:01 2025
    On 26/02/25 19:10, Miguel Ojeda wrote:
    On Wed, Feb 26, 2025 at 3:15 PM NoisyCoil<noisycoil@disroot.org> wrote:
    Yeah I'd imagined this could happen. Currently d/rules just copies
    rust/{*.rmeta,*.so} into the destination directory. My guess was that in
    the future they could be placed in subdirectories of rust/, but on a
    second thought I think as subsystems accept Rust they could be located
    kind of anywhere in the build directory? If this is the case, then I
    Unknown yet -- my current plan is to propose to generate them all in a
    single place (which may be different than their current place) for simplicity. But if people disagree, I may have to place them all over
    the tree.

    think hardcoding can hardly be avoided. One will just have to change the
    hardcoded paths as needed (not ideal, but oh, well; also, absolutely not
    unheard of :-)). I say this mostly because of the *.so files: assuming
    that all *.rmeta files must be installed probably is a good enough
    assumption, but the same is not true for *.so files.
    If a single place is better for you, that is another argument that I
    can use to push for the thing I mentioned above, so I wouldn't mind to
    hear that! 🙂

    My personal opinion (which tbf doesn't matter a lot since I won't be the
    one dealing with this in the long run) is if there is no strong reason
    for placing them at specific but seemingly random places, picking a
    single place would be best. A few reasons I can think of:

    1. it makes them more discoverable

    2. it relieves packagers from having to manually keep track of what must
    be installed, especially if whether they are generated or not depends on
    the config (I think this is not the case right now, but I don't see why
    it couldn't be in the future)

    3. if they are scattered all over the tree the package that ships them
    will be a large tree of directories each mostly containing a single rust artifact, which is quite ugly imo.

    On the other hand, one could maybe use something like `objdump -T
    whatever.so | grep rustc_proc_macro_decls` to discover if a .so file is
    a rust proc macro?
    In the case where we are everywhere, I guess I could output a file
    easily with the paths of all the needed dependencies, if that would
    help.

    If they cannot be put in a single place then, yeah, properly documenting
    their existence will make life easier for packagers. A config-dependent
    file $file generated at build time that one can just `cat $file | cpio
    -pd $DIR` would be enough. Again, this is not so much for .rmeta files,
    those are specific enough to Rust that one can just pull them all in
    (without solving issue 3. above though), but for other stuff like proc
    macro shared libraries which require deeper inspection than looking at
    file names.

    Thanks for the feedback!

    Thank you for taking the time to answer!

    Cheers,
    Miguel


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bastian Blank@21:1/5 to Miguel Ojeda on Wed Apr 2 23:00:01 2025
    On Wed, Feb 26, 2025 at 07:47:39PM +0100, Miguel Ojeda wrote:
    I was referring to the `.so`, not the `.rmeta`s, i.e. I thought Ben
    was referring to the last paragraph quoted.

    The .so are for the build architecture:
    | debian/build/build_arm64_none_cloud-arm64/rust/libmacros.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=0f3c61636b65abb0a499c45e78fb0edf48c82380, with debug_info, not stripped

    This means they need to go into linux-kbuild and can not use any config.

    Bastian

    --
    Conquest is easy. Control is not.
    -- Kirk, "Mirror, Mirror", stardate unknown

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bastian Blank@21:1/5 to Bastian Blank on Wed Apr 2 23:30:02 2025
    On Wed, Apr 02, 2025 at 10:38:04PM +0200, Bastian Blank wrote:
    On Wed, Feb 26, 2025 at 07:47:39PM +0100, Miguel Ojeda wrote:
    I was referring to the `.so`, not the `.rmeta`s, i.e. I thought Ben
    was referring to the last paragraph quoted.
    The .so are for the build architecture:
    This means they need to go into linux-kbuild and can not use any config.

    I don't see any reference to libmacros.so in anything apart the build of rust/kernel.o. Is this actually used for module build at all and where?

    Bastian

    --
    Schshschshchsch.
    -- The Gorn, "Arena", stardate 3046.2

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From NoisyCoil@21:1/5 to Bastian Blank on Wed Apr 2 23:40:01 2025
    On 02/04/25 23:10, Bastian Blank wrote:
    On Wed, Apr 02, 2025 at 10:38:04PM +0200, Bastian Blank wrote:
    On Wed, Feb 26, 2025 at 07:47:39PM +0100, Miguel Ojeda wrote:
    I was referring to the `.so`, not the `.rmeta`s, i.e. I thought Ben
    was referring to the last paragraph quoted.
    The .so are for the build architecture:
    This means they need to go into linux-kbuild and can not use any config.
    I don't see any reference to libmacros.so in anything apart the build of rust/kernel.o. Is this actually used for module build at all and where?

    Re-looping in Miguel. I am in fact able to build the reference
    out-of-tree kernel module without libmacros.so. However, I think this
    depends on whether the specific module being built uses the proc macros
    defined in the macros crate?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From NoisyCoil@21:1/5 to NoisyCoil on Thu Apr 3 00:50:01 2025
    On 02/04/25 23:33, NoisyCoil wrote:
    I am in fact able to build the reference out-of-tree kernel module
    without libmacros.so.

    I take that back, I had deleted libmacros.so from the wrong kernel
    package version. Not only one of the proc macros defined in the macros
    crate is module!(), which is used to declare kernel modules, but without libmacros.so the kernel crate is not even found:

    error[E0463]: can't find crate for `kernel`
    --> /home/noisycoil/Tmp/rust-out-of-tree-module/rust_out_of_tree.rs:5:5
    |
    5 | use kernel::prelude::*;
    | ^^^^^^ can't find crate

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bastian Blank@21:1/5 to NoisyCoil on Thu Apr 3 16:50:01 2025
    On Thu, Apr 03, 2025 at 12:33:44AM +0200, NoisyCoil wrote:
    On 02/04/25 23:33, NoisyCoil wrote:
    I am in fact able to build the reference out-of-tree kernel module
    without libmacros.so.

    I take that back, I had deleted libmacros.so from the wrong kernel package version. Not only one of the proc macros defined in the macros crate is module!(), which is used to declare kernel modules, but without libmacros.so the kernel crate is not even found:

    error[E0463]: can't find crate for `kernel`
    /home/noisycoil/Tmp/rust-out-of-tree-module/rust_out_of_tree.rs:5:5
    |
    5 | use kernel::prelude::*;
    | ^^^^^^ can't find crate

    Please reference the bug report for this. Removing a file needs to
    produce a file not found error if it is required.

    But this means this library needs to be shipped in linux-kbuild and
    properly referenced in the build.

    Bastian

    --
    ... The prejudices people feel about each other disappear when they get
    to know each other.
    -- Kirk, "Elaan of Troyius", stardate 4372.5

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From NoisyCoil@21:1/5 to Bastian Blank on Thu Apr 3 19:30:01 2025
    On 03/04/25 16:28, Bastian Blank wrote:
    Please reference the bug report for this. Removing a file needs to
    produce a file not found error if it is required.

    What bug report? Is this a suggestion to file one against the upstream
    kernel?

    But this means this library needs to be shipped in linux-kbuild and
    properly referenced in the build.

    Miguel said they are unwilling to commit to macros being
    config-independent, so it can't go in linux-kbuild as-is. To be clear,
    are you suggesting that it be made config-independent so it can be built
    in the package for the build architecture (in a cross-compilation
    scenario), and then used for arbitrary target architectures, hence why
    it must be config-independent?

    Related to this, I have a question for Miguel: assuming that macros can
    be made config-independent, can one then use the libmacros.so built with
    some kernel for building modules for another kernel (let's say same
    version and either a different config or a different architecture)? Or libmacros.so is specific to the build?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bastian Blank@21:1/5 to NoisyCoil on Thu Apr 3 21:00:01 2025
    On Thu, Apr 03, 2025 at 07:23:56PM +0200, NoisyCoil wrote:
    On 03/04/25 16:28, Bastian Blank wrote:
    Please reference the bug report for this. Removing a file needs to
    produce a file not found error if it is required.
    What bug report? Is this a suggestion to file one against the upstream kernel?

    I would assume it's rustc that's broken. It gives a diagnostic that is
    not at all related to the thing you claimed to have removed for testing.

    But this means this library needs to be shipped in linux-kbuild and properly referenced in the build.
    Miguel said they are unwilling to commit to macros being config-independent, so it can't go in linux-kbuild as-is.

    What the heck is this good for, where config dependency would be useful?

    To be clear, are you suggesting that
    it be made config-independent so it can be built in the package for the
    build architecture (in a cross-compilation scenario), and then used for arbitrary target architectures, hence why it must be config-independent?

    It's the basic setup of the Debian Linux package. linux-kbuild is for
    the build arch, aka the system the build runs on; linux-headers-* is for
    the host arch, aka the system the kernel is built for.

    So, either "macros" is static and unchanging, then we can just build it
    and don't care. Or it is dynamic, then it needs to be built next to the external module and not be shipped at all.

    Bastian

    --
    A father doesn't destroy his children.
    -- Lt. Carolyn Palamas, "Who Mourns for Adonais?",
    stardate 3468.1.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From NoisyCoil@21:1/5 to Miguel Ojeda on Thu Apr 3 23:40:02 2025
    On 03/04/25 23:08, Miguel Ojeda wrote:
    On Thu, Apr 3, 2025 at 8:33 PM Bastian Blank<waldi@debian.org> wrote:
    What the heck is this good for, where config dependency would be useful?
    C macros in the kernel use the kernel config all the time, why would
    this be different?

    What is the root issue here?

    I think the key issue here is Debian wants to support cross-compiling out-of-tree kernel modules, but doing so with Rust modules requires one
    to have libmacros.so compiled and packaged for the build architecture,
    which in the context of cross-compilation is different than the target architecture. Stuff for the build architecture is config-independent in Debian's packaging and should probably remain so, as otherwise one would
    need to build such stuff for every single build architecture + target configuration (and arch) combination supported by Debian, which doesn't
    scale up, if at all possible.

    The difference with C is C does not compile macros into shared
    libraries, so this issue does not arise. If libmacros.so (and more
    generally, for the future, every proc macro) could be compiled not only
    at kernel build time (which I understand is necessary anyway), but also recompiled while the out-of-tree module is being compiled, then this
    issue would not arise for Rust either. One could simply ship the headers package without the proc macros, and rebuild the proc macros when needed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bastian Blank@21:1/5 to Miguel Ojeda on Fri Apr 4 11:20:01 2025
    On Thu, Apr 03, 2025 at 11:08:08PM +0200, Miguel Ojeda wrote:
    On Thu, Apr 3, 2025 at 8:33 PM Bastian Blank <waldi@debian.org> wrote:
    What the heck is this good for, where config dependency would be useful?
    C macros in the kernel use the kernel config all the time, why would
    this be different?

    C macros are read by the preprocessor shipped in the compiler. The preprocessor changes it's behaviour depending on the input files.
    However you don't recompile the preprocessor depending on the kernel
    config.

    But this is not what the macros crate is. This crate _is_ a custom preprocessor. So, if it just reads the C macros the same way a C
    preprocessor would, during compilation of the final result, then we
    don't have a problem.

    What is the root issue here?

    The root issue is: libmacros.so, the output file, must be independent
    from the kernel config.

    So, either "macros" is static and unchanging, then we can just build it
    and don't care. Or it is dynamic, then it needs to be built next to the external module and not be shipped at all.
    What "external module" are you referring to?

    The one you build with "make M="

    Bastian

    --
    Beam me up, Scotty, there's no intelligent life down here!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From NoisyCoil@21:1/5 to Miguel Ojeda on Sat Apr 12 18:50:01 2025
    Hi Miguel,

    Sorry for the delay, I wanted to do some tests before answering.


    On 06/04/25 23:21, Miguel Ojeda wrote:
    So you want to build a kernel for architecture X in a host of
    architecture Y, and then build an out-of-tree module for architecture
    X in a host of architecture Z (possibly X)?

    This is my understanding based on what the kernel maintainers wrote in
    this thread. At least in terms of enablement (i.e. wanting to support
    this scenario, not sure they actually need to do such builds).

    As far as I have been told, the kernel generally requires that the
    exact same toolchain is used to build out-of-tree modules as the main
    kernel -- so the same host/target pair should be used for both the
    kernel build as the out-of-tree builds. Of course, things may happen
    to work otherwise.

    This is what I wanted to test. Using a different architecture's
    toolchain doesn't seem to work, even if the toolchain versions are the
    same. I built two twin kernels for arm64, first on an x86_64 host and
    then on an arm64 host. The toolchains were the exact same version-wise,
    but of course not the same in practice, being for two different hosts. Cross-compilation per se seems to work fine: I successfully
    cross-compiled the reference out-of-tree module using the build
    artifacts for the cross-compiled kernel and it loaded correctly on the
    target machine (after installing the cross-compiled kernel).

    On the other hand, I was not able to compile the module on arm64 using
    the build files from the cross-compiled kernel. Since libmacros.so was
    compiled for x86_64, I tried using both the libmacros.so from the twin
    native build and a third libmacros.so which I cross-compiled using the
    same toolchain as the cross-compiled kernel. Neither of these worked,
    the build system failed to recognize the build artifacts:


    ```error[E0463]: can't find crate for `kernel`
    --> /home/noisycoil/Tmp/rust-out-of-tree-module/rust_out_of_tree.rs:5:5
    |
    5 | use kernel::prelude::*;
    | ^^^^^^ can't find crate

    error: cannot find macro `pr_info` in this scope
    --> /home/noisycoil/Tmp/rust-out-of-tree-module/rust_out_of_tree.rs:35:9
    |
    35 | pr_info!("Rust out-of-tree sample (exit)\n");
    ```


    Similarly, I was unable to cross-compile the module on x86_64 using the
    build files from the natively compiled kernel (again except for
    libmacros.so, which I took from the cross-build). In doing so I got the
    very same errors as above.

    So I can confirm that builds in fact do not seem to work either way
    currently, the toolchain must be the exact same, as expected. In
    particular, it seems that the arch/config-(in)dependence of libmacros.so
    is not the main blocker here.

    Is the reason that there is a build farm on architecture Y that builds
    all the kernels for all X, and then you want users to be able to use
    their architecture X to build out-of-tree modules for their kernel on
    X? i.e. the X == Z case.

    In Debian proper we build everything natively (32-bit builds usually
    being done on corresponding 64-bit machines). I cannot speak for the
    kernel maintainers, but my guess would be this discussion is in part
    motivated by simply wanting to support the cross-compilation scenario,
    in part by wanting to adapt to the current packaging scheme (some
    packages are for the host machine, others are for the target machine, libmacros.so technically is for the host machine so it should go in the corresponding package which is config-independent, etc.).

    Cheers,
    Miguel

    Cheers!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bastian Blank@21:1/5 to NoisyCoil on Sun Apr 13 13:40:01 2025
    On Sat, Apr 12, 2025 at 06:46:31PM +0200, NoisyCoil wrote:
    So I can confirm that builds in fact do not seem to work either way currently, the toolchain must be the exact same, as expected. In particular, it seems that the arch/config-(in)dependence of libmacros.so is not the main blocker here.

    Okay, so all output of this step is only usable without any change to
    the environment. So we can only ship rust source in any way and the
    kernel module make stuff needs to rebuild anything for the external
    modules.

    Bastian

    --
    Vulcans never bluff.
    -- Spock, "The Doomsday Machine", stardate 4202.1

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From NoisyCoil@21:1/5 to Bastian Blank on Sun Apr 13 17:40:01 2025
    On 13/04/25 13:14, Bastian Blank wrote:
    So we can only ship rust source in any way and the
    kernel module make stuff needs to rebuild anything for the external
    modules.

    Yep, this seems to work [1]. The two tests I did are:

    1. Native build on arm64. Installed the build on amd64, replaced the
    Rust files (rmeta and libmacros) with ones obtained by cross-compiling
    the same kernel with the same config on amd64. Pointing KDIR to the natively-built /usr/src/linux-headers with the replaced Rust files
    correctly builds the OOT module, and the module correctly loads on arm64.

    2. Cross-build on amd64. Installed the build on arm64, replaced the Rust
    files with those from the twin native build. Same as above, the module
    builds and correctly loads on arm64.

    This seems to be the way to go, although my limited testing may well be neglecting unknown failure modes.


    [1] At least with MODVERSIONS enabled. Not sure what would happen with MODVERSIONS disabled, perhaps nothing, as long as the kernel version
    remains the same?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From NoisyCoil@21:1/5 to Miguel Ojeda on Sun Apr 13 20:30:01 2025
    On 13/04/25 18:17, Miguel Ojeda wrote:
    I am not sure I am following correctly the tests, but are you
    referring to rebuilding all the Rust artifacts (and not just the host
    ones)?

    Yes. Only rebuilding the host ones -- that is, the macros -- did not
    work. To recap:


    1. If the kernel is compiled natively on arm64

    a. building the OOT module on arm64 using the artifacts from the
    build obviously works

    b. if the natively built artifacts are transferred to an x86_64
    machine, and libmacros.so is replaced from a twin cross-build,
    cross-building the OOT module does not work. The compiler complains that
    the kernel crate and macros are not found

    c. like 1b., but all Rust artifacts (i.e. rmetas too) are replaced
    from a twin cross-build, works


    2. If the kernel is cross-built on x86_64

    a. cross-building the OOT module on x86_64 using the artifacts from
    the build works

    b. if the cross-built artifacts are transferred to an arm64 machine,
    and libmacros.so is replaced from a twin native build, natively building
    the OOT module does not work. As in 1b. the compiler complains that the
    kernel crate and macros are not found

    c. like 2b., but all Rust artifacts (i.e. rmetas too) are replaced
    from a twin native build, works


    The builds are of 6.14.2 [1], done in Debian unstable containers with
    the latest toolchain versions available at this time. When I say a build
    worked I also mean the module correctly loaded on arm64. I have
    MODVERSIONS enabled in my config, but this is probably irrelevant in
    this context because, even if it wasn't, vermagic should be the same for
    native and cross builds here IIUC.

    I think that would still be not generally supported by the kernel,
    since you still used a different host/target pair (i.e. different toolchains/binaries) -- if I understand correctly, your out-of-tree
    module ends up using `.rmeta`s that are different than those used for
    the main kernel since they come from a different build, even if they
    are all accepted by the Rust compiler since it built them all.

    Correct. Yeah, I get this is unsupported by the kernel, I was just
    throwing things at the wall and see what sticks :-) Apparently what
    stuck is pretending rustc was never part of the equation and present the
    kernel image with a module built whatever way it works (in this case, by rebuilding the Rust bits with a non-matching compiler together with the
    OOT module). As far as I understand, this is the most support the kernel
    ever provided to this kind of stuff -- none, but once you get binaries
    to build these may turn out to be compatible if you're lucky enough.


    Cheers!


    [1] One caveat though: I wanted to try to load the modules without
    setting up other arm64 machines or VMs, so it was easier for me to use
    the Asahi fork. AFAICS they don't make changes to the build system nor
    module loading bits.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ben Hutchings@21:1/5 to Miguel Ojeda on Wed Apr 16 17:40:01 2025
    On Sun, 2025-04-06 at 23:21 +0200, Miguel Ojeda wrote:
    On Thu, Apr 3, 2025 at 11:32 PM NoisyCoil <noisycoil@disroot.org> wrote:

    I think the key issue here is Debian wants to support cross-compiling out-of-tree kernel modules,

    I see, that sentence explains it, thanks!

    So you want to build a kernel for architecture X in a host of
    architecture Y, and then build an out-of-tree module for architecture
    X in a host of architecture Z (possibly X)?

    As far as I have been told, the kernel generally requires that the
    exact same toolchain is used to build out-of-tree modules as the main
    kernel -- so the same host/target pair should be used for both the
    kernel build as the out-of-tree builds. Of course, things may happen
    to work otherwise.

    I don't see why there would be a problem with mixing objects built using
    native and cross- C compilers with the same target and version. (Unless
    you enable gcc plugins, which we don't.)

    Is the reason that there is a build farm on architecture Y that builds
    all the kernels for all X, and then you want users to be able to use
    their architecture X to build out-of-tree modules for their kernel on
    X? i.e. the X == Z case.

    Official Debian binary packages are built natively, but there is also a standard way to cross-build them and the kernel source package supports
    this. The resulting binary packages should be functionally identical.

    We also try to give users the option to cross-build or natively build out-of-tree modules, independently of that. We apply a patch to always
    define CROSS_COMPILE according to the target architecture. (This works
    even for the native case because e.g. x86_64-linux-gnu-gcc exists as a
    native compiler on amd64.)

    Ben.

    --
    Ben Hutchings
    The obvious mathematical breakthrough [to break modern encryption]
    would be development of an easy way to factor large prime numbers.
    - Bill Gates

    -----BEGIN PGP SIGNATURE-----

    iQIzBAABCgAdFiEErCspvTSmr92z9o8157/I7JWGEQkFAmf/zpQACgkQ57/I7JWG EQkyqg/+N5cFSdYp40eNKdP1xfnz4cj7TGBmm6fzGAbtB2MSDFPdi3NjHP8BwvcE hm8k8NMSIgo+9FGDzimvhhC00nRAAZVMAtxXOHTJaASbVEk6wzk55AiDCa2GyBEl Jciqa4P87gtBnT3+r/5YFH3VR9BAr01O0YsqOOGucG8NVVuF+jI9soZIv/uvV+aG G7dfyKFW700doxw3jA/W3Y8HMSy6Hy76TxBcKOdHyyeksbttaF9D5+eWIiZ+X+WE uFdEArio9vrMqF74bjJNEGdtdGG0RdcMrZ6RcDJPfgPh/TZofDsCFYc36JJgBoBh mJIp+1tdL7IKM9kthGFEiuCOlbuFEPnnOEvM9DyJUQKytGspnHVtOclrF4QB2X9c dQVKdrgrkf2VuDeqYL2qS6sp7pqVgSz5ZVBUcmm4VVZ6yZqlAfx0IQxkE7o77y+J pRH8Xw8nf80JizJk8YJ5FCsteUnHxrPTnFlWN8dCBnNnLBaRhnC285B8ocYTyxfD fc0UnrTrEsmmdV4WWMRS96TL4J/CMjwm6fuCtgwn5Bg1E8YbKZGJhGHRxSp5lpAQ FBVBdtyqxZDbw7dpWvOEjVyoGTN3oTzh3M8agISRCcxS12k/FsjWLg6s7Q+1xUyE bsI2z31TPGJKcJubHVw/emqM0OBzVDlRDy