libgdiplus icon indicating copy to clipboard operation
libgdiplus copied to clipboard

the functionality broken after install the latest version in alpine

Open lzw5399 opened this issue 4 years ago • 9 comments

dockerfile

FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine3.7 AS base
WORKDIR /docker
EXPOSE 5000
RUN echo 'http://mirrors.aliyun.com/alpine/v3.7/main' > /etc/apk/repositories && \
	echo 'http://mirrors.aliyun.com/alpine/v3.7/community' >> /etc/apk/repositories && \
	echo 'http://mirrors.aliyun.com/alpine/edge/testing' >> /etc/apk/repositories && \
	sed -i "s@\(.*http://\)[^/]*\(.*\)@\1mirror.sy\2@" /etc/apk/repositories && apk update && apk add tzdata terminus-font libgdiplus && \
	ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
	echo "Asia/Shanghai" > /etc/timezone

there is a captcha functionality that depend the System.Drawing,the functionality works well before but it was broken recently, the error message

System.TypeInitializationException: 'The type initializer for 'Gdip' threw an exception.'

I found there is a new release of libgdiplus(libgdiplus-6.0.5-r1) at 2021-Jan-24 04:13:39.

And the early container that build from same dockerfile and install the libgdiplus-6.0.5-r0 works well

Is this package have any incompatible in alpine?

Regards.

lzw5399 avatar Feb 19 '21 12:02 lzw5399

public Bitmap CreateBitmapByImgVerifyCode(string verifyCode, int width, int height)
{
    Random random = new Random(); 
    Bitmap bitmap = new Bitmap(width, height);
    Graphics g = Graphics.FromImage(bitmap);

    using (var font = new Font("Arial", 36, (FontStyle.Bold | FontStyle.Italic)))
    {
        SizeF totalSizeF = g.MeasureString(verifyCode, font);
        SizeF curCharSizeF;
        PointF startPointF = new PointF(((width - totalSizeF.Width) / verifyCode.Length) / 2, (height - totalSizeF.Height) / 2);
        g.Clear(Color.White); 
        for (int i = 0; i < verifyCode.Length; i++)
        {
            using (var brush = new LinearGradientBrush(new Point(0, 0), new Point(1, 1), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255))))
            {

                g.DrawString(verifyCode[i].ToString(), font, brush, startPointF);
                curCharSizeF = g.MeasureString(verifyCode[i].ToString(), font);
                startPointF.X += width / verifyCode.Length;
            }
        }
    }

    using (var pen = new Pen(Color.FromArgb(random.Next()), 5))
    {
        for (int i = 0; i < 20; i++)
        {
            int x1 = random.Next(bitmap.Width);
            int x2 = random.Next(bitmap.Width);
            int y1 = random.Next(bitmap.Height);
            int y2 = random.Next(bitmap.Height);
            g.DrawLine(pen, x1, y1, x2, y2);
        }
    }

    for (int i = 0; i < 100; i++)
    {
        int x = random.Next(bitmap.Width);
        int y = random.Next(bitmap.Height);
        bitmap.SetPixel(x, y, Color.FromArgb(random.Next()));
    }

    using (var pen = new Pen(Color.Silver))
    {
        g.DrawRectangle(pen, 0, 0, bitmap.Width - 1, bitmap.Height - 1); 
        g.Dispose();
        return bitmap;
    }
}

public byte[] CreateByteByImgVerifyCode(string verifyCode, int width, int height)
{
    using (var bitmap = CreateBitmapByImgVerifyCode(verifyCode, width, height))
    {
        using (var stream = new MemoryStream())
        {
            bitmap.Save(stream, ImageFormat.Jpeg);
            return stream.ToArray();
        }
    }
}

lzw5399 avatar Feb 19 '21 13:02 lzw5399

That's odd. What output do you get if you export LD_DEBUG=libs (or ENV LD_DEBUG=libs in your Dockerfile) and then launch your .NET app?

qmfrederik avatar Feb 19 '21 13:02 qmfrederik

After added ENV LD_DEBUG=libs in dockerfile

The pod log doesn't output the library paths that trying file both in the application just started and the captcha API was invoked. It's weird, am I not use it correctly? FYI, Below is the error screenshot

lzw5399 avatar Feb 19 '21 14:02 lzw5399

Are you using a multi-stage build by any chance? Make sure you set ENV LD_DEBUG=libs in your final container, and not just a build container (which is later thrown away).

You should probably also be able to set the LD_DEBUG environment variable via the --env command line argument if you launch the container with docker run, or in the env: field for your container if you use Kubernetes.

qmfrederik avatar Feb 19 '21 14:02 qmfrederik

yeap. the base container is also the final container.

and run kubectl exec enter the current kubernetes pod, run echo $LD_DEBUG, output lib correctly

lzw5399 avatar Feb 19 '21 14:02 lzw5399

Ah yes, my bad. LD_DEBUG is a glbc-thing, and Alpine runs on musl. I'd recommend you use another mechanism, like strace, to see what's going on.

It's not my area of expertise so I can't help much, but this article seems like it might be useful: https://jvns.ca/blog/2014/03/10/debugging-shared-library-problems-with-strace/

qmfrederik avatar Feb 19 '21 15:02 qmfrederik

After comparing the two container which one is r1 and another r0. Found some clues

run ldd /usr/lib/libgdiplus.so.0 to trace the denpendency in r1 container, I found there is some error

same command in r0 container

And I copy the libgdiplus.so.0.0.0 file which the gdi package real link to from the r0 container to r1 container. then the captcha functionality recovered

lzw5399 avatar Feb 20 '21 03:02 lzw5399

Hi, I've also started having the same issue recently, it started happening around a month ago.

