Import Upstream version 0.8

This commit is contained in:
zhouganqing 2022-11-11 17:27:06 +08:00
commit a94fbe0d27
25 changed files with 1287 additions and 0 deletions

View File

@ -0,0 +1 @@
../../../dh/dh_package_notes

44
.github/workflows/linter.yml vendored Normal file
View File

@ -0,0 +1,44 @@
---
# SPDX-License-Identifier: CC0-1.0
# vi: ts=2 sw=2 et:
name: Lint Code Base
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
strategy:
fail-fast: false
steps:
- name: Repository checkout
uses: actions/checkout@v2
with:
# We need a full repo clone to get a correct list of changed files
fetch-depth: 0
# FIXME: super-linter doesn't recognize perl/python scripts without
# an extension; let's workaround it by creating a dummy symlink
# directory with necessary extensions until it's resolved.
# The `find` below just checks if all symlinks in the dummy
# directory are valid.
- name: Check test-related symlinks
run: find .github/super-linter-links/ -type l -exec ls -L {} +
- name: Lint Code Base
uses: github/super-linter/slim@latest
env:
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MULTI_STATUS: true
# FIXME: until the above FIXME is resolved, we need to always check
# the whole codebase, otherwise only changed files would be
# considered, which would ignore the symlinked files. Since
# we have only couple of files to check, it's not an issue
# (yet).
VALIDATE_ALL_CODEBASE: true
VALIDATE_BASH: true
VALIDATE_PERL: true
VALIDATE_PYTHON_ISORT: true
VALIDATE_PYTHON_PYLINT: true

29
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,29 @@
---
# SPDX-License-Identifier: CC0-1.0
# vi: ts=2 sw=2 et:
name: Run tests
on: [pull_request]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-20.04
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
strategy:
fail-fast: false
steps:
- name: Repository checkout
uses: actions/checkout@v2
- name: Install dependencies
run: sudo apt -y update && sudo apt -y install binutils python3-pytest python3-simplejson devscripts
- name: Run python tests
run: python3 -m pytest -v
- name: Run shell tests
run: ./tests/test_basics.sh
- name: Check shell script for bashism
run: checkbashisms ./tests/test_basics.sh

26
README.md Normal file
View File

@ -0,0 +1,26 @@
ELF Package Notes Reference Implementation
## Description
This repository provides a script to generate an ELF note that can be
linked into compiled binaries (programs and shared libraries) to provide
metadata about the package for which the binary was compiled.
See [Package Metadata for Core Files](https://systemd.io/COREDUMP_PACKAGE_METADATA/)
for the overview and details.
We provide implementations in Python and POSIX shell, with compatible CLI
interfaces.
## Requirements
### generate-package-notes.py
* python3 (>= 3.5)
* python3-simplejson
* binutils (>= 2.38) [--readonly true]
### generate-package-notes.sh
* POSIX shell
* binutils (>= 2.38) [--readonly true]

85
dh/dh_package_notes Executable file
View File

@ -0,0 +1,85 @@
#!/usr/bin/perl -w
# SPDX-License-Identifier: CC0-1.0
=head1 NAME
dh_package_notes - Add package metadata to ELF header
=cut
use strict;
use warnings;
use Debian::Debhelper::Dh_Lib
qw(%dh doit hostarch init isnative sourcepackage);
use Dpkg::Vendor qw(get_current_vendor);
our $VERSION = '0.8';
=head1 SYNOPSIS
B<dh_package_notes>
=cut
=head1 DESCRIPTION
B<dh_package_notes> is a debhelper program that creates a linker script to
include package metadata in ELF binaries built by packages.
The package metadata specification for ELF binaries can be found at
L<https://systemd.io/COREDUMP_PACKAGE_METADATA/>.
B<dh_package_notes> creates a linker script in a fixed location at
B<debian/.debhelper/notes.ld> with the package type set to B<deb>, the package
name and version set to the source package name and version respectively, and
the B<os> and B<osVersion> fields set to the values of the B<ID> and
B<VERSION_ID> fields found in B</etc/os-release>. Simply add
B<dh-sequence-package-notes> to the Build-Depends or add
B<--with package_notes> to the B<dh $@> call to make this happen.
The package using B<dh_package_notes> also needs to manually set in debian/rules
B<include /usr/share/debhelper/dh_package_notes/package-notes.mk> as it is
not possible to do so from a debhelper addon.
=cut
init();
isnative( $dh{MAINPACKAGE} ); # Necessary to have $dh{VERSION}
my $source_package = sourcepackage();
my $package_arch = hostarch();
my $vendor = get_current_vendor();
my $url = $ENV{'DEBUGINFOD_URLS'};
if (!defined $url) {
$url = "https://debuginfod.debian.net";
}
my %options = ( 'stdout' => "debian/.debhelper/notes.ld" );
my @cmd = (
"/usr/bin/generate-package-notes",
"--type", "deb",
"--os", lc ${vendor},
"--name", ${source_package},
"--architecture", ${package_arch},
"--version", $dh{VERSION},
"--debugInfoUrl", ${url}
);
if ( not mkdir("debian/.debhelper") ) {
error("mkdir debian/.debhelper failed: $!");
}
doit( \%options, @cmd );
# FIXME: neither of these work, so appending from each debian/rules file is
# currently the only way to make this work, which is less than ideal
#$ENV{'DEB_LDFLAGS_MAINT_APPEND'} .= ' -Wl,-dT,debian/.debhelper/notes.ld';
#$ENV{'LDFLAGS'} .= ' -Wl,-dT,debian/.debhelper/notes.ld';
=head1 SEE ALSO
L<debhelper(7)>
=head1 AUTHOR
Luca Boccassi <bluca@debian.org>
=cut

4
dh/package-notes.mk Normal file
View File

@ -0,0 +1,4 @@
# SPDX-License-Identifier: CC0-1.0
# Include from debian/rules to use with dh_package_notes.
# See dh_package_notes(1) for details
export DEB_LDFLAGS_MAINT_APPEND+= -Wl,-dT,$(CURDIR)/debian/.debhelper/notes.ld

10
dh/package_notes.pm Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/perl
# SPDX-License-Identifier: CC0-1.0
use warnings;
use strict;
use Debian::Debhelper::Dh_Lib;
insert_before('dh_auto_configure', 'dh_package_notes');
1

222
generate-package-notes.py Executable file
View File

@ -0,0 +1,222 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: CC0-1.0
"""
Generates a linker script to insert a .note.package section with a
JSON payload. The contents are derived from the specified options and the
os-release file. Use the output with -Wl,-dT,/path/to/output in $LDFLAGS.
$ ./generate-package-notes.py --package-type rpm --package-name systemd --package-version 248~rc2-1.fc34 --package-architecture x86_64 --cpe 'cpe:/o:fedoraproject:fedora:33'
SECTIONS
{
.note.package (READONLY) : ALIGN(4) {
BYTE(0x04) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Owner including NUL */
BYTE(0x7c) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Value including NUL */
BYTE(0x7e) BYTE(0x1a) BYTE(0xfe) BYTE(0xca) /* Note ID */
BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","name":"systemd","version":"248~rc2-1.fc34","architecture":"x86_64","osCpe":"cpe:/o:fedoraproject:fedora:33"}\x00' */
BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6e)
BYTE(0x61) BYTE(0x6d) BYTE(0x65) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x73) BYTE(0x79)
BYTE(0x73) BYTE(0x74) BYTE(0x65) BYTE(0x6d)
BYTE(0x64) BYTE(0x22) BYTE(0x2c) BYTE(0x22)
BYTE(0x76) BYTE(0x65) BYTE(0x72) BYTE(0x73)
BYTE(0x69) BYTE(0x6f) BYTE(0x6e) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x32) BYTE(0x34)
BYTE(0x38) BYTE(0x7e) BYTE(0x72) BYTE(0x63)
BYTE(0x32) BYTE(0x2d) BYTE(0x31) BYTE(0x2e)
BYTE(0x66) BYTE(0x63) BYTE(0x33) BYTE(0x34)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x61)
BYTE(0x72) BYTE(0x63) BYTE(0x68) BYTE(0x69)
BYTE(0x74) BYTE(0x65) BYTE(0x63) BYTE(0x74)
BYTE(0x75) BYTE(0x72) BYTE(0x65) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x78) BYTE(0x38)
BYTE(0x36) BYTE(0x5f) BYTE(0x36) BYTE(0x34)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x63)
BYTE(0x70) BYTE(0x65) BYTE(0x3a) BYTE(0x2f)
BYTE(0x6f) BYTE(0x3a) BYTE(0x66) BYTE(0x65)
BYTE(0x64) BYTE(0x6f) BYTE(0x72) BYTE(0x61)
BYTE(0x70) BYTE(0x72) BYTE(0x6f) BYTE(0x6a)
BYTE(0x65) BYTE(0x63) BYTE(0x74) BYTE(0x3a)
BYTE(0x66) BYTE(0x65) BYTE(0x64) BYTE(0x6f)
BYTE(0x72) BYTE(0x61) BYTE(0x3a) BYTE(0x33)
BYTE(0x33) BYTE(0x22) BYTE(0x7d) BYTE(0x00)
}
}
INSERT AFTER .note.gnu.build-id;
/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */
See https://systemd.io/COREDUMP_PACKAGE_METADATA/ for details.
"""
__version__ = '0.8'
import argparse
import itertools
import os
import re
from pathlib import Path
import simplejson as json
DOC_PARAGRAPHS = ['\n'.join(group)
for (key, group) in itertools.groupby(__doc__.splitlines(), bool)
if key]
def read_os_release(field, root=Path('/')):
try:
f = open(root / 'etc/os-release')
except FileNotFoundError:
f = open(root / 'usr/lib/os-release')
prefix = '{}='.format(field)
for line in f:
if line.startswith(prefix):
break
else:
return None
value = line.rstrip()
value = value[value.startswith(prefix) and len(prefix):]
if value[0] in '"\'' and value[0] == value[-1]:
value = value[1:-1]
return value
def str_to_bool(v):
if isinstance(v, bool):
return v
if v.lower() in {'yes', 'true', '1'}:
return True
if v.lower() in {'no', 'false', '0'}:
return False
raise argparse.ArgumentTypeError('"yes"/"true"/"1"/"no"/"false"/"0" expected')
def parse_args():
p = argparse.ArgumentParser(description=DOC_PARAGRAPHS[0],
epilog=DOC_PARAGRAPHS[-1],
allow_abbrev=False)
p.add_argument('--package-type', metavar='TYPE',
default='package',
help='Specify the package type, e.g. "rpm" or "deb"')
p.add_argument('--package-name', metavar='NAME',
help='The name of the package (e.g. "foo" or "libbar")')
p.add_argument('--package-version', metavar='VERSION',
help='The full version of the package (e.g. 1.5-1.fc35.s390x)')
p.add_argument('--package-architecture', metavar='ARCH',
help='The code architecture of the binaries (e.g. arm64 or s390x)')
p.add_argument('--cpe',
help='NIST CPE identifier of the vendor operating system, or \'auto\' to parse from system-release-cpe or os-release')
p.add_argument('--rpm', metavar='NEVRA',
help='Extract type,name,version,architecture from a full rpm name')
p.add_argument('--debug-info-url', metavar='URL',
help='URL of the debuginfod server where sources can be queried')
p.add_argument('--readonly', metavar='BOOL',
type=str_to_bool, default=True,
help='Make the notes section read-only (requires binutils 2.38)')
p.add_argument('--root', metavar='PATH', type=Path, default="/",
help='When a file (eg: /usr/lib/os-release) is parsed, open it relatively from this hierarchy')
p.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
opts = p.parse_args()
return opts
def encode_bytes(arr):
return ' '.join('BYTE(0x{:02x})'.format(n) for n in arr)
def encode_bytes_lines(arr, prefix='', label='string'):
assert len(arr) % 4 == 0
s = bytes(arr).decode()
yield prefix + encode_bytes(arr[:4]) + ' /* {}: {!r} */'.format(label, s)
for offset in range(4, len(arr), 4):
yield prefix + encode_bytes(arr[offset:offset+4])
def encode_length(s, prefix='', label='string'):
n = (len(s) + 1) * 4 // 4
return f'{prefix}LONG(0x{n:04x}) /* Length of {label} including NUL */'
def encode_note_id(id, prefix=''):
return f'{prefix}LONG(0x{id:04x}) /* Note ID */'
def pad_string(s):
return [0] * ((len(s) + 4) // 4 * 4 - len(s))
def encode_string(s, prefix='', label='string'):
arr = list(s.encode()) + pad_string(s)
yield from encode_bytes_lines(arr, prefix=prefix, label=label)
def encode_note(note_name, note_id, owner, value, readonly=True, prefix=''):
l1 = encode_length(owner, prefix=prefix + ' ', label='Owner')
l2 = encode_length(value, prefix=prefix + ' ', label='Value')
l3 = encode_note_id(note_id, prefix=prefix + ' ')
l4 = encode_string(owner, prefix=prefix + ' ', label='Owner')
l5 = encode_string(value, prefix=prefix + ' ', label='Value')
readonly = '(READONLY) ' if readonly else ''
return [prefix + '.note.{} {}: ALIGN(4) {{'.format(note_name, readonly),
l1, l2, l3, *l4, *l5,
prefix + '}']
NOTE_ID= 0xcafe1a7e
def json_serialize(s):
# Avoid taking space in the ELF header if there's no value to store
return json.dumps({k: v for k, v in s.items() if v is not None},
ensure_ascii=False,
separators=(',', ':'))
def gather_data(opts):
if opts.cpe == 'auto':
try:
with open(Path(opts.root, 'usr/lib/system-release-cpe'), 'r') as f:
opts.cpe = f.read()
except FileNotFoundError:
opts.cpe = read_os_release('CPE_NAME', root=opts.root)
if opts.cpe is None or opts.cpe == "":
raise ValueError(f"Could not read {opts.root}usr/lib/system-release-cpe or CPE_NAME from {opts.root}usr/lib/os-release")
if opts.rpm:
split = re.match(r'(.*?)-([0-9].*)\.(.*)', opts.rpm)
if not split:
raise ValueError('{!r} does not seem to be a valid package name'.format(opts.rpm))
opts.package_type = 'rpm'
opts.package_name = split.group(1)
opts.package_version = split.group(2)
opts.package_architecture = split.group(3)
data = {
'type': opts.package_type,
'name': opts.package_name,
'version': opts.package_version,
'architecture': opts.package_architecture,
}
if opts.cpe:
data['osCpe'] = opts.cpe
else:
data['os'] = read_os_release('ID', root=opts.root)
data['osVersion'] = read_os_release('VERSION_ID', root=opts.root)
if opts.debug_info_url:
data['debugInfoUrl'] = opts.debug_info_url
return data
def generate_section(data, readonly=True):
json = json_serialize(data)
section = encode_note('package', NOTE_ID, 'FDO', json, readonly=readonly, prefix=' ')
return ['SECTIONS', '{',
*section,
'}',
'INSERT AFTER .note.gnu.build-id;',
'/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */']
if __name__ == '__main__':
opts = parse_args()
data = gather_data(opts)
lines = generate_section(data, readonly=opts.readonly)
print('\n'.join(lines))

287
generate-package-notes.sh Executable file
View File

@ -0,0 +1,287 @@
#!/bin/sh
# SPDX-License-Identifier: CC0-1.0
#
# Generates a linker script to insert a .note.package section with a
# JSON payload. The contents are derived from the specified options and the
# os-release file. Use the output with -Wl,-dT,/path/to/output in $LDFLAGS.
#
# $ ./generate-package-notes.sh --type rpm --name systemd --version 248~rc2-1.fc34 --architecture x86_64 --osCpe 'cpe:/o:fedoraproject:fedora:33'
# SECTIONS
# {
# .note.package (READONLY) : ALIGN(4) {
# BYTE(0x04) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Owner including NUL */
# BYTE(0x7c) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Value including NUL */
# BYTE(0x7e) BYTE(0x1a) BYTE(0xfe) BYTE(0xca) /* Note ID */
# BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
# BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","name":"systemd","version":"248~rc2-1.fc34","architecture":"x86_64","osCpe":"cpe:/o:fedoraproject:fedora:33"}\x00' */
# BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
# BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
# BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6e)
# BYTE(0x61) BYTE(0x6d) BYTE(0x65) BYTE(0x22)
# BYTE(0x3a) BYTE(0x22) BYTE(0x73) BYTE(0x79)
# BYTE(0x73) BYTE(0x74) BYTE(0x65) BYTE(0x6d)
# BYTE(0x64) BYTE(0x22) BYTE(0x2c) BYTE(0x22)
# BYTE(0x76) BYTE(0x65) BYTE(0x72) BYTE(0x73)
# BYTE(0x69) BYTE(0x6f) BYTE(0x6e) BYTE(0x22)
# BYTE(0x3a) BYTE(0x22) BYTE(0x32) BYTE(0x34)
# BYTE(0x38) BYTE(0x7e) BYTE(0x72) BYTE(0x63)
# BYTE(0x32) BYTE(0x2d) BYTE(0x31) BYTE(0x2e)
# BYTE(0x66) BYTE(0x63) BYTE(0x33) BYTE(0x34)
# BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x61)
# BYTE(0x72) BYTE(0x63) BYTE(0x68) BYTE(0x69)
# BYTE(0x74) BYTE(0x65) BYTE(0x63) BYTE(0x74)
# BYTE(0x75) BYTE(0x72) BYTE(0x65) BYTE(0x22)
# BYTE(0x3a) BYTE(0x22) BYTE(0x78) BYTE(0x38)
# BYTE(0x36) BYTE(0x5f) BYTE(0x36) BYTE(0x34)
# BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
# BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
# BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x63)
# BYTE(0x70) BYTE(0x65) BYTE(0x3a) BYTE(0x2f)
# BYTE(0x6f) BYTE(0x3a) BYTE(0x66) BYTE(0x65)
# BYTE(0x64) BYTE(0x6f) BYTE(0x72) BYTE(0x61)
# BYTE(0x70) BYTE(0x72) BYTE(0x6f) BYTE(0x6a)
# BYTE(0x65) BYTE(0x63) BYTE(0x74) BYTE(0x3a)
# BYTE(0x66) BYTE(0x65) BYTE(0x64) BYTE(0x6f)
# BYTE(0x72) BYTE(0x61) BYTE(0x3a) BYTE(0x33)
# BYTE(0x33) BYTE(0x22) BYTE(0x7d) BYTE(0x00)
# }
# }
# INSERT AFTER .note.gnu.build-id;
# /* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */
#
# See https://systemd.io/COREDUMP_PACKAGE_METADATA/ for details.
json=
readonly="(READONLY) "
root=
help() {
echo "Usage: $0 [OPTION]..."
echo "Generate a package notes linker script from specified metadata."
echo
echo " -h, --help display this help and exit"
echo " --readonly BOOL whether to add the READONLY attribute to script (default: true)"
echo " --root PATH when a file (eg: os-release) is parsed, open it relatively to this hierarchy (default: not set)"
echo " --cpe VALUE NIST CPE identifier of the vendor operating system, or 'auto' to parse from system-release-cpe or os-release"
echo " --package-type TYPE set the package type (e.g. 'rpm' or 'deb')"
echo " --package-name NAME set the package name"
echo " --package-version VERSION set the package version"
echo " --package-architecture ARCH set the package architecture"
echo " --NAME VALUE set an arbitrary name/value pair"
}
invalid_argument() {
printf 'ERROR: "%s" requires a non-empty option argument.\n' "${1}" >&2
exit 1
}
append_parameter() {
if [ -z "${2}" ]; then
invalid_argument "${1}"
fi
# Posix-compatible substring check
case "$json" in
*"\"${1}\":"*) echo "Duplicated argument: --${1}"; exit 1 ;;
esac
if [ -z "${json}" ]; then
json="{\"${1}\":\"${2}\""
else
json="${json},\"${1}\":\"${2}\""
fi
}
# Support the same fixed parameters as the python script
parse_options() {
cpe=
while :; do
case $1 in
-h|-\?|--help)
help
exit
;;
--readonly)
if [ -z "${2}" ]; then
invalid_argument "${1}"
fi
case $2 in
no|NO|No|false|FALSE|False|0)
readonly=""
;;
esac
shift
;;
--root)
if [ -z "${2}" ] || [ ! -d "${2}" ]; then
invalid_argument "${1}"
fi
root="${2}"
shift
;;
--package-type)
append_parameter "type" "${2}"
shift
;;
--package-name)
append_parameter "name" "${2}"
shift
;;
--package-version)
append_parameter "version" "${2}"
shift
;;
--package-architecture)
append_parameter "architecture" "${2}"
shift
;;
--cpe)
if [ -z "${2}" ]; then
invalid_argument "${1}"
fi
cpe="${2}"
shift
;;
--debug-info-url)
append_parameter "debugInfoUrl" "${2}"
shift
;;
--*)
# Allow passing arbitrary name/value pairs
append_parameter "$(echo "${1}" | cut -c 3-)" "${2}"
shift
;;
-*)
printf 'WARNING: Unknown option (ignored): %s\n' "${1}" >&2
;;
*)
break
esac
shift
done
# Parse at the end, so that --root can be used in any position
if [ "${cpe}" = "auto" ]; then
if [ -r "${root}/usr/lib/system-release-cpe" ]; then
cpe="$(cat "${root}/usr/lib/system-release-cpe")"
elif [ -r "${root}/etc/os-release" ]; then
# shellcheck disable=SC1090 disable=SC1091
cpe="$(. "${root}/etc/os-release" && echo "${CPE_NAME}")"
elif [ -r "${root}/usr/lib/os-release" ]; then
# shellcheck disable=SC1090 disable=SC1091
cpe="$(. "${root}/usr/lib/os-release" && echo "${CPE_NAME}")"
fi
if [ -z "${cpe}" ]; then
printf 'ERROR: --cpe auto but cannot read %s/usr/lib/system-release-cpe or parse CPE_NAME from %s/etc/os-release or %s/usr/lib/os-release.\n' "${root}" "${root}" "${root}" >&2
exit 1
fi
fi
if [ -n "${cpe}" ]; then
append_parameter "osCpe" "${cpe}"
fi
# Terminate the JSON object
if [ -n "${json}" ]; then
json="${json}}"
fi
return "$#"
}
pad_comment() {
for _ in $(seq "$1"); do
printf '\\x00'
done
}
pad_string() {
for i in $(seq "$1"); do
if [ $(( ( $2 + i - 1) % 4 )) -eq 0 ]; then
printf '\n%sBYTE(0x00)' "${3}"
else
printf ' BYTE(0x00)'
fi
done
}
write_string() {
text="$1"
prefix="$2"
label="$3"
total="$4"
# We always have at least the terminating NULL
if [ $(( total % 4)) -eq 0 ]; then
padding_nulls=1
else
padding_nulls="$(( 1 + 4 - (total % 4) ))"
fi
for i in $(seq ${#text}); do
if [ $(( i % 4)) -eq 1 ]; then
printf '\n%s' "$prefix"
else
printf ' '
fi
byte=$(echo "${text}" | cut -c "${i}")
printf 'BYTE(0x%02x)' "'${byte}"
# Print the json object as a comment after the first 4 bytes
# to match the output of the older script, including padding NUL.
if [ "${i}" -eq 4 ]; then
printf " /* %s: '%s" "$label" "$text"
pad_comment "${padding_nulls}"
printf "' */"
fi
done
pad_string "${padding_nulls}" "${#text}" "$prefix"
printf '\n'
}
write_script() {
# NULL terminator is included in the size, but not padding
value_len=$(( ${#1} + 1 ))
if [ "${value_len}" -gt 65536 ]; then
printf 'ERROR: "%s" is too long.\n' "${1}" >&2
exit 1
fi
printf 'SECTIONS\n{\n'
printf ' .note.package %s: ALIGN(4) {\n' "${readonly}"
# Note that for the binary fields we use the native 4 bytes type, to avoid
# endianness issues.
printf ' LONG(0x0004) /* Length of Owner including NUL */\n'
printf ' LONG(0x%04x) /* Length of Value including NUL */\n' \
${value_len}
printf ' LONG(0xcafe1a7e) /* Note ID */\n'
printf " BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\\\\x00' */" # newline will be added by write_string
write_string "$1" ' ' 'Value' "$value_len"
printf ' }\n}\n'
printf 'INSERT AFTER .note.gnu.build-id;\n'
# shellcheck disable=SC2016
printf '/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */\n'
}
if ! parse_options "$@" && [ "$#" -gt 0 ]; then
# Not supported on every distro
if [ -r "${root}/usr/lib/system-release-cpe" ]; then
cpe="$(cat "${root}/usr/lib/system-release-cpe")"
json_cpe=",\"osCpe\":\"${cpe}\""
fi
# old-style invocation with positional parameters for backward compatibility
json="$(printf '{"type":"rpm","name":"%s","version":"%s","architecture":"%s"%s}' "$1" "$2" "$3" "$json_cpe")"
elif [ -z "${json}" ]; then
help
exit 1
fi
write_script "$json"

85
hello.spec Normal file
View File

@ -0,0 +1,85 @@
# SPDX-License-Identifier: CC0-1.0
%bcond_without notes
Name: hello
Version: 0
Release: 1%{?dist}%{!?with_notes:.nonotes}
Summary: Aloha!
License: CC0
BuildRequires: binutils
BuildRequires: gcc
BuildRequires: rpmdevtools
BuildRequires: python3
BuildRequires: python3-simplejson
Source0: generate-package-notes.py
%description
Test with:
objdump -s -j .note.package %{_bindir}/hello
objdump -s -j .note.package %{_libdir}/libhello.so
%prep
%setup -cT
set -eo pipefail
cat <<EOF >libhello.c
const char* greeting(void) {
return "Hello";
}
EOF
cat <<EOF >hello.c
#include <stdio.h>
extern char* greeting(void);
int main() {
puts(greeting());
return 0;
}
EOF
%build
set -eo pipefail
ld_version="$(ld --version | sed -r -n '1 {s/.*version (.*)/\1/p}')"
set +e
rpmdev-vercmp "$ld_version" "2.38" >/dev/null
if [ $? == 12 ]; then
readonly="--readonly=no"
fi
set -e
%if %{with notes}
python3 %{SOURCE0} $readonly --rpm '%{name}-%{VERSION}-%{RELEASE}.%{_arch}' | \
tee notes.ld
%endif
LDFLAGS="%{build_ldflags} %{?with_notes:-Wl,-dT,$PWD/notes.ld}"
CFLAGS="%{build_cflags}"
gcc -Wall -fPIC -o libhello.so -shared libhello.c $CFLAGS $LDFLAGS
gcc -Wall -o hello hello.c libhello.so $CFLAGS $LDFLAGS
%install
set -eo pipefail
install -Dt %{buildroot}%{_libdir}/ libhello.so
install -Dt %{buildroot}%{_bindir}/ hello
%check
set -eo pipefail
%if %{with notes}
objdump -s -j .note.package ./hello
objdump -s -j .note.package ./libhello.so
%endif
%files
%{_bindir}/hello
%{_libdir}/libhello.so
%changelog
* Wed Feb 3 2021 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> - 0-1
- Test

View File

@ -0,0 +1,136 @@
.TH GENERATE\-PACKAGE\-NOTES 1 "May 2021"
.SH NAME
generate\-package\-notes \- generate a linker script for package metadata
.SH SYNOPSIS
.B generate\-package\-notes
.RI [ OPTION ...]
.SH DESCRIPTION
ELF binaries get stamped with a unique, build-time generated hex string
identifier called build-id, which gets embedded as an ELF note called
.IR \%.note.gnu.build\-id .
In most cases, this allows to associate a stripped binary with
its debugging information.
It is used, for example, to dynamically fetch DWARF symbols from
a debuginfo server, or to query the local package manager and find out
the package metadata or, again, the DWARF symbols or program sources.
.PP
However, this usage of the build-id requires either local metadata,
usually set up by the package manager,
or access to a remote server over the network.
Both of those might be unavailable or forbidden.
.PP
Thus it becomes desirable to add additional metadata to a binary
at build time, so that
.BR \%systemd\-coredump (8)
and other services analyzing
core files are able to extract said metadata simply from the core file
itself, without external dependencies.
The metadata is embedded in a single ELF header section,
in a key-value JSON format.
.PP
The metadata format is intentionally left open,
so that vendors can add their own information.
A set of well-known keys is defined in the document
.UR https://systemd.io/COREDUMP_PACKAGE_METADATA/
.B Package Metadata for Core Files
.UE ,
and hopefully shared among all vendors.
.PP
.B generate\-package\-notes
generates a linker script on standard output,
which can then be used at build time via
.I \%LDFLAGS="\-Wl,\-dT,/path/to/generated/script"
to include the note in the binary.
If a Debian package is built using the
.BR dh (1)
sequencer, the generation can be partly automated using
.BR \%dh_package_notes (1).
.SH OPTIONS
.TP
.BI \-\-package\-type= TYPE
Set the key
.I type
to
.IR TYPE .
This defaults to
.IR package ,
but for Debian packages it should be set to
.IR deb .
.TP
.BI \-\-package\-name= NAME
Set the key
.I name
to
.IR NAME .
This defaults to the empty string, but for Debian packages it should
be set to the name of the binary package containing the binary.
.TP
.BI \-\-package\-version= VERSION
Set the key
.I version
to
.IR VERSION .
This defaults to the empty string, but for Debian packages it should
be set to the Debian version of the binary package containing the binary.
.TP
.BI \-\-package\-architecture= ARCHITECTURE
Set the key
.I architecture
to
.IR ARCHITECTURE .
This defaults to the empty string, but for Debian packages it should
be set to the Debian architecture identifier of the binary.
.TP
.TP
.BI \-\-root= PATH
When reading files, for example /usr/lib/system-release-cpe and /usr/lib/os-release,
open them relatively to the specified directory.
.TP
.BI \-\-cpe= CPE
Set the key
.I osCpe
to
.IR CPE .
If the special value
.I auto
is passed, then the content of the /usr/lib/system-release-cpe file, or the value of CPE_NAME
from os-release if the former was not found, will be used.
.TP
.BI \-\-rpm= PACKAGE \- VERSION
Set the keys
.I type
to
.BR rpm ,
.I name
to
.IR PACKAGE ,
and
.I version
to
.IR VERSION .
Overrides
.BR \-\-package\-type ,
.BR \-\-package\-name ,
.BR \-\-package\-architecture ,
and
.BR \-\-package\-version .
.TP
.BI \-\-debug\-info\-url= URL
Set the key
.I debugInfoUrl
to
.IR URL .
By default this key is omitted, but for Debian packages it should
be set to the official Debian debuginfod server address
.IR https://debuginfod.debian.org/ .
.TP
.BR \-h ", " \-\-help
Show a short help message and exit.
.SH SEE ALSO
.ad l
.nh
.BR dh_package_notes (1),
.BR systemd\-coredump (8),
.UR https://systemd.io/COREDUMP_PACKAGE_METADATA/
.B Package Metadata for Core Files
.UE

View File

@ -0,0 +1,57 @@
# SPDX-License-Identifier: CC0-1.0
#
# This file is part of the package-notes package.
#
# Add an ELF note with information about the package the code was compiled for.
# See https://fedoraproject.org/wiki/Changes/Package_information_on_ELF_objects
# for details.
#
# To opt out of the use of this feature completely, include this in
# the spec file:
#
# %undefine _package_note_file
#
# The other macros can be undefined too to replace parts of the
# functionality. If %_generate_package_note_file is undefined, the
# linker script will not be generated, but the link flags may still
# refer to it. This may be useful if the default generation method is
# insufficient and a different mechanism will be used to generate
# %_package_note_file. If %_package_note_flags is undefined, the
# linker argument that injects the script will not be added to
# %build_ldfags, but the linker script would still be generated.
# The name of the file with the linker script. If %{buildsubdir} is
# defined, the file will be placed therein. Otherwise, one level up,
# directly in %{_builddir}.
#
# Note that %{version}-%{release} used here might be redefined from
# the "primary" values when subpackages with different version-release
# are specified. The contents of the script use the shell variable
# $RPM_PACKAGE_NAME, $RPM_PACKAGE_VERSION, $RPM_PACKAGE_RELEASE,
# and $RPM_ARCH that are set early and seem to always contain the "primary"
# values for the main package.
%_package_note_file %{_builddir}%{?buildsubdir:/%{buildsubdir}}/.package_note-%{name}-%{version}-%{release}.%{_arch}.ld
# Which linker will be used? This should be either "bfd", "gold", or
# "lld". Unfortunately linkers other than bfd do not support some of
# the options that we'd like to use, so if this is set to anything
# other than "bfd", note insertion is disabled.
#
# (The default linker for clang on armv7hl is lld.)
%_package_note_linker %["%_target_cpu" == "armv7hl" && "%{toolchain}" == "clang" ? "lld" : "bfd"]
# Whether to specify the READONLY attribute for the inserted
# section. We generally want this, but binutils <= 2.37 and other
# linkers do not support it.
%_package_note_readonly %["%_package_note_linker" == "bfd"?"1":"0"]
# Overall status: 1 if looks like we can insert the note, 0 otherwise
%_package_note_status %[0%{?_package_note_file:1} && 0%{?name:1} && "%_target_cpu" != "noarch" && "%_package_note_linker" == "bfd" ? 1 : 0]
# The linker flags to be passed to the compiler to insert the notes section.
%_package_note_flags %[%_package_note_status?"-Wl,%["%_package_note_linker" != "lld"?"-dT":"-T"],%{_package_note_file}":""]
# The command to actually generate the linker script that inserts the
# notes file. This command is automatically used as part of the build
# preamble.
%_generate_package_note_file %[%_package_note_status?"if [ -f %{_rpmconfigdir}/generate-package-notes.sh ]; then %{_rpmconfigdir}/generate-package-notes.sh %[0%{?_package_note_readonly}?"":"--readonly no "]--package-name ${RPM_PACKAGE_NAME:?} --package-version ${RPM_PACKAGE_VERSION:?}-${RPM_PACKAGE_RELEASE:?} --package-architecture ${RPM_ARCH:?} --cpe auto >%{_package_note_file}; fi":""]

View File

@ -0,0 +1 @@
../generate-package-notes.py

View File

@ -0,0 +1 @@
# SPDX-License-Identifier: CC0-1.0

View File

@ -0,0 +1,25 @@
SECTIONS
{
.note.package (READONLY) : ALIGN(4) {
LONG(0x0004) /* Length of Owner including NUL */
LONG(0x0038) /* Length of Value including NUL */
LONG(0xcafe1a7e) /* Note ID */
BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","osCpe":"cpe:/o:fedoraproject:fedora:34"}\x00' */
BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x63)
BYTE(0x70) BYTE(0x65) BYTE(0x3a) BYTE(0x2f)
BYTE(0x6f) BYTE(0x3a) BYTE(0x66) BYTE(0x65)
BYTE(0x64) BYTE(0x6f) BYTE(0x72) BYTE(0x61)
BYTE(0x70) BYTE(0x72) BYTE(0x6f) BYTE(0x6a)
BYTE(0x65) BYTE(0x63) BYTE(0x74) BYTE(0x3a)
BYTE(0x66) BYTE(0x65) BYTE(0x64) BYTE(0x6f)
BYTE(0x72) BYTE(0x61) BYTE(0x3a) BYTE(0x33)
BYTE(0x34) BYTE(0x22) BYTE(0x7d) BYTE(0x00)
}
}
INSERT AFTER .note.gnu.build-id;
/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */

View File

@ -0,0 +1,25 @@
SECTIONS
{
.note.package (READONLY) : ALIGN(4) {
LONG(0x0004) /* Length of Owner including NUL */
LONG(0x0038) /* Length of Value including NUL */
LONG(0xcafe1a7e) /* Note ID */
BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","osCpe":"cpe:/o:fedoraproject:fedora:33"}\x00' */
BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x63)
BYTE(0x70) BYTE(0x65) BYTE(0x3a) BYTE(0x2f)
BYTE(0x6f) BYTE(0x3a) BYTE(0x66) BYTE(0x65)
BYTE(0x64) BYTE(0x6f) BYTE(0x72) BYTE(0x61)
BYTE(0x70) BYTE(0x72) BYTE(0x6f) BYTE(0x6a)
BYTE(0x65) BYTE(0x63) BYTE(0x74) BYTE(0x3a)
BYTE(0x66) BYTE(0x65) BYTE(0x64) BYTE(0x6f)
BYTE(0x72) BYTE(0x61) BYTE(0x3a) BYTE(0x33)
BYTE(0x33) BYTE(0x22) BYTE(0x7d) BYTE(0x00)
}
}
INSERT AFTER .note.gnu.build-id;
/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */

View File

@ -0,0 +1,88 @@
SECTIONS
{
.note.package (READONLY) : ALIGN(4) {
LONG(0x0004) /* Length of Owner including NUL */
LONG(0x0133) /* Length of Value including NUL */
LONG(0xcafe1a7e) /* Note ID */
BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","name":"rust-plist+enable_unstable_features_that_may_break_with_minor_version_bumps-devel","version":"200:1.3.1~rc1.post2^final3","architecture":"ppc64le","osCpe":"cpe:/o:fedoraproject:fedora:35","debugInfoUrl":"https://somewhere.on.the.internet.there.is.a.server.which.is.never.wrong/query"}\x00\x00' */
BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6e)
BYTE(0x61) BYTE(0x6d) BYTE(0x65) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x72) BYTE(0x75)
BYTE(0x73) BYTE(0x74) BYTE(0x2d) BYTE(0x70)
BYTE(0x6c) BYTE(0x69) BYTE(0x73) BYTE(0x74)
BYTE(0x2b) BYTE(0x65) BYTE(0x6e) BYTE(0x61)
BYTE(0x62) BYTE(0x6c) BYTE(0x65) BYTE(0x5f)
BYTE(0x75) BYTE(0x6e) BYTE(0x73) BYTE(0x74)
BYTE(0x61) BYTE(0x62) BYTE(0x6c) BYTE(0x65)
BYTE(0x5f) BYTE(0x66) BYTE(0x65) BYTE(0x61)
BYTE(0x74) BYTE(0x75) BYTE(0x72) BYTE(0x65)
BYTE(0x73) BYTE(0x5f) BYTE(0x74) BYTE(0x68)
BYTE(0x61) BYTE(0x74) BYTE(0x5f) BYTE(0x6d)
BYTE(0x61) BYTE(0x79) BYTE(0x5f) BYTE(0x62)
BYTE(0x72) BYTE(0x65) BYTE(0x61) BYTE(0x6b)
BYTE(0x5f) BYTE(0x77) BYTE(0x69) BYTE(0x74)
BYTE(0x68) BYTE(0x5f) BYTE(0x6d) BYTE(0x69)
BYTE(0x6e) BYTE(0x6f) BYTE(0x72) BYTE(0x5f)
BYTE(0x76) BYTE(0x65) BYTE(0x72) BYTE(0x73)
BYTE(0x69) BYTE(0x6f) BYTE(0x6e) BYTE(0x5f)
BYTE(0x62) BYTE(0x75) BYTE(0x6d) BYTE(0x70)
BYTE(0x73) BYTE(0x2d) BYTE(0x64) BYTE(0x65)
BYTE(0x76) BYTE(0x65) BYTE(0x6c) BYTE(0x22)
BYTE(0x2c) BYTE(0x22) BYTE(0x76) BYTE(0x65)
BYTE(0x72) BYTE(0x73) BYTE(0x69) BYTE(0x6f)
BYTE(0x6e) BYTE(0x22) BYTE(0x3a) BYTE(0x22)
BYTE(0x32) BYTE(0x30) BYTE(0x30) BYTE(0x3a)
BYTE(0x31) BYTE(0x2e) BYTE(0x33) BYTE(0x2e)
BYTE(0x31) BYTE(0x7e) BYTE(0x72) BYTE(0x63)
BYTE(0x31) BYTE(0x2e) BYTE(0x70) BYTE(0x6f)
BYTE(0x73) BYTE(0x74) BYTE(0x32) BYTE(0x5e)
BYTE(0x66) BYTE(0x69) BYTE(0x6e) BYTE(0x61)
BYTE(0x6c) BYTE(0x33) BYTE(0x22) BYTE(0x2c)
BYTE(0x22) BYTE(0x61) BYTE(0x72) BYTE(0x63)
BYTE(0x68) BYTE(0x69) BYTE(0x74) BYTE(0x65)
BYTE(0x63) BYTE(0x74) BYTE(0x75) BYTE(0x72)
BYTE(0x65) BYTE(0x22) BYTE(0x3a) BYTE(0x22)
BYTE(0x70) BYTE(0x70) BYTE(0x63) BYTE(0x36)
BYTE(0x34) BYTE(0x6c) BYTE(0x65) BYTE(0x22)
BYTE(0x2c) BYTE(0x22) BYTE(0x6f) BYTE(0x73)
BYTE(0x43) BYTE(0x70) BYTE(0x65) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x63) BYTE(0x70)
BYTE(0x65) BYTE(0x3a) BYTE(0x2f) BYTE(0x6f)
BYTE(0x3a) BYTE(0x66) BYTE(0x65) BYTE(0x64)
BYTE(0x6f) BYTE(0x72) BYTE(0x61) BYTE(0x70)
BYTE(0x72) BYTE(0x6f) BYTE(0x6a) BYTE(0x65)
BYTE(0x63) BYTE(0x74) BYTE(0x3a) BYTE(0x66)
BYTE(0x65) BYTE(0x64) BYTE(0x6f) BYTE(0x72)
BYTE(0x61) BYTE(0x3a) BYTE(0x33) BYTE(0x35)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x64)
BYTE(0x65) BYTE(0x62) BYTE(0x75) BYTE(0x67)
BYTE(0x49) BYTE(0x6e) BYTE(0x66) BYTE(0x6f)
BYTE(0x55) BYTE(0x72) BYTE(0x6c) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x68) BYTE(0x74)
BYTE(0x74) BYTE(0x70) BYTE(0x73) BYTE(0x3a)
BYTE(0x2f) BYTE(0x2f) BYTE(0x73) BYTE(0x6f)
BYTE(0x6d) BYTE(0x65) BYTE(0x77) BYTE(0x68)
BYTE(0x65) BYTE(0x72) BYTE(0x65) BYTE(0x2e)
BYTE(0x6f) BYTE(0x6e) BYTE(0x2e) BYTE(0x74)
BYTE(0x68) BYTE(0x65) BYTE(0x2e) BYTE(0x69)
BYTE(0x6e) BYTE(0x74) BYTE(0x65) BYTE(0x72)
BYTE(0x6e) BYTE(0x65) BYTE(0x74) BYTE(0x2e)
BYTE(0x74) BYTE(0x68) BYTE(0x65) BYTE(0x72)
BYTE(0x65) BYTE(0x2e) BYTE(0x69) BYTE(0x73)
BYTE(0x2e) BYTE(0x61) BYTE(0x2e) BYTE(0x73)
BYTE(0x65) BYTE(0x72) BYTE(0x76) BYTE(0x65)
BYTE(0x72) BYTE(0x2e) BYTE(0x77) BYTE(0x68)
BYTE(0x69) BYTE(0x63) BYTE(0x68) BYTE(0x2e)
BYTE(0x69) BYTE(0x73) BYTE(0x2e) BYTE(0x6e)
BYTE(0x65) BYTE(0x76) BYTE(0x65) BYTE(0x72)
BYTE(0x2e) BYTE(0x77) BYTE(0x72) BYTE(0x6f)
BYTE(0x6e) BYTE(0x67) BYTE(0x2f) BYTE(0x71)
BYTE(0x75) BYTE(0x65) BYTE(0x72) BYTE(0x79)
BYTE(0x22) BYTE(0x7d) BYTE(0x00) BYTE(0x00)
}
}
INSERT AFTER .note.gnu.build-id;
/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */

View File

@ -0,0 +1,33 @@
SECTIONS
{
.note.package (READONLY) : ALIGN(4) {
LONG(0x0004) /* Length of Owner including NUL */
LONG(0x0058) /* Length of Value including NUL */
LONG(0xcafe1a7e) /* Note ID */
BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","name":"package","version":"1.2.3","architecture":"noarch","osCpe":"CPE"}\x00' */
BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6e)
BYTE(0x61) BYTE(0x6d) BYTE(0x65) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x70) BYTE(0x61)
BYTE(0x63) BYTE(0x6b) BYTE(0x61) BYTE(0x67)
BYTE(0x65) BYTE(0x22) BYTE(0x2c) BYTE(0x22)
BYTE(0x76) BYTE(0x65) BYTE(0x72) BYTE(0x73)
BYTE(0x69) BYTE(0x6f) BYTE(0x6e) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x31) BYTE(0x2e)
BYTE(0x32) BYTE(0x2e) BYTE(0x33) BYTE(0x22)
BYTE(0x2c) BYTE(0x22) BYTE(0x61) BYTE(0x72)
BYTE(0x63) BYTE(0x68) BYTE(0x69) BYTE(0x74)
BYTE(0x65) BYTE(0x63) BYTE(0x74) BYTE(0x75)
BYTE(0x72) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
BYTE(0x22) BYTE(0x6e) BYTE(0x6f) BYTE(0x61)
BYTE(0x72) BYTE(0x63) BYTE(0x68) BYTE(0x22)
BYTE(0x2c) BYTE(0x22) BYTE(0x6f) BYTE(0x73)
BYTE(0x43) BYTE(0x70) BYTE(0x65) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x43) BYTE(0x50)
BYTE(0x45) BYTE(0x22) BYTE(0x7d) BYTE(0x00)
}
}
INSERT AFTER .note.gnu.build-id;
/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */

View File

@ -0,0 +1 @@
CPE_NAME="cpe:/o:fedoraproject:fedora:34"

View File

@ -0,0 +1 @@
ID=fedora

View File

@ -0,0 +1 @@
cpe:/o:fedoraproject:fedora:33

View File

@ -0,0 +1,29 @@
SECTIONS
{
.note.package : ALIGN(4) {
LONG(0x0004) /* Length of Owner including NUL */
LONG(0x0047) /* Length of Value including NUL */
LONG(0xcafe1a7e) /* Note ID */
BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"deb","name":"A","version":"0","architecture":"x","osCpe":"o"}\x00\x00' */
BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
BYTE(0x22) BYTE(0x64) BYTE(0x65) BYTE(0x62)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6e)
BYTE(0x61) BYTE(0x6d) BYTE(0x65) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x41) BYTE(0x22)
BYTE(0x2c) BYTE(0x22) BYTE(0x76) BYTE(0x65)
BYTE(0x72) BYTE(0x73) BYTE(0x69) BYTE(0x6f)
BYTE(0x6e) BYTE(0x22) BYTE(0x3a) BYTE(0x22)
BYTE(0x30) BYTE(0x22) BYTE(0x2c) BYTE(0x22)
BYTE(0x61) BYTE(0x72) BYTE(0x63) BYTE(0x68)
BYTE(0x69) BYTE(0x74) BYTE(0x65) BYTE(0x63)
BYTE(0x74) BYTE(0x75) BYTE(0x72) BYTE(0x65)
BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x78)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x6f)
BYTE(0x22) BYTE(0x7d) BYTE(0x00) BYTE(0x00)
}
}
INSERT AFTER .note.gnu.build-id;
/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */

View File

@ -0,0 +1,29 @@
SECTIONS
{
.note.package (READONLY) : ALIGN(4) {
LONG(0x0004) /* Length of Owner including NUL */
LONG(0x0047) /* Length of Value including NUL */
LONG(0xcafe1a7e) /* Note ID */
BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"deb","name":"A","version":"0","architecture":"x","osCpe":"o"}\x00\x00' */
BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
BYTE(0x22) BYTE(0x64) BYTE(0x65) BYTE(0x62)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6e)
BYTE(0x61) BYTE(0x6d) BYTE(0x65) BYTE(0x22)
BYTE(0x3a) BYTE(0x22) BYTE(0x41) BYTE(0x22)
BYTE(0x2c) BYTE(0x22) BYTE(0x76) BYTE(0x65)
BYTE(0x72) BYTE(0x73) BYTE(0x69) BYTE(0x6f)
BYTE(0x6e) BYTE(0x22) BYTE(0x3a) BYTE(0x22)
BYTE(0x30) BYTE(0x22) BYTE(0x2c) BYTE(0x22)
BYTE(0x61) BYTE(0x72) BYTE(0x63) BYTE(0x68)
BYTE(0x69) BYTE(0x74) BYTE(0x65) BYTE(0x63)
BYTE(0x74) BYTE(0x75) BYTE(0x72) BYTE(0x65)
BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x78)
BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x6f)
BYTE(0x22) BYTE(0x7d) BYTE(0x00) BYTE(0x00)
}
}
INSERT AFTER .note.gnu.build-id;
/* HINT: add -Wl,-dT,/path/to/this/file to $LDFLAGS */

