Перекрестная компиляция из Ubuntu amd64 в arm7l: пользовательский процесс exec вызвал «ошибку формата exec» - PullRequest
0 голосов
/ 17 июня 2019

У меня болит голова при кросс-компиляции от amd64 до arm7l

Я мог бы наконец-то сделать это с помощью Gitlab CI, так что теперь я собираю свой бинарный файл в образе docker, вот файл docker:

FROM golang

WORKDIR /go/src/gitlab.com/company/edge_to_bc

COPY . .
RUN dpkg --add-architecture armhf && apt update && apt-get install -y gcc-arm-linux-gnueabihf libltdl-dev:armhf

Я создаю его как Тогда я создам новый контейнер "кросс-компиляции", готовый с именем ubuntu:cross-compil

Теперь я могу скомпилировать свой двоичный файл с помощью:

docker run -it -v ${EDGE_TO_BC_PATH}/release:/go/src/gitlab.com/company/edge_to_bc/release ubuntu:cross-compil  bash -c 'CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 go build -v -o ./release/edge_to_bc '

Я вижу мой исполняемый файл, сгенерированный в ./release/edge_to_bc

Затем я создаю свой образ докера:

docker build -t registry.gitlab.com/company/edge_to_bc:armhf .

И я нажимаю на него.

В файле DockerЯ просто копирую исполняемый файл с хоста:

FROM alpine:3.7

RUN apk --no-cache add ca-certificates libtool

WORKDIR /sunclient/

COPY ./release/edge_to_bc ./
EXPOSE 5555

CMD [ "./edge_to_bc" ]

Но когда я запускаю его на своей плате с:

docker run --rm registry.gitlab.com/company/edge_to_bc:armhf

, я получаю:

standard_init_linux.go:207: exec user process caused "no such file or directory"

При отладке, если я хочу получить список файлов с

docker run --rm registry.gitlab.com/company/edge_to_bc:armhf

, я получаю:

standard_init_linux.go:207: exec user process caused "exec format error"

, которые указывают, что двоичный файл все еще не имеет правильный формат ...

Что я упустил ?Я потратил много времени на эту тему, и у меня не так много идей.

Когда я проверяю архитектуру двоичного файла, я получаю следующее:

 edge_to_bc git:(master) ✗ readelf -h ./release/edge_to_bc
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x19209
  Start of program headers:          52 (bytes into file)
  Start of section headers:          23993360 (bytes into file)
  Flags:                             0x5000400, Version5 EABI, hard-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         10
  Size of section headers:           40 (bytes)
  Number of section headers:         49
  Section header string table index: 48

Нацелевая ОС, вот что я получаю:

[root@gw-sol1 ~]# uname -a
Linux gw-sol-1 4.4.113-UNRELEASED #1 SMP PREEMPT Thu Mar 7 16:46:40 CET 2019 armv7l armv7l armv7l GNU/Linux

РЕДАКТИРОВАТЬ:

Когда я создаю приложение непосредственно на устройстве ARM, оно будет работать:

go build -o ./release/edge_to_bc -v -ldflags '-w -s -extldflags "-static"' ./...

ELF:

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - GNU
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x125f1
  Start of program headers:          52 (bytes into file)
  Start of section headers:          16594072 (bytes into file)
  Flags:                             0x5000400, Version5 EABI, hard-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         7
  Size of section headers:           40 (bytes)
  Number of section headers:         38
  Section header string table index: 37

Кажется, что он очень похож на другой, по крайней мере, в архитектуре.

Затем создайте образ докера:

docker build . -t image/peer-test:armhf

1 Ответ

1 голос
/ 18 июня 2019

Когда вы запускаете сборку в системе amd64 образа arm7 с помощью этой команды:

docker build -t registry.gitlab.com/company/edge_to_bc:armhf .

Он будет использовать базовые образы и запускать команды в этом образе для amd64. Таким образом, даже если ваш двоичный файл edge_to_bc может быть скомпилирован, остальная часть изображения - нет. Далее, сама команда бинарной компиляции выглядит так, как будто она соединяется с библиотеками, которых, скорее всего, нет в вашем конечном образе. Вы можете запустить ldd edge_to_bc, чтобы просмотреть эти ссылки, и, если они отсутствуют, вы получите сообщение об ошибке «файл не найден».


