Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit eacdd5d

Browse files
committed
Merge pull request #2445 from nguerrera/build-pal-in-corefx
Initial commit of native shims building in corefx
2 parents 47020df + 52f57e8 commit eacdd5d

File tree

12 files changed

+582
-5
lines changed

12 files changed

+582
-5
lines changed

Documentation/coding-guidelines/interop-guidelines.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ We have the following goals related to interop code being used in CoreFX:
1818
- This is both for good hygiene and to help keep platform-specific code
1919
separated from platform-neutral code, which is important for maximizing
2020
reusable code above PAL layers.
21+
- Ensure maximal managed code reuse across different OS flavors which have
22+
the same API but not the same ABI.
23+
- This is the case for UNIX and addressing it is a work-in-progress (see issue
24+
#2137 and section on "shims" below.)
2125

2226
## Approach
2327

@@ -244,3 +248,43 @@ rather than the underlying integral type.
244248
should be named with the most discoverable name possible; if that name
245249
is a concept (e.g. Errors), it can be named using managed naming
246250
guidelines.
251+
252+
253+
## UNIX shims
254+
255+
Often, various UNIX flavors offer the same API from the point-of-view of compatibility
256+
with C/C++ source code, but they do not have the same ABI. e.g. Fields can be laid out
257+
differently, constants cn have different numeric values, exports can
258+
be named differently, etc. There are not only differences between operating systems
259+
(Mac OS X vs. Ubuntu vs. FreeBSD), but also differences related to the underlying
260+
processor architecture (x64 vs. x86 vs. ARM).
261+
262+
This leaves us with a situation where we can't write portable P/Invoke declarations
263+
that will work on all flavors, and writing separate declarations per flavor is quite
264+
fragile and won't scale.
265+
266+
To address this, we're moving to a model where all UNIX interop from corefx starts with
267+
a P/Invoke to a C++ lib written specifically for corefx. These libs -- System.*.Native.so
268+
(aka "shims") -- are intended to be very thin layers over underlying platform libraries.
269+
Generally, they are not there to add any significant abstraction, but to create a
270+
stable ABI such that the same IL assembly can work across UNIX flavors.
271+
272+
Guidelines for shim C++ API:
273+
274+
- Keep them as "thin"/1:1 as possible.
275+
- We want to write the majority of code in C#.
276+
- Never skip the shim and P/Invoke directly to the underlying platform API. It's
277+
easy to assume something is safe/guaranteed when it isn't.
278+
- Don't cheat and take advantage of coincidental agreement between
279+
one flavor's ABI and the shim's ABI.
280+
- Use PascalCase in a style closer to Win32 than libc.
281+
- If an export point has a 1:1 correspondence to the platform API, then name
282+
it after the platform API in PascalCase (e.g. stat -> Stat, fstat -> FStat).
283+
- If an export is not 1:1, then spell things out as we typically would in
284+
CoreFX code (i.e. don't use abbreviations unless they come from the underlying
285+
API.
286+
- At first, it seemed that we'd want to use 1:1 names throughout, but it
287+
turns out there are many cases where being strictly 1:1 isn't practical.
288+
- Stick to data types which are guaranteed not to vary in size across flavors.
289+
- e.g. use int32_t, int64_t from stdint.h and not int, long.
290+

run-test.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ create_test_overlay()
128128
exit 1
129129
fi
130130
find $CoreFxBins -name '*.dll' -exec cp '{}' "$OverlayDir" ";"
131+
132+
# Then the native CoreFX binaries
133+
#
134+
# TODO: Currently, CI does not build the native CoreFX components so build them here
135+
# in the test phase for now.
136+
( $ProjectRoot/src/Native/build.sh && cp $ProjectRoot/bin/$OS.x64.$Configuration/Native/* $OverlayDir ) || exit 1
131137
}
132138

133139
copy_test_overlay()

src/Common/src/Interop/Unix/Interop.Libraries.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ private static partial class Libraries
99
internal const string LibCoreClr= "libcoreclr"; // CoreCLR runtime
1010
internal const string LibCrypto = "libcrypto"; // OpenSSL crypto library
1111
internal const string Zlib = "libz"; // zlib compression library
12+
internal const string IOInterop = "System.IO.Native";
1213
internal const string CryptoInterop = "System.Security.Cryptography.Native";
1314
}
1415
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
internal static partial class Interop
8+
{
9+
internal static partial class NativeIO
10+
{
11+
internal struct FileStats
12+
{
13+
private FileStatsFlags Flags;
14+
internal int Mode;
15+
internal int Uid;
16+
internal int Gid;
17+
internal int Size;
18+
internal int AccessTime;
19+
internal int ModificationTime;
20+
internal int StatusChangeTime;
21+
internal int CreationTime;
22+
}
23+
24+
internal static class FileTypes
25+
{
26+
internal const int S_IFMT = 0xF000;
27+
internal const int S_IFIFO = 0x1000;
28+
internal const int S_IFCHR = 0x2000;
29+
internal const int S_IFDIR = 0x4000;
30+
internal const int S_IFREG = 0x8000;
31+
internal const int S_IFLNK = 0xA000;
32+
}
33+
34+
[Flags]
35+
internal enum FileStatsFlags
36+
{
37+
None = 0,
38+
HasCreationTime = 1,
39+
}
40+
41+
[DllImport(Libraries.IOInterop, SetLastError = true)]
42+
internal static extern int FStat(int fileDescriptor, out FileStats output);
43+
44+
[DllImport(Libraries.IOInterop, SetLastError = true)]
45+
internal static extern int Stat(string path, out FileStats output);
46+
}
47+
}

src/Native/CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
cmake_minimum_required(VERSION 2.8.12)
2+
project(CoreFX)
3+
4+
set(CMAKE_INSTALL_PREFIX $ENV{__CMakeBinDir})
5+
set(CMAKE_INCLUDE_CURRENT_DIR ON)
6+
set(CMAKE_C_FLAGS "-std=c11")
7+
set(CMAKE_CXX_FLAGS "-std=c++11")
8+
set(CMAKE_SHARED_LIBRARY_PREFIX "")
9+
add_compile_options(-Wall -Werror -fPIC)
10+
11+
if (CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL amd64)
12+
add_definitions(-DBIT64=1)
13+
endif ()
14+
15+
string(TOUPPER ${CMAKE_BUILD_TYPE} UPPERCASE_CMAKE_BUILD_TYPE)
16+
if (UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG)
17+
add_compile_options(-g -O0)
18+
add_definitions(-DDEBUG)
19+
elseif (UPPERCASE_CMAKE_BUILD_TYPE STREQUAL RELEASE)
20+
add_compile_options (-O3)
21+
add_definitions(-DNDEBUG)
22+
else ()
23+
message(FATAL_ERROR "Unknown build type. Set CMAKE_BUILD_TYPE to DEBUG or RELEASE.")
24+
endif ()
25+
26+
add_subdirectory(System.IO.Native)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
project(System.IO.Native)
3+
4+
set(NATIVEIO_SOURCES
5+
nativeio.h
6+
nativeio.cpp
7+
)
8+
9+
add_library(System.IO.Native
10+
SHARED
11+
${NATIVEIO_SOURCES}
12+
)
13+
14+
install (TARGETS System.IO.Native DESTINATION .)
15+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
#include "nativeio.h"
7+
#include <sys/stat.h>
8+
9+
#if HAVE_STAT64 && !(defined(__APPLE__) && defined(_AMD64_))
10+
# define stat_ stat64
11+
# define fstat_ fstat64
12+
#else
13+
# define stat_ stat
14+
# define fstat_ fstat
15+
#endif
16+
17+
static void ConvertFileStats(const struct stat_& src, FileStats* dst)
18+
{
19+
dst->Flags = FILESTATS_FLAGS_NONE;
20+
dst->Mode = src.st_mode;
21+
dst->Uid = src.st_uid;
22+
dst->Gid = src.st_gid;
23+
dst->Size = src.st_size;
24+
dst->AccessTime = src.st_atime;
25+
dst->ModificationTime = src.st_mtime;
26+
dst->StatusChangeTime = src.st_ctime;
27+
28+
#if HAVE_STAT_BIRTHTIME
29+
dst->CreationTime = src->st_birthtime;
30+
dst->Flags |= FILESTATS_FLAGS_HAS_CREATION_TIME;
31+
#endif
32+
}
33+
34+
extern "C"
35+
{
36+
int32_t Stat(const char* path, struct FileStats* output)
37+
{
38+
struct stat_ result;
39+
int ret = stat_(path, &result);
40+
41+
if (ret == 0)
42+
{
43+
ConvertFileStats(result, output);
44+
}
45+
46+
return ret; // TODO: errno conversion
47+
}
48+
49+
int32_t FStat(int32_t fileDescriptor, FileStats* output)
50+
{
51+
struct stat_ result;
52+
int ret = fstat_(fileDescriptor, &result);
53+
54+
if (ret == 0)
55+
{
56+
ConvertFileStats(result, output);
57+
}
58+
59+
return ret; // TODO: errno conversion
60+
}
61+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
#pragma once
7+
8+
#include <stdint.h>
9+
10+
extern "C"
11+
{
12+
struct FileStats
13+
{
14+
int32_t Flags; // flags for testing if some members are present (see values below)
15+
int32_t Mode; // protection
16+
int32_t Uid; // user ID of owner
17+
int32_t Gid; // group ID of owner
18+
int64_t Size; // total size, in bytes
19+
int64_t AccessTime; // time of last access (atime)
20+
int64_t ModificationTime; // time of last modification (mtime)
21+
int64_t StatusChangeTime; // time of last status change (ctime)
22+
int64_t CreationTime; // time the file was created (birthtime)
23+
};
24+
25+
enum
26+
{
27+
FILESTATS_FLAGS_NONE = 0,
28+
FILESTATS_FLAGS_HAS_CREATION_TIME = 1,
29+
};
30+
31+
/**
32+
* Get file stats from a decriptor. Implemented as shim to fstat(2).
33+
*
34+
* Returns 0 for success, -1 for failure. Sets errno on failure.
35+
*/
36+
int32_t FStat(int32_t fileDescriptor, FileStats* output);
37+
38+
/**
39+
* Get file stats from a full path. Implemented as shim to stat(2).
40+
*
41+
* Returns 0 for success, -1 for failure. Sets errno on failure.
42+
*/
43+
int32_t Stat(const char* path, FileStats* output);
44+
}
45+

0 commit comments

Comments
 (0)