Using .NET Core 3.1 Alpine (FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine) and libgdiplus-dev apk package from dl-3.alpinelinux.org/alpine/edge/testing. The containers are hosted by AWS Fargate (1.4.0), the error seems to be consistent and spinning up new containers doesn't help. At the same time I was unable to reproduce the issue in plain docker on my MacOS. :(

ldd gives pretty much the similar output as above:

/lib/ld-musl-x86_64.so.1 (0x7f862cc18000) libpangocairo-1.0.so.0 => /usr/lib/libpangocairo-1.0.so.0 (0x7f862cb95000) libpango-1.0.so.0 => /usr/lib/libpango-1.0.so.0 (0x7f862cb57000) libgobject-2.0.so.0 => /usr/lib/libgobject-2.0.so.0 (0x7f862cb0d000) libglib-2.0.so.0 => /usr/lib/libglib-2.0.so.0 (0x7f862ca13000) libharfbuzz.so.0 => /usr/lib/libharfbuzz.so.0 (0x7f862c97e000) libcairo.so.2 => /usr/lib/libcairo.so.2 (0x7f862c891000) libjpeg.so.8 => /usr/lib/libjpeg.so.8 (0x7f862c7fb000) libtiff.so.5 => /usr/lib/libtiff.so.5 (0x7f862c791000) libgif.so.7 => /usr/lib/libgif.so.7 (0x7f862c786000) libpng16.so.16 => /usr/lib/libpng16.so.16 (0x7f862c756000) libX11.so.6 => /usr/lib/libX11.so.6 (0x7f862c634000) libexif.so.12 => /usr/lib/libexif.so.12 (0x7f862c5f1000) libfontconfig.so.1 => /usr/lib/libfontconfig.so.1 (0x7f862c5b5000) libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f862cc18000) libpangoft2-1.0.so.0 => /usr/lib/libpangoft2-1.0.so.0 (0x7f862c59e000) libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0x7f862c4ec000) libfribidi.so.0 => /usr/lib/libfribidi.so.0 (0x7f862c4cf000) libffi.so.6 => /usr/lib/libffi.so.6 (0x7f862c4c6000) libpcre.so.1 => /usr/lib/libpcre.so.1 (0x7f862c469000) libintl.so.8 => /usr/lib/libintl.so.8 (0x7f862c459000) libgraphite2.so.3 => /usr/lib/libgraphite2.so.3 (0x7f862c432000) libpixman-1.so.0 => /usr/lib/libpixman-1.so.0 (0x7f862c3a1000) libxcb-shm.so.0 => /usr/lib/libxcb-shm.so.0 (0x7f862c39c000) libxcb.so.1 => /usr/lib/libxcb.so.1 (0x7f862c375000) libxcb-render.so.0 => /usr/lib/libxcb-render.so.0 (0x7f862c366000) libXrender.so.1 => /usr/lib/libXrender.so.1 (0x7f862c35a000) libXext.so.6 => /usr/lib/libXext.so.6 (0x7f862c347000) libz.so.1 => /lib/libz.so.1 (0x7f862c32d000) libexpat.so.1 => /usr/lib/libexpat.so.1 (0x7f862c30b000) libuuid.so.1 => /lib/libuuid.so.1 (0x7f862c302000) libbz2.so.1 => /usr/lib/libbz2.so.1 (0x7f862c2f3000) libXau.so.6 => /usr/lib/libXau.so.6 (0x7f862c2ee000) libXdmcp.so.6 => /usr/lib/libXdmcp.so.6 (0x7f862c2e6000) libbsd.so.0 => /usr/lib/libbsd.so.0 (0x7f862c2d0000) Error relocating /usr/lib/libgdiplus.so.0: hb_ot_metrics_get_position: symbol not found Error relocating /usr/lib/libgdiplus.so.0: pango_font_get_hb_font: symbol not found

The workaround of copying the older libgdiplus.so.0.0.0 file (from libgdiplus-dev, probably 6.0.5 or 6.0.4, whichever was previously on alpine apk feed in late 2020) does help (not sure if it's a legit thing to do and if that could cause any hard-to-trace issues in runtime).

Happy to provide more info if needed.

P.S. Would it be possible to restore previous version of libgdiplus-dev package back to alpine apk feed? If not, is there any way to build & install on (or for) alpine from github sources? (I've tried building on ubuntu, but the binaries it creates are significantly bigger than the ones in alpine feed packages)

darkms avatar Feb 22 '21 18:02 darkms

I ended up building 6.0.5 (r0) using abuild to avoid any accidental binaries incompatibility in future, in case if anyone else needs it, here's how:

FROM alpine:latest AS build_libgdiplus
RUN apk add alpine-sdk
RUN adduser -D abuild_user \
    && addgroup abuild_user abuild
RUN mkdir /build \
    && chmod -R a+rwx /build
USER abuild_user
WORKDIR /build
COPY APKBUILD .
RUN abuild-keygen -a
RUN abuild -r -P /build
USER root
RUN chown -R root:root /build

FROM alpine:latest
COPY --from=build_libgdiplus /build/x86_64/ /libgdiplus-apk
RUN apk add --allow-untrusted /libgdiplus-apk/libgdiplus-6.0.5-r0.apk /libgdiplus-apk/libgdiplus-dev-6.0.5-r0.apk

Note: you'd need to have a file named APKBUILD in the working directory of where you build that dockerfile, the contents can be downloaded from alpine repo: https://gitlab.alpinelinux.org/alpine/aports/-/blob/44b1843ba5ebd29dc4c66b4269cb679bd14fad4b/testing/libgdiplus/APKBUILD (this one is 6.0.5-r0).

darkms avatar Apr 16 '21 11:04 darkms