В моем собственном тесте кросс-компиляции я использую BuildKit, Buildx и некоторые экспериментальные функции на 19.03.0-rc2, так что будут некоторые части, которые не имеют обратной совместимости, но, надеюсь, вы найдете их полезными. Я использую многоэтапную сборку, чтобы избежать компиляции вне докера, а затем вторую сборку. Я также указываю платформу для хоста сборки и использую целевые значения arch и переменные ОС для настройки иди кросс-компиляции. В этом сценарии я использовал полностью статически связанный двоичный файл, поэтому не было никаких библиотек для включения, и только с командами копирования в моем образе окончательного выпуска я избегал проблем при запуске сборки на другой платформе.

# syntax=docker/dockerfile:experimental
# ^ above line must be at the beginning of the Dockerfile for BuildKit

# --platform pulls an image for the build host rather than target OS/Arch
FROM --platform=$BUILDPLATFORM golang:1.12-alpine as dev
RUN apk add --no-cache git ca-certificates
RUN adduser -D appuser
WORKDIR /src
COPY . /src/
CMD CGO_ENABLED=0 go build -o app . && ./app

# my dev stage is separate from build to allow mounting source and rebuilding
# on developer machines    
FROM --platform=$BUILDPLATFORM dev as build
ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH
# --mount is an experimental BuildKit feature
RUN --mount=type=cache,id=gomod,target=/go/pkg/mod/cache \
    --mount=type=cache,id=goroot,target=/root/.cache/go-build \
    CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
    go build -ldflags '-w -extldflags -static' -o app .
USER appuser
CMD [ "./app" ]

# this stage will have the target OS/Arch and cannot have any RUN commands
FROM scratch as release
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /etc/passwd /etc/group /etc/
COPY --from=build /src/app /app
USER appuser
CMD [ "/app" ]

FROM scratch as artifact
COPY --from=build /src/app /app

FROM release

Чтобы построить это, я могу запустить одноразовые команды сборки с BuildKit:

DOCKER_BUILDKIT=1 docker build --platform=linux/amd64 -f Dockerfile -t golang-app:amd64 .
DOCKER_BUILDKIT=1 docker build --platform=linux/arm64 -f Dockerfile -t golang-app:arm64 .

Но еще лучше версия Buildx, которая создает многоархитивный образ, который будет работать на нескольких платформах. Для этого необходимо нажать на сервер реестра:

docker buildx build -f Dockerfile --platform linux/amd64,linux/arm64 \
  -t ${docker_hub_id}/golang-app:multi-arch --output type=registry .

Для вашего сценария вы должны поменять ссылки с arm64 на свои собственные архитектуры. Параметр --platform был указан как экспериментальный во многих командах, которые я запускал, поэтому вам может потребоваться настроить следующее в файле /etc/docker/daemon.json для включения:

{ "experimental": true }

Я полагаю, что для вступления в силу необходим полный перезапуск демона Docker (systemctl restart docker), а не просто перезагрузка.

Чтобы извлечь артефакт из сборки (скомпилированный двоичный файл для конкретной архитектуры), я использую:

docker build -f Dockerfile --target artifact -o type=local,dest=. .

Это выведет содержимое стадии артефакта (один двоичный файл) в локальный каталог.


Выше приведен вариант 3 в списке Docker способов создания многоархивных изображений.

Вариант 1 - настроить qemu с binfmt_misc для создания и запуска образов для разных платформ. Я еще не смог заставить это работать на Linux с Buildx, но Docker сделал это для Mac, и вы, возможно, сможете найти более подробную информацию о том, что они сделали в проекте LinuxKit. Использование qemu для вашей ситуации может быть идеальным, если вам нужно запускать команды и устанавливать другие инструменты как часть вашей сборки, а не просто кросс-компилировать один статически связанный двоичный файл.

Вариант 2 - запустить сборку на целевой платформе, которая, как вы видели, работает хорошо. С Buildx вы можете добавить несколько узлов сборки, по одному на каждую платформу, что позволяет запускать сборку из одного места (сервер CI).

...