簡體   English   中英

從Ubuntu amd64到arm7l進行交叉編譯:exec用戶進程導致“ exec格式錯誤”

[英]Go Cross Compilation from Ubuntu amd64 to arm7l : exec user process caused “exec format error”

我對從amd64到arm7l的交叉編譯感到頭痛

我終於可以用Gitlab CI做到這一點,所以現在,我在docker映像中編譯我的二進制文件,這是dockerfile:

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的新容器“ 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 .

然后我推。

在Dockerfile中,我只是從主機復制可執行文件:

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鏡像:

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

使用以下命令在arm7映像的amd64系統上運行構建時:

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

它將使用基本映像並在該映像中為amd64運行命令。 因此,即使您的單個edge_to_bc二進制文件可以交叉編譯,映像的其余部分也不會被交叉編譯。 接下來,二進制編譯命令本身看起來就像是鏈接到庫,而這些庫很可能不在最終映像中。 您可以運行ldd edge_to_bc來查看這些鏈接,如果缺少這些鏈接,則會收到“找不到文件”錯誤。


在我自己的交叉編譯測試中,我在19.03.0-rc2上使用BuildKit,Buildx和一些實驗性功能,因此其中的某些部分不向后兼容,但希望對您有所幫助。 我正在使用一個多階段構建,以避免在docker之外進行編譯,然后再進行第二個構建。 我還為構建主機指定了平台,並使用目標arch和OS變量來配置go進行交叉編譯。 在這種情況下,我使用了完全靜態鏈接的二進制文件,因此沒有要包含的庫,並且在最終發行映像中僅使用了復制命令,避免了在其他平台上運行構建時出現的問題。

# 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=. .

這會將工件階段(單個二進制文件)的內容輸出到本地目錄。


以上是Docker的構建多體系結構映像的方法列表中的選項3。

選項1是使用binfmt_misc配置qemu以構建和運行用於不同平台的映像。 我尚未能夠在Buildx的Linux上使用它,但是Docker在Mac上已經做到了,您可能可以找到有關LinuxKit項目中它們所做的更多詳細信息。 如果您需要在構建過程中運行命令並安裝其他工具,而不僅僅是交叉編譯單個靜態鏈接的二進制文件,那么使用qemu可能是理想的選擇。

選項2是在目標平台上運行構建,如您所見,它運行良好。 使用Buildx ,您可以添加多個構建節點,每個平台最多可以添加一個,從而可以從單個位置(CI服務器)運行構建。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM