[llvm] r361949 - [llvm-objcopy] Implement IHEX writer

Russell Gallop via llvm-commits llvm-commits at lists.llvm.org
Wed May 29 09:23:31 PDT 2019


Hi Eugene,

In case you hadn't noticed due to the build failures, it looks like the
ihex-writer.test you added is failing on some bots (e.g.
http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast/builds/26164/steps/test/logs/stdio).
For example:

C:\ps4-buildslave2\llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast\llvm.src\test\tools\llvm-objcopy\ELF\ihex-writer.test:70:13:
error: BAD-ADDR: expected string not found in input
# BAD-ADDR: error: {{.*}}: Section '.text2' address range [0xfffffff8,
0x100000000] is not 32 bit
            ^
<stdin>:1:1: note: scanning from here
c:\ps4-buildslave2\llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast\llvm.obj\bin\llvm-objcopy.exe:
error:
'C:\ps4-buildslave2\llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast\llvm.obj\test\tools\llvm-objcopy\ELF\Output\ihex-writer.test.tmp-sec2':
Section '.text2' address range [00000000FFFFFFF8, 0000000100000000] is not
32 bit
^
<stdin>:1:242: note: possible intended match here
c:\ps4-buildslave2\llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast\llvm.obj\bin\llvm-objcopy.exe:
error:
'C:\ps4-buildslave2\llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast\llvm.obj\test\tools\llvm-objcopy\ELF\Output\ihex-writer.test.tmp-sec2':
Section '.text2' address range [00000000FFFFFFF8, 0000000100000000] is not
32 bit

Please could you take a look?

Thanks
Russ

On Wed, 29 May 2019 at 12:34, Eugene Leviant via llvm-commits <
llvm-commits at lists.llvm.org> wrote:

