Skip to contentSkip to conversation

clumsy cast in dlopen.3

PATCH In review Bruno Haible 4 participants 10 messages May 14 – May 17 Man-pages: manual pages for Linux -- Sections 2, 3, 4, 5, and 7

lore
Share & tools
lore
#0 lore raw

The dlopen.3 man page contains this text:

*(void **) &cosine = dlsym(handle, "cos");

This (clumsy) cast conforms with the ISO C standard and will avoid any compiler warnings.

However, such a cast violates the strict aliasing rules of ISO C, no?

The proper workaround is to use a union:

  union { double (*cosine) (double); void *pointer; } u;

  u.pointer = dlsym(handle, "cos");
  ...
  printf("%f\n", u.cosine(2.0));

Bruno

#1 lore raw

Hi Bruno,

Quoted 8 lines · Bruno Haible

The dlopen.3 man page contains this text:

*(void **) &cosine = dlsym(handle, "cos");

This (clumsy) cast conforms with the ISO C standard and will avoid any compiler warnings.

However, such a cast violates the strict aliasing rules of ISO C, no?

I think I agree. Dereferencing the pointer &cosine with a type different than the type of the object is not allowed. I've CCed Martin, who might be able to confirm.

Quoted 8 lines

The proper workaround is to use a union:

  union { double (*cosine) (double); void *pointer; } u;

  u.pointer = dlsym(handle, "cos");
  ...
  printf("%f\n", u.cosine(2.0));

This is seems much better, indeed.

Have a lovely day!
Alex

Quoted 6 lines

Bruno

Signature · 1 line
<https://www.alejandro-colomar.es>
#2 lore raw

Hello,
I agree the cast is not nice, (someone for a extension of C standard ?) but i have to admit that i have never seen the trick with the union. But it needs some explaination. The comment in the example is already huge, i would ask for a comment subsektion for this behavier here.

btw: the original code in the example looks like this ... cosine = (typeof(double (double)) *) dlsym(handle, "cos");

my2c
wh

________________________________________
Von: Alejandro Colomar <[email protected]>
Gesendet: Donnerstag, 14. Mai 2026 13:29:20
An: Bruno Haible
Cc: [email protected]; Martin Uecker
Betreff: Re: clumsy cast in dlopen.3

Hi Bruno,

Quoted 8 lines · Bruno Haible

The dlopen.3 man page contains this text:

*(void **) &cosine = dlsym(handle, "cos");

This (clumsy) cast conforms with the ISO C standard and will avoid any compiler warnings.

However, such a cast violates the strict aliasing rules of ISO C, no?

I think I agree. Dereferencing the pointer &cosine with a type different than the type of the object is not allowed. I've CCed Martin, who might be able to confirm.

Quoted 8 lines

The proper workaround is to use a union:

  union { double (*cosine) (double); void *pointer; } u;

  u.pointer = dlsym(handle, "cos");
  ...
  printf("%f\n", u.cosine(2.0));

This is seems much better, indeed.

Have a lovely day!
Alex

Quoted 6 lines

Bruno

--
<https://www.alejandro-colomar.es>

#3 lore raw

Hi Walter,

Quoted 5 lines · Walter Harms

Hello,
I agree the cast is not nice, (someone for a extension of C standard ?) but i have to admit that i have never seen the trick with the union. But it needs some explaination. The comment in the example is already huge, i would ask for a comment subsektion for this behavier here.

The thing about unions is that the only two ways for type punning that are blessed by ISO C are unions and memcpy(3). Everything else isn't allowed.

Perfectly valid:

	static_assert(sizeof(int) == sizeof(float));

	union u {int i; float f;};

	float    f;
	union u  u;

	u.i = 42;
	f = u.f;

Perfectly valid:

	static_assert(sizeof(int) == sizeof(float));

	int    i;
	float  f;

	i = 42;
	memcpy(&f, &i, sizeof(float));

UB:

	static_assert(sizeof(int) == sizeof(float));

	int    i;
	float  f;

	i = 42;
	f = *(float *) &i;

Have a lovely day!
Alex

Quoted 53 lines

btw: the original code in the example looks like this ... cosine = (typeof(double (double)) *) dlsym(handle, "cos");

my2c
wh

________________________________________
Von: Alejandro Colomar <[email protected]>
Gesendet: Donnerstag, 14. Mai 2026 13:29:20
An: Bruno Haible
Cc: [email protected]; Martin Uecker
Betreff: Re: clumsy cast in dlopen.3