54
tests/test_basics.py Normal file
View File

@ -0,0 +1,54 @@
# SPDX-License-Identifier: CC0-1.0
import sys
from importlib import resources
from pathlib import Path
from _generate_package_notes import gather_data, generate_section
class dict_dot(dict):
__getattr__ = dict.get
def test_fedora_package():
input = dict(type='rpm', name='package', version='1.2.3', architecture='noarch', osCpe='CPE')
text = '\n'.join(generate_section(input))
expected = resources.read_text('resources', 'fedora-package.ld')
assert text == expected[:-1]
def test_very_short():
input = dict(type='deb', name='A', version='0', architecture='x', osCpe='o')
text = '\n'.join(generate_section(input))
expected = resources.read_text('resources', 'very-short.ld')
assert text == expected[:-1]
def test_very_short_rw():
input = dict(type='deb', name='A', version='0', architecture='x', osCpe='o')
text = '\n'.join(generate_section(input, readonly=False))
expected = resources.read_text('resources', 'very-short-rw.ld')
assert text == expected[:-1]
def test_fedora_long_name():
input = dict(type='rpm',
name='rust-plist+enable_unstable_features_that_may_break_with_minor_version_bumps-devel',
version='200:1.3.1~rc1.post2^final3',
architecture='ppc64le',
osCpe='cpe:/o:fedoraproject:fedora:35',
debugInfoUrl='https://somewhere.on.the.internet.there.is.a.server.which.is.never.wrong/query')
text = '\n'.join(generate_section(input))
expected = resources.read_text('resources', 'fedora-long-name.ld')
assert text == expected[:-1]
def test_auto_cpe_system_release():
opts = dict_dot(package_type='rpm', cpe='auto', root=Path(__file__).absolute().parent / 'resources/root/')
input = gather_data(opts)
text = '\n'.join(generate_section(input))
expected = resources.read_text('resources', 'fedora-cpe-system-release.ld')
assert text == expected[:-1]
def test_auto_cpe_os_release():
opts = dict_dot(package_type='rpm', cpe='auto', root=Path(__file__).absolute().parent / 'resources/root-no-cpe/')
input = gather_data(opts)
text = '\n'.join(generate_section(input))
expected = resources.read_text('resources', 'fedora-cpe-os-release.ld')
assert text == expected[:-1]

