Skip to content

Commit ef6cea2

Browse files
VSadovvitek-karas
andauthored
Making singlefile apps work when wrapped in an OSX universal binary (#52997)
* singlefile support for universal binaries * Update src/native/corehost/bundle/reader.h Co-authored-by: Vitek Karas <vitek.karas@microsoft.com> * tests for OSX universal apps * Added a comment about testing strategy Co-authored-by: Vitek Karas <vitek.karas@microsoft.com>
1 parent fe8ae98 commit ef6cea2

6 files changed

Lines changed: 92 additions & 4 deletions

File tree

src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@ private void CheckFileNotarizable(string path)
4646
.Pass();
4747
}
4848

49+
private string MakeUniversalBinary(string path, string rid)
50+
{
51+
string fatApp = path + ".fat";
52+
string arch = BundleHelper.GetTargetArch(rid) == Architecture.Arm64 ? "arm64" : "x86_64";
53+
54+
// We will create a universal binary with just one arch slice and run it.
55+
// It is enough for testing purposes. The code that finds the releavant slice
56+
// would work the same regardless if there is 1, 2, 3 or more slices.
57+
Command.Create("lipo", $"-create -arch {arch} {path} -output {fatApp}")
58+
.CaptureStdErr()
59+
.CaptureStdOut()
60+
.Execute()
61+
.Should()
62+
.Pass();
63+
64+
return fatApp;
65+
}
66+
4967
private void BundleRun(TestProjectFixture fixture, string publishPath)
5068
{
5169
var hostName = BundleHelper.GetHostName(fixture);
@@ -65,6 +83,14 @@ private void BundleRun(TestProjectFixture fixture, string publishPath)
6583

6684
// Run the extracted app
6785
RunTheApp(singleFile);
86+
87+
if (targetOS == OSPlatform.OSX)
88+
{
89+
string fatApp = MakeUniversalBinary(singleFile, fixture.CurrentRid);
90+
91+
// Run the fat app
92+
RunTheApp(fatApp);
93+
}
6894
}
6995

7096
private string RelativePath(string path)

src/native/corehost/bundle/info.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ StatusCode info_t::process_header()
6464
const char* addr = map_bundle();
6565

6666
reader_t reader(addr, m_bundle_size, m_header_offset);
67+
m_offset_in_file = reader.offset_in_file();
6768

6869
m_header = header_t::read(reader);
6970
m_deps_json.set_location(&m_header.deps_json_location());
@@ -116,13 +117,15 @@ char* info_t::config_t::map(const pal::string_t& path, const location_t* &locati
116117

117118
trace::info(_X("Mapped bundle for [%s]"), path.c_str());
118119

119-
return addr + location->offset;
120+
return addr + location->offset + app->m_offset_in_file;
120121
}
121122

122123
void info_t::config_t::unmap(const char* addr, const location_t* location)
123124
{
124125
// Adjust to the beginning of the bundle.
125-
addr -= location->offset;
126+
const bundle::info_t* app = bundle::info_t::the_app;
127+
addr -= location->offset - app->m_offset_in_file;
128+
126129
bundle::info_t::the_app->unmap_bundle(addr);
127130
}
128131

src/native/corehost/bundle/info.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ namespace bundle
7979
pal::string_t m_base_path;
8080
size_t m_bundle_size;
8181
int64_t m_header_offset;
82+
int64_t m_offset_in_file;
8283
header_t m_header;
8384
config_t m_deps_json;
8485
config_t m_runtimeconfig_json;

src/native/corehost/bundle/reader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ void reader_t::set_offset(int64_t offset)
3232
throw StatusCode::BundleExtractionFailure;
3333
}
3434

35-
m_ptr = m_base_ptr + offset;
35+
m_ptr = m_base_ptr + offset + m_offset_in_file;
3636
}
3737

3838
void reader_t::bounds_check(int64_t len)

src/native/corehost/bundle/reader.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,39 @@
88
#include "pal.h"
99
#include "utils.h"
1010

