"Shared libraries and bundles don't have direct access to environ, which
is only available to the loader ld(1) when a complete program is being
linked. The environment routines can still be used, but if direct access
to environ is needed, the _NSGetEnviron() routine, defined in
<crt_externs.h>, can be used to retrieve the address of environ at run-
time."

That command line looks suspect to me:
1. "-o libocaml_rl_pi_rnn.so": this tells ocamlopt to name its output file libocaml_rl_pi_rnn.so, but there is nothing to tell it that you want to build a shared library, so ocamlopt is going to make an executable and name it libocaml_rl_pi_rnn.so. This is probably not what you want. Note that you never get "undefined symbol" errors when building shared libraries.
2. "-ccopt -shared":
from gcc's man page:
-shared
Produce a shared object which can then be linked with other objects
to form an executable. Not all systems support this option. For
predictable results, you must also specify the same set of options
that were used to generate code (-fpic, -fPIC, or model suboptions)
when you specify this option.[1]

This option is not supported on Mac OS X.

In any case, I cannot reproduce the bug as reported. When I do "ocamlopt -o foo.so unix.cmxa -ccopt -shared", I get errors, not only on _environ, but also on _caml_atom_table, _caml_code_area_start, and _caml_code_area_end.

I have uploaded an example that's almost working, but when I run a.out I get this error:
dlopen(./foo.so, 2): Symbol not found: _caml_atom_table
Referenced from: /Users/doligez/dynlink-example/foo.so
Expected in: flat namespace
in /Users/doligez/dynlink-example/foo.so

OK, what I was missing is the surprising semantics of COMMON data in C object files and its variations across C linkers...

We'll probably have a better fix in a future version of OCaml, but for now you need to reference a function from "startup.c", to prevent the Mac OS X linker from dropping the file when building your shared library.

See the new version of my example code (I've also fixed bar.c to avoid a segfault).

Note for the future: the best solution we found so far is to add an initializer to every global variable of the runtime system, to force them into the data segment.
An other possibility: make the OCaml compiler output references to caml_atom_table as common rather than imported symbol.