need help with GLibc module map problem


(Dave Grove) #1

As part of helping to resolve old libdispatch pull requests, I have run
into a problem with the GLibc module map on Linux that I need help
understanding and fixing.

The symptom I am trying to resolve is that somewhere between the March 16
and March 24 Swift development snapshots, the Dispatch overlay for
libdispatch on Linux stopped building. We've been hacking around the
problem for 2 months (
https://github.com/apple/swift-corelibs-libdispatch/pull/62), but we don't
want to merge that hack back to the master branch of libdispatch.

The issue is that swiftc is convinced that off_t and mode_t should be
defined in the stdio module (by including stdio.h). We don't want to
include stdio.h in dispatch.h; it is sufficient (and the C compilation of
libdispatch succeeds) to only include fcntl.h and unistd.h. However
without including stdio.h in dispatch.h, building the swiftmodule from
Dispatch.swift fails, starting with the March 24 driver (and is still
broken on master as of this morning).

I can hack around this by (a) including stdio.h in dispatch.h or (b)
editing glibc.modulemap.gyb to put the module statements for fnctl and
unistd before the modile statement for stdio. Neither of these seem like
the right fix.

Can anyone point me to a better solution?

I've attached the full buildlog. The actual build errors are excerpted
below as well.

thanks,

--dave

(See attached file: buildLog.txt)

make[2]: Entering directory
'/home/dgrove/swift/build/dpg/libdispatch-linux-x86_64/src'
/home/dgrove/swift/build/dpg/swift-linux-x86_64/bin/swiftc -Xcc
-fmodule-map-file=/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/module.map
-I/home/dgrove/swift/swift-corelibs-libdispatch -parse-as-library -Xcc
-fblocks -c
-o /home/dgrove/swift/build/dpg/libdispatch-linux-x86_64/src/Dispatch.o /home/dgrove/swift/swift-corelibs-libdispatch/src/swift/Dispatch.swift
<module-includes>:1:10: note: in file included from <module-includes>:1:
#include "dispatch.h"
         ^
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/dispatch.h:59:10:
note: in file included
from /home/dgrove/swift/swift-corelibs-libdispatch/dispatch/dispatch.h:59:
#include <dispatch/io.h>
         ^
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/io.h:253:31: error:
declaration of 'mode_t' must be imported from module
'SwiftGlibc.POSIX.sys.types' before it is required
        const char *path, int oflag, mode_t mode,
                                     ^
/usr/include/x86_64-linux-gnu/sys/types.h:70:18: note: previous declaration
is here
typedef __mode_t mode_t;
                 ^
<module-includes>:1:10: note: in file included from <module-includes>:1:
#include "dispatch.h"
         ^
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/dispatch.h:59:10:
note: in file included
from /home/dgrove/swift/swift-corelibs-libdispatch/dispatch/dispatch.h:59:
#include <dispatch/io.h>
         ^
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/io.h:355:2: error:
declaration of 'off_t' must be imported from module 'SwiftGlibc.C.stdio'
before it is required
        off_t offset,
        ^
/usr/include/stdio.h:90:17: note: previous declaration is here
typedef __off_t off_t;
                ^
/home/dgrove/swift/swift-corelibs-libdispatch/src/swift/Dispatch.swift:13:19:
error: could not build Objective-C module 'Dispatch'
@_exported import Dispatch
                  ^
Makefile:909: recipe for target
'/home/dgrove/swift/build/dpg/libdispatch-linux-x86_64/src/Dispatch.o'
failed
make[2]: ***
[/home/dgrove/swift/build/dpg/libdispatch-linux-x86_64/src/Dispatch.o]
Error 1
make[2]: Leaving directory
'/home/dgrove/swift/build/dpg/libdispatch-linux-x86_64/src'
Makefile:493: recipe for target 'all' failed
make[1]: *** [all] Error 2
make[1]: Leaving directory
'/home/dgrove/swift/build/dpg/libdispatch-linux-x86_64/src'
Makefile:454: recipe for target 'all-recursive' failed
make: *** [all-recursive] Error 1
./utils/build-script: command terminated with a non-zero exit status 2,
aborting
./utils/build-script: command terminated with a non-zero exit status 1,
aborting

buildLog.txt (61.5 KB)


(Dmitri Gribenko) #2

Could you check if there are multiple definitions of off_t or mode_t
in different headers guarded by macros? Something like this,
duplicated across several headers:

#if !defined(_OFF_T_DEFINED)
typedef __off_t off_t;
#define _OFF_T_DEFINED
#endif

This is a frequent pattern used in C headers, but it is an antipattern
for modules. With modules, all headers that are included into the
module get #included into a single translation unit and compiled into
a module, so only one of those typedefs will get activated (let's say
in foo.h), and the other one will get hidden because it is seen second
in the translation unit (let's say the one in bar.h gets hidden). If
you then import just the submodule for bar.h, the off_t definition
won't be visible, because it was #ifdef'ed out.

There are fragile solutions to this (like including a header that
happens to fix the build this time), but the only real fix is to make
sure that there is only one place where a declaration can be located
in a module, regardless of the set of headers.

Dmitri

···

On Thu, Jun 2, 2016 at 2:58 PM, David P Grove via swift-dev <swift-dev@swift.org> wrote:

As part of helping to resolve old libdispatch pull requests, I have run into
a problem with the GLibc module map on Linux that I need help understanding
and fixing.

The symptom I am trying to resolve is that somewhere between the March 16
and March 24 Swift development snapshots, the Dispatch overlay for
libdispatch on Linux stopped building. We've been hacking around the problem
for 2 months (https://github.com/apple/swift-corelibs-libdispatch/pull/62),
but we don't want to merge that hack back to the master branch of
libdispatch.

The issue is that swiftc is convinced that off_t and mode_t should be
defined in the stdio module (by including stdio.h). We don't want to include
stdio.h in dispatch.h; it is sufficient (and the C compilation of
libdispatch succeeds) to only include fcntl.h and unistd.h. However without
including stdio.h in dispatch.h, building the swiftmodule from
Dispatch.swift fails, starting with the March 24 driver (and is still broken
on master as of this morning).

I can hack around this by (a) including stdio.h in dispatch.h or (b) editing
glibc.modulemap.gyb to put the module statements for fnctl and unistd before
the modile statement for stdio. Neither of these seem like the right fix.

Can anyone point me to a better solution?

I've attached the full buildlog. The actual build errors are excerpted below
as well.

thanks,

--dave

(See attached file: buildLog.txt)

make[2]: Entering directory
'/home/dgrove/swift/build/dpg/libdispatch-linux-x86_64/src'
/home/dgrove/swift/build/dpg/swift-linux-x86_64/bin/swiftc -Xcc
-fmodule-map-file=/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/module.map
-I/home/dgrove/swift/swift-corelibs-libdispatch -parse-as-library -Xcc
-fblocks -c -o
/home/dgrove/swift/build/dpg/libdispatch-linux-x86_64/src/Dispatch.o
/home/dgrove/swift/swift-corelibs-libdispatch/src/swift/Dispatch.swift
<module-includes>:1:10: note: in file included from <module-includes>:1:
#include "dispatch.h"
^
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/dispatch.h:59:10:
note: in file included from
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/dispatch.h:59:
#include <dispatch/io.h>
^
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/io.h:253:31: error:
declaration of 'mode_t' must be imported from module
'SwiftGlibc.POSIX.sys.types' before it is required
const char *path, int oflag, mode_t mode,
^
/usr/include/x86_64-linux-gnu/sys/types.h:70:18: note: previous declaration
is here
typedef __mode_t mode_t;
^
<module-includes>:1:10: note: in file included from <module-includes>:1:
#include "dispatch.h"
^
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/dispatch.h:59:10:
note: in file included from
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/dispatch.h:59:
#include <dispatch/io.h>
^
/home/dgrove/swift/swift-corelibs-libdispatch/dispatch/io.h:355:2: error:
declaration of 'off_t' must be imported from module 'SwiftGlibc.C.stdio'
before it is required
off_t offset,
^
/usr/include/stdio.h:90:17: note: previous declaration is here
typedef __off_t off_t;

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Dmitri Gribenko) #3

Try including sys/types.h before including any other header that can
define these types. (For example, you can just include it at the
top.)

Another thing to try is to move sys/types.h to the very top in the
modulemap, to make sure that it gets included first, and gets to "own"
the definition.

But the only real fix is to work with EGLIBC to make the headers
module-friendly. You might want to ask on the cfe-dev, the Clang dev
list, if there are other people interested in this. (I think Google
might be, they are contributing heavily to the Clang modules
implementation.)

Dmitri

···

On Fri, Jun 3, 2016 at 8:23 AM, David P Grove <groved@us.ibm.com> wrote:

Dmitri Gribenko <gribozavr@gmail.com> wrote on 06/02/2016 06:19:33 PM: >> >> Could you check if there are multiple definitions of off_t or mode_t >> in different headers guarded by macros? Something like this, >> duplicated across several headers:

#if !defined(_OFF_T_DEFINED)
typedef __off_t off_t;
#define _OFF_T_DEFINED
#endif

This is a frequent pattern used in C headers, but it is an antipattern
for modules. With modules, all headers that are included into the
module get #included into a single translation unit and compiled into
a module, so only one of those typedefs will get activated (let's say
in foo.h), and the other one will get hidden because it is seen second
in the translation unit (let's say the one in bar.h gets hidden). If
you then import just the submodule for bar.h, the off_t definition
won't be visible, because it was #ifdef'ed out.

There are fragile solutions to this (like including a header that
happens to fix the build this time), but the only real fix is to make
sure that there is only one place where a declaration can be located
in a module, regardless of the set of headers.

Thanks for the response Dmitri. Yes this is indeed the case. On my Ubuntu
15.10 system there are conditional typedefs of this flavor in 5
/usr/include/* header files for mode_t and in 6 header files for off_t.

What do you recommend I should try to work around the messy state of the
system headers?

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Dave Grove) #4

Thanks for the response Dmitri. Yes this is indeed the case. On my Ubuntu
15.10 system there are conditional typedefs of this flavor in
5 /usr/include/* header files for mode_t and in 6 header files for off_t.

What do you recommend I should try to work around the messy state of the
system headers?

--dave

···

Dmitri Gribenko <gribozavr@gmail.com> wrote on 06/02/2016 06:19:33 PM: > > Could you check if there are multiple definitions of off_t or mode_t > in different headers guarded by macros? Something like this, > duplicated across several headers:

#if !defined(_OFF_T_DEFINED)
typedef __off_t off_t;
#define _OFF_T_DEFINED
#endif

This is a frequent pattern used in C headers, but it is an antipattern
for modules. With modules, all headers that are included into the
module get #included into a single translation unit and compiled into
a module, so only one of those typedefs will get activated (let's say
in foo.h), and the other one will get hidden because it is seen second
in the translation unit (let's say the one in bar.h gets hidden). If
you then import just the submodule for bar.h, the off_t definition
won't be visible, because it was #ifdef'ed out.

There are fragile solutions to this (like including a header that
happens to fix the build this time), but the only real fix is to make
sure that there is only one place where a declaration can be located
in a module, regardless of the set of headers.