11+
// support for parsing OSX universal binary headers
12+
#ifdef TARGET_OSX
13+
#include "error_codes.h"
14+
#include <libkern/OSByteOrder.h>
15+
#include <mach-o/fat.h>
16+
#include <mach/machine.h>
17+
18+
#if defined(TARGET_ARM64)
19+
#define TARGET_CPU_TYPE CPU_TYPE_ARM64
20+
#else
21+
#define TARGET_CPU_TYPE CPU_TYPE_X86_64
22+
#endif
23+
24+
template <typename fat_arch_type>
25+
void* offset_in_FAT_universal_binary(const char* addr)
26+
{
27+
uint32_t nfat_arch = OSSwapBigToHostInt32(((uint32_t*)addr)[1]);
28+
29+
fat_arch_type* arch_list = (fat_arch_type*)(addr + sizeof(uint32_t) * 2);
30+
for (int i = 0; i < nfat_arch; i++)
31+
{
32+
if (OSSwapBigToHostInt32((uint32_t)arch_list[i].cputype) == TARGET_CPU_TYPE)
33+
{
34+
return &arch_list[i].offset;
35+
}
36+
}
37+
38+
trace::error(_X("Couldn't find offset in an universal fat binary."));
39+
throw StatusCode::BundleExtractionFailure;
40+
}
41+
#endif // TARGET_OSX
42+
43+
1144
namespace bundle
1245
{
1346
// Helper class for reading sequentially from the memory-mapped bundle file.
@@ -19,6 +52,23 @@ namespace bundle
1952
, m_bound(bound)
2053
, m_bound_ptr(add_without_overflow(base_ptr, bound))
2154
{
55+
m_offset_in_file = 0;
56+
57+
#ifdef TARGET_OSX
58+
// check for universal binary container and adjust the offset accordingly
59+
uint32_t magic = OSSwapBigToHostInt32(((uint32_t*)base_ptr)[0]);
60+
if (magic == FAT_MAGIC)
61+
{
62+
m_offset_in_file = OSSwapBigToHostInt32(*(uint32_t*)offset_in_FAT_universal_binary<fat_arch>(base_ptr));
63+
trace::info(_X("FAT container detected. Offset in file:[%lx]"), m_offset_in_file);
64+
}
65+
else if (magic == FAT_MAGIC_64)
66+
{
67+
m_offset_in_file = OSSwapBigToHostInt64(*(uint64_t*)offset_in_FAT_universal_binary<fat_arch_64>(base_ptr));
68+
trace::info(_X("FAT64 container detected. Offset in file:[%lx]"), m_offset_in_file);
69+
}
70+
#endif
71+
2272
set_offset(start_offset);
2373
}
2474

@@ -37,6 +87,11 @@ namespace bundle
3787
return *m_ptr++;
3888
}
3989

90+
int64_t offset_in_file()
91+
{
92+
return m_offset_in_file;
93+
}
94+
4095
// Copy len bytes from m_ptr to dest
4196
void read(void* dest, int64_t len)
4297
{
@@ -67,6 +122,8 @@ namespace bundle
67122
const char* m_ptr;
68123
const int64_t m_bound;
69124
const char* const m_bound_ptr;
125+
126+
int64_t m_offset_in_file;
70127
};
71128
}
72129

src/native/corehost/bundle/runner.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ StatusCode runner_t::extract()
2121

2222
// Set the Reader at header_offset
2323
reader_t reader(addr, m_bundle_size, m_header_offset);
24+
m_offset_in_file = reader.offset_in_file();
2425

2526
// Read the bundle header
2627
m_header = header_t::read(reader);
@@ -74,7 +75,7 @@ bool runner_t::probe(const pal::string_t& relative_path, int64_t* offset, int64_
7475
assert(!entry->is_disabled());
7576
assert(entry->offset() != 0);
7677

77-
*offset = entry->offset();
78+
*offset = entry->offset() + m_offset_in_file;
7879
*size = entry->size();
7980
*compressedSize = entry->compressedSize();
8081

0 commit comments

Comments
 (0)