13
tests/test_basics.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
# SPDX-License-Identifier: CC0-1.0
set -e
testdir="$(dirname "$(realpath "${0}")")"
diff <("${testdir}/../generate-package-notes.sh" --type rpm --name package --version 1.2.3 --architecture noarch --osCpe CPE) "${testdir}/resources/fedora-package.ld"
diff <("${testdir}/../generate-package-notes.sh" --type deb --name A --package-version 0 --architecture x --cpe o) "${testdir}/resources/very-short.ld"
diff <("${testdir}/../generate-package-notes.sh" --type deb --name A --version 0 --architecture x --osCpe o --readonly false) "${testdir}/resources/very-short-rw.ld"
diff <("${testdir}/../generate-package-notes.sh" --type rpm --name rust-plist+enable_unstable_features_that_may_break_with_minor_version_bumps-devel --version 200:1.3.1~rc1.post2^final3 --architecture ppc64le --osCpe cpe:/o:fedoraproject:fedora:35 --debugInfoUrl https://somewhere.on.the.internet.there.is.a.server.which.is.never.wrong/query) "${testdir}/resources/fedora-long-name.ld"
diff <("${testdir}/../generate-package-notes.sh" --type rpm --cpe auto --root "${testdir}/resources/root-no-cpe/") "${testdir}/resources/fedora-cpe-os-release.ld"
diff <("${testdir}/../generate-package-notes.sh" --type rpm --cpe auto --root "${testdir}/resources/root/") "${testdir}/resources/fedora-cpe-system-release.ld"