Hi Bruno,

Quoted 8 lines · Bruno Haible

The dlopen.3 man page contains this text:

*(void **) &cosine = dlsym(handle, "cos");

This (clumsy) cast conforms with the ISO C standard and will avoid any compiler warnings.

However, such a cast violates the strict aliasing rules of ISO C, no?

I think I agree. Dereferencing the pointer &cosine with a type different than the type of the object is not allowed. I've CCed Martin, who might be able to confirm.

Quoted 8 lines

The proper workaround is to use a union:

  union { double (*cosine) (double); void *pointer; } u;

  u.pointer = dlsym(handle, "cos");
  ...
  printf("%f\n", u.cosine(2.0));

This is seems much better, indeed.

Have a lovely day!
Alex

Quoted 6 lines

Bruno

--
<https://www.alejandro-colomar.es>

Signature · 1 line
<https://www.alejandro-colomar.es>
#4 lore raw

There is no such thing needed here, you just do

double (*cosine)(double);

cosine = dlsym(handle, "cos");

Best,
Martin

Am Freitag, dem 15.05.2026 um 11:56 +0200 schrieb Alejandro Colomar:

Quoted 102 lines

Hi Walter,

Quoted 5 lines · Walter Harms

Hello,
I agree the cast is not nice, (someone for a extension of C standard ?) but i have to admit that i have never seen the trick with the union. But it needs some explaination. The comment in the example is already huge, i would ask for a comment subsektion for this behavier here.

The thing about unions is that the only two ways for type punning that are blessed by ISO C are unions and memcpy(3). Everything else isn't allowed.

Perfectly valid:

	static_assert(sizeof(int) == sizeof(float));

	union u {int i; float f;};

	float    f;
	union u  u;

	u.i = 42;
	f = u.f;

Perfectly valid:

	static_assert(sizeof(int) == sizeof(float));

	int    i;
	float  f;

	i = 42;
	memcpy(&f, &i, sizeof(float));

UB:

	static_assert(sizeof(int) == sizeof(float));

	int    i;
	float  f;

	i = 42;
	f = *(float *) &i;

Have a lovely day!
Alex

Quoted 53 lines

btw: the original code in the example looks like this ... cosine = (typeof(double (double)) *) dlsym(handle, "cos");

my2c
wh

________________________________________
Von: Alejandro Colomar <[email protected]>
Gesendet: Donnerstag, 14. Mai 2026 13:29:20
An: Bruno Haible
Cc: [email protected]; Martin Uecker
Betreff: Re: clumsy cast in dlopen.3

Hi Bruno,

Quoted 8 lines · Bruno Haible

The dlopen.3 man page contains this text:

*(void **) &cosine = dlsym(handle, "cos");

This (clumsy) cast conforms with the ISO C standard and will avoid any compiler warnings.

However, such a cast violates the strict aliasing rules of ISO C, no?

I think I agree. Dereferencing the pointer &cosine with a type different than the type of the object is not allowed. I've CCed Martin, who might be able to confirm.

Quoted 8 lines

The proper workaround is to use a union:

  union { double (*cosine) (double); void *pointer; } u;

  u.pointer = dlsym(handle, "cos");
  ...
  printf("%f\n", u.cosine(2.0));

This is seems much better, indeed.

Have a lovely day!
Alex

Quoted 6 lines

Bruno

--
<https://www.alejandro-colomar.es>

#5 lore raw

Hi Martin,

Quoted 6 lines · Martin Uecker

There is no such thing needed here, you just do

double (*cosine)(double);

cosine = dlsym(handle, "cos");

Hmmmm, yes. I don't know why the manual page uses a cast in the POSIX version.

