-
Notifications
You must be signed in to change notification settings - Fork 253
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
204 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Build helper binaries for musllinux tests. | ||
# Usages: | ||
# build.sh # Build everything. | ||
# build.sh $DISTRO $ARCH # Build one executable in $ARCH using $DISTRO. | ||
# | ||
# Either invocation ultimately runs this script in a Docker container with | ||
# `build.sh glibc|musl $ARCH` to actually build the executable. | ||
|
||
set -euo pipefail | ||
set -x | ||
|
||
build_one_in_ubuntu () { | ||
$1 "multiarch/ubuntu-core:${2}-focal" \ | ||
bash "/home/hello-world/musllinux/build.sh" glibc "glibc-$2" | ||
} | ||
|
||
build_one_in_alpine () { | ||
$1 "multiarch/alpine:${2}-edge" \ | ||
sh "/home/hello-world/musllinux/build.sh" musl "musl-$2" | ||
} | ||
|
||
build_in_container () { | ||
local SOURCE="$(dirname $(dirname $(realpath ${BASH_SOURCE[0]})))" | ||
local DOCKER="docker run --rm -v ${SOURCE}:/home/hello-world" | ||
|
||
if [[ $# -ne 0 ]]; then | ||
"build_one_in_${1}" "$DOCKER" "$2" | ||
return | ||
fi | ||
|
||
build_one_in_alpine "$DOCKER" x86_64 | ||
build_one_in_alpine "$DOCKER" i386 | ||
build_one_in_alpine "$DOCKER" aarch64 | ||
build_one_in_ubuntu "$DOCKER" x86_64 | ||
} | ||
|
||
if [[ $# -eq 0 ]]; then | ||
build_in_container | ||
exit 0 | ||
elif [[ "$1" == "glibc" ]]; then | ||
DEBIAN_FRONTEND=noninteractive apt-get update -qq \ | ||
&& apt-get install -qqy --no-install-recommends gcc libc6-dev | ||
elif [[ "$1" == "musl" ]]; then | ||
apk add -q build-base | ||
else | ||
build_in_container "$@" | ||
exit 0 | ||
fi | ||
|
||
build () { | ||
local CFLAGS="" | ||
local OUT="/home/hello-world/musllinux/${2}" | ||
gcc -Os ${CFLAGS} -o "$OUT-full" "/home/hello-world/hello-world.c" | ||
head -c1024 "$OUT-full" > "$OUT" | ||
rm -f "$OUT-full" | ||
} | ||
|
||
build "$@" |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import collections | ||
import io | ||
import pathlib | ||
import struct | ||
|
||
import pretend | ||
import pytest | ||
|
||
from packaging import _musllinux | ||
from packaging._musllinux import ( | ||
_MuslVersion, | ||
_get_musl_version, | ||
_parse_musl_version, | ||
_parse_ld_musl_from_elf, | ||
) | ||
|
||
|
||
MUSL_AMD64_122 = "musl libc (x86_64)\nVersion 1.2.2\n" | ||
MUSL_I386_121 = "musl libc (i386)\nVersion 1.2.1\n" | ||
MUSL_AARCH64_1124 = "musl libc (aarch64)\nVersion 1.1.24\n" | ||
MUSL_INVALID = "musl libc (invalid)\n" | ||
MUSL_UNKNOWN = "musl libc (unknown)\nVersion unknown\n" | ||
|
||
MUSL_DIR = pathlib.Path(__file__).with_name("musllinux").resolve() | ||
|
||
BINARY_GLIBC_X86_64 = MUSL_DIR.joinpath("glibc-x86_64") | ||
BINARY_MUSL_X86_64 = MUSL_DIR.joinpath("musl-x86_64") | ||
BINARY_MUSL_I386 = MUSL_DIR.joinpath("musl-i386") | ||
BINARY_MUSL_AARCH64 = MUSL_DIR.joinpath("musl-aarch64") | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"output, version", | ||
[ | ||
(MUSL_AMD64_122, _MuslVersion(1, 2)), | ||
(MUSL_I386_121, _MuslVersion(1, 2)), | ||
(MUSL_AARCH64_1124, _MuslVersion(1, 1)), | ||
(MUSL_INVALID, None), | ||
(MUSL_UNKNOWN, None), | ||
], | ||
ids=["amd64-1.2.2", "i386-1.2.1", "aarch64-1.1.24", "invalid", "unknown"], | ||
) | ||
def test_parse_musl_version(output, version): | ||
assert _parse_musl_version(output) == version | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"executable, location", | ||
[ | ||
(BINARY_GLIBC_X86_64, None), | ||
(BINARY_MUSL_X86_64, "/lib/ld-musl-x86_64.so.1"), | ||
(BINARY_MUSL_I386, "/lib/ld-musl-i386.so.1"), | ||
(BINARY_MUSL_AARCH64, "/lib/ld-musl-aarch64.so.1"), | ||
], | ||
ids=["glibc", "x86_64", "i386", "aarch64"], | ||
) | ||
def test_parse_ld_musl_from_elf(executable, location): | ||
with executable.open("rb") as f: | ||
assert _parse_ld_musl_from_elf(f) == location | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"data", | ||
[ | ||
# Too short for magic. | ||
b"\0", | ||
# Enough for magic, but not ELF. | ||
b"#!/bin/bash" + b"\0" * 16, | ||
# ELF, but unknown byte declaration. | ||
b"\x7fELF\3" + b"\0" * 16, | ||
], | ||
ids=["no-magic", "wrong-magic", "unknown-format"], | ||
) | ||
def test_parse_ld_musl_from_elf_invalid(data): | ||
assert _parse_ld_musl_from_elf(io.BytesIO(data)) is None | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"head", | ||
[ | ||
25, # Enough for magic, but not the section definitions. | ||
58, # Enough for section definitions, but not the actual sections. | ||
], | ||
) | ||
def test_parse_ld_musl_from_elf_invalid_section(head): | ||
data = BINARY_MUSL_X86_64.read_bytes()[:head] | ||
assert _parse_ld_musl_from_elf(io.BytesIO(data)) is None | ||
|
||
|
||
def test_parse_ld_musl_from_elf_no_interpreter_section(): | ||
with BINARY_MUSL_X86_64.open("rb") as f: | ||
data = f.read() | ||
|
||
# Change all sections to *not* PT_INTERP. | ||
unpacked = struct.unpack("16BHHIQQQIHHH", data[:58]) | ||
*_, e_phoff, _, _, _, e_phentsize, e_phnum = unpacked | ||
for i in range(e_phnum + 1): | ||
sb = e_phoff + e_phentsize * i | ||
se = sb + 56 | ||
section = struct.unpack("IIQQQQQQ", data[sb:se]) | ||
data = data[:sb] + struct.pack("IIQQQQQQ", 0, *section[1:]) + data[se:] | ||
|
||
assert _parse_ld_musl_from_elf(io.BytesIO(data)) is None | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"executable, output, version", | ||
[ | ||
(MUSL_DIR.joinpath("does-not-exist"), "error", None), | ||
(BINARY_GLIBC_X86_64, "error", None), | ||
(BINARY_MUSL_X86_64, MUSL_AMD64_122, _MuslVersion(1, 2)), | ||
(BINARY_MUSL_I386, MUSL_I386_121, _MuslVersion(1, 2)), | ||
(BINARY_MUSL_AARCH64, MUSL_AARCH64_1124, _MuslVersion(1, 1)), | ||
], | ||
ids=["does-not-exist", "glibc", "x86_64", "i386", "aarch64"], | ||
) | ||
def test_get_musl_version(monkeypatch, executable, output, version): | ||
def mock_run(*args, **kwargs): | ||
return collections.namedtuple("Proc", "stderr")(output) | ||
|
||
run_recorder = pretend.call_recorder(mock_run) | ||
monkeypatch.setattr(_musllinux.subprocess, "run", run_recorder) | ||
|
||
assert _get_musl_version(str(executable)) == version |