* Establish the abbreviations DSO and PID in the lead paragraph
since they are used later.
* Parallelize descriptions of help, usage, and version options
with the "and exit" language used in getent(1), iconv(1),
locale(1), localedef(1), memusage(1), memusagestat(1),
mtrace(1), pldd(1), sprof(1), time(1), iconvconfig(8),
zdump(8), and zic(8).
Signed-off-by: G. Branden Robinson <g.branden.robinson@gmail.com>
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
glibc 2.30 isn't released yet, but a fix has been committed, and
Debian has even cherry-picked it for Debian GNU/Linux 10
("buster"). pldd works nicely now.
Signed-off-by: G. Branden Robinson <g.branden.robinson@gmail.com>
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
The --export-dynamic linker option is not the only way that main's
global symbols may end up in the dynamic symbol table and thus be
used to satisfy symbol reference in a shared object. A symbol
may also be placed into the dynamic symbol table if ld(1)
notices a dependency in another object during the static link.
Verified by experiment; see previous commit.
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
The existing text wrongly implied that symbol look up first
occurred in the object and then in main, and did not mention
whether dependencies of main where used for symbol resolution.
Verified by experiment:
$ cat prog.c
#define _GNU_SOURCE
#include <link.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void /* A function defined in both main and lib_x1 */
prog_x1(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
/* The following function is forced into prog's dynamic symbol table
because of the static link-time reference in lib_m1.so */
void /* A function defined in both main and lib_y1 */
prog_y1_exp(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
/* The following function is not forced into prog's dynamic symbol table */
void /* A function defined in both main and lib_y1 */
prog_y1_noexp(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
printf("\tName = %s\n", info->dlpi_name);
return 0;
}
int
main(int argc, char *argv[])
{
void *xHandle, *yHandle;
void (*funcp)(void);
char *err;
xHandle = dlopen("./lib_x1.so", RTLD_NOW | RTLD_GLOBAL);
if (xHandle == NULL) {
fprintf(stderr, "dlopen: %s\n", dlerror());
exit(EXIT_FAILURE);
}
yHandle = dlopen("./lib_y1.so", RTLD_NOW | RTLD_GLOBAL);
if (yHandle == NULL) {
fprintf(stderr, "dlopen: %s\n", dlerror());
exit(EXIT_FAILURE);
}
/* Optionally display the link map() */
if (argc > 1) {
printf("Link map as shown from dl_iterate_phdr() callbacks:\n");
dl_iterate_phdr(callback, NULL);
printf("\n");
}
(void) dlerror(); /* Clear dlerror() */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
funcp = (void (*)(void)) dlsym(yHandle, "y1_enter");
#pragma GCC diagnostic pop
err = dlerror();
if (err != NULL) {
fprintf(stderr, "dlsym: %s", err);
exit(EXIT_FAILURE);
}
(*funcp)();
exit(EXIT_SUCCESS);
}
$ cat lib_m1.c
#include <stdio.h>
void /* A function defined in both lib_m1 and lib_y1 */
m1_y1(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
#if 1
void
dummy(void)
{
extern void prog_y1_exp(void);
prog_y1_exp(); /* Forces prog_y1_exp into prog's dynamic symbol table,
so that it will be visible also to lib_y1.so */
}
#endif
$ cat lib_x1.c
#include <stdio.h>
void /* A function defined in both main and lib_x1 */
prog_x1(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
void /* A function defined in both lib_x1 and lib_y1 */
x1_y1(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
$ cat lib_y1.c
#include <stdio.h>
void /* A function defined in both lib_x1 and lib_y1 */
x1_y1(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
void /* A function defined in both main and lib_y1 */
prog_y1_exp(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
void /* A function defined in both lib_m1 and lib_y1 */
m1_y1(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
void /* A function defined in both main and lib_y1 */
prog_y1_noexp(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
void
y1_enter(void)
{
extern void y2(void);
printf("Called %s\n\n", __func__);
prog_x1();
prog_y1_exp();
prog_y1_noexp();
x1_y1();
m1_y1();
y2();
}
$ cat lib_y2.c
#include <stdio.h>
void
y2(void)
{
printf("Called %s::%s\n", __FILE__, __func__);
}
$ cat Build.sh
#!/bin/sh
CFLAGS="-Wno-implicit-function-declaration -Wl,--no-as-needed"
cc $CFLAGS -g -fPIC -shared -o lib_x1.so lib_x1.c
cc $CFLAGS -g -fPIC -shared -o lib_y2.so lib_y2.c
cc $CFLAGS -g -fPIC -shared -o lib_y1.so lib_y1.c ./lib_y2.so
cc $CFLAGS -g -fPIC -shared -o lib_m1.so lib_m1.c
#ED="-Wl,--export-dynamic"
cc $CFLAGS $ED -Wl,--rpath,$PWD -o prog prog.c -ldl lib_m1.so
$ sh Build.sh
$ ./prog x
Link map as shown from dl_iterate_phdr() callbacks:
Name =
Name = linux-vdso.so.1
Name = /lib64/libdl.so.2
Name = /home/mtk/tlpi/code/shlibs/dlopen_sym_res_expt/lib_m1.so
Name = /lib64/libc.so.6
Name = /lib64/ld-linux-x86-64.so.2
Name = ./lib_x1.so
Name = ./lib_y1.so
Name = ./lib_y2.so
Called y1_enter
Called lib_x1.c::prog_x1
Called prog.c::prog_y1_exp
Called lib_y1.c::prog_y1_noexp
Called lib_x1.c::x1_y1
Called lib_m1.c::m1_y1
Called lib_y2.c::y2
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
My earlier commit was in error:
commit 4a1af09bd1
Author: Michael Kerrisk <mtk.manpages@gmail.com>
Date: Sat Mar 14 21:40:35 2015 +0100
dlopen.3: Amend error in description of dlclose() behavior
-If the reference count drops to zero and no other loaded libraries use
-symbols in it, then the dynamic library is unloaded.
+If the reference count drops to zero,
+then the dynamic library is unloaded.
I doubted the removed text, because it provide little clue about
the scenario. The POSIX dlclose(3) specification actually details
the scenario sufficiently:
Although a dlclose() operation is not required to remove
any functions or data objects from the address space,
neither is an implementation prohibited from doing so.
The only restriction on such a removal is that no func‐
tion nor data object shall be removed to which references
have been relocated, until or unless all such references
are removed. For instance, an executable object file that
had been loaded with a dlopen() operation specifying the
RTLD_GLOBAL flag might provide a target for dynamic relo‐
cations performed in the processing of other relocatable
objects—in such environments, an application may assume
that no relocation, once made, shall be undone or remade
unless the executable object file containing the relo‐
cated object has itself been removed.
Verified by experiment:
$ cat openlibs.c # Test program
int
main(int argc, char *argv[])
{
void *libHandle[MAX_LIBS];
int lcnt;
if (argc < 2) {
fprintf(stderr, "Usage: %s lib-path...\n", argv[0]);
exit(EXIT_FAILURE);
}
lcnt = 0;
for (int j = 1; j < argc; j++) {
if (argv[j][0] != '-') {
if (lcnt >= MAX_LIBS) {
fprintf(stderr, "Too many libraries (limit: %d)\n", MAX_LIBS);
exit(EXIT_FAILURE);
}
printf("[%d] Opening %s\n", lcnt, argv[j]);
libHandle[lcnt] = dlopen(argv[j], RTLD_NOW | RTLD_GLOBAL);
if (libHandle[lcnt] == NULL) {
fprintf(stderr, "dlopen: %s\n", dlerror());
exit(EXIT_FAILURE);
}
lcnt++;
} else { /* "-N" closes the Nth handle */
int i = atoi(&argv[j][1]);
printf("Closing handle %d\n", i);
dlclose(libHandle[i]);
}
sleep(1);
printf("\n");
}
printf("Program about to exit\n");
exit(EXIT_SUCCESS);
}
$ cat lib_x1.c
void x1_func(void) { printf("Hello world\n"); }
__attribute__((constructor)) void x1_cstor(void)
{ printf("Called %s\n", __FUNCTION__); }
__attribute__((destructor)) void x1_dstor(void)
{ printf("Called %s\n", __FUNCTION__); }
$ cat lib_y1.c
void y1_func(void) { printf("Hello world\n"); }
__attribute__((constructor)) void y1_cstor(void)
{ printf("Called %s\n", __FUNCTION__); }
__attribute__((destructor)) void y1_dstor(void)
{ printf("Called %s\n", __FUNCTION__); }
static void testref(void) {
/* The following reference, to a symbol in lib_x1.so shows that
RTLD_GLOBAL may pin a library when it might otherwise have been
released with dlclose() */
extern void x1_func(void);
x1_func();
}
$ cc -shared -fPIC -o lib_x1.so lib_x1.c
$ cc -shared -fPIC -o lib_y1.so lib_y1.c
$ cc -o openlibs openlibs.c -ldl
$ LD_LIBRARY_PATH=. ./openlibs lib_x1.so lib_y1.so -0 -1
[0] Opening lib_x1.so
Called x1_cstor
[1] Opening lib_y1.so
Called y1_cstor
Closing handle 0
Closing handle 1
Called y1_dstor
Called x1_dstor
Program about to exit
<end program output>
Note that x1_dstor was called only when handle 1 (lib_y1.so) was closed.
But, if we edit lib_y1 to remove the reference to x1_func(), things are
different:
$ cat lib_y1.c # After editing
void y1_func(void) { printf("Hello world\n"); }
__attribute__((constructor)) void y1_cstor(void)
{ printf("Called %s\n", __FUNCTION__); }
__attribute__((destructor)) void y1_dstor(void)
{ printf("Called %s\n", __FUNCTION__); }
static void testref(void) {
// extern void x1_func(void);
// x1_func();
}
$ cc -shared -fPIC -o lib_y1.so lib_y1.c
$ LD_LIBRARY_PATH=. ./openlibs lib_x1.so lib_y1.so -0 -1
[0] Opening lib_x1.so
Called x1_cstor
[1] Opening lib_y1.so
Called y1_cstor
Closing handle 0
Called x1_dstor
Closing handle 1
Called y1_dstor
Program about to exit
<end program output>
This time, x1_dstor was called when handle 0 (lib_x1.so) was closed.
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
See fs/xattr.c::xattr_permission()"
/*
* In the user.* namespace, only regular files and directories can have
* extended attributes. For sticky directories, only the owner and
* privileged users can write attributes.
*/
if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) {
if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
return (mask & MAY_WRITE) ? -EPERM : -ENODATA;
if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) &&
(mask & MAY_WRITE) && !inode_owner_or_capable(inode))
return -EPERM;
}
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
If the file descriptors received in SCM_RIGHTS would cause
the process to its exceed RLIMIT_NOFILE limit, the excess
FDs are discarded.
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
Long ago, the sysvipc.7 page was called ipc.5, which was both a
misnaming (too general a name) and an inconsistent section. The
page was renamed (to svipc.7) many years ago, and the link with
the old name has probably ceased to be needed. So, remove it.
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>