Skip to content

Commit e4d4624

Browse files
committed
Support linux jail on FreeBSD
1 parent b2e380f commit e4d4624

File tree

4 files changed

+206
-34
lines changed

4 files changed

+206
-34
lines changed

.github/workflows/build.yml

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,26 @@ jobs:
8080
- os: ubuntu-latest
8181
vm:
8282
os: freebsd
83-
run: pkg install -y node npm protobuf ruby rubygem-bundler rubygem-rake
83+
run: |
84+
pkg install -y node npm protobuf ruby rubygem-bundler rubygem-rake
85+
- os: ubuntu-latest
86+
vm:
87+
os: freebsd
88+
run: |
89+
pkg install -y node npm protobuf ruby rubygem-bundler rubygem-rake
90+
sysrc linux_enable="YES"
91+
service linux start
92+
- os: ubuntu-latest
93+
vm:
94+
os: freebsd
95+
run: |
96+
pkg install -y debootstrap ruby rubygem-bundler rubygem-rake
97+
sysrc linux_enable="YES"
98+
service linux start
99+
debootstrap jammy /compat/ubuntu
100+
ln -sf ../lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /compat/ubuntu/lib64/ld-linux-x86-64.so.2
101+
mount -t linprocfs linproc /compat/ubuntu/proc
102+
sysctl compat.linux.emul_path=/compat/ubuntu
84103
- os: ubuntu-latest
85104
vm:
86105
os: openbsd
@@ -90,11 +109,13 @@ jobs:
90109
- os: ubuntu-latest
91110
vm:
92111
os: netbsd
93-
run: /usr/sbin/pkg_add nodejs protobuf ruby
112+
run: |
113+
/usr/sbin/pkg_add nodejs protobuf ruby
94114
- os: ubuntu-latest
95115
vm:
96116
os: dragonflybsd
97-
run: pkg install -y libnghttp2 libuv node npm protobuf ruby rubygem-bundler rubygem-rake
117+
run: |
118+
pkg install -y libnghttp2 libuv node npm protobuf ruby rubygem-bundler rubygem-rake
98119
- os: ubuntu-latest
99120
vm:
100121
os: omnios
@@ -139,7 +160,7 @@ jobs:
139160
run: bundle exec rake compile
140161

141162
- name: Spec
142-
if: "!matrix.vm" # TODO: remove after https://github.com/sass/dart-sass/pull/2413
163+
if: "!matrix.vm || contains(matrix.vm.run, 'sysctl compat.linux.emul_path')" # TODO: remove after https://github.com/sass/dart-sass/pull/2413
143164
run: bundle exec rake spec
144165

145166
- name: Install

ext/sass/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/dart-sass/
66
/embedded_sass.proto
77
/embedded_sass_pb.rb
8+
/exit-*
89
/node_modules/
910
/package-lock.json
1011
/pnpm-lock.yaml

ext/sass/Rakefile

Lines changed: 162 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ task install: %w[cli.rb] do
99
end
1010

