[Mono-devel-list] Re: Potential GAC implementation ideas.

Jonathan Pryor jonpryor at vt.edu
Thu Oct 23 20:22:12 EDT 2003

Finally!  A voice of reason! Thank you Jushua.  ;-)

I think some clarification/history is in order, especially with Michal
Moskal's email from 01:25:01 +0200 (7:25 PM EDT).

What is the point of the Assembly?  To hold managed code.  That's all. 
So it's no different than a .dll, or a .so, or a .exe, or whatever.  I
have no idea why Microsoft had to come up with a new name (Assembly),
but they did.

The crucial point is that there are *two* kinds of Assemblies: public
(shared) and private (non-shared).

Why?  Because years of experience on Windows demonstrated that
Windows-default behavior (share everything) was seriously broken,
causing apps to break randomly, leading to "DLL Hell".

(They tried to fix this at least once -- in Windows 2000 they changed
the default ordering for finding DLLs so that the directory the EXE was
started from came *before* anything else.  This way programs would
always know what libraries they were running against.  This behavior was
reverted in Windows Server 2003, as it proved to be a security risk --
you could place a trojaned OLE32.DLL in the app's directory, and it
would get loaded instead of the OS OLE32.DLL.  Oops.)

Take a step back.  What do you want?  (Why have Assemblies in the first
place?)  You want what Mac users have enjoyed for years -- when you
install an app, the app works *forever*, no matter what other software
is installed or removed.  (Yes, the Mac is not perfect; it doesn't
matter.)  Windows is the antithesis to this -- install/remove an app,
and you could break *anything*.

So, keeping this goal in mind (apps run forever without breaking), you
see why the *default* Assembly type is private (non-shared) Assemblies. 
They're installed in the application's directory, each app gets its own
copy, installing a new app doesn't impact any other apps.  Wonderful.

The problem is this leads to code duplication (as each app has its own
copy of the private assembly).  People don't like code duplication, so
most (all?) platforms have some way of sharing code.  Thus, we have
public (shared) assemblies.

The key features of shared assemblies are twofold: (1) they're versioned
(like Unix shared libraries), so you can have multiple different
versions installed; and (2) they have a public key token, so that if two
developers, by chance, create a public assembly with the same name, they
can both be installed without clobbering each other.  This, plus
security assurances (you know if the code has been tampered with), is
why there's a public key token.

This is also why Michal's suggestion to place all assemblies in the GAC
is horribly broken -- the *only* assemblies that can go in the GAC are
assemblies *designed* to go in the GAC.  This implies that they'll
follow reasonable versioning policies, have a public/private key for
code signing, etc.  Private assemblies lack these assurances, and thus
are prohibited from being placed in the GAC.

With this bit of history out of the way, the problem currently being
discussed is that Linux isn't Windows.  Linux has package managers,
which like to know where all files are (a reasonable requirement).

Which is why I think Joshua Tauberer's suggestion is the sanest one so
far.  Each app's RPM has it's own copy of the shared assembly (in fact,
each *requires* their own copy), but places a copy of this shared
assembly into it's own app directory, e.g.


Then, during the post-run RPM stage, you can pass this file to gacutil.

(Why /usr/share/<app-name>?  To avoid filename conflicts with the
Assembly name.  Presumably app-names are less likely to conflict than
library names.)

On RPM removal, run gacutil in the pre-remove stage to remove the
SharedAssembly, then remove the actual file.

The problem with this is that you have two copies of the Shared Assembly
(one in the app's private dir, and one in the GAC).  This can be
"solved" using sym-links, as someone else suggested, but it isn't likely
to be an issue.

The benefit to this is if the GAC layout ever changes, the app needn't
care.  It's future proof.  That's what gacutil is for, after all.

(As an aside, an alternative to the above is to place the shared
assembly into it's own RPM, and use the standard RPM dependency handling
to ensure that the shared assembly is present.)

(As a further aside, the this process is directly equivalent to adding a
directory to /etc/ld.so.conf and running /sbin/ldconfig afterward.)

 - Jon

On Thu, 2003-10-23 at 19:18, Joshua Tauberer wrote:
> Here is another idea (I don't think it's been suggested yet)...
> Let packages install files in their own application-dependent way, e.g.:
> /usr/share/mymonoapp/version2/lib/MyMonoLibrary.dll
> and then have them call the GAC installation program on that file. 
> Gacutil might then add a symlink from the GAC path to the DLL.  If the 
> target of the symlink disappears or if it no longer points to the right 
> version of the DLL, then the symlink is deleted and the runtime 
> continues looking elsewhere for the DLL.  So, I think the RPM system 
> would still work as it should, and it doesn't expose the internals of 
> the GAC.  (Of course a database could, and probably should, be used 
> instead of symlinks.)
> Something to think about is that normally one doesn't have more than one 
> version of an RPM installed at a time (is it even possible?).  If a 
> package is making use of the GAC, then presumably it's because there's a 
> reason to have multiple versions installed, which isn't very RPM-like.

More information about the Mono-devel-list mailing list