> Author: evgeny777
> Date: Wed May 29 04:37:16 2019
> New Revision: 361949
>
> URL: http://llvm.org/viewvc/llvm-project?rev=361949&view=rev
> Log:
> [llvm-objcopy] Implement IHEX writer
>
> Differential revision: https://reviews.llvm.org/D60270
>
> Added:
>     llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml
>     llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml
>     llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml
>     llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml
>     llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test
> Modified:
>     llvm/trunk/include/llvm/Support/Error.h
>     llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp
>     llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
>     llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp
>     llvm/trunk/tools/llvm-objcopy/ELF/Object.h
>
> Modified: llvm/trunk/include/llvm/Support/Error.h
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Error.h?rev=361949&r1=361948&r2=361949&view=diff
>
> ==============================================================================
> --- llvm/trunk/include/llvm/Support/Error.h (original)
> +++ llvm/trunk/include/llvm/Support/Error.h Wed May 29 04:37:16 2019
> @@ -1177,11 +1177,14 @@ Error createStringError(std::error_code
>  class FileError final : public ErrorInfo<FileError> {
>
>    friend Error createFileError(const Twine &, Error);
> +  friend Error createFileError(const Twine &, size_t, Error);
>
>  public:
>    void log(raw_ostream &OS) const override {
>      assert(Err && !FileName.empty() && "Trying to log after
> takeError().");
>      OS << "'" << FileName << "': ";
> +    if (Line.hasValue())
> +      OS << "line " << Line.getValue() << ": ";
>      Err->log(OS);
>    }
>
> @@ -1193,26 +1196,36 @@ public:
>    static char ID;
>
>  private:
> -  FileError(const Twine &F, std::unique_ptr<ErrorInfoBase> E) {
> +  FileError(const Twine &F, Optional<size_t> LineNum,
> +            std::unique_ptr<ErrorInfoBase> E) {
>      assert(E && "Cannot create FileError from Error success value.");
>      assert(!F.isTriviallyEmpty() &&
>             "The file name provided to FileError must not be empty.");
>      FileName = F.str();
>      Err = std::move(E);
> +    Line = std::move(LineNum);
>    }
>
> -  static Error build(const Twine &F, Error E) {
> -    return Error(std::unique_ptr<FileError>(new FileError(F,
> E.takePayload())));
> +  static Error build(const Twine &F, Optional<size_t> Line, Error E) {
> +    return Error(
> +        std::unique_ptr<FileError>(new FileError(F, Line,
> E.takePayload())));
>    }
>
>    std::string FileName;
> +  Optional<size_t> Line;
>    std::unique_ptr<ErrorInfoBase> Err;
>  };
>
>  /// Concatenate a source file path and/or name with an Error. The
> resulting
>  /// Error is unchecked.
>  inline Error createFileError(const Twine &F, Error E) {
> -  return FileError::build(F, std::move(E));
> +  return FileError::build(F, Optional<size_t>(), std::move(E));
> +}
> +
> +/// Concatenate a source file path and/or name with line number and an
> Error.
> +/// The resulting Error is unchecked.
> +inline Error createFileError(const Twine &F, size_t Line, Error E) {
> +  return FileError::build(F, Optional<size_t>(Line), std::move(E));
>  }
>
>  /// Concatenate a source file path and/or name with a std::error_code
> @@ -1221,6 +1234,12 @@ inline Error createFileError(const Twine
>    return createFileError(F, errorCodeToError(EC));
>  }
>
> +/// Concatenate a source file path and/or name with line number and
> +/// std::error_code to form an Error object.
> +inline Error createFileError(const Twine &F, size_t Line, std::error_code
> EC) {
> +  return createFileError(F, Line, errorCodeToError(EC));
> +}
> +
>  Error createFileError(const Twine &F, ErrorSuccess) = delete;
>
>  /// Helper for check-and-exit error handling.
>
> Added: llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml?rev=361949&view=auto
>
> ==============================================================================
> --- llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml
> (added)
> +++ llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml
> Wed May 29 04:37:16 2019
> @@ -0,0 +1,20 @@
> +!ELF
> +FileHeader:
> +  Class:           ELFCLASS64
> +  Data:            ELFDATA2LSB
> +  Type:            ET_EXEC
> +  Machine:         EM_X86_64
> +Sections:
> +  - Name:            .text
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
> +    Address:         0x0
> +    AddressAlign:    0x8
> +    Content:         "0001020304"
> +ProgramHeaders:
> +  - Type: PT_NULL
> +    Flags: [ PF_X, PF_R ]
> +    VAddr: 0xF00000000
> +    PAddr: 0x100000
> +    Sections:
> +      - Section: .text
>
> Added: llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml?rev=361949&view=auto
>
> ==============================================================================
> --- llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml
> (added)
> +++ llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml
> Wed May 29 04:37:16 2019
> @@ -0,0 +1,60 @@
> +!ELF
> +FileHeader:
> +  Class:           ELFCLASS64
> +  Data:            ELFDATA2LSB
> +  Type:            ET_EXEC
> +  Machine:         EM_X86_64
> +Sections:
> +  - Name:            .text
> +# This section contents exceeds default IHex line length of 16 bytes
> +# so we expect two lines created for it.
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
> +    Address:         0x0
> +    AddressAlign:    0x8
> +    Content:         "000102030405060708090A0B0C0D0E0F1011121314"
> +  - Name:            .data
> +# This section overlap 16-bit segment boundary, so we expect
> +# additional 'SegmentAddr' record of type '02'
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC ]
> +    Content:         "3031323334353637383940"
> +    Address:         0xFFF8
> +    AddressAlign:    0x8
> +  - Name:            .data2
> +# Previous section '.data' should have forced creation of
> +# 'SegmentAddr'(02) record with segment address of 0x10000,
> +# so this section should have address of 0x100.
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC ]
> +    Content:         "40414243"
> +    Address:         0x10100
> +    AddressAlign:    0x8
> +  - Name:            .data3
> +# The last section not only overlaps segment boundary, but
> +# also has linear address which doesn't fit 20 bits. The
> +# following records should be craeted:
> +# 'SegmentAddr'(02) record with address 0x0
> +# 'ExtendedAddr'(04) record with address 0x100000
> +# 'Data'(00) record with 8 bytes of section data
> +# 'SegmentAddr'(02) record with address 0x10000
> +# 'Data'(00) record with remaining 3 bytes of data.
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC ]
> +    Content:         "5051525354555657585960"
> +    Address:         0x10FFF8
> +    AddressAlign:    0x8
> +  - Name:            .bss
> +# NOBITS sections are not written to IHex
> +    Type:            SHT_NOBITS
> +    Flags:           [ SHF_ALLOC ]
> +    Address:         0x10100
> +    Size:            0x1000
> +    AddressAlign:    0x8
> +  - Name:            .dummy
> +# Non-allocatable sections are not written to IHex
> +    Type:            SHT_PROGBITS
> +    Flags:           [ ]
> +    Address:         0x20FFF8
> +    Size:            65536
> +    AddressAlign:    0x8
>
> Added:
> llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml?rev=361949&view=auto
>
> ==============================================================================
> --- llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml
> (added)
> +++ llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml
> Wed May 29 04:37:16 2019
> @@ -0,0 +1,39 @@
> +!ELF
> +FileHeader:
> +  Class:           ELFCLASS64
> +  Data:            ELFDATA2LSB
> +  Type:            ET_EXEC
> +  Machine:         EM_X86_64
> +Sections:
> +  - Name:            .text
> +# Zero length sections are not exported to IHex
> +# 'SegmentAddr' and 'ExtendedAddr' records aren't
> +# created either.
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
> +    Address:         0x7FFFFFFF
> +    AddressAlign:    0x8
> +    Size:            0
> +  - Name:            .text1
> +# Section address is sign-extended 32-bit address
> +# Data fits 32-bit range
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
> +    Address:         0xFFFFFFFF80001000
> +    AddressAlign:    0x8
> +    Content:         "0001020304"
> +  - Name:            .text2
> +# Part of section data is in 32-bit address range
> +# and part isn't.
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
> +    Address:         0xFFFFFFF8
> +    AddressAlign:    0x8
> +    Content:         "000102030405060708"
> +  - Name:            .text3
> +  # Entire secion is outside of 32-bit range
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
> +    Address:         0xFFFFFFFF0
> +    AddressAlign:    0x8
> +    Content:         "0001020304"
>
> Added: llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml?rev=361949&view=auto
>
> ==============================================================================
> --- llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml
> (added)
> +++ llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml
> Wed May 29 04:37:16 2019
> @@ -0,0 +1,60 @@
> +# Here we use yaml from ihex-elf-sections.yaml, but add single load
> +# segment containing all exported sections. In such case we should
> +# use physical address of a section intead of virtual address. Physical
> +# addresses start from 0x100000, so we create two additional
> 'ExtenededAddr'
> +# (03) record in the beginning of IHex file with that physical address
> +!ELF
> +FileHeader:
> +  Class:           ELFCLASS64
> +  Data:            ELFDATA2LSB
> +  Type:            ET_EXEC
> +  Machine:         EM_X86_64
> +  Entry:           0x100000
> +Sections:
> +  - Name:            .text
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
> +    Address:         0x0
> +    AddressAlign:    0x8
> +    Content:         "000102030405060708090A0B0C0D0E0F1011121314"
> +  - Name:            .data1
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC ]
> +    Content:         "3031323334353637383940"
> +    Address:         0xFFF8
> +    AddressAlign:    0x8
> +  - Name:            .data2
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC ]
> +    Content:         "40414243"
> +    Address:         0x10100
> +    AddressAlign:    0x8
> +  - Name:            .data3
> +    Type:            SHT_PROGBITS
> +    Flags:           [ SHF_ALLOC ]
> +    Content:         "5051525354555657585960"
> +    Address:         0x10FFF8
> +    AddressAlign:    0x8
> +  - Name:            .bss
> +    Type:            SHT_NOBITS
> +    Flags:           [ SHF_ALLOC ]
> +    Address:         0x10100
> +    Size:            0x1000
> +    AddressAlign:    0x8
> +  - Name:            .dummy
> +    Type:            SHT_PROGBITS
> +    Flags:           [ ]
> +    Address:         0x20FFF8
> +    Size:            65536
> +    AddressAlign:    0x8
> +ProgramHeaders:
> +  - Type: PT_LOAD
> +    Flags: [ PF_X, PF_R ]
> +    VAddr: 0xF00000000
> +    PAddr: 0x100000
> +    Sections:
> +      - Section: .text
> +      - Section: .data1
> +      - Section: .data2
> +      - Section: .data3
> +      - Section: .bss
>
> Added: llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test?rev=361949&view=auto
>
> ==============================================================================
> --- llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test (added)
> +++ llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test Wed May 29
> 04:37:16 2019
> @@ -0,0 +1,81 @@
> +# RUN: yaml2obj %p/Inputs/ihex-elf-sections.yaml -o %t
> +# RUN: llvm-objcopy -O ihex %t - | FileCheck %s
> +
> +# Check ihex output, when we have segments in ELF file
> +# In such case only sections in PT_LOAD segments will
> +# be exported and their physical addresses will be used
> +# RUN: yaml2obj %p/Inputs/ihex-elf-segments.yaml -o %t-segs
> +# RUN: llvm-objcopy -O ihex %t-segs - | FileCheck %s
> --check-prefix=SEGMENTS
> +
> +# Check that non-load segments are ignored:
> +# RUN: yaml2obj %p/Inputs/ihex-elf-pt-null.yaml -o %t2-segs
> +# RUN: llvm-objcopy -O ihex %t2-segs - | FileCheck %s
> --check-prefix=PT_NULL
> +
> +# Check that sign-extended 32-bit section addresses are processed
> +# correctly
> +# RUN: yaml2obj %p/Inputs/ihex-elf-sections2.yaml -o %t-sec2
> +# RUN: llvm-objcopy -O ihex --only-section=.text1 %t-sec2 - | FileCheck
> %s --check-prefix=SIGN_EXTENDED
> +
> +# Check that section address range overlapping 32 bit range
> +# triggers an error
> +# RUN: not llvm-objcopy -O ihex --only-section=.text2 %t-sec2
> %t-sec2-2.hex 2>&1 | FileCheck %s --check-prefix=BAD-ADDR
> +# RUN: not llvm-objcopy -O ihex --only-section=.text3 %t-sec2
> %t-sec2-3.hex 2>&1 | FileCheck %s --check-prefix=BAD-ADDR2
> +
> +# Check that zero length section is not written
> +# RUN: llvm-objcopy -O ihex --only-section=.text %t-sec2 - | FileCheck %s
> --check-prefix=ZERO_SIZE_SEC
> +
> +# Check 80x86 start address record. It is created for start
> +# addresses less than 0x100000
> +# RUN: llvm-objcopy -O ihex --set-start=0xFFFF %t - | FileCheck %s
> --check-prefix=START1
> +
> +# Check i386 start address record (05). It is created for
> +# start addresses which doesn't fit 20 bits
> +# RUN: llvm-objcopy -O ihex --set-start=0x100000 %t - | FileCheck %s
> --check-prefix=START2
> +
> +# We allow sign extended 32 bit start addresses as well.
> +# RUN: llvm-objcopy -O ihex --set-start=0xFFFFFFFF80001000 %t - |
> FileCheck %s --check-prefix=START3
> +
> +# Start address which exceeds 32 bit range triggers an error
> +# RUN: not llvm-objcopy -O ihex --set-start=0xF00000000 %t %t6.hex 2>&1 |
> FileCheck %s --check-prefix=BAD-START
> +
> +# CHECK:      :10000000000102030405060708090A0B0C0D0E0F78
> +# CHECK-NEXT: :05001000101112131491
> +# CHECK-NEXT: :08FFF800303132333435363765
> +# CHECK-NEXT: :020000021000EC
> +# CHECK-NEXT: :030000003839404C
> +# CHECK-NEXT: :0401000040414243F5
> +# CHECK-NEXT: :020000020000FC
> +# CHECK-NEXT: :020000040010EA
> +# CHECK-NEXT: :08FFF800505152535455565765
> +# CHECK-NEXT: :020000040011E9
> +# CHECK-NEXT: :03000000585960EC
> +# CHECK-NEXT: :00000001FF
> +
> +# SEGMENTS:       :020000040010EA
> +# SEGMENTS-NEXT:  :10000000000102030405060708090A0B0C0D0E0F78
> +# SEGMENTS-NEXT:  :05001000101112131491
> +# SEGMENTS-NEXT:  :0B001800303132333435363738394090
> +# SEGMENTS-NEXT:  :0400280040414243CE
> +# SEGMENTS-NEXT:  :0B003000505152535455565758596018
> +# SEGMENTS-NEXT:  :0400000500100000E7
> +# SEGMENTS-NEXT:  :00000001FF
> +
> +# 'ExtendedAddr' (04) record shouldn't be created
> +# PT_NULL-NOT: :02000004
> +
> +# SIGN_EXTENDED:      :0200000480007A
> +# SIGN_EXTENDED-NEXT: :051000000001020304E1
> +# SIGN_EXTENDED-NEXT: :00000001FF
> +
> +# BAD-ADDR: error: {{.*}}: Section '.text2' address range [0xfffffff8,
> 0x100000000] is not 32 bit
> +# BAD-ADDR2: error: {{.*}}: Section '.text3' address range [0xffffffff0,
> 0xffffffff4] is not 32 bit
> +
> +# There shouldn't be 'ExtendedAddr' nor 'Data' records
> +# ZERO_SIZE_SEC-NOT:  :02000004
> +# ZERO_SIZE_SEC-NOT:  :00FFFF00
> +# ZERO_SIZE_SEC:      :00000001FF
> +
> +# START1: :040000030000FFFFFB
> +# START2: :0400000500100000E7
> +# START3: :040000058000100067
> +# BAD-START: error: {{.*}}: Entry point address 0xf00000000 overflows 32
> bits
>
> Modified: llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp?rev=361949&r1=361948&r2=361949&view=diff
>
> ==============================================================================
> --- llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp (original)
> +++ llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp Wed May 29 04:37:16 2019
> @@ -458,7 +458,8 @@ Expected<DriverConfig> parseObjcopyOptio
>        return MI.takeError();
>      Config.BinaryArch = *MI;
>    }
> -  if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") {
> +  if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary" &&
> +      Config.OutputFormat != "ihex") {
>      Expected<MachineInfo> MI =
> getOutputFormatMachineInfo(Config.OutputFormat);
>      if (!MI)
>        return MI.takeError();
>
> Modified: llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp?rev=361949&r1=361948&r2=361949&view=diff
>
> ==============================================================================
> --- llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp (original)
> +++ llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp Wed May 29 04:37:16
> 2019
> @@ -130,12 +130,9 @@ static ElfType getOutputElfType(const Ma
>      return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE;
>  }
>
> -static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
> -                                            Object &Obj, Buffer &Buf,
> -                                            ElfType OutputElfType) {
> -  if (Config.OutputFormat == "binary") {
> -    return llvm::make_unique<BinaryWriter>(Obj, Buf);
> -  }
> +static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config,
> +                                               Object &Obj, Buffer &Buf,
> +                                               ElfType OutputElfType) {
>    // Depending on the initial ELFT and OutputFormat we need a different
> Writer.
>    switch (OutputElfType) {
>    case ELFT_ELF32LE:
> @@ -154,6 +151,17 @@ static std::unique_ptr<Writer> createWri
>    llvm_unreachable("Invalid output format");
>  }
>
> +static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
> +                                            Object &Obj, Buffer &Buf,
> +                                            ElfType OutputElfType) {
> +  using Functor = std::function<std::unique_ptr<Writer>()>;
> +  return StringSwitch<Functor>(Config.OutputFormat)
> +      .Case("binary", [&] { return llvm::make_unique<BinaryWriter>(Obj,
> Buf); })
> +      .Case("ihex", [&] { return llvm::make_unique<IHexWriter>(Obj, Buf);
> })
> +      .Default(
> +          [&] { return createELFWriter(Config, Obj, Buf, OutputElfType);
> })();
> +}
> +
>  template <class ELFT>
>  static Expected<ArrayRef<uint8_t>>
>  findBuildID(const CopyConfig &Config, const object::ELFFile<ELFT> &In) {
> @@ -714,6 +722,15 @@ static Error handleArgs(const CopyConfig
>    return Error::success();
>  }
>
> +static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer
> &Out,
> +                         ElfType OutputElfType) {
> +  std::unique_ptr<Writer> Writer =
> +      createWriter(Config, Obj, Out, OutputElfType);
> +  if (Error E = Writer->finalize())
> +    return E;
> +  return Writer->write();
> +}
> +
>  Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer
> &In,
>                                  Buffer &Out) {
>    BinaryReader Reader(Config.BinaryArch, &In);
> @@ -721,15 +738,11 @@ Error executeObjcopyOnRawBinary(const Co
>
>    // Prefer OutputArch (-O<format>) if set, otherwise fallback to
> BinaryArch
>    // (-B<arch>).
> -  const ElfType OutputElfType = getOutputElfType(
> -      Config.OutputArch ? Config.OutputArch.getValue() :
> Config.BinaryArch);
> +  const ElfType OutputElfType =
> +      getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch));
>    if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType))
>      return E;
> -  std::unique_ptr<Writer> Writer =
> -      createWriter(Config, *Obj, Out, OutputElfType);
> -  if (Error E = Writer->finalize())
> -    return E;
> -  return Writer->write();
> +  return writeOutput(Config, *Obj, Out, OutputElfType);
>  }
>
>  Error executeObjcopyOnBinary(const CopyConfig &Config,
> @@ -764,12 +777,8 @@ Error executeObjcopyOnBinary(const CopyC
>    if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType))
>      return createFileError(Config.InputFilename, std::move(E));
>
> -  std::unique_ptr<Writer> Writer =
> -      createWriter(Config, *Obj, Out, OutputElfType);
> -  if (Error E = Writer->finalize())
> +  if (Error E = writeOutput(Config, *Obj, Out, OutputElfType))
>      return createFileError(Config.InputFilename, std::move(E));
> -  if (Error E = Writer->write())
> -    return E;
>    if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput)
>      if (Error E =
>              linkToBuildIdDir(Config, Config.OutputFilename,
>
> Modified: llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp?rev=361949&r1=361948&r2=361949&view=diff
>
> ==============================================================================
> --- llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp (original)
> +++ llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp Wed May 29 04:37:16 2019
> @@ -17,7 +17,7 @@
>  #include "llvm/MC/MCTargetOptions.h"
>  #include "llvm/Object/ELFObjectFile.h"
>  #include "llvm/Support/Compression.h"
> -#include "llvm/Support/Errc.h"
> +#include "llvm/Support/Endian.h"
>  #include "llvm/Support/ErrorHandling.h"
>  #include "llvm/Support/FileOutputBuffer.h"
>  #include "llvm/Support/Path.h"
> @@ -147,6 +147,156 @@ void SectionWriter::visit(const Section
>      llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset);
>  }
>
> +static bool addressOverflows32bit(uint64_t Addr) {
> +  // Sign extended 32 bit addresses (e.g 0xFFFFFFFF80000000) are ok
> +  return Addr > UINT32_MAX && Addr + 0x80000000 > UINT32_MAX;
> +}
> +
> +template <class T> static T checkedGetHex(StringRef S) {
> +  T Value;
> +  bool Fail = S.getAsInteger(16, Value);
> +  assert(!Fail);
> +  (void)Fail;
> +  return Value;
> +}
> +
> +// Fills exactly Len bytes of buffer with hexadecimal characters
> +// representing value 'X'
> +template <class T, class Iterator>
> +static Iterator utohexstr(T X, Iterator It, size_t Len) {
> +  // Fill range with '0'
> +  std::fill(It, It + Len, '0');
> +
> +  for (long I = Len - 1; I >= 0; --I) {
> +    unsigned char Mod = static_cast<unsigned char>(X) & 15;
> +    *(It + I) = hexdigit(Mod, false);
> +    X >>= 4;
> +  }
> +  assert(X == 0);
> +  return It + Len;
> +}
> +
> +uint8_t IHexRecord::getChecksum(StringRef S) {
> +  assert((S.size() & 1) == 0);
> +  uint8_t Checksum = 0;
> +  while (!S.empty()) {
> +    Checksum += checkedGetHex<uint8_t>(S.take_front(2));
> +    S = S.drop_front(2);
> +  }
> +  return -Checksum;
> +}
> +
> +IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr,
> +                                 ArrayRef<uint8_t> Data) {
> +  IHexLineData Line(getLineLength(Data.size()));
> +  assert(Line.size());
> +  auto Iter = Line.begin();
> +  *Iter++ = ':';
> +  Iter = utohexstr(Data.size(), Iter, 2);
> +  Iter = utohexstr(Addr, Iter, 4);
> +  Iter = utohexstr(Type, Iter, 2);
> +  for (uint8_t X : Data)
> +    Iter = utohexstr(X, Iter, 2);
> +  StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter));
> +  Iter = utohexstr(getChecksum(S), Iter, 2);
> +  *Iter++ = '\r';
> +  *Iter++ = '\n';
> +  assert(Iter == Line.end());
> +  return Line;
> +}
> +
> +static uint64_t sectionPhysicalAddr(const SectionBase *Sec) {
> +  Segment *Seg = Sec->ParentSegment;
> +  if (Seg && Seg->Type != ELF::PT_LOAD)
> +    Seg = nullptr;
> +  return Seg ? Seg->PAddr + Sec->OriginalOffset - Seg->OriginalOffset
> +             : Sec->Addr;
> +}
> +
> +void IHexSectionWriterBase::writeSection(const SectionBase *Sec,
> +                                         ArrayRef<uint8_t> Data) {
> +  assert(Data.size() == Sec->Size);
> +  const uint32_t ChunkSize = 16;
> +  uint32_t Addr = sectionPhysicalAddr(Sec) & 0xFFFFFFFFU;
> +  while (!Data.empty()) {
> +    uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize);
> +    if (Addr > SegmentAddr + BaseAddr + 0xFFFFU) {
> +      if (Addr > 0xFFFFFU) {
> +        // Write extended address record, zeroing segment address
> +        // if needed.
> +        if (SegmentAddr != 0)
> +          SegmentAddr = writeSegmentAddr(0U);
> +        BaseAddr = writeBaseAddr(Addr);
> +      } else {
> +        // We can still remain 16-bit
> +        SegmentAddr = writeSegmentAddr(Addr);
> +      }
> +    }
> +    uint64_t SegOffset = Addr - BaseAddr - SegmentAddr;
> +    assert(SegOffset <= 0xFFFFU);
> +    DataSize = std::min(DataSize, 0x10000U - SegOffset);
> +    writeData(0, SegOffset, Data.take_front(DataSize));
> +    Addr += DataSize;
> +    Data = Data.drop_front(DataSize);
> +  }
> +}
> +
> +uint64_t IHexSectionWriterBase::writeSegmentAddr(uint64_t Addr) {
> +  assert(Addr <= 0xFFFFFU);
> +  uint8_t Data[] = {static_cast<uint8_t>((Addr & 0xF0000U) >> 12), 0};
> +  writeData(2, 0, Data);
> +  return Addr & 0xF0000U;
> +}
> +
> +uint64_t IHexSectionWriterBase::writeBaseAddr(uint64_t Addr) {
> +  assert(Addr <= 0xFFFFFFFFU);
> +  uint64_t Base = Addr & 0xFFFF0000U;
> +  uint8_t Data[] = {static_cast<uint8_t>(Base >> 24),
> +                    static_cast<uint8_t>((Base >> 16) & 0xFF)};
> +  writeData(4, 0, Data);
> +  return Base;
> +}
> +
> +void IHexSectionWriterBase::writeData(uint8_t Type, uint16_t Addr,
> +                                      ArrayRef<uint8_t> Data) {
> +  Offset += IHexRecord::getLineLength(Data.size());
> +}
> +
> +void IHexSectionWriterBase::visit(const Section &Sec) {
> +  writeSection(&Sec, Sec.Contents);
> +}
> +
> +void IHexSectionWriterBase::visit(const OwnedDataSection &Sec) {
> +  writeSection(&Sec, Sec.Data);
> +}
> +
> +void IHexSectionWriterBase::visit(const StringTableSection &Sec) {
> +  // Check that sizer has already done its work
> +  assert(Sec.Size == Sec.StrTabBuilder.getSize());
> +  // We are free to pass an invalid pointer to writeSection as long
> +  // as we don't actually write any data. The real writer class has
> +  // to override this method .
> +  writeSection(&Sec, {nullptr, Sec.Size});
> +}
> +
> +void IHexSectionWriterBase::visit(const DynamicRelocationSection &Sec) {
> +  writeSection(&Sec, Sec.Contents);
> +}
> +
> +void IHexSectionWriter::writeData(uint8_t Type, uint16_t Addr,
> +                                  ArrayRef<uint8_t> Data) {
> +  IHexLineData HexData = IHexRecord::getLine(Type, Addr, Data);
> +  memcpy(Out.getBufferStart() + Offset, HexData.data(), HexData.size());
> +  Offset += HexData.size();
> +}
> +
> +void IHexSectionWriter::visit(const StringTableSection &Sec) {
> +  assert(Sec.Size == Sec.StrTabBuilder.getSize());
> +  std::vector<uint8_t> Data(Sec.Size);
> +  Sec.StrTabBuilder.write(Data.data());
> +  writeSection(&Sec, Data);
> +}
> +
>  void Section::accept(SectionVisitor &Visitor) const {
> Visitor.visit(*this); }
>
>  void Section::accept(MutableSectionVisitor &Visitor) {
> Visitor.visit(*this); }
> @@ -217,6 +367,15 @@ void OwnedDataSection::accept(MutableSec
>    Visitor.visit(*this);
>  }
>
> +void OwnedDataSection::appendHexData(StringRef HexData) {
> +  assert((HexData.size() & 1) == 0);
> +  while (!HexData.empty()) {
> +    Data.push_back(checkedGetHex<uint8_t>(HexData.take_front(2)));
> +    HexData = HexData.drop_front(2);
> +  }
> +  Size = Data.size();
> +}
> +
>  void BinarySectionWriter::visit(const CompressedSection &Sec) {
>    error("cannot write compressed section '" + Sec.Name + "' ");
>  }
> @@ -1807,6 +1966,109 @@ Error BinaryWriter::finalize() {
>    return Error::success();
>  }
>
> +bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs,
> +                                            const SectionBase *Rhs) const
> {
> +  return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) <
> +         (sectionPhysicalAddr(Rhs) & 0xFFFFFFFFU);
> +}
> +
> +uint64_t IHexWriter::writeEntryPointRecord(uint8_t *Buf) {
> +  IHexLineData HexData;
> +  uint8_t Data[4] = {};
> +  // We don't write entry point record if entry is zero.
> +  if (Obj.Entry == 0)
> +    return 0;
> +
> +  if (Obj.Entry <= 0xFFFFFU) {
> +    Data[0] = ((Obj.Entry & 0xF0000U) >> 12) & 0xFF;
> +    support::endian::write(&Data[2], static_cast<uint16_t>(Obj.Entry),
> +                           support::big);
> +    HexData = IHexRecord::getLine(IHexRecord::StartAddr80x86, 0, Data);
> +  } else {
> +    support::endian::write(Data, static_cast<uint32_t>(Obj.Entry),
> +                           support::big);
> +    HexData = IHexRecord::getLine(IHexRecord::StartAddr, 0, Data);
> +  }
> +  memcpy(Buf, HexData.data(), HexData.size());
> +  return HexData.size();
> +}
> +
> +uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) {
> +  IHexLineData HexData = IHexRecord::getLine(IHexRecord::EndOfFile, 0,
> {});
> +  memcpy(Buf, HexData.data(), HexData.size());
> +  return HexData.size();
> +}
> +
> +Error IHexWriter::write() {
> +  IHexSectionWriter Writer(Buf);
> +  // Write sections.
> +  for (const SectionBase *Sec : Sections)
> +    Sec->accept(Writer);
> +
> +  uint64_t Offset = Writer.getBufferOffset();
> +  // Write entry point address.
> +  Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset);
> +  // Write EOF.
> +  Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset);
> +  assert(Offset == TotalSize);
> +  return Buf.commit();
> +}
> +
> +Error IHexWriter::checkSection(const SectionBase &Sec) {
> +  uint64_t Addr = sectionPhysicalAddr(&Sec);
> +  if (addressOverflows32bit(Addr) || addressOverflows32bit(Addr +
> Sec.Size - 1))
> +    return createStringError(
> +        errc::invalid_argument,
> +        "Section '%s' address range [%p, %p] is not 32 bit",
> Sec.Name.c_str(),
> +        Addr, Addr + Sec.Size - 1);
> +  return Error::success();
> +}
> +
> +Error IHexWriter::finalize() {
> +  bool UseSegments = false;
> +  auto ShouldWrite = [](const SectionBase &Sec) {
> +    return (Sec.Flags & ELF::SHF_ALLOC) && (Sec.Type != ELF::SHT_NOBITS);
> +  };
> +  auto IsInPtLoad = [](const SectionBase &Sec) {
> +    return Sec.ParentSegment && Sec.ParentSegment->Type == ELF::PT_LOAD;
> +  };
> +
> +  // We can't write 64-bit addresses.
> +  if (addressOverflows32bit(Obj.Entry))
> +    return createStringError(errc::invalid_argument,
> +                             "Entry point address %p overflows 32 bits.",
> +                             Obj.Entry);
> +
> +  // If any section we're to write has segment then we
> +  // switch to using physical addresses. Otherwise we
> +  // use section virtual address.
> +  for (auto &Section : Obj.sections())
> +    if (ShouldWrite(Section) && IsInPtLoad(Section)) {
> +      UseSegments = true;
> +      break;
> +    }
> +
> +  for (auto &Section : Obj.sections())
> +    if (ShouldWrite(Section) && (!UseSegments || IsInPtLoad(Section))) {
> +      if (Error E = checkSection(Section))
> +        return E;
> +      Sections.insert(&Section);
> +    }
> +
> +  IHexSectionWriterBase LengthCalc(Buf);
> +  for (const SectionBase *Sec : Sections)
> +    Sec->accept(LengthCalc);
> +
> +  // We need space to write section records + StartAddress record
> +  // (if start adress is not zero) + EndOfFile record.
> +  TotalSize = LengthCalc.getBufferOffset() +
> +              (Obj.Entry ? IHexRecord::getLineLength(4) : 0) +
> +              IHexRecord::getLineLength(0);
> +  if (Error E = Buf.allocate(TotalSize))
> +    return E;
> +  return Error::success();
> +}
> +
>  template class ELFBuilder<ELF64LE>;
>  template class ELFBuilder<ELF64BE>;
>  template class ELFBuilder<ELF32LE>;
>
> Modified: llvm/trunk/tools/llvm-objcopy/ELF/Object.h
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/ELF/Object.h?rev=361949&r1=361948&r2=361949&view=diff
>
> ==============================================================================
> --- llvm/trunk/tools/llvm-objcopy/ELF/Object.h (original)
> +++ llvm/trunk/tools/llvm-objcopy/ELF/Object.h Wed May 29 04:37:16 2019
> @@ -17,6 +17,7 @@
>  #include "llvm/BinaryFormat/ELF.h"
>  #include "llvm/MC/StringTableBuilder.h"
>  #include "llvm/Object/ELFObjectFile.h"
> +#include "llvm/Support/Errc.h"
>  #include "llvm/Support/FileOutputBuffer.h"
>  #include <cstddef>
>  #include <cstdint>
> @@ -168,6 +169,8 @@ public:
>
>  #define MAKE_SEC_WRITER_FRIEND
>      \
>    friend class SectionWriter;
>       \
> +  friend class IHexSectionWriterBase;
>       \
> +  friend class IHexSectionWriter;
>       \
>    template <class ELFT> friend class ELFSectionWriter;
>      \
>    template <class ELFT> friend class ELFSectionSizer;
>
> @@ -186,6 +189,114 @@ public:
>    explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
>  };
>
> +using IHexLineData = SmallVector<char, 64>;
> +
> +struct IHexRecord {
> +  // Memory address of the record.
> +  uint16_t Addr;
> +  // Record type (see below).
> +  uint16_t Type;
> +  // Record data in hexadecimal form.
> +  StringRef HexData;
> +
> +  // Helper method to get file length of the record
> +  // including newline character
> +  static size_t getLength(size_t DataSize) {
> +    // :LLAAAATT[DD...DD]CC'
> +    return DataSize * 2 + 11;
> +  }
> +
> +  // Gets length of line in a file (getLength + CRLF).
> +  static size_t getLineLength(size_t DataSize) {
> +    return getLength(DataSize) + 2;
> +  }
> +
> +  // Given type, address and data returns line which can
> +  // be written to output file.
> +  static IHexLineData getLine(uint8_t Type, uint16_t Addr,
> +                              ArrayRef<uint8_t> Data);
> +
> +  // Calculates checksum of stringified record representation
> +  // S must NOT contain leading ':' and trailing whitespace
> +  // characters
> +  static uint8_t getChecksum(StringRef S);
> +
> +  enum Type {
> +    // Contains data and a 16-bit starting address for the data.
> +    // The byte count specifies number of data bytes in the record.
> +    Data = 0,
> +    // Must occur exactly once per file in the last line of the file.
> +    // The data field is empty (thus byte count is 00) and the address
> +    // field is typically 0000.
> +    EndOfFile = 1,
> +    // The data field contains a 16-bit segment base address (thus byte
> +    // count is always 02) compatible with 80x86 real mode addressing.
> +    // The address field (typically 0000) is ignored. The segment address
> +    // from the most recent 02 record is multiplied by 16 and added to
> each
> +    // subsequent data record address to form the physical starting
> address
> +    // for the data. This allows addressing up to one megabyte of address
> +    // space.
> +    SegmentAddr = 2,
> +    // or 80x86 processors, specifies the initial content of the CS:IP
> +    // registers. The address field is 0000, the byte count is always 04,
> +    // the first two data bytes are the CS value, the latter two are the
> +    // IP value.
> +    StartAddr80x86 = 3,
> +    // Allows for 32 bit addressing (up to 4GiB). The record's address
> field
> +    // is ignored (typically 0000) and its byte count is always 02. The
> two
> +    // data bytes (big endian) specify the upper 16 bits of the 32 bit
> +    // absolute address for all subsequent type 00 records
> +    ExtendedAddr = 4,
> +    // The address field is 0000 (not used) and the byte count is always
> 04.
> +    // The four data bytes represent a 32-bit address value. In the case
> of
> +    // 80386 and higher CPUs, this address is loaded into the EIP
> register.
> +    StartAddr = 5,
> +    // We have no other valid types
> +    InvalidType = 6
> +  };
> +};
> +
> +// Base class for IHexSectionWriter. This class implements writing
> algorithm,
> +// but doesn't actually write records. It is used for output buffer size
> +// calculation in IHexWriter::finalize.
> +class IHexSectionWriterBase : public BinarySectionWriter {
> +  // 20-bit segment address
> +  uint32_t SegmentAddr = 0;
> +  // Extended linear address
> +  uint32_t BaseAddr = 0;
> +
> +  // Write segment address corresponding to 'Addr'
> +  uint64_t writeSegmentAddr(uint64_t Addr);
> +  // Write extended linear (base) address corresponding to 'Addr'
> +  uint64_t writeBaseAddr(uint64_t Addr);
> +
> +protected:
> +  // Offset in the output buffer
> +  uint64_t Offset = 0;
> +
> +  void writeSection(const SectionBase *Sec, ArrayRef<uint8_t> Data);
> +  virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t>
> Data);
> +
> +public:
> +  explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf)
> {}
> +
> +  uint64_t getBufferOffset() const { return Offset; }
> +  void visit(const Section &Sec) final;
> +  void visit(const OwnedDataSection &Sec) final;
> +  void visit(const StringTableSection &Sec) override;
> +  void visit(const DynamicRelocationSection &Sec) final;
> +  using BinarySectionWriter::visit;
> +};
> +
> +// Real IHEX section writer
> +class IHexSectionWriter : public IHexSectionWriterBase {
> +public:
> +  IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {}
> +
> +  void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data)
> override;
> +  void visit(const StringTableSection &Sec) override;
> +};
> +
>  class Writer {
>  protected:
>    Object &Obj;
> @@ -245,6 +356,25 @@ public:
>    BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
>  };
>
> +class IHexWriter : public Writer {
> +  struct SectionCompare {
> +    bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const;
> +  };
> +
> +  std::set<const SectionBase *, SectionCompare> Sections;
> +  size_t TotalSize;
> +
> +  Error checkSection(const SectionBase &Sec);
> +  uint64_t writeEntryPointRecord(uint8_t *Buf);
> +  uint64_t writeEndOfFileRecord(uint8_t *Buf);
> +
> +public:
> +  ~IHexWriter() {}
> +  Error finalize() override;
> +  Error write() override;
> +  IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
> +};
> +
>  class SectionBase {
>  public:
>    std::string Name;
> @@ -361,6 +491,16 @@ public:
>      OriginalOffset = std::numeric_limits<uint64_t>::max();
>    }
>
> +  OwnedDataSection(const Twine &SecName, uint64_t SecAddr, uint64_t
> SecFlags,
> +                   uint64_t SecOff) {
> +    Name = SecName.str();
> +    Type = ELF::SHT_PROGBITS;
> +    Addr = SecAddr;
> +    Flags = SecFlags;
> +    OriginalOffset = SecOff;
> +  }
> +
> +  void appendHexData(StringRef HexData);
>    void accept(SectionVisitor &Sec) const override;
>    void accept(MutableSectionVisitor &Visitor) override;
>  };
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20190529/c2256623/attachment-0001.html>


More information about the llvm-commits mailing list