On Sun, Apr 28, 2024 at 01:02:26PM +0100, Alberto Bertogli wrote:
The problem seems to be that some of the functions that have 64-bit variants (e.g. pread64, pwrite64) have an assembler name declared for the regular variant in the header; while other platforms don't do that and have the two functions declared separately.

https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html

For example, this is the declaration of pread and pwrite in a post-preprocessed file, diff between x86_64 (without the bug, '-') and armel (with the bug, '+'):

```
@@ -1068,18 +1111,14 @@
extern ssize_t write (int __fd, const void *__buf, size_t __n)
    __attribute__ ((__access__ (__read_only__, 2, 3)));
-# 389 "/usr/include/unistd.h" 3 4
-extern ssize_t pread (int __fd, void *__buf, size_t __nbytes,
-        __off_t __offset)
-    __attribute__ ((__access__ (__write_only__, 2, 3)));
-
-
+# 404 "/usr/include/unistd.h" 3 4
+extern ssize_t pread (int __fd, void *__buf, size_t __nbytes, __off64_t __offset) __asm__ 
("" "pread64")
+    __attribute__ ((__access__ (__write_only__, 2, 3)));
+extern ssize_t pwrite (int __fd, const void *__buf, size_t __nbytes, __off64_t __offset) __asm__ 
("" "pwrite64")
-extern ssize_t pwrite (int __fd, const void *__buf, size_t __n,
-         __off_t __offset)
    __attribute__ ((__access__ (__read_only__, 2, 3)));
# 422 "/usr/include/unistd.h" 3 4
extern ssize_t pread64 (int __fd, void *__buf, size_t __nbytes,
```

That's why it's the assembler (and not linking) stage that's complaining, because that means both functions end up named as the 64-bit variant. This can be seen in the assembly file. Continuing to use pread/pread64 as an example, there is no definition for pread(), only pread64() twice: once for pread and one for pread64.

It's tricky to support this in a generic way, because it's difficult to detect this is even happening, as the assembler name operates at a compiler level so we can't just undo it.

More things that make this interesting.

This program:

```
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

int main() {
        printf("pread: %p\n", pread);
        printf("pread64: %p\n", pread64);
        printf("pread64 is pread: %b\n", pread == pread64);

        void *lib = dlopen(NULL, RTLD_NOW);
        void *l_pread = dlsym(lib, "pread");
        void *l_pread64 = dlsym(lib, "pread64");

        printf("l_pread: %p\n", l_pread);
        printf("l_pread64: %p\n", l_pread64);
        printf("l_pread64 is l_pread: %b\n", l_pread == l_pread64);
}
```

Built with:
  cc -save-temps=obj -D_XOPEN_SOURCE=600 -D_LARGEFILE64_SOURCE=1  -std=c99 
-Wall demo.c

Prints this on x86_64 Debian testing (which does not show this bug):

```
pread: 0x7fc1970f0c10
pread64: 0x7fc1970f0c10
pread64 is pread: 1
l_pread: 0x7fc1970f0c10
l_pread64: 0x7fc1970f0c10
l_pread64 is l_pread: 1
```

And prints this on the armel chroot:

```
pread: 0xf7da6a90
pread64: 0xf7da6a90
pread64 is pread: 1
l_pread: 0xf7da68f8
l_pread64: 0xf7da6a90
l_pread64 is l_pread: 0
```

So on x86_64 both pread() and pread64() are the same function, yet the headers allow us to declare them individually.

But on armel, even though there is a separate implementation of pread() on libc, the headers prevent us from declaring them separately (because of the asm name declaration in unistd.h).

Just putting this here in case it helps someone else debug a similar problem in the future.

I'm still trying to find a reasonable workaround.

Thanks,
                Alberto

Reply via email to