The above wouldn't be enough in ISO C, since void* isn't guaranteed to work with function pointers. However, any systems where dlsym(3) works must support holding function pointers in void* (because otherwise, dlsym(3) itself wouldn't work), so ISO C shouldn't be a concern at all.

Hmmm; I'll simplify the page.

Thanks!

Have a lovely day!
Alex

Quoted 108 lines

Best,
Martin

Am Freitag, dem 15.05.2026 um 11:56 +0200 schrieb Alejandro Colomar:

Quoted 102 lines

Hi Walter,

Quoted 5 lines · Walter Harms

Hello,
I agree the cast is not nice, (someone for a extension of C standard ?) but i have to admit that i have never seen the trick with the union. But it needs some explaination. The comment in the example is already huge, i would ask for a comment subsektion for this behavier here.

The thing about unions is that the only two ways for type punning that are blessed by ISO C are unions and memcpy(3). Everything else isn't allowed.

Perfectly valid:

	static_assert(sizeof(int) == sizeof(float));

	union u {int i; float f;};

	float    f;
	union u  u;

	u.i = 42;
	f = u.f;

Perfectly valid:

	static_assert(sizeof(int) == sizeof(float));

	int    i;
	float  f;

	i = 42;
	memcpy(&f, &i, sizeof(float));

UB:

	static_assert(sizeof(int) == sizeof(float));

	int    i;
	float  f;

	i = 42;
	f = *(float *) &i;

Have a lovely day!
Alex

Quoted 53 lines

btw: the original code in the example looks like this ... cosine = (typeof(double (double)) *) dlsym(handle, "cos");

my2c
wh

________________________________________
Von: Alejandro Colomar <[email protected]>
Gesendet: Donnerstag, 14. Mai 2026 13:29:20
An: Bruno Haible
Cc: [email protected]; Martin Uecker
Betreff: Re: clumsy cast in dlopen.3

Hi Bruno,

Quoted 8 lines · Bruno Haible

The dlopen.3 man page contains this text:

*(void **) &cosine = dlsym(handle, "cos");

This (clumsy) cast conforms with the ISO C standard and will avoid any compiler warnings.

However, such a cast violates the strict aliasing rules of ISO C, no?

I think I agree. Dereferencing the pointer &cosine with a type different than the type of the object is not allowed. I've CCed Martin, who might be able to confirm.

Quoted 8 lines

The proper workaround is to use a union:

  union { double (*cosine) (double); void *pointer; } u;

  u.pointer = dlsym(handle, "cos");
  ...
  printf("%f\n", u.cosine(2.0));

This is seems much better, indeed.

Have a lovely day!
Alex

Quoted 6 lines

Bruno

--
<https://www.alejandro-colomar.es>

Signature · 1 line
<https://www.alejandro-colomar.es>
#6 lore raw

Just for clarification, my comment was not about the patch itself. I was just expressing my fear that a comment discussing various complex topics will be added to the example and confusing future readers. I have notice that Bruno has send a patch removing most of the comments, i am happy with that outcome.

CU
________________________________________
Von: Alejandro Colomar <[email protected]>
Gesendet: Freitag, 15. Mai 2026 17:15:03
An: Martin Uecker
Cc: Walter Harms; Bruno Haible; [email protected]
Betreff: Re: AW: clumsy cast in dlopen.3

Hi Martin,

Quoted 6 lines · Martin Uecker

There is no such thing needed here, you just do

double (*cosine)(double);

cosine = dlsym(handle, "cos");

Hmmmm, yes. I don't know why the manual page uses a cast in the POSIX version.

The above wouldn't be enough in ISO C, since void* isn't guaranteed to work with function pointers. However, any systems where dlsym(3) works must support holding function pointers in void* (because otherwise, dlsym(3) itself wouldn't work), so ISO C shouldn't be a concern at all.

Hmmm; I'll simplify the page.

Thanks!

Have a lovely day!
Alex

Quoted 108 lines

Best,
Martin

Am Freitag, dem 15.05.2026 um 11:56 +0200 schrieb Alejandro Colomar:

Quoted 102 lines

Hi Walter,

Quoted 5 lines · Walter Harms

Hello,
I agree the cast is not nice, (someone for a extension of C standard ?) but i have to admit that i have never seen the trick with the union. But it needs some explaination. The comment in the example is already huge, i would ask for a comment subsektion for this behavier here.

The thing about unions is that the only two ways for type punning that are blessed by ISO C are unions and memcpy(3). Everything else isn't allowed.

Perfectly valid:

    static_assert(sizeof(int) == sizeof(float));

    union u {int i; float f;};

    float    f;
    union u  u;

    u.i = 42;
    f = u.f;

Perfectly valid:

    static_assert(sizeof(int) == sizeof(float));

    int    i;
    float  f;

    i = 42;
    memcpy(&f, &i, sizeof(float));

UB:

    static_assert(sizeof(int) == sizeof(float));

    int    i;
    float  f;

    i = 42;
    f = *(float *) &i;

Have a lovely day!
Alex

Quoted 53 lines

btw: the original code in the example looks like this ... cosine = (typeof(double (double)) *) dlsym(handle, "cos");

my2c
wh

________________________________________
Von: Alejandro Colomar <[email protected]>
Gesendet: Donnerstag, 14. Mai 2026 13:29:20
An: Bruno Haible
Cc: [email protected]; Martin Uecker
Betreff: Re: clumsy cast in dlopen.3

Hi Bruno,

Quoted 8 lines · Bruno Haible

The dlopen.3 man page contains this text:

*(void **) &cosine = dlsym(handle, "cos");

This (clumsy) cast conforms with the ISO C standard and will avoid any compiler warnings.

However, such a cast violates the strict aliasing rules of ISO C, no?

I think I agree. Dereferencing the pointer &cosine with a type different than the type of the object is not allowed. I've CCed Martin, who might be able to confirm.

Quoted 8 lines

The proper workaround is to use a union:

  union { double (*cosine) (double); void *pointer; } u;

  u.pointer = dlsym(handle, "cos");
  ...
  printf("%f\n", u.cosine(2.0));

This is seems much better, indeed.

Have a lovely day!
Alex

Quoted 6 lines

Bruno

--
<https://www.alejandro-colomar.es>

--
<https://www.alejandro-colomar.es>

#7 lore raw

PATCH 1/1 man/man3/dlopen.3: EXAMPLES: Simplify use of dlsym(3)

In reply to Martin Uecker

The cast is entirely unnecessary. Conversion from/to void* and function pointers is implicit, and it's guaranteed by POSIX. ISO C is irrelevant here, because dlsym(3) requires POSIX, which provides the stronger guarantees that we need.

And even before POSIX standardized these guarantees about void* and function pointers, and system implementing dlsym(3) would have to support them anyway, as otherwise dlsym(3) couldn't work at all.

---
 man/man3/dlopen.3 | 20 ++++----------------
 1 file changed, 4 insertions(+), 16 deletions(-)
man/man3/dlopen.3 +4 -16
index 482af099..d9be7502 100644
@@ -567,23 +567,11 @@ .SS Program source
 \&
     dlerror();    /* Clear any existing error */
 \&
-    cosine = (typeof(double (double)) *) dlsym(handle, "cos");
+    cosine = dlsym(handle, "cos");
 \&
-    /* According to the ISO C standard, casting between function
-       pointers and \[aq]void *\[aq], as done above, produces undefined results.
-       POSIX.1\-2001 and POSIX.1\-2008 accepted this state of affairs and
-       proposed the following workaround:
-\&
-           *(void **) &cosine = dlsym(handle, "cos");
-\&
-       This (clumsy) cast conforms with the ISO C standard and will
-       avoid any compiler warnings.
-\&
-       The 2013 Technical Corrigendum 1 to POSIX.1\-2008 improved matters
-       by requiring that conforming implementations support casting
-       \[aq]void *\[aq] to a function pointer.  Nevertheless, some compilers
-       (e.g., gcc with the \[aq]\-pedantic\[aq] option) may complain about the
-       cast used in this program.  */
+    /* The 2013 Technical Corrigendum 1 to POSIX.1\-2008
+       required that conforming implementations support
+       converting \[aq]void *\[aq] to a function pointer.  */
 .\" http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html#tag_03_112_08
 .\" http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html#tag_16_96_07
 .\" http://austingroupbugs.net/view.php?id=74

Range-diff against v0:
-: -------- > 1: 508bb497 man/man3/dlopen.3: EXAMPLES: Simplify use of dlsym(3)

Signature · 1 line
2.53.0
#8 lore raw

Re: [PATCH v1] man/man3/dlopen.3: EXAMPLES: Simplify use of dlsym(3)

The patch looks good to me. Thanks for removing the misleading comment!

Bruno

#9 lore raw

Re: [PATCH v1] man/man3/dlopen.3: EXAMPLES: Simplify use of dlsym(3)

Hi Bruno,

Quoted 1 line · Bruno Haible

The patch looks good to me. Thanks for removing the misleading comment!

You're welcome! Thanks for the report! I've taken your reply as an Acked-by tag in the patch.

Have a lovely night!
Alex

Quoted 4 lines

Bruno

Signature · 1 line
<https://www.alejandro-colomar.es>