1111
CLEAN.include %w[
12+
exit-aarch64
13+
exit-arm
14+
exit-i386
15+
exit-riscv64
16+
exit-x86_64
1217
protoc.exe
1318
ruby
1419
*.proto
@@ -65,6 +70,15 @@ end
6570

6671
task 'dart-sass' do
6772
Rake::Task['dart-sass/sass'].invoke
73+
74+
if SassConfig.linuxulator?
75+
begin
76+
sh 'dart-sass/sass', '--version'
77+
rescue StandardError
78+
rm_rf 'dart-sass'
79+
raise NotImplementedError
80+
end
81+
end
6882
rescue NotImplementedError
6983
Rake::Task['node_modules/sass'].invoke
7084
end
@@ -158,6 +172,123 @@ rule '_pb.rb' => %w[.proto protoc.exe] do |t|
158172
sh './protoc.exe', '--proto_path=.', '--ruby_out=.', t.source
159173
end
160174

175+
rule(/^exit-\w+$/) do |t|
176+
require_relative '../../lib/sass/elf'
177+
178+
elf = Sass.const_get(:ELF)
179+
180+
case t.name.delete_prefix('exit-')
181+
when 'aarch64'
182+
ei_class = elf::ELFCLASS64
183+
ei_data = elf::ELFDATA2LSB
184+
e_machine = 0xb7
185+
e_flags = 0
186+
187+
# 0x0000000000000078: A8 0B 80 D2 mov x8, #0x5d
188+
# 0x000000000000007c: 00 00 80 D2 mov x0, #0
189+
# 0x0000000000000080: 01 00 00 D4 svc #0
190+
entry = ['a80b80d2000080d2010000d4'].pack('H*')
191+
when 'arm'
192+
ei_class = elf::ELFCLASS32
193+
ei_data = elf::ELFDATA2LSB
194+
e_machine = 0x28
195+
e_flags = 0x5000400
196+
197+
# 0x0000000000000054: 00 00 A0 E3 mov r0, #0
198+
# 0x0000000000000058: 01 70 A0 E3 mov r7, #1
199+
# 0x000000000000005c: 00 00 00 EF svc #0
200+
entry = ['0000a0e30170a0e3000000ef'].pack('H*')
201+
when 'riscv64'
202+
ei_class = elf::ELFCLASS64
203+
ei_data = elf::ELFDATA2LSB
204+
e_machine = 0xf3
205+
e_flags = 0x5
206+
207+
# 0x0000000000000078: 93 08 D0 05 addi a7, x0, 93
208+
# 0x000000000000007c: 01 45 c.li a0, 0
209+
# 0x000000000000007e: 73 00 00 00 ecall
210+
entry = ['9308d005014573000000'].pack('H*')
211+
when 'x86_64'
212+
ei_class = elf::ELFCLASS64
213+
ei_data = elf::ELFDATA2LSB
214+
e_machine = 0x3e
215+
e_flags = 0
216+
217+
# 0x0000000000000078: 31 FF xor edi, edi
218+
# 0x000000000000007a: B8 3C 00 00 00 mov eax, 0x3c
219+
# 0x000000000000007f: 0F 05 syscall
220+
entry = ['31ffb83c0000000f05'].pack('H*')
221+
when 'i386'
222+
ei_class = elf::ELFCLASS32
223+
ei_data = elf::ELFDATA2LSB
224+
e_machine = 0x03
225+
e_flags = 0
226+
227+
# 0x0000000000000054: 31 DB xor ebx, ebx
228+
# 0x0000000000000056: B8 01 00 00 00 mov eax, 1
229+
# 0x000000000000005b: CD 80 int 0x80
230+
entry = ['31dbb801000000cd80'].pack('H*')
231+
else
232+
raise NotImplementedError
233+
end
234+
235+
File.open(t.name, 'wb', 0o755) do |file|
236+
elf.allocate.instance_eval do
237+
case ei_class
238+
when elf::ELFCLASS32
239+
e_ehsize = elf::Elf32_Ehdr.sizeof
240+
e_phentsize = elf::Elf32_Phdr.sizeof
241+
e_shentsize = elf::Elf32_Shdr.sizeof
242+
when elf::ELFCLASS64
243+
e_ehsize = elf::Elf64_Ehdr.sizeof
244+
e_phentsize = elf::Elf64_Phdr.sizeof
245+
e_shentsize = elf::Elf64_Shdr.sizeof
246+
else
247+
raise EncodingError
248+
end
249+
e_phoff = e_ehsize
250+
p_offset = e_phoff + e_phentsize
251+
e_entry = (2**22) + p_offset
252+
p_vaddr = e_entry
253+
p_filesz = entry.length
254+
p_memsz = p_filesz
255+
256+
@ehdr = {
257+
e_ident: [127, 69, 76, 70, ei_class, ei_data, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
258+
e_type: 2,
259+
e_machine:,
260+
e_version: 1,
261+
e_entry:,
262+
e_phoff:,
263+
e_shoff: 0,
264+
e_flags:,
265+
e_ehsize:,
266+
e_phentsize:,
267+
e_phnum: 1,
268+
e_shentsize:,
269+
e_shnum: 0,
270+
e_shstrndx: 0
271+
}
272+
@phdrs = [
273+
{
274+
p_type: 1,
275+
p_flags: 5,
276+
p_offset:,
277+
p_vaddr:,
278+
p_paddr: 0,
279+
p_filesz:,
280+
p_memsz:,
281+
p_align: 4096
282+
}
283+
]
284+
@shdrs = []
285+
286+
dump(file)
287+
file.write(entry)
288+
end
289+
end
290+
end
291+
161292
# This is a FileUtils extension that defines several additional commands to be
162293
# added to the FileUtils utility functions.
163294
module FileUtils
@@ -305,6 +436,33 @@ end
305436
# The {SassConfig} module.
306437
module SassConfig
307438
module Platform
439+
CPU = case RbConfig::CONFIG['host_cpu'].downcase
440+
when /amd64|x86_64|x64/
441+
'x86_64'
442+
when /i\d86|x86|i86pc/
443+
'x86'
444+
when /arm64|aarch64/
445+
'aarch64'
446+
when /arm/
447+
'arm'
448+
when /ppc64le|powerpc64le/
449+
'powerpc64le'
450+
else
451+
RbConfig::CONFIG['host_cpu']
452+
end
453+
454+
LINUXULATOR = if RbConfig::CONFIG['host_os'].include?('freebsd')
455+
begin
456+
Rake::Task["exit-#{CPU}"].invoke
457+
RbConfig::CONFIG['host_os'] = 'linux-gnu' if system("./exit-#{CPU}")
458+
true
459+
rescue NotImplementedError
460+
false
461+
end
462+
else
463+
false
464+
end
465+
308466
OS = case RbConfig::CONFIG['host_os'].downcase
309467
when /darwin/
310468
'darwin'
@@ -322,21 +480,6 @@ module SassConfig
322480
RbConfig::CONFIG['host_os'].downcase
323481
end
324482

325-
CPU = case RbConfig::CONFIG['host_cpu'].downcase
326-
when /amd64|x86_64|x64/
327-
'x86_64'
328-
when /i\d86|x86|i86pc/
329-
'x86'
330-
when /arm64|aarch64/
331-
'aarch64'
332-
when /arm/
333-
'arm'
334-
when /ppc64le|powerpc64le/
335-
'powerpc64le'
336-
else
337-
RbConfig::CONFIG['host_cpu']
338-
end
339-
340483
ARCH = "#{CPU}-#{OS}".freeze
341484
end
342485

@@ -518,4 +661,8 @@ module SassConfig
518661
platform
519662
end
520663
end
664+
665+
def linuxulator?
666+
Platform::LINUXULATOR
667+
end
521668
end

spec/sass/elf_spec.rb

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,35 @@
77
Sass.const_get(:ELF)
88
end
99

10-
describe 'ruby program interpreter' do
11-
it 'starts with ld-' do
10+
describe 'ruby', skip: (File.exist?('/proc/self/exe') ? false : 'procfs is not available') do
11+
it 'extracts program interpreter' do
1212
expect(File.basename(described_class::INTERPRETER)).to start_with('ld-')
1313
end
14+
15+
it 'dumps elf headers' do
16+
input = StringIO.new(File.binread('/proc/self/exe'))
17+
output = StringIO.new.binmode
18+
19+
described_class.new(input).dump(output)
20+
expect(output.string).to eq(input.string.slice(0, output.length))
21+
end
1422
end
1523

16-
describe 'dart program interpreter' do
24+
describe 'dart' do
1725
subject(:interpreter) do
1826
Sass.const_get(:CLI)::INTERPRETER
1927
end
2028

21-
it 'starts with ld-' do
29+
it 'extracts program interpreter' do
2230
expect(File.basename(interpreter)).to start_with('ld-')
2331
end
2432

25-
it 'is the same as ruby' do
26-
expect(File.basename(interpreter))
27-
.to eq(File.basename(described_class::INTERPRETER))
28-
end
29-
end
30-
31-
it 'dumps elf headers' do
32-
input = StringIO.new(File.binread('/proc/self/exe'))
33-
output = StringIO.new.binmode
33+
it 'dumps elf headers' do
34+
input = StringIO.new(File.binread(Sass.const_get(:CLI)::COMMAND[0]))
35+
output = StringIO.new.binmode
3436

35-
described_class.new(input).dump(output)
36-
expect(output.string).to eq(input.string.slice(0, output.length))
37+
described_class.new(input).dump(output)
38+
expect(output.string).to eq(input.string.slice(0, output.length))
39+
end
3740
end
3841
end

0 commit comments

Comments
 (0)